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 }