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.NoSuchCommandException; 029 import org.crsh.plugin.PluginContext; 030 import org.crsh.shell.ErrorType; 031 import org.crsh.util.TimestampedObject; 032 import org.crsh.vfs.Resource; 033 034 import java.io.UnsupportedEncodingException; 035 036 public abstract class AbstractClassManager<T> { 037 038 /** . */ 039 private final PluginContext context; 040 041 /** . */ 042 private final CompilerConfiguration config; 043 044 /** . */ 045 private final Class<T> baseClass; 046 047 protected AbstractClassManager(PluginContext context, Class<T> baseClass, Class<? extends Script> baseScriptClass) { 048 CompilerConfiguration config = new CompilerConfiguration(); 049 config.setRecompileGroovySource(true); 050 config.setScriptBaseClass(baseScriptClass.getName()); 051 052 // 053 this.context = context; 054 this.config = config; 055 this.baseClass = baseClass; 056 } 057 058 protected abstract TimestampedObject<Class<? extends T>> loadClass(String name); 059 060 protected abstract void saveClass(String name, TimestampedObject<Class<? extends T>> clazz); 061 062 protected abstract Resource getResource(String name); 063 064 Class<? extends T> getClass(String name) throws NoSuchCommandException, NullPointerException { 065 if (name == null) { 066 throw new NullPointerException("No null argument allowed"); 067 } 068 069 TimestampedObject<Class<? extends T>> providerRef = loadClass(name); 070 071 // 072 Resource script = getResource(name); 073 074 // 075 if (script != null) { 076 if (providerRef != null) { 077 if (script.getTimestamp() != providerRef.getTimestamp()) { 078 providerRef = null; 079 } 080 } 081 082 // 083 if (providerRef == null) { 084 085 // 086 String source; 087 try { 088 source = new String(script.getContent(), "UTF-8"); 089 } 090 catch (UnsupportedEncodingException e) { 091 throw new NoSuchCommandException(name, ErrorType.INTERNAL, "Could not compile command script " + name, e); 092 } 093 094 // 095 Class<?> clazz; 096 try { 097 GroovyCodeSource gcs = new GroovyCodeSource(source, name, "/groovy/shell"); 098 GroovyClassLoader gcl = new GroovyClassLoader(context.getLoader(), config); 099 clazz = gcl.parseClass(gcs, false); 100 } 101 catch (NoClassDefFoundError e) { 102 throw new NoSuchCommandException(name, ErrorType.INTERNAL, "Could not compile command script " + name, e); 103 } 104 catch (CompilationFailedException e) { 105 throw new NoSuchCommandException(name, ErrorType.INTERNAL, "Could not compile command script " + name, e); 106 } 107 108 // 109 if (baseClass.isAssignableFrom(clazz)) { 110 Class<? extends T> providerClass = clazz.asSubclass(baseClass); 111 providerRef = new TimestampedObject<Class<? extends T>>(script.getTimestamp(), providerClass); 112 saveClass(name, providerRef); 113 } else { 114 throw new NoSuchCommandException(name, ErrorType.INTERNAL, "Parsed script " + clazz.getName() + 115 " does not implements " + CommandInvoker.class.getName()); 116 } 117 } 118 } 119 120 // 121 if (providerRef == null) { 122 return null; 123 } 124 125 // 126 return providerRef.getObject(); 127 } 128 129 T getInstance(String name) throws NoSuchCommandException, NullPointerException { 130 Class<? extends T> clazz = getClass(name); 131 if (clazz == null) { 132 return null; 133 } 134 135 // 136 try { 137 return clazz.newInstance(); 138 } 139 catch (Exception e) { 140 throw new NoSuchCommandException(name, ErrorType.INTERNAL, "Could not create command " + name + " instance", e); 141 } 142 } 143 }