package com.sandwich.util.io.classloader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import com.sandwich.util.io.filecompiler.CompilationListener; import com.sandwich.util.io.filecompiler.CompilerConfig; import com.sandwich.util.io.filecompiler.FileCompiler; import com.sandwich.util.io.filecompiler.FileCompilerAction; public abstract class DynamicClassLoader extends ClassLoader { private static Map<URL, Class<?>> classesByLocation = new HashMap<URL, Class<?>>(); private static Map<Class<?>, URL> locationByClass = new HashMap<Class<?>, URL>(); private final long timeout; private final String binDir, sourceDir; private final String[] classPath; public DynamicClassLoader(String binDir, String sourceDir, String[] classPath){ this(binDir, sourceDir, classPath, ClassLoader.getSystemClassLoader()); } public DynamicClassLoader(String binDir, String sourceDir, String[] classPath, ClassLoader parent) { this(binDir, sourceDir, classPath, parent, 5000l); } public DynamicClassLoader(String binDir, String sourceDir, String[] classPath, ClassLoader parent, long timeout) { super(parent); this.binDir = binDir; this.sourceDir = sourceDir; this.classPath = classPath; this.timeout = timeout; } public abstract boolean isFileModifiedSinceLastPoll(String sourcePath, long lastModified); public abstract void updateFileSavedTime(File sourceFile); public static void remove(URL url){ String urlToString = url.toString().replace(FileCompiler.CLASS_SUFFIX, ""); for(String suffix : CompilerConfig.getSupportedFileSuffixes()){ urlToString.replace(suffix, ""); } for(Entry<URL, Class<?>> entry : classesByLocation.entrySet()){ if(entry.getKey().toString().contains(urlToString)){ locationByClass.remove(entry.getValue()); entry.setValue(null); } } } public static void remove(Class<?> clas){ for(Entry<Class<?>, URL> entry : locationByClass.entrySet()){ if(entry.getKey().getName().contains(clas.getName())){ classesByLocation.remove(entry.getValue()); entry.setValue(null); } } } public Class<?> loadClass(String className){ return loadClass(className, FileCompilerAction.LOGGING_HANDLER); } public Class<?> loadClass(String className, CompilationListener listener){ String fileSeparator = System.getProperty("file.separator"); String fileName = binDir + fileSeparator + className.replace(".", fileSeparator) + FileCompiler.CLASS_SUFFIX; File classFile = new File(fileName); try { // file may have never been compiled, go ahead and compile it now File sourceFile = FileCompiler.classToSource(binDir, sourceDir, classFile); if(classFile.exists()){ String absolutePath = classFile.getAbsolutePath(); boolean isAnonymous = absolutePath.contains("$"); if(isFileModifiedSinceLastPoll(sourceFile.getAbsolutePath(), sourceFile.lastModified())){ if(!isAnonymous){ compile(fileName, sourceFile, timeout, listener); } } return loadClass(classFile.toURI().toURL(), className); } try{ return super.loadClass(className); }catch(ClassNotFoundException x){ compile(fileName, sourceFile, timeout, listener); classFile = new File(fileName); return loadClass(classFile.toURI().toURL(), className); } } catch (Exception e) { throw new RuntimeException(e); } } private void compile(String fileName, File sourceFile, long timeout, CompilationListener listener) throws IOException { FileCompiler.compile(sourceFile, new File(binDir), listener, timeout, classPath); updateFileSavedTime(sourceFile); } public Class<?> loadClass(URL url, String className){ Class<?> clazz = classesByLocation.get(url); if(clazz != null){ return clazz; } ByteArrayOutputStream buffer = new ByteArrayOutputStream(); try { URLConnection connection = url.openConnection(); InputStream input = connection.getInputStream(); int data = input.read(); while(data != -1){ buffer.write(data); data = input.read(); } input.close(); } catch (IOException e) { e.printStackTrace(); } byte[] classData = buffer.toByteArray(); clazz = defineClass(className, classData, 0, classData.length); classesByLocation.put(url, clazz); locationByClass.put(clazz, url); return clazz; } public long getTimeout() { return timeout; } public String getBinDir() { return binDir; } public String getSourceDir() { return sourceDir; } public String[] getClassPath() { return classPath; } }