001 package org.crsh.shell.impl;
002
003 import groovy.lang.GroovyClassLoader;
004 import groovy.lang.GroovyCodeSource;
005 import groovy.lang.Script;
006 import org.codehaus.groovy.control.CompilationFailedException;
007 import org.codehaus.groovy.control.CompilerConfiguration;
008 import org.crsh.command.CommandInvoker;
009 import org.crsh.command.GroovyScriptCommand;
010 import org.crsh.command.NoSuchCommandException;
011 import org.crsh.plugin.PluginContext;
012 import org.crsh.plugin.ResourceKind;
013 import org.crsh.shell.ErrorType;
014 import org.crsh.util.TimestampedObject;
015 import org.crsh.vfs.Resource;
016
017 import java.util.Map;
018 import java.util.concurrent.ConcurrentHashMap;
019
020 /** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */
021 class ClassManager<T> {
022
023 /** . */
024 private final Map<String, TimestampedObject<Class<? extends T>>> classes = new ConcurrentHashMap<String, TimestampedObject<Class<? extends T>>>();
025
026 /** . */
027 private final PluginContext context;
028
029 /** . */
030 private final Class<? extends Script> baseScriptClass;
031
032 /** . */
033 private final CompilerConfiguration config;
034
035 /** . */
036 private final Class<T> baseClass;
037
038 /** . */
039 private final ResourceKind kind;
040
041 ClassManager(PluginContext context, ResourceKind kind, Class<T> baseClass, Class<? extends Script> baseScriptClass) {
042 CompilerConfiguration config = new CompilerConfiguration();
043 config.setRecompileGroovySource(true);
044 config.setScriptBaseClass(GroovyScriptCommand.class.getName());
045
046 //
047 this.context = context;
048 this.baseScriptClass = baseScriptClass;
049 this.config = config;
050 this.baseClass = baseClass;
051 this.kind = kind;
052 }
053
054 Class<? extends T> getClass(String name) throws NoSuchCommandException, NullPointerException {
055 if (name == null) {
056 throw new NullPointerException("No null argument allowed");
057 }
058
059 TimestampedObject<Class<? extends T>> providerRef = classes.get(name);
060
061 //
062 Resource script = context.loadResource(name, kind);
063
064 //
065 if (script != null) {
066 if (providerRef != null) {
067 if (script.getTimestamp() != providerRef.getTimestamp()) {
068 providerRef = null;
069 }
070 }
071
072 //
073 if (providerRef == null) {
074
075 Class<?> clazz;
076 try {
077 GroovyCodeSource gcs = new GroovyCodeSource(script.getContent(), name, "/groovy/shell");
078 GroovyClassLoader gcl = new GroovyClassLoader(context.getLoader(), config);
079 clazz = gcl.parseClass(gcs, false);
080 }
081 catch (CompilationFailedException e) {
082 throw new NoSuchCommandException(name, ErrorType.INTERNAL, "Could not compile command script " + name, e);
083 }
084
085 //
086 if (baseClass.isAssignableFrom(clazz)) {
087 Class<? extends T> providerClass = clazz.asSubclass(baseClass);
088 providerRef = new TimestampedObject<Class<? extends T>>(script.getTimestamp(), providerClass);
089 classes.put(name, providerRef);
090 } else {
091 throw new NoSuchCommandException(name, ErrorType.INTERNAL, "Parsed script " + clazz.getName() +
092 " does not implements " + CommandInvoker.class.getName());
093 }
094 }
095 }
096
097 //
098 if (providerRef == null) {
099 return null;
100 }
101
102 //
103 return providerRef.getObject();
104 }
105
106 T getInstance(String name) throws NoSuchCommandException, NullPointerException {
107 Class<? extends T> clazz = getClass(name);
108 if (clazz == null) {
109 return null;
110 }
111
112 //
113 try {
114 return clazz.newInstance();
115 }
116 catch (Exception e) {
117 throw new NoSuchCommandException(name, ErrorType.INTERNAL, "Could not create command " + name + " instance", e);
118 }
119 }
120 }