Last active
November 29, 2017 08:44
-
-
Save basinilya/4fb957c575891f75b03c210e15466d27 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.foo.inject; | |
import java.io.ByteArrayOutputStream; | |
import java.io.InputStream; | |
import java.lang.reflect.Field; | |
import java.lang.reflect.Method; | |
import java.net.URL; | |
import java.net.URLClassLoader; | |
import java.util.HashMap; | |
/** | |
* Copies the bytecode of existing loaded classes into another {@link URLClassLoader}. Usually the | |
* source classes are of a webapp and the target is the system classloader of a web container. | |
*/ | |
public final class ClassInjector { | |
private ClassInjector() {} | |
/** | |
* Checks that classes are available in the classloader and if not, copies the bytecode to it. | |
* | |
* @param ucl the target classloader | |
* @param classes the source classes | |
* @return the array of classes available in the target classloader | |
* @throws Exception | |
*/ | |
public static Class<?>[] inject(final URLClassLoader ucl, final Class<?>... classes) | |
throws Exception { | |
final Class<?>[] res = new Class<?>[classes.length]; | |
final Field handlerField = URL.class.getDeclaredField("handler"); | |
handlerField.setAccessible(true); | |
final Method addURLmethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); | |
addURLmethod.setAccessible(true); | |
final HashMap<String, byte[]> map = new HashMap<String, byte[]>(); | |
final MemURLStreamHandler handler = new MemURLStreamHandler(map); | |
URL baseURL = new URL("x-buffer", null, -1, MemURLStreamHandler.NONEXDIR, handler); | |
final String sBaseURL = baseURL.toString(); | |
for (final URL u : ucl.getURLs()) { | |
if (sBaseURL.equals(u.toString())) { | |
baseURL = u; | |
handlerField.set(baseURL, handler); | |
break; | |
} | |
} | |
try { | |
boolean needInject = false; | |
for (int i = 0; i < classes.length; i++) { | |
final Class<?> clazz = classes[i]; | |
try { | |
res[i] = ucl.loadClass(clazz.getName()); | |
} catch (final Exception e) { | |
needInject = true; | |
loadToMem(map, clazz); | |
} | |
} | |
if (needInject) { | |
addURLmethod.invoke(ucl, baseURL); | |
for (int i = 0; i < classes.length; i++) { | |
final Class<?> clazz = classes[i]; | |
res[i] = ucl.loadClass(clazz.getName()); | |
} | |
} | |
} finally { | |
// let the custom handler be collected | |
final Object o = handlerField.get(new URL("file:///")); | |
handlerField.set(baseURL, o); | |
} | |
return res; | |
} | |
private static void loadToMem(final HashMap<String, byte[]> map, final Class<?> clazz) | |
throws Exception { | |
final String clazzName = clazz.getName(); | |
final InputStream in = clazz.getResourceAsStream(clazz.getSimpleName() + ".class"); | |
try { | |
final ByteArrayOutputStream out = new ByteArrayOutputStream(); | |
byte[] buf = new byte[1024]; | |
int nb; | |
while ((nb = in.read(buf)) != -1) { | |
out.write(buf, 0, nb); | |
} | |
buf = out.toByteArray(); | |
final String path = | |
MemURLStreamHandler.NONEXDIR + clazzName.replace('.', '/') + ".class"; | |
map.put(path, buf); | |
} finally { | |
in.close(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.foo.inject; | |
import java.io.ByteArrayInputStream; | |
import java.io.FileNotFoundException; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.net.URL; | |
import java.net.URLConnection; | |
import java.net.URLStreamHandler; | |
import java.util.Map; | |
public class MemURLStreamHandler extends URLStreamHandler { | |
public static final String NONEXDIR = "/nonexistent/dir/"; | |
private final Map<String, byte[]> map; | |
public MemURLStreamHandler(final Map<String, byte[]> map) { | |
this.map = map; | |
} | |
@Override | |
protected URLConnection openConnection(final URL u) throws IOException { | |
final byte[] data = map.get(u.getPath()); | |
if (data == null) { | |
throw new FileNotFoundException(u.getFile()); | |
} | |
return new URLConnection(u) { | |
@Override | |
public void connect() throws IOException {} | |
@Override | |
public InputStream getInputStream() throws IOException { | |
return new ByteArrayInputStream(data); | |
} | |
@Override | |
protected void finalize() throws Throwable { | |
super.finalize(); | |
} | |
}; | |
} | |
@Override | |
protected void finalize() throws Throwable { | |
super.finalize(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment