📄 dynacode.java
字号:
package com.testDataBuilder.dynamicCompile;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import org.apache.log4j.Logger;
import com.testDataBuilder.exception.BaseException;
import com.testDataBuilder.resources.RM;
public final class DynaCode {
static Logger logger = Logger.getLogger(DynaCode.class);
private String compileClasspath;
private ClassLoader parentClassLoader;
private ArrayList sourceDirs = new ArrayList();
private static String baseDir = System.getProperty("user.dir") + "\\dynacode";
// class name => LoadedClass
private HashMap loadedClasses = new HashMap();
public DynaCode() {
this(Thread.currentThread().getContextClassLoader());
}
public DynaCode(ClassLoader parentClassLoader) {
this(extractClasspath(parentClassLoader), parentClassLoader);
}
/**
* @param compileClasspath
* used to compile dynamic classes
* @param parentClassLoader
* the parent of the class loader that loads all the dynamic
* classes
*/
public DynaCode(String compileClasspath, ClassLoader parentClassLoader) {
this.compileClasspath = compileClasspath;
this.parentClassLoader = parentClassLoader;
}
/**
* Add a directory that contains the source of dynamic java code.
*
* @param srcDir
* @return true if the add is successful
*/
public boolean addSourceDir(File srcDir) {
try {
srcDir = srcDir.getCanonicalFile();
} catch (IOException e) {
// ignore
}
synchronized (sourceDirs) {
// check existence
for (int i = 0; i < sourceDirs.size(); i++) {
SourceDir src = (SourceDir) sourceDirs.get(i);
if (src.srcDir.equals(srcDir)) {
return false;
}
}
// add new
SourceDir src = new SourceDir(srcDir);
sourceDirs.add(src);
debug("Add source dir " + srcDir);
}
return true;
}
/**
* Returns the up-to-date dynamic class by name.
*
* @param className
* @return
* @throws ClassNotFoundException
* if source file not found or compilation error
* @throws BaseException
*/
public Class loadClass(String className) throws ClassNotFoundException, BaseException {
LoadedClass loadedClass = null;
synchronized (loadedClasses) {
loadedClass = (LoadedClass) loadedClasses.get(className);
}
// first access of a class
if (loadedClass == null) {
String resource = className.replace('.', '/') + ".java";
SourceDir src = locateResource(resource);
if (src == null) {
throw new ClassNotFoundException("DynaCode class not found "
+ className);
}
synchronized (this) {
// compile and load class
loadedClass = new LoadedClass(className, src);
synchronized (loadedClasses) {
loadedClasses.put(className, loadedClass);
}
}
return loadedClass.clazz;
}
// subsequent access
if (loadedClass.isChanged()) {
// unload and load again
unload(loadedClass.srcDir);
return loadClass(className);
}
return loadedClass.clazz;
}
private SourceDir locateResource(String resource) {
for (int i = 0; i < sourceDirs.size(); i++) {
SourceDir src = (SourceDir) sourceDirs.get(i);
if (new File(src.srcDir, resource).exists()) {
return src;
}
}
return null;
}
private void unload(SourceDir src) {
// clear loaded classes
synchronized (loadedClasses) {
for (Iterator iter = loadedClasses.values().iterator(); iter
.hasNext();) {
LoadedClass loadedClass = (LoadedClass) iter.next();
if (loadedClass.srcDir == src) {
iter.remove();
}
}
}
// create new class loader
src.recreateClassLoader();
}
/**
* Get a resource from added source directories.
*
* @param resource
* @return the resource URL, or null if resource not found
*/
public URL getResource(String resource) {
try {
SourceDir src = locateResource(resource);
return src == null ? null : new File(src.srcDir, resource).toURL();
} catch (MalformedURLException e) {
// should not happen
return null;
}
}
/**
* Get a resource stream from added source directories.
*
* @param resource
* @return the resource stream, or null if resource not found
*/
public InputStream getResourceAsStream(String resource) {
try {
SourceDir src = locateResource(resource);
return src == null ? null : new FileInputStream(new File(
src.srcDir, resource));
} catch (FileNotFoundException e) {
// should not happen
return null;
}
}
/**
* Create a proxy instance that implements the specified access interface
* and delegates incoming invocations to the specified dynamic
* implementation. The dynamic implementation may change at run-time, and
* the proxy will always delegates to the up-to-date implementation.
*
* @param interfaceClass
* the access interface
* @param implClassName
* the backend dynamic implementation
* @return
* @throws RuntimeException
* if an instance cannot be created, because of class not found
* for example
* @throws BaseException
*/
public Object newProxyInstance(Class interfaceClass, String implClassName)
throws RuntimeException, BaseException {
MyInvocationHandler handler = new MyInvocationHandler(
implClassName);
return Proxy.newProxyInstance(interfaceClass.getClassLoader(),
new Class[] { interfaceClass }, handler);
}
private class SourceDir {
File srcDir;
File binDir ;
Javac javac;
URLClassLoader classLoader;
SourceDir(File srcDir) {
this.srcDir = srcDir;
// String subdir = srcDir.getAbsolutePath().replace(':', '_').replace(
// '/', '_').replace('\\', '_');
this.binDir = new File(DynaCode.getBaseDir());
this.binDir.mkdirs();
// prepare compiler
this.javac = new Javac(compileClasspath, binDir.getAbsolutePath());
// class loader
recreateClassLoader();
}
void recreateClassLoader() {
try {
classLoader = new URLClassLoader(new URL[] { binDir.toURL() },
parentClassLoader);
} catch (MalformedURLException e) {
// should not happen
}
}
}
private static class LoadedClass {
String className;
SourceDir srcDir;
File srcFile;
File binFile;
Class clazz;
long lastModified;
LoadedClass(String className, SourceDir src) throws BaseException {
this.className = className;
this.srcDir = src;
String path = className.replace('.', '/');
this.srcFile = new File(src.srcDir, path + ".java");
this.binFile = new File(src.binDir, path + ".class");
compileAndLoadClass();
}
boolean isChanged() {
return srcFile.lastModified() != lastModified;
}
void compileAndLoadClass() throws BaseException {
if (clazz != null) {
return; // class already loaded
}
// compile, if required
String error = null;
if (binFile.lastModified() < srcFile.lastModified()) {
error = srcDir.javac.compile(new File[] { srcFile });
}
if (error != null) {
throw new BaseException(String.format(RM.R("label.RoleDialog.error.compileError"),
new String[]{srcFile.getName(),error}));
// System.out.println("Failed to compile "
// + srcFile.getAbsolutePath() + ". Error: " + error);
// return ;
}
try {
// load class
clazz = srcDir.classLoader.loadClass(className);
// load class success, remember timestamp
lastModified = srcFile.lastModified();
} catch (ClassNotFoundException e) {
throw new RuntimeException("Failed to load DynaCode class "
+ srcFile.getAbsolutePath());
}
debug("Init " + clazz);
}
}
private class MyInvocationHandler implements InvocationHandler {
String backendClassName;
Object backend;
MyInvocationHandler(String className) throws BaseException {
backendClassName = className;
try {
Class clz = loadClass(backendClassName);
backend = newDynaCodeInstance(clz);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// check if class has been updated
Class clz = loadClass(backendClassName);
if (backend.getClass() != clz) {
backend = newDynaCodeInstance(clz);
}
try {
// invoke on backend
return method.invoke(backend, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
private Object newDynaCodeInstance(Class clz) {
try {
return clz.newInstance();
} catch (Exception e) {
throw new RuntimeException(
"Failed to new instance of DynaCode class "
+ clz.getName(), e);
}
}
}
/**
* Extracts a classpath string from a given class loader. Recognizes only
* URLClassLoader.
*/
private static String extractClasspath(ClassLoader cl) {
StringBuffer buf = new StringBuffer();
while (cl != null) {
if (cl instanceof URLClassLoader) {
URL urls[] = ((URLClassLoader) cl).getURLs();
for (int i = 0; i < urls.length; i++) {
if (buf.length() > 0) {
buf.append(File.pathSeparatorChar);
}
buf.append(urls[i].getFile().toString());
}
}
cl = cl.getParent();
}
return buf.toString();
}
/**
* Log a message.
*/
private static void debug(String msg) {
logger.debug(msg);
}
public static String getBaseDir() {
return baseDir;
}
public static void setBaseDir(String baseDir) {
DynaCode.baseDir = baseDir;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -