I have a very simple Java class, src/main/java/webbrowser/WebBrowser.java:
package webbrowser;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.web.WebView;
import javafx.scene.web.WebEngine;
import javafx.stage.Stage;
public class WebBrowser extends Application {
@Override
public void start(Stage stage) {
WebView browser = new WebView();
WebEngine webEngine = browser.getEngine();
webEngine.load("http://www.oracle.com");
Scene scene = new Scene(browser, 1200, 900);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
I have a pom.xml file that lists the JavaFX dependencies and configures the assembly plugin:
<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>webbrowser</groupId>
<artifactId>WebBrowser</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>WebBrowser</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-web</artifactId>
<version>18-ea+9</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>18-ea+9</version>
</dependency>
</dependencies>
<build>
<finalName>WebBrowser</finalName>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<archive>
<manifest>
<mainClass>
webbrowser.WebBrowser
</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
When I run on the command-line, mvn clean install assembly:single, it says "BUILD SUCCESS" and there are no warnings or errors in the log.
Then I run:
% java -jar target/WebBrowser-jar-with-dependencies.jar
Error: JavaFX runtime components are missing, and are required to run this application
For some reason, the mvn assembly:single plugin did not include every JavaFX dependency.
However, when I run this on the command-line, it works fine and launches the application:
% java --module-path $JAVAFX --add-modules javafx.controls,javafx.web -jar target/WebBrowser-jar-with-dependencies.jar
Questions
target/WebBrowser-jar-with-dependencies.jar?target/WebBrowser-jar-with-dependencies.jar file?Recommended: Change your packaging approach
Don't try to create a single jar for your application.
There are links to packaging resources under the:
I suggest you review the information there, then choose and implement an appropriate (and different) packaging solution for your application.
Understanding the error message: JavaFX runtime components are missing
It means the JavaFX runtime is not accessible to your application. JavaFX modules should be on your module path, which is currently not possible if you put them in your application jar.
See the Eden coding tutorial for some information on this and some basic solutions (though it does not cover packaging apps in general as that is a very complex topic):
To address your specific questions
1 Why javafx dependencies aren't in the jar-with-dependencies.jar
I copied your code and tried it locally on a Windows machine with OpenJDK 17.
A jar-with-dependencies was built and the javafx Windows libraries were included in the jar. You can see this by running jar tvf on the jar file. It includes both the JavaFX platform class files and the Windows native DLLs.
But when the jar was built, there was an info message:
[INFO] module-info.class already added, skipping
This is key. Each modular JavaFX jar has its own module-info file which is required for it to function in a modular environment. When some are skipped, then the modularity is broken, and the app will no longer run off the module path.
It would be nice if the Maven assembly plugin was clever enough to recognize modular jars are in the dependencies and explicitly warn about that when asked to create a single jar-with-dependencies, because the java runtime does not currently support multi-module jars.
To understand more about why you can't currently put multiple Java platform modules in a single jar, see:
2 Creating a jar-with-dependencies
You could switch from the assembly plugin to the shade plugin and use the solution at:
But you don't have to, you can continue using the assembly plugin for this if that is what you really want to do.
If you do so, understand the warning in the link that then you are running JavaFX in an unsupported configuration off the classpath rather than the module path.
To do this with an assembly:
Add a launcher class to trick the JavaFX runtime into allowing itself to run from the classpath instead of the module path.
package webbrowser;
public class WebBrowserLauncher {
public static void main(String[] args) {
WebBrowser.main(args);
}
}
Change the main class defined for your assembly to reference the new class:
<mainClass>
webbrowser.WebBrowserLauncher
</mainClass>
If you want to allow the app to run on different operating systems than the one you are building it on, then add dependencies for those platforms to your pom.xml. For an example of the additional dependencies required, see:
I made changes 1&2 and tested them and your app packaged this way ran for me. I didn't bother with change 3 as I can't easily test that at this time.
Execution command and output:
java -jar target/WebBrowser-jar-with-dependencies.jar
Jan 21, 2022 7:59:51 PM com.sun.javafx.application.PlatformImpl startup
WARNING: Unsupported JavaFX configuration: classes were loaded from 'unnamed module @41dc7fe'
The warning exists because JavaFX is being loaded from the classpath rather than the module path.
Assembling multiple jars into a zip or tar archive
Assembly could still be a useful plugin to use in your build process, but for a different purpose.
If you did want to stick with an assembly approach, you could:
The user would then be able to decompress your archive and use your launcher script to run the app (as long as they had an appropriate JVM preinstalled).
Assembly would be good for that, but jlink or jpackage may be preferred alternatives regardless as those options don't require a preinstalled JVM.
The openjfx-maven-plugin has a similar option in its jlink feature but includes a linked java runtime:
jlinkZipName: When set, creates a zip of the resulting runtime image.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With