Graal: [native-image] URLClassLoader getResourceAsStream returns null

Created on 8 Dec 2019  路  7Comments  路  Source: oracle/graal

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.

native-image

Most helpful comment

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.

All 7 comments

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.

Was this page helpful?
0 / 5 - 0 ratings