Search code examples
dockerquarkusgraalvmquarkus-native

How to access resources in a Quarkus native image?


I started playing with quarkus and graalvm. I added files (txt and jpg) to resources in the project (src/main/resources/). To be sure that I have access to this file in controller I display size of it:

URL url = Thread.currentThread().getContextClassLoader().getResource("/Resource2.txt");
File file = new File(url.toURI());
return "Hello My Friend! File size in bytes = " + file.length();

and when I run it with maven (mvn quarkus:dev) it works. Controller code is here.

Problem occurred when I created native Quarkus application and try to run inside docker. To be sure that file is included in native image, I added a big jpg file (3.3MB), created resources-config.json:

{ "resources": 
 { "includes": [
        { "pattern": "IMG_3_3M\\.jpg$"},
        { "pattern": "Resources2\\.txt$"}
       ]
}}

and in application.properties added: quarkus.native.additional-build-args = -H:ResourceConfigurationFiles=resources-config.json. The native runner size was increased from:

  • 39M Mar 21 12:48 hello-quarkus-1.0-SNAPSHOT-runner
  • to: 44M Mar 21 12:19 hello-quarkus-1.0-SNAPSHOT-runner

So I assume that jpg file was included, but still when run native application inside docker image, I got NPE:

 Caused by: java.lang.NullPointerException
  at it.tostao.quickstart.GreetingResource.hello(GreetingResource.java:24)

where line 24: is url.toURI().

Any idea how I can read resources in native image? Is something missing in the configuration?

here is sample image to reproduce the problem, all commands needed to build and run native image you can find in README.MD:

https://github.com/sleski/hello-quarkus

So far I checked this urls and still was not able to find resources in native image:

How to include classpath resources in a Quarkus native image?

How to read classpath resources in Quarkus native image?

https://quarkus.io/guides/writing-native-applications-tips

Read txt file from resources folder on maven Quarkus project From Docker Container


