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    }