Skip to content

Instantly share code, notes, and snippets.

@basinilya
Last active November 29, 2017 08:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save basinilya/4fb957c575891f75b03c210e15466d27 to your computer and use it in GitHub Desktop.
Save basinilya/4fb957c575891f75b03c210e15466d27 to your computer and use it in GitHub Desktop.
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();
}
}
}
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