Maven Shade JavaFX runtime components are missing
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.
Bitclef
Updated on October 03, 2021Comments
-
Bitclef over 2 years
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>
-
Bitclef over 5 yearsWow you're absolutely incredible! Thank you so much for your help.
-
José Pereda over 5 yearsGlad of being helpful. Make sure you mark the answer as accepted, as it might be also useful to others in the near future.
-
Ryan Michela about 5 yearsI found you can bundle only the needed
javafx-graphics
implementation using maven profiles with os specific activations. stackoverflow.com/questions/19023109/… -
james.sw.clark about 5 yearsSuper helpful, especially when combined with stackoverflow.com/a/54065502/4261132 for gradle Compile dependencies (n.b. comment them out if not doing a Shadow build!)
-
Piotr G over 4 yearsHi, this is what I need. Unfortunately, I followed your cross-platform solution and it is not exactly working, which I described here: stackoverflow.com/questions/58769916/…. Please have a look.
-
Jamshaid over 4 yearsCreating a
NewMain.java
worked for me in Apache Netbeans 11 with java 11. -
Admin almost 4 years@JoséPereda hello! Your answer on creating a cross platform jar helped, and I can now run the jar both on windows and linux at least. I am concerned over one error by the shade pluging about overlapping classes though, could you explain what it means? pastebin.com/58Ktccnp
-
José Pereda almost 4 years@DiscapeTanki It means that the same Java classes are present in the Windows/Linux/Mac jars, and the shadow plugin is picking only one of them. This shouldn't be a problem as the codebase is the same.
-
Admin almost 4 years@JoséPereda What are the differences between the different classifier versions then? Also is there a way to suppress the warning?
-
Celeste Capece almost 4 yearsHad this same problem on IntelliJ with Gradle, Kotlin and TornadoFX, you saved my life!
-
Cheetaiean over 3 yearsI wish your comment was the answer under all the JavaFX troubleshooting questions. I have spent literal weeks trying to make things work.
-
jewelsea over 2 yearsOne of the highest voted answers for the
javafx
tag, and yet it documents how to perform a hack to run JavaFX in an unsupported configuration :-(. JavaFX 11+ releases are created as a set of Java modules designed to be run off the module path only. Bundling multiple modules in a single jar is not possible. This hack can allow JavaFX to be placed on the classpath rather the module path, which usually works currently, but is not supported. -
José Pereda over 2 years@jewelsea I agree with you that ideally JavaFX should run only on the module path, and that fat jars should be totally discouraged. The JavaFX plugins try to do so, but it is not always possible (legacy dependencies for instance, jlink...). This answer doesn't advocate for the use of fat jars and classpath, but simply explains how it can be done for those that need it. There has been long discussions about this topic, but it is definitely not solved yet... The number of votes and views of this post confirms it.
-
jewelsea over 2 yearsMy main issue with this answer is that, although "fat jars should be totally discouraged", the answer doesn't do that at all. I know that this is not the place for a full discussion on this topic, and that is not my intention here. However, I do think that this answer should be edited to highlight this very important point.
-
José Pereda over 2 years@jewelsea I've edited the answer.
-
jewelsea over 2 yearsI think if you want to be able to run your fat jar on new generation (non-intel) macs, you need additional dependencies for
<classifier>mac-aarch64</classifier>
as well as the<classifier>mac</classifier>
. I don't know for sure though as I haven't tried it and don't have the right kind of mac to test it on if I did.