5

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

tostao
  • 2,803
  • 4
  • 38
  • 61
  • 1
    Can you check if the url variable or the uri variable is null? If getResource() doesn't find it, it returns a null url. But doing new File(url.toUrl()) probably will never end up well in native image. – Geoffrey De Smet Mar 22 '22 at 16:27
  • 1
    I checked, this returns null: `Thread.currentThread().getContextClassLoader().getResource("/Resource2.txt");` So looks like in Native image I am not able to find this resource, but why? What should I use instead of `new File(url.toUrl())` in native image? – tostao Mar 22 '22 at 16:45
  • @GeoffreyDeSmet should I use something like this to find resources: https://github.com/quarkusio/quarkus/blob/main/core/runtime/src/main/java/io/quarkus/runtime/ResourceHelper.java ? – tostao Mar 23 '22 at 07:52
  • Not sure - I haven't loaded image resources etc yet in quarkus native apps. But it must be possible. Double check if the file name matches case-sensitive (resource2.txt vs Resource2.txt). On windows, in an non-jarred classpath, case-sensitive would not break non-native, but would break native. If that's not it, and no one replies here, ask on Quarkus's zulip chat. – Geoffrey De Smet Mar 23 '22 at 08:08

2 Answers2

4

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] ------------------------------------------------------------------------
ozkanpakdil
  • 3,199
  • 31
  • 48
0

This is how I solved the problem - example with changes are in this branch: https://github.com/sleski/hello-quarkus/tree/solution_for_native

Explanations:

Image is in: src/main/resources/images and name is:IMG_3_3M.jpg. In application.properties I added images.location variable:

images.location=src/main/resources/images/
%prod.images.location=/work/images/

and in the java controller I added:

@ConfigProperty(name = "images.location")
String imageLocation;

in docker.native added: COPY target/classes/images/*.jpg /work/images/

When I start application with querkus:dev it is getting image from src/main/resources/images and when I run narive image: /work/images.

In both cases work: File size is = 3412177.

tostao
  • 2,803
  • 4
  • 38
  • 61