package er.profiling.classloader; import java.lang.instrument.ClassFileTransformer; import java.lang.reflect.Constructor; import java.net.URL; import java.net.URLClassLoader; import sun.misc.Resource; import sun.misc.URLClassPath; /** * The WeavingClassLoader is a custom URLClassLoader that implements the * same functionality as the -javaagent transformation feature, passing all * classes through the ClassTransformer prior to definition. * * When this classloader is enabled as a "root loader" and not a conventional hierarchical * classloader it is important to use caution as it may not work as expected in some cases. * * @author q * */ public class WeavingClassLoader extends URLClassLoader { public static final String ROOT_LOADER_KEY = "gluonj.WeavingClassLoader.isRootLoader"; public static final String GLUE_NAME_KEY = "gluonj.GlueName"; private String glueName; private ClassFileTransformer transformer; { glueName = System.getProperty(GLUE_NAME_KEY); if (glueName == null) { System.err.println("GlueName is not defined. Transforming is disabled"); } } public WeavingClassLoader(URL[] urls, ClassLoader parent) { super(urls, parent); if (glueName != null) { try { @SuppressWarnings("unchecked") Class<ClassFileTransformer> clazz = (Class<ClassFileTransformer>) findClass(ClassTransformer.class.getName()); Constructor<ClassFileTransformer> c = clazz.getConstructor(new Class<?>[] { String.class }); transformer = c.newInstance(glueName); } catch (Exception e) { System.err.println("ClassTransformer could not be initialized. Transforming is disabled"); e.printStackTrace(System.err); } } } @Override protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { Class<?> clazz = findLoadedClass(name); if (clazz != null) return clazz; if (isNonTransformable(name)) { return super.loadClass(name, resolve); } if (isRootLoader()) { try { Class<?> c = findClass(name); if (resolve) { resolveClass(c); } return c; } catch (ClassNotFoundException e) { } } return super.loadClass(name, resolve); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { Class<?> clazz = findLoadedClass(name); if (clazz != null) return clazz; if (isNonTransformable(name)) { return super.findClass(name); } String path = name.replace('.', '/').concat(".class"); Resource res = new URLClassPath(getURLs()).getResource(path, false); if (res != null) { try { byte[] b = res.getBytes(); byte[] transformed = transformer.transform(this, name, null, null, b); if (transformed == null) transformed = b; return defineClass(name, transformed, 0, transformed.length); } catch (Exception e) { return super.findClass(name); } } return super.findClass(name); } /** * Determines if a class can be transformed based on the name. In most cases * as class is non transformable because it is part of the JRE and loaded by * bootstrap classloader. * * @param className * @return whether the class is transformable. */ protected boolean isNonTransformable(String className) { return transformer == null || className.startsWith("java.") || className.startsWith("javax.") || (className.startsWith("com.sun.") && !className .startsWith("com.sun.script.")) || className.startsWith("sun.") || className.startsWith("sunw.") || className.startsWith("javassist.") || className.startsWith("org.xml.sax.") || className.equals(glueName); } protected boolean isRootLoader() { return Boolean.parseBoolean(System.getProperty(ROOT_LOADER_KEY, "false")); } }