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?

- 169
- 1
- 8
-
http://stackoverflow.com/questions/60764/how-should-i-load-jars-dynamically-at-runtime – Maksym Mar 10 '15 at 13:10
-
@Maksym This does not answer my question since I've asked how to do it straight from memory, and not from path. – John Doe Mar 10 '15 at 13:16
-
Doesn't matter where it's placed... – Maksym Mar 10 '15 at 13:24
-
Take a look at the solution by @Chris at http://stackoverflow.com/questions/60764/how-should-i-load-jars-dynamically-at-runtime using JCL - that has an example of loading a class from an arbitrary inputstream (a byte stream from memory in your case) – Elemental Mar 10 '15 at 14:02
-
@Elemental The problem is that the JCL itself is a JAR, so I have to load it someway before I can start using it. – John Doe Mar 10 '15 at 15:13
4 Answers
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 »);
}

- 285,553
- 42
- 434
- 765
-
Nice solution, unfortunately it doesn't handle resources, so this is not a fully functional classloader. Adding additional URL with "classpath" protocol allow serving resources via ClasspathURLStreamHandler but only if this classloader is set as contextClassLoader. – tporeba Dec 13 '18 at 12:59
-
@tporeba I just verified that calling `getResource` or `getResourceAsStream` on that `ClassLoader` or on a `Class` loaded with this loader works. Since Java doesn’t care about the context class loader, your issue seems to stem from another software, application or framework, using the context class loader for resource lookup. If you want to go with that convention, you have to set this class loader as such. But this applies to *all* class loaders and doesn’t make this an incomplete class loader. The correct way to lookup your own resource would be `YourClass.class.getResource[AsStream](…)`… – Holger Dec 13 '18 at 16:53
-
Yes, you are right, I wasn't precise enough. When you use the classloader directly via `getResource` or `getResourceAsStream` then you get resources correctly. But if you put this classloader into framework that manipulates `java.net.URL` instance returned by `getResource` then it doesn't work like normal classloader. In my case that URL is transformed into String and then it is impossible to recreate a `java.net.URL` instance from that string, because "x-buffer" protocol is unknown - this ends with a `MalformedURLException`. `URLClassLoader` for instance does not have such limitation. – tporeba Dec 17 '18 at 16:55
-
2@tporeba there is no requirement in the specification that converting the URL into a string and back into a URL in this way must be possible for class loader resources and it exceeds the scope of this Q&A, but if you need it, you can use [`setURLStreamHandlerFactory`](https://docs.oracle.com/javase/8/docs/api/java/net/URL.html#setURLStreamHandlerFactory-java.net.URLStreamHandlerFactory-) to register a factory that returns the stream handler for x-buffer URLs. You have to care to interoperate with an existing custom handler factory if there is one. That’s why I did not do this for this answer. – Holger Dec 18 '18 at 07:58
-
You may have to write the jar to the disk first, then you can use the following to add it to the classpath: (full answer here)
URLClassLoader child = new URLClassLoader (myJar.toURL(), this.getClass().getClassLoader());
Class classToLoad = Class.forName ("com.MyClass", true, child);
Method method = classToLoad.getDeclaredMethod ("myMethod");
Object instance = classToLoad.newInstance ();
Object result = method.invoke (instance);
If you want to enumerate the content of a jar that is not in the classpath, you can always treat it as a zip file: (see full answer here)
ZipFile zipFile = new ZipFile("testfile.zip");
Enumeration zipEntries = zipFile.entries();
String fname;
while (zipEntries.hasMoreElements()) {
fname = ((ZipEntry)zipEntries.nextElement()).getName();
}

- 1
- 1

- 3,746
- 3
- 25
- 30
You should use custom ClassLoader
and set the JAR file to its classpath.
Classes are always loaded lazily, you do not explicitly load them. Once the JAR is on classpath of the ClassLoader
, you can resolve resources.

- 9,343
- 2
- 31
- 40
-
Since I want to load the classes inside the JAR straight from memory, I guess I can not set the JAR in the classpath. Or am I wrong? – John Doe Mar 10 '15 at 13:17
-
What do you mean by "Since I want to load the classes inside the JAR straight from memory"? Are those in JAR, or in memory? – Crazyjavahacking Mar 10 '15 at 13:20
-
There is a buffer in the memory, which its structure is like a JAR file. – John Doe Mar 10 '15 at 13:48
Here you can use URLClassloader to load a jar. see in the example I have a jar that has class with name com.x.Test and it has print method, Below code is describ how to load class and invoke the method, It just an example but better use of Interfaces, factory methods and reflection can make this code better,
File jarFile = new File("myspace\\my.jar");
URLClassLoader loader = new URLClassLoader(new URL[]{jarFile.toURL()});
Class<?> clazz = loader.loadClass("com.x.Test");
clazz.getMethod("print").invoke(clazz.getConstructor().newInstance(), args);

- 1,425
- 1
- 15
- 27