Reputation: 20178
I am trying to package the default HelloApplication (full app) that intellij community edition creates for a javafx application. The HelloApplication does not contain much dependencies besides JavaFX, but the intention is to package a bigger app with more dependencies. The aim is to create an independent executable that contains all required dependencies such that the executable can be installed even if the client does not have java, javafx, etc. already installed
Below are the information I provide in this question:
jpackage
1. Part of the pom.xml containing definition for the shaded plugin
For this, from what I read, I am first creating a shaded jar then using jpackage to create an independent executable. The definition for the shaded plugin in the pom.xml(full pom.xml) is:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<shadedArtifactAttached>true</shadedArtifactAttached>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.intelidia.simplejavafxapp.simplejavafxapp.HelloApplication</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
2. Execution of the shaded jar
The jar runs perfectly with this command: java --module-path /home/noor/Documents/Apps/javafx-sdk-22.0.1/lib --add-modules javafx.controls,javafx.fxml -jar SimpleJavaFXApp-1.0-SNAPSHOT-shaded.jar
3. Content of the shaded jar
Output of jar tf SimpleJavaFXApp-1.0-SNAPSHOT-shaded.jar
can be found here. Sorry for not providing it here, if I do so, the limit of characters allowed exceed.
4. Generation of rpm using jpackage
jpackage
is generating an rpm with the command:
jpackage --name SimpleJavaFXApp --input ./target --main-jar ./target/SimpleJavaFXApp-1.0-SNAPSHOT-shaded.jar --main-class com.intelidia.simplejavafxapp.simplejavafxapp.HelloApplication --module-path /home/noor/Documents/Apps/javafx-sdk-22.0.1/lib --add-modules javafx.controls,javafx.fxml
5. Error when installating app from rpm
jpackage
is generating an rpm with the command:
Running the installed application generates the following error:
Graphics Device initialization failed for : es2, sw
Error initializing QuantumRenderer: no suitable pipeline found
java.lang.RuntimeException: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
at [email protected]/com.sun.javafx.tk.quantum.QuantumRenderer.getInstance(Unknown Source)
at [email protected]/com.sun.javafx.tk.quantum.QuantumToolkit.init(Unknown Source)
at [email protected]/com.sun.javafx.tk.Toolkit.getToolkit(Unknown Source)
at [email protected]/com.sun.javafx.application.PlatformImpl.startup(Unknown Source)
at [email protected]/com.sun.javafx.application.PlatformImpl.startup(Unknown Source)
at [email protected]/com.sun.javafx.application.LauncherImpl.startToolkit(Unknown Source)
at [email protected]/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(Unknown Source)
at [email protected]/com.sun.javafx.application.LauncherImpl.launchApplication(Unknown Source)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at java.base/sun.launcher.LauncherHelper$FXHelper.main(Unknown Source)
Caused by: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
at [email protected]/com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.init(Unknown Source)
at [email protected]/com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
Exception in thread "main" java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at java.base/sun.launcher.LauncherHelper$FXHelper.main(Unknown Source)
Caused by: java.lang.RuntimeException: No toolkit found
at [email protected]/com.sun.javafx.tk.Toolkit.getToolkit(Unknown Source)
at [email protected]/com.sun.javafx.application.PlatformImpl.startup(Unknown Source)
at [email protected]/com.sun.javafx.application.PlatformImpl.startup(Unknown Source)
at [email protected]/com.sun.javafx.application.LauncherImpl.startToolkit(Unknown Source)
at [email protected]/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(Unknown Source)
at [email protected]/com.sun.javafx.application.LauncherImpl.launchApplication(Unknown Source)
... 3 more
6. Content of the generated rpm shown below:
/opt/simplejavafxapp
/opt/simplejavafxapp/bin
/opt/simplejavafxapp/bin/SimpleJavaFXApp
/opt/simplejavafxapp/lib
/opt/simplejavafxapp/lib/SimpleJavaFXApp.png
/opt/simplejavafxapp/lib/app
/opt/simplejavafxapp/lib/app/SimpleJavaFXApp-1.0-SNAPSHOT-shaded.jar
/opt/simplejavafxapp/lib/app/SimpleJavaFXApp-1.0-SNAPSHOT.jar
/opt/simplejavafxapp/lib/app/SimpleJavaFXApp.cfg
/opt/simplejavafxapp/lib/app/classes
/opt/simplejavafxapp/lib/app/classes/com
/opt/simplejavafxapp/lib/app/classes/com/intelidia
/opt/simplejavafxapp/lib/app/classes/com/intelidia/simplejavafxapp
/opt/simplejavafxapp/lib/app/classes/com/intelidia/simplejavafxapp/simplejavafxapp
/opt/simplejavafxapp/lib/app/classes/com/intelidia/simplejavafxapp/simplejavafxapp/HelloApplication.class
/opt/simplejavafxapp/lib/app/classes/com/intelidia/simplejavafxapp/simplejavafxapp/HelloController.class
/opt/simplejavafxapp/lib/app/classes/com/intelidia/simplejavafxapp/simplejavafxapp/hello-view.fxml
/opt/simplejavafxapp/lib/app/classes/module-info.class
/opt/simplejavafxapp/lib/app/generated-sources
/opt/simplejavafxapp/lib/app/generated-sources/annotations
/opt/simplejavafxapp/lib/app/maven-archiver
/opt/simplejavafxapp/lib/app/maven-archiver/pom.properties
/opt/simplejavafxapp/lib/app/maven-status
/opt/simplejavafxapp/lib/app/maven-status/maven-compiler-plugin
/opt/simplejavafxapp/lib/app/maven-status/maven-compiler-plugin/compile
/opt/simplejavafxapp/lib/app/maven-status/maven-compiler-plugin/compile/default-compile
/opt/simplejavafxapp/lib/app/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
/opt/simplejavafxapp/lib/app/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
/opt/simplejavafxapp/lib/libapplauncher.so
/opt/simplejavafxapp/lib/runtime
/opt/simplejavafxapp/lib/runtime/conf
/opt/simplejavafxapp/lib/runtime/conf/jaxp.properties
/opt/simplejavafxapp/lib/runtime/conf/net.properties
/opt/simplejavafxapp/lib/runtime/conf/sdp
/opt/simplejavafxapp/lib/runtime/conf/sdp/sdp.conf.template
/opt/simplejavafxapp/lib/runtime/conf/security
/opt/simplejavafxapp/lib/runtime/conf/security/java.policy
/opt/simplejavafxapp/lib/runtime/conf/security/java.security
/opt/simplejavafxapp/lib/runtime/conf/security/policy
/opt/simplejavafxapp/lib/runtime/conf/security/policy/README.txt
/opt/simplejavafxapp/lib/runtime/conf/security/policy/limited
/opt/simplejavafxapp/lib/runtime/conf/security/policy/limited/default_US_export.policy
/opt/simplejavafxapp/lib/runtime/conf/security/policy/limited/default_local.policy
/opt/simplejavafxapp/lib/runtime/conf/security/policy/limited/exempt_local.policy
/opt/simplejavafxapp/lib/runtime/conf/security/policy/unlimited
/opt/simplejavafxapp/lib/runtime/conf/security/policy/unlimited/default_US_export.policy
/opt/simplejavafxapp/lib/runtime/conf/security/policy/unlimited/default_local.policy
/opt/simplejavafxapp/lib/runtime/conf/sound.properties
/opt/simplejavafxapp/lib/runtime/legal
/opt/simplejavafxapp/lib/runtime/legal/java.base
/opt/simplejavafxapp/lib/runtime/legal/java.base/COPYRIGHT
/opt/simplejavafxapp/lib/runtime/legal/java.base/LICENSE
/opt/simplejavafxapp/lib/runtime/legal/java.base/aes.md
/opt/simplejavafxapp/lib/runtime/legal/java.base/asm.md
/opt/simplejavafxapp/lib/runtime/legal/java.base/c-libutl.md
/opt/simplejavafxapp/lib/runtime/legal/java.base/cldr.md
/opt/simplejavafxapp/lib/runtime/legal/java.base/icu.md
/opt/simplejavafxapp/lib/runtime/legal/java.base/public_suffix.md
/opt/simplejavafxapp/lib/runtime/legal/java.base/unicode.md
/opt/simplejavafxapp/lib/runtime/legal/java.datatransfer
/opt/simplejavafxapp/lib/runtime/legal/java.datatransfer/COPYRIGHT
/opt/simplejavafxapp/lib/runtime/legal/java.desktop
/opt/simplejavafxapp/lib/runtime/legal/java.desktop/colorimaging.md
/opt/simplejavafxapp/lib/runtime/legal/java.desktop/giflib.md
/opt/simplejavafxapp/lib/runtime/legal/java.desktop/harfbuzz.md
/opt/simplejavafxapp/lib/runtime/legal/java.desktop/jpeg.md
/opt/simplejavafxapp/lib/runtime/legal/java.desktop/lcms.md
/opt/simplejavafxapp/lib/runtime/legal/java.desktop/libpng.md
/opt/simplejavafxapp/lib/runtime/legal/java.desktop/mesa3d.md
/opt/simplejavafxapp/lib/runtime/legal/java.desktop/pipewire.md
/opt/simplejavafxapp/lib/runtime/legal/java.desktop/xwd.md
/opt/simplejavafxapp/lib/runtime/legal/java.prefs
/opt/simplejavafxapp/lib/runtime/legal/java.prefs/LICENSE
/opt/simplejavafxapp/lib/runtime/legal/java.scripting
/opt/simplejavafxapp/lib/runtime/legal/java.scripting/LICENSE
/opt/simplejavafxapp/lib/runtime/legal/java.xml
/opt/simplejavafxapp/lib/runtime/legal/java.xml/bcel.md
/opt/simplejavafxapp/lib/runtime/legal/java.xml/dom.md
/opt/simplejavafxapp/lib/runtime/legal/java.xml/jcup.md
/opt/simplejavafxapp/lib/runtime/legal/java.xml/xalan.md
/opt/simplejavafxapp/lib/runtime/legal/java.xml/xerces.md
/opt/simplejavafxapp/lib/runtime/legal/jdk.unsupported
/opt/simplejavafxapp/lib/runtime/lib
/opt/simplejavafxapp/lib/runtime/lib/classlist
/opt/simplejavafxapp/lib/runtime/lib/jexec
/opt/simplejavafxapp/lib/runtime/lib/jrt-fs.jar
/opt/simplejavafxapp/lib/runtime/lib/jspawnhelper
/opt/simplejavafxapp/lib/runtime/lib/jvm.cfg
/opt/simplejavafxapp/lib/runtime/lib/libawt.so
/opt/simplejavafxapp/lib/runtime/lib/libawt_headless.so
/opt/simplejavafxapp/lib/runtime/lib/libawt_xawt.so
/opt/simplejavafxapp/lib/runtime/lib/libfontmanager.so
/opt/simplejavafxapp/lib/runtime/lib/libjava.so
/opt/simplejavafxapp/lib/runtime/lib/libjavajpeg.so
/opt/simplejavafxapp/lib/runtime/lib/libjawt.so
/opt/simplejavafxapp/lib/runtime/lib/libjimage.so
/opt/simplejavafxapp/lib/runtime/lib/libjli.so
/opt/simplejavafxapp/lib/runtime/lib/libjsig.so
/opt/simplejavafxapp/lib/runtime/lib/libjsound.so
/opt/simplejavafxapp/lib/runtime/lib/liblcms.so
/opt/simplejavafxapp/lib/runtime/lib/libmlib_image.so
/opt/simplejavafxapp/lib/runtime/lib/libnet.so
/opt/simplejavafxapp/lib/runtime/lib/libnio.so
/opt/simplejavafxapp/lib/runtime/lib/libprefs.so
/opt/simplejavafxapp/lib/runtime/lib/libsimdsort.so
/opt/simplejavafxapp/lib/runtime/lib/libsplashscreen.so
/opt/simplejavafxapp/lib/runtime/lib/libsyslookup.so
/opt/simplejavafxapp/lib/runtime/lib/libverify.so
/opt/simplejavafxapp/lib/runtime/lib/libzip.so
/opt/simplejavafxapp/lib/runtime/lib/modules
/opt/simplejavafxapp/lib/runtime/lib/psfont.properties.ja
/opt/simplejavafxapp/lib/runtime/lib/psfontj2d.properties
/opt/simplejavafxapp/lib/runtime/lib/security
/opt/simplejavafxapp/lib/runtime/lib/security/blocked.certs
/opt/simplejavafxapp/lib/runtime/lib/security/cacerts
/opt/simplejavafxapp/lib/runtime/lib/security/default.policy
/opt/simplejavafxapp/lib/runtime/lib/security/public_suffix_list.dat
/opt/simplejavafxapp/lib/runtime/lib/server
/opt/simplejavafxapp/lib/runtime/lib/server/libjsig.so
/opt/simplejavafxapp/lib/runtime/lib/server/libjvm.so
/opt/simplejavafxapp/lib/runtime/lib/tzdb.dat
/opt/simplejavafxapp/lib/runtime/release
Upvotes: 3
Views: 491
Reputation: 46255
TL;DR: Remove the --module-path
and --add-modules
arguments from your jpackage
command. Additionally, create a new main class that simply launches the JavaFX application (when JavaFX is on the class-path, the main class cannot be a subtype of Application
). You should also fix your --input
argument so that you pass a directory that contains only what you need (in your case, a directory that contains only the shaded JAR file).
That said, you ideally should be linking JavaFX into the modular run-time image created by jlink
. See trashgod's answer.
Your error is:
Caused by: java.lang.RuntimeException: No toolkit found
at [email protected]/com.sun.javafx.tk.Toolkit.getToolkit(Unknown Source)
at [email protected]/com.sun.javafx.application.PlatformImpl.startup(Unknown Source)
at [email protected]/com.sun.javafx.application.PlatformImpl.startup(Unknown Source)
at [email protected]/com.sun.javafx.application.LauncherImpl.startToolkit(Unknown Source)
at [email protected]/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(Unknown Source)
at [email protected]/com.sun.javafx.application.LauncherImpl.launchApplication(Unknown Source)
... 3 more
A "no toolkit found" error typically means the native libraries needed by JavaFX are missing entirely, included incorrectly, or are for a different platform than the current host.
The com.sun.glass.utils.NativeLibLoader
class in the javafx.graphics
module handles finding and loading the JavaFX native libraries. As I understand it, that class searches in the following locations, in the following order, for the native libraries:
Location relative to java.home
if and only if the NativeLibLoader
class was loaded from a modular run-time image (i.e., if JavaFX was linked into a custom JRE via jlink
).
Location relative to the JAR file containing NativeLibLoader
. This is primarily used to load the native libraries when using a JavaFX SDK.
An embedded resource. This is primarily used to load the native libraries when using the JavaFX artifacts from Maven Central.
Note the native library is extracted to some location before it's loaded. The location can be customized by setting the javafx.cachedir
system property. By default, the location will be under ~/.openjfx
(if the default or custom location cannot be used then it will attempt to use a temporary directory).
A location from the java.library.path
system property (searched in order).
Calling System#loadLibrary(String)
.
Warning: The steps described above are an implementation detail. Or if this is documented then I'm not aware of it.
You are creating a weird application image with jlink
/ jpackage
.
You are shading all dependencies into your application's JAR file. This includes the JavaFX dependencies declared in your POM. The JavaFX artifacts from Maven Central embed the platform-specific native code as a resource.
You are separately including JavaFX in the modular run-time image created by jlink
(used implicitly by jpackage
in your case). You're doing this by pointing at JavaFX on the --module-path
and adding JavaFX modules via --add-modules
. But you are pointing at a JavaFX SDK, and those JAR files do not embed the native libraries as a resource.
The first point indicates the native libraries should be found. They are embedded as resources in your shaded JAR file, and therefore should be extracted and loaded at run-time. But the second point interferes with this. Java seems to prefer loading classes from modules; or at least, it seems to prefer loading classes from the JRT image. So, if a class is both on the module-path and the class-path, then the class will be loaded from the module-path into a named module.
This means JavaFX is searching for the native library in a JavaFX module linked into the JRT image. But those modules came from the JavaFX SDK and thus does not contain the native libraries. And due to how JavaFX attempts to find the resources, it won't fall back to searching the class-path. Which means the native libraries won't be found despite them being included in your shaded JAR file.
You are passing --input target
. This includes the entire target
directory in your application image. Not only does this include unnecessary things in general, but it also means you are including three copies of your project's code (the standard JAR file, the shaded JAR file, and the classes/
directory).
You should be creating a directory specially for --input
and only adding what you need under it.
The solution is to only include JavaFX once. You should either be including JavaFX on the class-path or be linking it into the JRT image, not both.
This is the ideal solution. The answer by trashgod shows how to do this using Maven with the javafx-maven-plugin
plugin. Note if your project and all its dependencies are modular, then I recommend you link all modules into a JRT image. Otherwise, you should at least link JavaFX (and any modules needed from the JDK), then configure jpackage
to put everything else on the class-path via --input
.
If you want the cleanest run-time image, then I recommend using the JavaFX JMOD files instead of the Maven Central JAR files when linking. You can get the JMOD files separately from Gluon. But the easiest way is to use a JDK that includes JavaFX such as LibericaFX by BellSoft, or ZuluFX by Azul Systems. If you use a JDK that includes JavaFX then the JMOD files will be used automatically; otherwise, you have to explicitly point at them.
If you use the JARs instead of the JMODs, then you must use the JARs from Maven Central (as they embed the native libraries).
Warning: This is an unsupported configuration. Though I'm not aware of any critical problems caused by this configuration as of JavaFX 22.
To include JavaFX on the class-path with jpackage
, you need to have the JavaFX classes and resources in one or more JARs under the directory you pass to --input
. You are accomplishing this by shading JavaFX into your application's JAR file and including that JAR file. But you could instead copy the JavaFX dependencies into the directory your pass to --input
if you want to keep things separate.
In this case, there are a few things to note:
You should remove your module-info.java
file since it serves no purpose when your application will always be loaded from the class-path.
Do not separately include JavaFX in the JRT image.
You must use the JavaFX artifacts from Maven Central (as they embed the native libraries).
The main class cannot be a subtype of javafx.application.Application
. Create a separate main class that simply launches the JavaFX application:
import javafx.application.Application;
public class Launcher {
public static void main(String[] args) {
Application.launch(YourAppClass.class, args);
}
}
Upvotes: 4
Reputation: 205875
Expanding on @jewelsea's guidance, consider using openjfx and starting from an archetype as shown here:
mvn -B archetype:generate \
-DarchetypeGroupId=org.openjfx \
-DarchetypeArtifactId=javafx-archetype-fxml \
-DarchetypeVersion=0.0.6 \
-DgroupId=org.openjfx \
-DartifactId=sample \
-Dversion=1.0.0 \
-Djavafx-version=21
Edit the generated pom.xml
to specify your preferred Java version, and configure the desired javafx-maven-plugin
options for jlink
:
<configuration>
<mainClass>org.openjfx/org.openjfx.App</mainClass>
<stripDebug>true</stripDebug>
<compress>2</compress>
<noHeaderFiles>true</noHeaderFiles>
<noManPages>true</noManPages>
<launcher>sample</launcher>
<jlinkImageName>sample</jlinkImageName>
<jlinkVerbose>false</jlinkVerbose>
</configuration>
Invoke jlink
and execute the image:
mvn javafx:jlink
./target/sample/bin/sample
Now, jpackage
can use the --runtime-image
:
jpackage --type app-image -n Sample --runtime-image target/sample -m org.openjfx/org.openjfx.App
This more elaborate example uses the same idea.
Pros:
Straightforward, end-to-end packaging of a modular application.
Easily incorporates dependent modules.
Wide IDE support for maven archetypes used to initialize projects; NetBeans is illustrated here and here.
Potential for local customization of existing archetypes via git clone
and mvn install
.
Cons:
Because jlink
assembles modules to build a run-time image, it does not work with dependencies in an automatic module.
Alternatives are more flexible but correspondingly more complex. See these sections on Packaging and Deployment.
Upvotes: 4
Reputation: 159566
There are some issues with your approach:
ea
version software (I don't advise that - despite it being an official openjfx.io project publication). However, I note your version info is 22.0.1
, which differs from the project you linked, which is confusing. It is always better to include the info needed (in this case the pom.xml) in the question itself rather than linking to a github repository that can change and outdate your question.The linked project is ONLY depending on javafx components and no other third-party libraries, so my advice is this:
I didn't try the above approach, (I don't have the right systems available to test it anyway), so you may need to modify it a bit (e.g. the JDKs from Zulu and Liberica probably ship with jmods in a directory, which you might need to specify explicitly - though I don't think you will). But I guess that it will work.
There are other ways to solve your issue (for instance trashgod's excellent answer) that I won't discuss here.
FAQ
on inspecting the content generated rpm shown below, I cannot find javafx libraries even if I included them when generating the rpm using jpackage
If this is done correctly, then you won't find JavaFX files listed in your RPM, just like you don't find other JDK modules listed there. That is because the packaging process links all of the required modules (whether they be JDK, or JavaFX, or third party, or your own modules) into a single jimage.
What if I need third party non-modular dependencies?
If this is just a demo project and your actual project includes dependencies (like Spring 6) that are automatic modules (don't define module-info), then you could still adopt an adaptation of the approach, by making your application non-modular, and shading your app and jar dependencies (which would not include the JavaFX modules, because those aren't in your pom.xml anymore), and adding the JavaFX modules via command line switches. I won't include a complete demo of how you would do that.
Upvotes: 4