package er.profiling.classloader;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.HashMap;
import java.util.Map;
import javassist.ClassPool;
import javassist.LoaderClassPath;
import javassist.gluonj.weave.Weaver;
/**
* ClassTransformer implements ClassFileTransformer and operates in a factory
* like capacity to ensure that each classloader has its own transformer with an
* appropriate classpool to ensure while transforming that classes are resolved
* using the correct classpath.
*
* @author q
* Some portions of this code are copied from GluonJ's HotSwapper class
*/
public class ClassTransformer implements ClassFileTransformer {
private final String glueName;
private Map<ClassLoader, ClassPool> knownClassLoaders = new HashMap<>();
private Map<ClassLoader, ClassFileTransformer> transformers = new HashMap<>();
public ClassTransformer(String glueName) {
this.glueName = glueName;
}
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer)
throws IllegalClassFormatException {
if (!knownClassLoaders.containsKey(loader)) {
if (loader.getParent() == null) {
transformers.put(loader, createTransformer(loader, null));
} else {
ClassPool parentPool = knownClassLoaders.get(loader.getParent());
ClassPool pool = new ClassPool(parentPool);
knownClassLoaders.put(loader, pool);
transformers.put(loader, createTransformer(loader, pool));
}
}
return transformers.get(loader).transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer);
}
private ClassFileTransformer createTransformer(ClassLoader loader, ClassPool pool) {
pool.appendClassPath(new LoaderClassPath(loader));
pool.childFirstLookup = true;
return new WeavingClassTransformer(glueName, pool);
}
/**
* The WeavingClassTransformer does the real work of transforming a class. It
* maintains its own weaver and classpool for resolving classes with the correct
* classpath.
*
* @author q
*
*/
private static class WeavingClassTransformer implements ClassFileTransformer {
private String glueName;
private Weaver weaver;
private boolean stop;
private ClassPool classPool;
public WeavingClassTransformer(String glue, ClassPool cp) {
glueName = glue;
weaver = null;
stop = false;
classPool = cp;
}
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain domain, byte[] classfile)
throws IllegalClassFormatException {
if (stop)
return null;
if (weaver == null) {
try {
if (classPool != null) {
weaver = new Weaver(glueName, classPool, loader);
} else {
weaver = new Weaver(glueName, loader, true);
}
} catch (Throwable t) {
t.printStackTrace();
stop = true;
showError("cannot read a glue: " + glueName, t);
return null;
}
}
try {
return weaver.transform(className, classfile);
} catch (Throwable t) {
String msg = "cannot transform a class: " + className.replace('/', '.');
showError(msg, t);
return null;
}
}
private void showError(String msg, Throwable e) {
System.err.println("GluonJ Error: " + msg);
System.err.println(" by " + e);
e.printStackTrace(System.err);
}
}
}