Issue
I am trying to build a native executable for an app that works with Quarkus and JavaFx. The only way I have managed to achieve this has been marking lots of javaFx classes as --initialize-at-run-time, but this causes that, when trying to start the app, it fails with the following message:
java.lang.ClassNotFoundException: com.sun.javafx.tk.quantum.QuantumToolkit
at com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:71)
at java.lang.Class.forName(DynamicHub.java:1319)
at com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:251)
at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:267)
at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:158)
at com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:658)
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:678)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
at java.lang.Thread.run(Thread.java:829)
at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:567)
at com.oracle.svm.core.windows.WindowsJavaThreads.osThreadStartRoutine(WindowsJavaThreads.java:138)
2021-09-09 16:02:21,173 ERROR [io.qua.run.Application] (main) Failed to start application (with profile prod): java.lang.RuntimeException: No toolkit found
at com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:273)
at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:267)
at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:158)
at com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:658)
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:678)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
at java.lang.Thread.run(Thread.java:829)
at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:567)
at
com.oracle.svm.core.windows.WindowsJavaThreads.osThreadStartRoutine(WindowsJavaThreads.java:138)
I guess I need to add the javafx modules before build, but don't know how to achieve this from Maven. If anyone can help me I would be very grateful. Thanks in advance.
PS: If someone knows an alternative solution using springboot please share :)
Updated: Tested with SpringBoot and also fails with spring-native and gluonfx.
Made a minimal reproducible you can download from: https://github.com/ikaro143/JavaFx-SpringBoot-example/tree/master
The maven commands should be executed in the VisualStudio Native Tools Command Promp
To build with spring plugin use: mvn clean package -Pnative
To build with gluonfx use: mvn clean gluonfx:build -Pnative-gluonfx
In both cases the .exe is built, but none of both works.
Executing spring compilation from the console throws this stackTrace (Execution of gluon build does not give any feedback but neither works):
Exception in thread "main" java.lang.RuntimeException: Application launch error
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:202)
at java.lang.Thread.run(Thread.java:829)
at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:567)
at com.oracle.svm.core.windows.WindowsJavaThreads.osThreadStartRoutine(WindowsJavaThreads.java:138)
Caused by: java.lang.AssertionError: java.lang.ClassNotFoundException: javafx.scene.image.Image
at com.sun.javafx.util.Utils.forceInit(Utils.java:868)
at com.sun.javafx.tk.Toolkit.<clinit>(Toolkit.java:945)
at com.oracle.svm.core.classinitialization.ClassInitializationInfo.invokeClassInitializer(ClassInitializationInfo.java:375)
at com.oracle.svm.core.classinitialization.ClassInitializationInfo.initialize(ClassInitializationInfo.java:295)
at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:286)
at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:160)
at com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:658)
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:678)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
... 3 more
Caused by: java.lang.ClassNotFoundException: javafx.scene.image.Image
at com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:71)
at java.lang.Class.forName(DynamicHub.java:1319)
at com.sun.javafx.util.Utils.forceInit(Utils.java:865)
... 11 more
Updated: Sharing a minimal reproducible for Quarkus + Jafavx
https://github.com/ikaro143/JavaFx-Quarkus-example
To build with quarkus plugin use: mvn clean package -Pnative
To build with gluonfx use: mvn clean gluonfx:build -Pnative-gluonfx
The Quarkus way fails at analysis step. With several errors as below:
analysis: 45,621.43 ms, 3.89 GB
Error: Unsupported features in 12 methods
Detailed message:
Error: Class initialization of com.sun.javafx.tk.quantum.PrismImageLoader2$AsyncImageLoader failed. Use the option --initialize-at-run-time=com.sun.javafx.tk.quantum.PrismImageLoader2$AsyncImageLoader to explicitly request delayed initialization of this class.
Original exception that caused the problem: java.lang.ExceptionInInitializerError
at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized0(Native Method)
at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized(Unsafe.java:1042)
at jdk.unsupported/sun.misc.Unsafe.ensureClassInitialized(Unsafe.java:698)
at jdk.internal.vm.compiler/org.graalvm.compiler.serviceprovider.GraalUnsafeAccess.ensureClassInitialized(GraalUnsafeAccess.java:77)
The gluonfx way fails at setup step with this vague error:
[com.quarkusjavafx.example.applauncher.cdiapplication:9740] classlist: 3,592.40 ms, 1.19 GB
[com.quarkusjavafx.example.applauncher.cdiapplication:9740] (cap): 3,086.44 ms, 1.19 GB
[com.quarkusjavafx.example.applauncher.cdiapplication:9740] setup: 6,095.10 ms, 1.19 GB
Fatal error:java.lang.NullPointerException
at java.base/java.io.Reader.<init>(Reader.java:167)
at java.base/java.io.InputStreamReader.<init>(InputStreamReader.java:72)
at io.quarkus.runtime.graal.ResourcesFeature.beforeAnalysis(ResourcesFeature.java:21)
at com.oracle.svm.hosted.NativeImageGenerator.lambda$runPointsToAnalysis$11(NativeImageGenerator.java:691)
at com.oracle.svm.hosted.FeatureHandler.forEachFeature(FeatureHandler.java:71)
at com.oracle.svm.hosted.NativeImageGenerator.runPointsToAnalysis(NativeImageGenerator.java:691)
at com.oracle.svm.hosted.NativeImageGenerator.doRun(NativeImageGenerator.java:532)
at com.oracle.svm.hosted.NativeImageGenerator.run(NativeImageGenerator.java:491)
at com.oracle.svm.hosted.NativeImageGeneratorRunner.buildImage(NativeImageGeneratorRunner.java:380)
at com.oracle.svm.hosted.NativeImageGeneratorRunner.build(NativeImageGeneratorRunner.java:543)
at com.oracle.svm.hosted.NativeImageGeneratorRunner.main(NativeImageGeneratorRunner.java:119)
at com.oracle.svm.hosted.NativeImageGeneratorRunner$JDK9Plus.main(NativeImageGeneratorRunner.java:573)
[com.quarkusjavafx.example.applauncher.cdiapplication:9740] [total]: 9,723.35 ms, 1.19 GB
Solution
Updated After a suggestion from José Pereda I have tried migrating the backend to Micronaut and have been able to build the native image successfully. The migration from springboot to micronaut was very easy to do. Just changing the parent in the pom and the basic dependencies.
The native image is generated with the maven code:
mvn clean gluonfx: build -Pnative-gluonfx
Maybe you need to use first the goal gluonfx:runagent
to generate the config files needed to run.
In my experience I had some dependencies issues that only noticed when running the goal gluonfx:nativerun
after the build was done. And then adjust the code.
I leave here a minimal example in case someone needs it: https://github.com/ikaro143/example-micronaut
PS: Be sure your main class is registered for reflection.
Answered By - Ikaro
Answer Checked By - Senaida (JavaFixing Volunteer)