Tuesday, January 30, 2018

load a java class with a different parent classloader

in the process of debugging issues with an incorrect classpath for the compiler api when run inside maven, i wanted to be able to create a duplicate instance of an existing object (that had been produced by a factory) by loading the class using a custom classloader with a different parent. it didn't fix the classpath problem, so i also tried loading directly by the path that i found, to no greater success my solution is somewhere between kludgy and elegant, so i wanted to publish it in case some other sucker goes down a similar road less traveled (and it doesn't deserve it's own repository), so here it is: public static void demoJailbreak() throws Exception { ClassLoader parent = Thread.currentThread().getContextClassLoader(); String jre = System.getProperty("java.home"); String jdk = jre.endsWith("/jre") ? jre.substring(0,jre.length()-3) : jre; JavaCompiler broken, byPath=null, compiler; try { byPath = (JavaCompiler) loadByPath( "com.sun.tools.javac.api.JavacTool", "file:" + jdk + "lib/tools.jar", parent ); } catch (Exception ex) {} compiler = ToolProvider.getSystemJavaCompiler(); broken = (JavaCompiler) jailbreak(compiler.getClass(),parent); System.out.println("jailbreak : " + broken); System.out.println("loadByPath: " + byPath); } /** * create a new instance of a class * using a new url classloader with a specified parent * and the same resource path as the original class instance */ public static Object jailbreak(Class klass,ClassLoader parent) throws Exception { String cname = klass.getName(); String path = getPathForClass(cname,klass.getClassLoader()); Object dup = loadByPath(cname,path,parent); return dup; } /** return the path at which an original classloader finds a class at */ public static String getPathForClass(String cname,ClassLoader orig) throws Exception { String rname = cname.replace(".","/") + ".class"; java.net.URL url = orig.getResource(rname); String full = url.getPath(); // need to split the jar url on an exclamation mark per: // https://docs.oracle.com/javase/7/docs/api/java/net/JarURLConnection.html String path = full.split("!",0)[0]; return path; } /** * create a new instance of a class by name * from a new classloader using a file:/path/to/jar style path * and the designated parent */ public static Object loadByPath(String cname,String path,ClassLoader parent) throws Exception { java.net.URL url2 = new java.net.URL(path); java.net.URL [] urls = new java.net.URL [] {url2}; ClassLoader cl = new java.net.URLClassLoader(urls,parent); Class klass = cl.loadClass(cname); return klass.newInstance(); } all static methods, no maven dependencies and shouldn't require importing anything, so cut and paste should work. note: this example will only work if you have a jdk installed (the compiler isn't in the jre), and the byPath version will only work if the jre path is "jdk_path/jre". that said, the jailbreak and loadByPath will work fine anywhere

1 comment:

weldaucci said...

Las Vegas, NV | Casino - Mapyro
Casino Property 경기도 출장샵 Map 대전광역 출장마사지 and Las Vegas Realtime Reviews of Las Vegas Casino RealTime Gaming 강릉 출장샵 RealTime Gaming RealTime Gaming RealTime Gaming RealTime Gaming RealTime 화성 출장샵 Gaming RealTime Gaming 수원 출장샵 RealTime