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 }