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