Problem statement:
I want to find and retrieve a file in a given collection of .jar files or directories. URLClassLoader seems like the perfect fit and works on the JVM. But not on GraalVM.
Repro:
Given the following Java code in ClassLoader.java:
import java.net.*;
import java.io.File;
public class ClassLoader {
public static void main(String [] args) throws MalformedURLException {
File path = new File(args[0]);
URL url = path.toURI().toURL();
URL[] urls = { url };
URLClassLoader uc = new URLClassLoader(urls);
System.out.println(uc.getResourceAsStream("foo/bar.clj"));
}
}
and a file /tmp/foo/bar.clj, when I compile and run this with the JVM:
javac ClassLoader.java
java ClassLoader /tmp
I see:
java.io.BufferedInputStream@6d06d69c
but when compiled and run with native image:
native-image ClassLoader
./classloader /tmp
I get null.
Expected result:
I expected the same result as on the JVM.
Did you include the resource while generating the native-image?
See: https://github.com/oracle/graal/blob/master/substratevm/RESOURCES.md
As a note: dynamic classloading / resource-loading from jars is not supported, because everything reachable must be available during generation-time. If the resource is unavailable/ unknown during generation-time, it will not be found.
See: https://github.com/oracle/graal/blob/master/substratevm/LIMITATIONS.md
@SergejIsbrecht No, the point of this code is to search through a collection of directories and .jar files for a file at runtime. The string consisting of directories and jar files is not known at compile time.
Well, as I see it you are out of luck on this one. I also updated my previous answer.
Please have a look at the limitation of native-image for further information:
https://github.com/oracle/graal/blob/master/substratevm/LIMITATIONS.md
Note that the below program does exactly what I expected with native-image. What is the reason not to support this kind of behavior? It seems the functionality is already there, but only disabled in a higher level somehow.
import java.net.*;
import sun.misc.*;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
public class ClassLoader {
public static void main(String [] args) throws IOException {
String classpath = args[0];
URL[] urls = URLClassPath.pathToURLs(classpath);
URLClassPath cp = new URLClassPath(urls);
Resource r = cp.getResource("foo/bar.clj");
if (r != null) {
System.out.println(r.getInputStream());
}
}
}
$ ./classloader /tmp
java.io.FileInputStream@104603c98
EDIT: this code as is doesn't work with native image JDK 11 since the package sun.misc has been renamed to jdk.internal.loader.
A workaround for me would be to re-implement URLClassLoader functionality myself.
IIUC, the official position has to do with dynamic "class" loading.
getResourceAsStream can be used for things other than classes IIUC. That seems like a reasonable thing to be able to do.
I had a similar need, but I never considered using a URLClassLoader to load resources out of JARs on disk. Instead I just used the JarFile API directly to read the JARs. That seems to work just fine in a native-image.
@tjwatson That is also how I solved that problem. I basically replicated classpath functionality myself.
Most helpful comment
IIUC, the official position has to do with dynamic "class" loading.
getResourceAsStreamcan be used for things other than classes IIUC. That seems like a reasonable thing to be able to do.