I need to compile a JavaFX 20 application into an executable Jar file that includes all the dependencies listed in the pom.xml (including javaFX itself and all the other JAR files). My project is based on Maven. I tried many different maven plugins but none of them were able to make the final jar executable by double clicking, although I was able to run in in the command line with the java -jar command. The intention is to distribute this app with next/next/finish installers on Linux, Windows and MacOS. The end user profile is a lab researcher with low IT knowledge (I work for a NPO that helps protecting the Amazon forest). Is there an objective way to do this?
I already tried many maven plugins with different goals (resources, dependencies, shade, compiler, etc) but no success at all.
Self-Contained Application
Your desire to create an installer lends itself to creating an entirely self-contained application. That is an application that bundles not only your code and its third-party dependencies, but also the Java Run-Time Environment itself. Your end user won't need to install Java separately.
GraalVM Native Image
One option for creating a self-contained application is GraalVM's native image. Note that this will also ahead-of-time compile your Java code to native code.
I have very little experience with GraalVM, so I won't give an example, but here are some links that may help:
Create native JavaFX applications using GraalVM 22 builds from Gluon
How to create JavaFX native images | BellSoft Java
jpackage
A relatively accessible tool for creating a self-contained applications and installers is
jpackage. It comes with the JDK since version 16. See the Packaging Tool User Guide for more information. It would also help to understandjlink, asjpackagewill either usejlink"behind the scenes" to generate a custom run-time image, or you'll usejlinkexplicitly and then providejpackagewith the custom run-time image.One significant downside to using
jpackage, however, is that it can only create application images and installers for the operating system it is running on. If you want a Windows application, then you'll have to build your project on Windows, with the same being true for Linux and macOS. And as jewelsea explains in a comment, there are also platform-specific issues you'll have to resolve:If you're going to create a self-contained application, then I would suggest you forgo creating a fat/uber/shadowed JAR file. A self-contained application already contains all dependencies, so a shadowed JAR file is unlikely to provide any benefits. Plus, if you're going to be using
jpackageto create the installer, you might as well design your entire deployment aroundjpackagefrom the start.Using Maven profiles can solve a lot of the platform-specific configuration issues. While any remaining platform-specific configuration, as well as building the application on multiple platforms and publishing it automatically, can likely be accomplished with some kind of CI/CD pipeline. Though remember to never commit any secrets to your VCS (e.g., Git), which by extension means do not put any secrets in the POM file(s).
Example
Here is a POM that is configured to create an
msiinstaller file when thewindowsprofile is activated. Note there is only configurations for Windows in this example. The org.panteleyev:jpackage-maven-plugin plugin is used to executejpackage.This POM is designed for a non-modular application. As such, it's configured so that the JavaFX modules are added to the custom run-time image, while the project code and any other dependencies are configured to be placed on the class-path via
--inputand--main-jar. By having JavaFX in the custom run-time image, it will implicitly be on the module-path.Disclaimer: I'm more familiar with Gradle than Maven, so there may be ways to simplify the following POM.
And here's an example
com.example.app.Mainclass to go along with the POM:To create the
msifile, execute:And you should get a
target/jpackage/windows-msi/app-1.0.msiinstaller file.Note you must execute that on Windows, and you must have WiX Toolset 3.x.x installed (version 4.x.x doesn't seem to work with
jpackage, or at least I couldn't get it to work). If you don't want to install WiX 3.x.x just for running the example, then change the type to<type>APP_IMAGE</type>. You won't get an installer file, but you'll end up with an application image that you can run as is.Executable JAR
You can create an executable fat/uber/shadowed JAR file with JavaFX via Maven using the Maven Shade Plugin. Note this will inherently put everything on the class-path. Any code that relies explicitly on modules may break with this setup, though such code is rare. Additionally, JavaFX does not technically support being loaded from the class-path. Doing so will not break anything as of JavaFX 21, as far as I know, but any issues caused by this configuration are unlikely to be fixed by the JavaFX developers.
See David Weber's answer for an example using the Maven Shade Plugin.
If you want to go with a shadowed executable JAR file as your deployment strategy, then I suggest you forgo creating an installer. Just have your end user install the appropriate version of Java (you can send them the installer for that), and then they can just double-click the JAR file to make it run. Make sure your end user enables associating
*.jarfiles with Java if the Java installer gives that option.If you want a cross-platform executable JAR file, you'll have to declare a JavaFX dependency for each platform manually. JavaFX relies on platform-specific native code, and the Maven artifacts embed that native code in the JAR file, but only for a particular platform. For instance,
javafx-graphics-21.0.1-win.jarcontains native code for Windows, but not Linux or macOS.Note that when JavaFX is on the class-path, as is the case here, then your main class cannot be a subclass of
javafx.application.Application. You must have a separate "launcher class" as the main class. Something like: