Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

In native mode a static instance cannot load resources as the resource URL references a jar in the path #27777

Closed
tarsij opened this issue Sep 7, 2022 · 7 comments
Labels
kind/bug Something isn't working triage/invalid This doesn't seem right

Comments

@tarsij
Copy link

tarsij commented Sep 7, 2022

Describe the bug

When an object is instantiated as static then the object or any objects instantiated by it are not able to load resources.

If I instantiate the object as non static, then when the object tries to load a resource, the resource URL looks like this:

resource:/diagram-interchange-dish-example.dmn

If I instantiate the same object as static, then the resource URL looks like this:

jar:file:/project/quarkus-bug-1.0-SNAPSHOT-runner.jar!/diagram-interchange-dish-example.dmn

This is the exception I get when I try to open a resource loaded by a static object:

Caused by: java.nio.file.NoSuchFileException: /project/quarkus-bug-1.0-SNAPSHOT-runner.jar
    at sun.nio.fs.UnixFileAttributeViews$Basic.readAttributes(UnixFileAttributeViews.java:55)
    at sun.nio.fs.UnixFileSystemProvider.readAttributes(UnixFileSystemProvider.java:149)
    at sun.nio.fs.LinuxFileSystemProvider.readAttributes(LinuxFileSystemProvider.java:99)
    at java.nio.file.Files.readAttributes(Files.java:1764)
    at java.util.zip.ZipFile$Source.get(ZipFile.java:1259)
    at java.util.zip.ZipFile$CleanableResource.<init>(ZipFile.java:733)
    at java.util.zip.ZipFile$CleanableResource.get(ZipFile.java:850)
    at java.util.zip.ZipFile.<init>(ZipFile.java:248)
    at java.util.zip.ZipFile.<init>(ZipFile.java:177)
    at java.util.jar.JarFile.<init>(JarFile.java:350)
    at sun.net.www.protocol.jar.URLJarFile.<init>(URLJarFile.java:103)
    at sun.net.www.protocol.jar.URLJarFile.getJarFile(URLJarFile.java:72)
    at sun.net.www.protocol.jar.JarFileFactory.get(JarFileFactory.java:84)
    at sun.net.www.protocol.jar.JarURLConnection.connect(JarURLConnection.java:125)
    at sun.net.www.protocol.jar.JarURLConnection.getInputStream(JarURLConnection.java:155)
    at java.net.URL.openStream(URL.java:1165)
    at my.quarkus.test.QuarkusBug$Loader.read(QuarkusBug.java:30)

Expected behavior

the resource URL is the same for both scenarios (static and non static resource loader objects)

resource:/diagram-interchange-dish-example.dmn

Actual behavior

when the object which loads the resource is instantiated as static then the resource URL references a non-existent jar file:

jar:file:/project/quarkus-bug-1.0-SNAPSHOT-runner.jar!/diagram-interchange-dish-example.dmn

How to Reproduce?

Link to a minimal project to reproduce the issue: https://drive.google.com/file/d/1ZeIZypM1rCpA0SVsUW33uwi2hV1GlntF/view?usp=sharing

build it with

mvn clean install -Pnative

sample code:

'''
public class QuarkusBug implements RequestHandler<Map<String, Object>, Map<String, Object>> {

@slf4j
public static class Loader {
private URL resource;

public Loader() {
  resource = Loader.class.getClassLoader().getResource("diagram-interchange-dish-example.dmn");
  log.info("-------------- LOADER CONSTRUCTOR: {}", resource);
}

public void loader() {
  resource = Loader.class.getClassLoader().getResource("diagram-interchange-dish-example.dmn");
  log.info("-------------- LOADER METHOD: {}", resource);
}

public void read() {
  log.info("-------------- LOADER READ RESOURCE: {}", resource);
  try (InputStream is = resource.openStream()) {
    log.info("-------------- LOADER READ OPERATION: {}", new String(is.readNBytes(38)));
  } catch (IOException e) {
    throw new RuntimeException(e);
  }
}

}

// THE FAILING ONE
private static Loader loader = new Loader();

// THE PASSING ONE
// private Loader loader = new Loader();

public QuarkusBug() {
loader.read();
}

@OverRide
public Map<String, Object> handleRequest(Map<String, Object> request, Context context) {
return request;
}
}
'''

