Noor
Noor

Reputation: 20178

jpackage executable fails to run

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:

  1. Part of the pom.xml containing definition for the shaded plugin
  2. Execution of the shaded jar
  3. Content of the shaded jar
  4. Generation of rpm using jpackage
  5. Error when installating app from rpm
  6. Content of the generated rpm

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

Answers (3)

Slaw
Slaw

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.


The Error

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.

Loading JavaFX's Native Libraries

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:

  1. 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).

  2. Location relative to the JAR file containing NativeLibLoader. This is primarily used to load the native libraries when using a JavaFX SDK.

  3. 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).

  4. A location from the java.library.path system property (searched in order).

  5. 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.

Why Your Error Occurs

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.

Other Issues

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.


Solution

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.

Link JavaFX into the JRT Image

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).

Include JavaFX on the Class-Path

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

trashgod
trashgod

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 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

jewelsea
jewelsea

Reputation: 159566

There are some issues with your approach:

  1. The linked pom.xml uses 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.
  2. The linked pom.xml includes javafx components. If you shade them into your jar then JavaFX will run an unsupported mode off the classpath (I don't recommend that - and it could be what is contributing to the breakage when you attempt to run your application).

The linked project is ONLY depending on javafx components and no other third-party libraries, so my advice is this:

  1. Don't shade your jar at all.
  2. Make your app modular (include a module-info for it, that requires the necessary JavaFX modules).
  3. Remove the javafx dependencies from your pom.xml.
  4. Use a JDK that includes JavaFX (e.g. Zulu "JDK FX" or Liberica "Full JDK). As you are generating an RPM, then the JDK used should be a version the runs on your target system and architecture (e.g. a Linux system that understands RPM).
  5. The standard Maven packaging will then create a modular (non-shaded) jar for your application.
  6. For the jpackage command, because you are now using a JDK that includes JavaFX and a modular app that defines the modules you need, then you don't need to add modules or set an additional module path to the JavaFX SDK (you don't need the JavaFX SDK at all in such a setup, I advise deleting it to prevent confusion).
  7. For this setup, jpackage will pick up the JavaFX modules from the JDK, which will include the necessary native components (which your current approach seems to be either missing or cannot find).

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

Related Questions