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