001 /* 002 * Copyright (C) 2012 eXo Platform SAS. 003 * 004 * This is free software; you can redistribute it and/or modify it 005 * under the terms of the GNU Lesser General Public License as 006 * published by the Free Software Foundation; either version 2.1 of 007 * the License, or (at your option) any later version. 008 * 009 * This software is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * You should have received a copy of the GNU Lesser General Public 015 * License along with this software; if not, write to the Free 016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 018 */ 019 020 package org.crsh.shell.impl.command; 021 022 import groovy.lang.GroovyClassLoader; 023 import groovy.lang.GroovyCodeSource; 024 import groovy.lang.Script; 025 import org.codehaus.groovy.control.CompilationFailedException; 026 import org.codehaus.groovy.control.CompilerConfiguration; 027 import org.crsh.command.CommandInvoker; 028 import org.crsh.command.GroovyScriptCommand; 029 import org.crsh.command.NoSuchCommandException; 030 import org.crsh.plugin.PluginContext; 031 import org.crsh.plugin.ResourceKind; 032 import org.crsh.shell.ErrorType; 033 import org.crsh.util.TimestampedObject; 034 import org.crsh.vfs.Resource; 035 036 import java.io.UnsupportedEncodingException; 037 import java.util.Map; 038 import java.util.concurrent.ConcurrentHashMap; 039 040 class ClassManager<T> { 041 042 /** . */ 043 private final Map<String, TimestampedObject<Class<? extends T>>> classes = new ConcurrentHashMap<String, TimestampedObject<Class<? extends T>>>(); 044 045 /** . */ 046 private final PluginContext context; 047 048 /** . */ 049 private final Class<? extends Script> baseScriptClass; 050 051 /** . */ 052 private final CompilerConfiguration config; 053 054 /** . */ 055 private final Class<T> baseClass; 056 057 /** . */ 058 private final ResourceKind kind; 059 060 ClassManager(PluginContext context, ResourceKind kind, Class<T> baseClass, Class<? extends Script> baseScriptClass) { 061 CompilerConfiguration config = new CompilerConfiguration(); 062 config.setRecompileGroovySource(true); 063 config.setScriptBaseClass(GroovyScriptCommand.class.getName()); 064 065 // 066 this.context = context; 067 this.baseScriptClass = baseScriptClass; 068 this.config = config; 069 this.baseClass = baseClass; 070 this.kind = kind; 071 } 072 073 Class<? extends T> getClass(String name) throws NoSuchCommandException, NullPointerException { 074 if (name == null) { 075 throw new NullPointerException("No null argument allowed"); 076 } 077 078 TimestampedObject<Class<? extends T>> providerRef = classes.get(name); 079 080 // 081 Resource script = context.loadResource(name, kind); 082 083 // 084 if (script != null) { 085 if (providerRef != null) { 086 if (script.getTimestamp() != providerRef.getTimestamp()) { 087 providerRef = null; 088 } 089 } 090 091 // 092 if (providerRef == null) { 093 094 // 095 String source; 096 try { 097 source = new String(script.getContent(), "UTF-8"); 098 } 099 catch (UnsupportedEncodingException e) { 100 throw new NoSuchCommandException(name, ErrorType.INTERNAL, "Could not compile command script " + name, e); 101 } 102 103 // 104 Class<?> clazz; 105 try { 106 GroovyCodeSource gcs = new GroovyCodeSource(source, name, "/groovy/shell"); 107 GroovyClassLoader gcl = new GroovyClassLoader(context.getLoader(), config); 108 clazz = gcl.parseClass(gcs, false); 109 } 110 catch (NoClassDefFoundError e) { 111 throw new NoSuchCommandException(name, ErrorType.INTERNAL, "Could not compile command script " + name, e); 112 } 113 catch (CompilationFailedException e) { 114 throw new NoSuchCommandException(name, ErrorType.INTERNAL, "Could not compile command script " + name, e); 115 } 116 117 // 118 if (baseClass.isAssignableFrom(clazz)) { 119 Class<? extends T> providerClass = clazz.asSubclass(baseClass); 120 providerRef = new TimestampedObject<Class<? extends T>>(script.getTimestamp(), providerClass); 121 classes.put(name, providerRef); 122 } else { 123 throw new NoSuchCommandException(name, ErrorType.INTERNAL, "Parsed script " + clazz.getName() + 124 " does not implements " + CommandInvoker.class.getName()); 125 } 126 } 127 } 128 129 // 130 if (providerRef == null) { 131 return null; 132 } 133 134 // 135 return providerRef.getObject(); 136 } 137 138 T getInstance(String name) throws NoSuchCommandException, NullPointerException { 139 Class<? extends T> clazz = getClass(name); 140 if (clazz == null) { 141 return null; 142 } 143 144 // 145 try { 146 return clazz.newInstance(); 147 } 148 catch (Exception e) { 149 throw new NoSuchCommandException(name, ErrorType.INTERNAL, "Could not create command " + name + " instance", e); 150 } 151 } 152 }