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    }