Output of uname -a or ver

Linux 5.15.0-46-generic #49~20.04.1-Ubuntu SMP Thu Aug 4 19:15:44 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

Output of java -version

openjdk version "11.0.16" 2022-07-19

GraalVM version (if different from Java)

GraalVM 22.2.0 Java 11 CE (quay.io/quarkus/ubi-quarkus-native-image:22.2-java11)

Quarkus version or git rev

2.12.0.Final

Build tool (ie. output of mvnw --version or gradlew --version)

Apache Maven 3.6.3

Additional information

No response

@tarsij tarsij added the kind/bug Something isn't working label Sep 7, 2022
@geoand
Copy link
Contributor

geoand commented Sep 7, 2022

This is actually the expected behavior as all statically reachable code is "frozen" at native image build time.

To make your code work, you need to write a GraalVM substitution that resets the fields.
An example of such a substitution can be found here.

@geoand geoand closed this as not planned Won't fix, can't repro, duplicate, stale Sep 7, 2022
@geoand geoand added the triage/invalid This doesn't seem right label Sep 7, 2022
@tarsij
Copy link
Author

tarsij commented Sep 7, 2022

Hi @geoand,

Thanks a mill for your answer.
Will try the solution you mentioned.

Though I'm surprised this is the expected behaviour. Why is it expected to have a wrong resource URL?
I have no issues with having everything "frozen" for statically reachable code. My problem is when the statically reachable code is generated the resource URL is not translated to the native resource path.
The static object just gets the resource URL, but the resource load (openStream()) does not happen in the "static phase".

@geoand
Copy link
Contributor

geoand commented Sep 7, 2022

My problem is when the statically reachable code is generated the resource URL is not translated to the native resource path.

Yeah, I completely see your point, it's definitely not intuitive behavior - I actually wonder if GraalVM could detect this - @zakkak any idea?

@tarsij
Copy link
Author

tarsij commented Sep 7, 2022

@geoand this might seem like a noob question but which lib contains the annotations?
Looks like they were removed from

  <dependency>
    <groupId>org.graalvm.nativeimage</groupId>
    <artifactId>svm</artifactId>
    <version>22.2.0</version>
    <scope>provided</scope>
  </dependency>

Is there a guide how to use them?

@geoand
Copy link
Contributor

geoand commented Sep 7, 2022

@zakkak can give you all the info about to the latest changes

@zakkak
Copy link
Contributor

zakkak commented Sep 12, 2022

Though I'm surprised this is the expected behaviour. Why is it expected to have a wrong resource URL?
I have no issues with having everything "frozen" for statically reachable code. My problem is when the statically reachable code is generated the resource URL is not translated to the native resource path.

This is a caveat of ahead of time compilation. native-image doesn't know how to automatically do the "translation" for us so we need to do the substitution ourselves.

Note that native-image doesn't initialize all classes at build time by default, it only does so for a core of libraries for which it provides all the necessary substitutions. As a result this issue is not that common outside of Quarkus (which forces build time initialization for all classes other than a few exceptions).

@geoand this might seem like a noob question but which lib contains the annotations?

org.graalvm.nativeimage:svm:jar:22.2.0 should still contain all the annotations you need (under the package com.oracle.svm.core.annotate). Moving to 22.3.0 the annotations are going to move to package com.oracle.svm.core and become part of org.graalvm.sdk:graal-sdk:jar:22.3.0

@tarsij
Copy link
Author

tarsij commented Sep 13, 2022

Thanks a million @zakkak

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/bug Something isn't working triage/invalid This doesn't seem right
Projects
None yet
Development

No branches or pull requests

3 participants