Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Maven Shade JavaFX runtime components are missing

I'm trying to create a JFX11 self-containing jar using maven dependencies. From the research I've done, it seems the best way to do this is through the maven shade plugin. However, When I run it, I get the this error:

Error: JavaFX runtime components are missing, and are required to run this application

I don't understand why this is happening. What am I messing up? Is there a better way to do this? I've also tried the maven assembly plugin with the same message.

pom file for reference

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>Application</groupId>
    <artifactId>Main</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>SpaceRunner</name>
    <url>http://maven.apache.org</url>
    <dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>11</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <release>10</release>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.6.0</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>java</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <mainClass>Application.Main</mainClass>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>
                                Application.Main
                            </mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.0</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>Application.Main</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
like image 238
Bitclef Avatar asked Oct 04 '18 19:10

Bitclef


People also ask

What is Maven Shade plugin?

This plugin provides the capability to package the artifact in an uber-jar, including its dependencies and to shade - i.e. rename - the packages of some of the dependencies.

Is JavaFX included in Java 13?

It's no longer shipped with the JDK, you need to install it separately (it's now a distinct product called OpenJFX). Show activity on this post. JavaFX is no longer provided in the builds after Java 10 ; however commercial support for JavaFX in JDK 8 (LTS) will continue through at least 2022.

What does Maven shaded mean?

Together literally it means "jar-over-all-other-jars". "Shading" is the same as "package reallocation" which is needed for classes or resources that collide.


1 Answers

UPDATE 10/2021

Since JavaFX 16 a warning is displayed when JavaFX doesn't run on the module path, which is the case of an uber/fat jar:

$ java -jar myFatJar-1.0-SNAPSHOT.jar Oct 02, 2021 1:45:21 PM com.sun.javafx.application.PlatformImpl startup WARNING: Unsupported JavaFX configuration: classes were loaded from 'unnamed module @14c24f4c' 

Also, you get a warning from the shade plugin itself:

[WARNING] Discovered module-info.class. Shading will break its strong encapsulation. 

While these warnings can be initially ignored, there is a reason for them.

As explained in this CSR:

JavaFX is built and distributed as a set of named modules, each in its own modular jar file, and the JavaFX runtime expects its classes to be loaded from a set of named javafx.* modules, and does not support loading those modules from the classpath.

And:

when the JavaFX classes are loaded from the classpath, it breaks encapsulation, since we no longer get the benefit of the java module system.

Therefore, even this widely accepted answer explains how can an uber/fat jar can be created on Maven projects, its use is discouraged, and other modern alternatives to distribute your application, like jlink, jpackage or native-image, should be used.

ORIGINAL ANSWER

This answer explains why a fat/uber jar fails on JavaFX 11. In short:

This error comes from sun.launcher.LauncherHelper in the java.base module. The reason for this is that the Main app extends Application and has a main method. If that is the case, the LauncherHelper will check for the javafx.graphics module to be present as a named module. If that module is not present, the launch is aborted.

And already proposes a fix for Gradle.

For Maven the solution is exactly the same: provide a new main class that doesn't extend from Application.

You will have new class in your application package (bad name):

// NewMain.java public class NewMain {      public static void main(String[] args) {         Main.main(args);     } } 

And your existing Main class, as is:

//Main.java public class Main extends Application {      @Override     public void start(Stage stage) {         ...     }      public static void main(String[] args) {         launch(args);     } } 

Now you need to modify your pom and set your main class for the different plugins:

<mainClass>application.NewMain</mainClass> 

Platform-specific Fat jar

Finally, with the shade plugin you are going to produce a fat jar, on your machine.

This means that, so far, your JavaFX dependencies are using a unique classifier. If for instance you are on Windows, Maven will be using internally the win classifier. This has the effect of including only the native libraries for Windows.

So you are using:

  • org.openjfx:javafx-controls:11
  • org.openjfx:javafx-controls:11:win
  • org.openjfx:javafx-graphics:11
  • org.openjfx:javafx-graphics:11:win <-- this contains the native dlls for Windows
  • org.openjfx:javafx-base:11
  • org.openjfx:javafx-base:11:win

Now, if you produce the fat jar, you will bundle all those dependencies (and those other regular third party dependencies from your project), and you will be able to run your project as:

java -jar myFatJar-1.0-SNAPSHOT.jar 

While this is very nice, if you want to distribute you jar, be aware that this jar is not cross-platform, and it will work only on your platform, in this case Windows.

Cross-Platform Fat Jar

There is a solution to generate a cross-platform jar that you can distribute: include the rest of the native libraries of the other platforms.

This can be easily done, as you just need to include the graphics module dependencies for the three platforms:

<dependencies>     <dependency>         <groupId>org.openjfx</groupId>         <artifactId>javafx-controls</artifactId>         <version>11</version>     </dependency>     <dependency>         <groupId>org.openjfx</groupId>         <artifactId>javafx-graphics </artifactId>         <version>11</version>         <classifier>win</classifier>     </dependency>     <dependency>         <groupId>org.openjfx</groupId>         <artifactId>javafx-graphics </artifactId>         <version>11</version>         <classifier>linux</classifier>     </dependency>     <dependency>         <groupId>org.openjfx</groupId>         <artifactId>javafx-graphics </artifactId>         <version>11</version>         <classifier>mac</classifier>     </dependency> </dependencies> 

Size

There is a main issue with this approach: the size. As you can see in this other answer, if you use the WebView control, you will be bundling around 220 MB due to the WebKit native libraries.

like image 179
José Pereda Avatar answered Sep 23 '22 21:09

José Pereda