I want to load a JAR dynamically, straight for memory.
Say, I have a buffer that contains a JAR, and I want to load all the classes inside the JAR, or at least list all of the files that exist inside the JAR. (classes, images, etc...).
What do I do if the first class that I load depends on the second class, for example?
Does java know how to handle this? Or I have take care of this by myself?
Since you said “at least list all of the files that exist inside the JAR”, let’s begin with that rather easy task.
Suppose, you have your JarFile in a byte array, byte[] buffer
:
try(JarInputStream is=new JarInputStream(new ByteArrayInputStream(buffer))) {
for(;;) {
JarEntry nextEntry = is.getNextJarEntry();
if(nextEntry==null) break;
System.out.println(nextEntry);
}
}
Loading classes from such a representation doesn’t work out-of-the-box because the standard ClassLoader
implementations rely on the JarFile
implementation which relies on a physical file rather than an abstraction.
So unless you simply write the buffer into a temporary file, it boils down to implement your own ClassLoader
. Since the JRE supports only stream access as shown above, you will have to scan linearly to find a requested resource/class or iterate once and store the entries into a Map
.
One alternative to implementing a ClassLoader
is to implement a custom URL
handler to use together with a URLClassLoader
which reduces the task to the lookup as described above:
final Map<String,byte[]> map=new HashMap<>();
try(JarInputStream is=new JarInputStream(new ByteArrayInputStream(buffer))) {
for(;;) {
JarEntry nextEntry = is.getNextJarEntry();
if(nextEntry==null) break;
final int est=(int)nextEntry.getSize();
byte[] data=new byte[est>0? est: 1024];
int real=0;
for(int r=is.read(data); r>0; r=is.read(data, real, data.length-real))
if(data.length==(real+=r)) data=Arrays.copyOf(data, data.length*2);
if(real!=data.length) data=Arrays.copyOf(data, real);
map.put("/"+nextEntry.getName(), data);
}
}
URL u=new URL("x-buffer", null, -1, "/", new URLStreamHandler() {
protected URLConnection openConnection(URL u) throws IOException {
final byte[] data = map.get(u.getFile());
if(data==null) throw new FileNotFoundException(u.getFile());
return new URLConnection(u) {
public void connect() throws IOException {}
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(data);
}
};
}
});
try(URLClassLoader cl=new URLClassLoader(new URL[]{u})) {
cl.loadClass( « a class from your JarFile buffer »);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With