Solution

  • First fix json

    {
        "resources": [
          {
            "pattern": "Resource2.txt"
          }
        ]
    }
    

    or you can have *.txt as pattern. like in the doc

    https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/Resources.md says use

    InputStream resource = ModuleLayer.boot().findModule(moduleName).getResourceAsStream(resourcePath);
    

    when I tried I had issues. you can see the working code below for your project

    @Path("/hello")
    public class GreetingResource {
    
    
        @GET
        @Produces(MediaType.TEXT_PLAIN)
        public String hello() throws IOException {
            String moduleName = "java.base";
            String resourcePath = "/Resource2.txt";
            Module resource = ModuleLayer.boot().findModule(moduleName).get();
            InputStream ins = resource.getResourceAsStream(resourcePath);
            if (ins == null) {
                System.out.println("module came empty, now trying to load from GreetingResource");
                ins = GreetingResource.class.getResourceAsStream(resourcePath);
            }
            if (ins != null) {
                StringBuilder sb = new StringBuilder();
                for (int ch; (ch = ins.read()) != -1; ) {
                    sb.append((char) ch);
                }
                return "Hello My Friend! File size in bytes = " + sb;
            }
            return "empty";
        }
    
    }
    

    GreetingResource.class.getResourceAsStream(resourcePath); is actually bringing the resource here. I think this feature may change in the future so I left ModuleLayer in the code too. I used graalvm 17-21.3.0

    you can find the build log below

    [INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildRunner] C:\Program Files\GraalVM\graalvm-ce-java17-21.3.0\bin\native-image.cmd -J-Dsun.nio.ch.maxUpdateArraySize=100 -J-Djava.util.logging.manager=org.jboss.logmanager.LogManager -J-Dvertx.logger-delegate-factory-class-name=io.quarkus.vertx.core.runtime.VertxLogDelegateFactory -J-Dvertx.disableDnsResolver=true -J-Dio.netty.leakDetection.level=DISABLED -J-Dio.netty.allocator.maxOrder=3 -J-Duser.language=en -J-Duser.country=GB -J-Dfile.encoding=UTF-8 -H:-ParseOnce -J--add-exports=java.security.jgss/sun.security.krb5=ALL-UNNAMED -J--add-opens=java.base/java.text=ALL-UNNAMED -H:ResourceConfigurationFiles=resources-config.json -H:+PrintAnalysisCallTree -H:Log=registerResource:verbose -H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy\$BySpaceAndTime -H:+JNI -H:+AllowFoldMethods -J-Djava.awt.headless=true -H:FallbackThreshold=0 -H:+ReportExceptionStackTraces -H:-AddAllCharsets -H:EnableURLProtocols=http -H:-UseServiceLoaderFeature -H:+StackTrace -J--add-exports=java.management/sun.management=ALL-UNNAMED hello-quarkus-1.0-SNAPSHOT-runner -jar hello-quarkus-1.0-SNAPSHOT-runner.jar
    [hello-quarkus-1.0-SNAPSHOT-runner:20428]    classlist:   2,920.35 ms,  0.94 GB
    [hello-quarkus-1.0-SNAPSHOT-runner:20428]        (cap):   1,493.84 ms,  0.94 GB
    [hello-quarkus-1.0-SNAPSHOT-runner:20428]        setup:   2,871.07 ms,  0.94 GB
    [Use -Dgraal.LogFile=<path> to redirect Graal log output to a file.]
    [thread:1] scope: main
      [thread:1] scope: main.registerResource
      ResourcesFeature: registerResource: Resource2.txt
    14:23:38,709 INFO  [org.jbo.threads] JBoss Threads version 3.4.2.Final
      [thread:1] scope: main.registerResource
      ResourcesFeature: registerResource: java/lang/uniName.dat
    [hello-quarkus-1.0-SNAPSHOT-runner:20428]     (clinit):     475.20 ms,  5.14 GB
    [hello-quarkus-1.0-SNAPSHOT-runner:20428]   (typeflow):   2,931.83 ms,  5.14 GB
    [hello-quarkus-1.0-SNAPSHOT-runner:20428]    (objects):  24,294.27 ms,  5.14 GB
    [hello-quarkus-1.0-SNAPSHOT-runner:20428]   (features):   2,979.07 ms,  5.14 GB
    [hello-quarkus-1.0-SNAPSHOT-runner:20428]     analysis:  32,083.24 ms,  5.14 GB
    # Printing call tree to: C:\Users\ozkan\tmp\hello-quarkus\target\hello-quarkus-1.0-SNAPSHOT-native-image-source-jar\reports\call_tree_hello-quarkus-1.0-SNAPSHOT-runner_20220324_142406.txt
    # Printing list of used methods to: C:\Users\ozkan\tmp\hello-quarkus\target\hello-quarkus-1.0-SNAPSHOT-native-image-source-jar\reports\used_methods_hello-quarkus-1.0-SNAPSHOT-runner_20220324_142407.txt
    # Printing list of used classes to: C:\Users\ozkan\tmp\hello-quarkus\target\hello-quarkus-1.0-SNAPSHOT-native-image-source-jar\reports\used_classes_hello-quarkus-1.0-SNAPSHOT-runner_20220324_142407.txt
    # Printing list of used packages to: C:\Users\ozkan\tmp\hello-quarkus\target\hello-quarkus-1.0-SNAPSHOT-native-image-source-jar\reports\used_packages_hello-quarkus-1.0-SNAPSHOT-runner_20220324_142407.txt
    # Printing call tree for vm entry point to: C:\Users\ozkan\tmp\hello-quarkus\target\hello-quarkus-1.0-SNAPSHOT-native-image-source-jar\reports\csv_call_tree_vm_hello-quarkus-1.0-SNAPSHOT-runner_20220324_142408.csv
    # Printing call tree for methods to: C:\Users\ozkan\tmp\hello-quarkus\target\hello-quarkus-1.0-SNAPSHOT-native-image-source-jar\reports\csv_call_tree_methods_hello-quarkus-1.0-SNAPSHOT-runner_20220324_142408.csv
    # Printing call tree for virtual methods to: C:\Users\ozkan\tmp\hello-quarkus\target\hello-quarkus-1.0-SNAPSHOT-native-image-source-jar\reports\csv_call_tree_virtual_methods_hello-quarkus-1.0-SNAPSHOT-runner_20220324_142408.csv
    # Printing call tree for entry points to: C:\Users\ozkan\tmp\hello-quarkus\target\hello-quarkus-1.0-SNAPSHOT-native-image-source-jar\reports\csv_call_tree_entry_points_hello-quarkus-1.0-SNAPSHOT-runner_20220324_142408.csv
    # Printing call tree for direct edges to: C:\Users\ozkan\tmp\hello-quarkus\target\hello-quarkus-1.0-SNAPSHOT-native-image-source-jar\reports\csv_call_tree_direct_edges_hello-quarkus-1.0-SNAPSHOT-runner_20220324_142408.csv
    # Printing call tree for overriden by edges to: C:\Users\ozkan\tmp\hello-quarkus\target\hello-quarkus-1.0-SNAPSHOT-native-image-source-jar\reports\csv_call_tree_override_by_edges_hello-quarkus-1.0-SNAPSHOT-runner_20220324_142408.csv
    # Printing call tree for virtual edges to: C:\Users\ozkan\tmp\hello-quarkus\target\hello-quarkus-1.0-SNAPSHOT-native-image-source-jar\reports\csv_call_tree_virtual_edges_hello-quarkus-1.0-SNAPSHOT-runner_20220324_142408.csv
    [hello-quarkus-1.0-SNAPSHOT-runner:20428]     universe:   1,547.28 ms,  5.14 GB
    [hello-quarkus-1.0-SNAPSHOT-runner:20428]      (parse):   4,919.32 ms,  4.87 GB
    [hello-quarkus-1.0-SNAPSHOT-runner:20428]     (inline):   7,013.78 ms,  5.83 GB
    [hello-quarkus-1.0-SNAPSHOT-runner:20428]    (compile):  27,387.04 ms,  5.56 GB
    [hello-quarkus-1.0-SNAPSHOT-runner:20428]      compile:  41,595.59 ms,  5.56 GB
    [hello-quarkus-1.0-SNAPSHOT-runner:20428]        image:   2,515.22 ms,  5.56 GB
    [hello-quarkus-1.0-SNAPSHOT-runner:20428]        write:     858.79 ms,  5.56 GB
    [hello-quarkus-1.0-SNAPSHOT-runner:20428]      [total]:  90,068.97 ms,  5.56 GB
    # Printing build artifacts to: C:\Users\ozkan\tmp\hello-quarkus\target\hello-quarkus-1.0-SNAPSHOT-native-image-source-jar\hello-quarkus-1.0-SNAPSHOT-runner.build_artifacts.txt
    [INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 94323ms
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time:  01:37 min
    [INFO] Finished at: 2022-03-24T14:24:56Z
    [INFO] ------------------------------------------------------------------------