I'm looking to write a custom class loader that will load a JAR
file from across a custom network. In the end, all I have to work with is a byte array of the JAR
file.
I cannot dump the byte array onto the file system and use a URLClassLoader
.
My first plan was to create a JarFile
object from a stream or byte array, but it only supports a File
object.
I've already written up something that uses a JarInputStream
:
public class RemoteClassLoader extends ClassLoader {
private final byte[] jarBytes;
public RemoteClassLoader(byte[] jarBytes) {
this.jarBytes = jarBytes;
}
@Override
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class<?> clazz = findLoadedClass(name);
if (clazz == null) {
try {
InputStream in = getResourceAsStream(name.replace('.', '/') + ".class");
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamUtils.writeTo(in, out);
byte[] bytes = out.toByteArray();
clazz = defineClass(name, bytes, 0, bytes.length);
if (resolve) {
resolveClass(clazz);
}
} catch (Exception e) {
clazz = super.loadClass(name, resolve);
}
}
return clazz;
}
@Override
public URL getResource(String name) {
return null;
}
@Override
public InputStream getResourceAsStream(String name) {
try (JarInputStream jis = new JarInputStream(new ByteArrayInputStream(jarBytes))) {
JarEntry entry;
while ((entry = jis.getNextJarEntry()) != null) {
if (entry.getName().equals(name)) {
return jis;
}
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
This may work fine for small JAR
files, but I tried loading up a 2.7MB
jar file with almost 2000
classes and it was taking around 160 ms
just to iterate through all the entries let alone load the class it found.
If anyone knows a solution that's faster than iterating through a JarInputStream
's entries each time a class is loaded, please share!
I would iterate through the class once and cache the entries. I would also look at the source code for URLClassLoader to see how it does it. If that fails, write the data to a temporary file and load it via the normal class loader.