Written by José Pereda and Johan Vos
Creating a Java client application is only the first step in the lifecycle of the application. Your application will be used by end users who interact with it. Whether your application is intended for internal, intranet-only usage or for a wide public audience, you want the barrier to use your application to be as low as possible. End users don’t like complex procedures before they can start working with an application.
In this chapter, we will explain how you can make it easy for the end user to install and use your application by using the same approach as they use for installing other (non-Java) applications. We will explain two important modern tools that are developed for helping Java developers to create bundled applications that fulfil the expectations of your users.
Web vs. Desktop Applications
When you create a Java client application, or by extension any client application, the end user will access your application directly. This is very different from creating cloud or web-based applications, where your code is probably running on a cloud or server infrastructure and the end user accesses your code typically via a web browser. In that case, you have to provision your application to a cloud or server, and there are specific tools that help you with this task.
Evolutions in Application Deployment
In the past, most deployment of Java client applications required a clear separation between your application code, its dependencies, and the Java Virtual Machine (JVM) it was required to run. Starting with Java Applets in 1995 and later with Java Web Start, developers of Java client applications could assume that there was some Java Virtual Machine already installed on the end user system. If this was not the case, the developer could assume some tool existed to assist the end user in installing a JVM.
The rationale behind this concept was clear: when there are multiple Java applications running on a client, it makes sense to have a single JVM installation to provide the foundation for the different applications. With Java’s strong focus on backward compatibility, it was also often fair to assume that your applications would still run with later versions of the JVM. As a consequence, a single JVM on the end user system would be enough to serve the needs of all Java client applications. As a result, this concept requires less disk space (the JVM is shared between applications) and less bandwidth (the JVM code does not need to be transferred with the application; it is assumed to be present).
Typical disk space and bandwidth are increasing. Today’s average users are used to higher volumes of both storage capacity and streaming capacity. One of the drivers for this is the increase in media streaming and storage. Compared to the requirements of typical streaming services, the transmission and storage of the JVM code is relatively small. Hence, the argument that the JVM code should not be sent with the application or stored with the application because this requires too much bandwidth or storage capacity is becoming less relevant.
The changed release cadence of the Java Development Kit would put even more responsibilities for the end users. Many end users find it very annoying to see a popup on their PC asking to upgrade to a newer version of Java. Their applications might be using Java, but this should be transparent to the end user. While different applications might still run on older JVMs, developers often want to use new features in more recent JVMs for their applications. That would lead to even more popups annoying the end user.
The concept of application stores, or app stores, changed the mobile landscape and is also gaining traction on the desktop. Using app stores, end users simply decide to install an application using a uniform “click and install” concept. Everything that is required to execute the application is assumed to be part of that application. The end user doesn’t need to manage required components (e.g., a JVM) that are not adding functionality to him.
The Java ecosystem clearly saw this evolution. The JavaFX components used to contain a “javafxpackager” tool that allowed developers to package their JavaFX application, including a Java Virtual Machine and all dependencies, for different platforms (Linux, Windows, Mac OS X).
With the Java Platform Module System, where the JVM itself is broken up into a number of modules, the situation changed. It is now possible to create sub-versions of the JVM that contain only the modules that a specific application needs. Since packaging is relevant to all applications (not just client applications that require a user interface), a new Java packaging tool is part of the JDK since Java 14. We will discuss this tool, called jpackage in the next section.
Apart from jpackage, there is another tool that allows you to bundle your application into a self-contained native application, the GraalVM’s Native Image. With this tool, the application and its dependencies are compiled ahead of time into native applications. We discuss this tool later in this chapter.
The jpackage Tool
What Is jpackage?
Starting with JDK 14, the jpackage tool will be part of the JDK distributions. The specification for jpackage is JEP 343: https://openjdk.java.net/jeps/343.
The summary of this JEP is clear:
Create a new tool for packaging self-contained Java applications.
This summary matches what we discussed earlier in this chapter: we want to create self-contained Java applications, which means that the JVM should be included in the application.
The goals of the JEP, according to the web site, are the following:
Supports native packaging formats to give the end user a natural installation experience. These formats include msi and exe on Windows, pkg and dmg on Mac OS X, and deb and rpm on Linux.
Allows launch-time parameters to be specified at packaging time.
Can be invoked directly, from the command line, or programmatically, via the ToolProvider API.
From this, it is clear that the jpackage tool builds on top of the JavaFX packaging tool that used to be part of JavaFX 8. Also, the jpackage tool is designed to support existing and common native packaging formats for the popular desktop operating systems. Hence, Java applications created with jpackage can be installed similar to how most other native applications can be installed.
- 1.
Thanks to the Write Once, Run Anywhere paradigm, the code that a developer has to write is really platform independent.
- 2.
Thanks to the jpackage tool, although the code itself is platform independent, the deployment procedure is completely in line with a specific platform.
Because jpackage is part of the JDK distribution, it is as easy to invoke as the regular java or javac command. The jpackage executable is located in the same directory as the java and javac executables.
Using jpackage
To start using the jpackage tool, you have to use Java 14; but, as it will be explained later on, you can bundle your application with other versions of the Java runtime.
Non-modular applications that run on the classpath, from one or more jar files
Modular applications with one or more modular jar files or jmod files
Modular applications that have been jlinked into a custom runtime image
Note that, for the two first cases, the tool will run jlink to create a Java runtime for the application and bundle that into the final image. In the third case, the custom runtime you provided will be bundled into the image.
Native application launcher (generated by the tool)
Application resources (like jar, icns, ico, png)
Configuration files (like plist, cfg, properties)
Helper libraries for the launcher
The Java runtime image, for example, the files that are needed to execute Java bytecode
Although jpackage is part of the same directory that also contains the java and javac commands that are platform independent, using jpackage slightly differs based on the platform you are using. This makes sense, since the output of jpackage is very platform specific. Therefore, jpackage can be considered in the middle of the platform-independent java bytecode and the platform-dependent native executable.
Since jpackage is capable of building an executable for the current platform, you have to run jpackage on all platforms you want to support. Cross-compiling a Java application from one platform into native executables for other platforms is not supported.
Jpackage Usage
Jpackage Options
jpackage – generic options
@<filename> | Read options and/or mode from a file. |
---|---|
--package-type <type> | The type of package to create. Valid values are {“exe”, “msi”, “rpm”, “deb”, “pkg”, “dmg”}. If this option is not specified, an application image will be created. |
--app-version <version> | Version of the application and/or package. |
--copyright <copyright string> | Copyright for the application. |
--description <description string> | Description of the application. |
--help or -h | Print the usage text with a list and description of each valid option for the current platform to the output stream and exit. |
--name or -n <name> | Name of the application and/or package. |
--output or -o <output path> | Path where generated output file is placed (absolute path or relative to the current directory). |
--temp-root <file path> | Path of a new or empty directory used to create temporary files (absolute path or relative to the current directory). If specified, the temp-root will not be removed upon the task completion and must be removed manually. If not specified, a temporary directory will be created and removed upon the task completion. |
--vendor <vendor string> | Vendor of the application. |
--verbose | Enables verbose output. |
--version | Print the product version to the output stream and exit. |
jpackage – options for creating the runtime image
--add-modules <module name>[,<module name>...] | A comma (“,”)-separated list of modules to add. This module list, along with the main module (if specified), will be passed to jlink as the --add-module argument. If not specified, either just the main module (if --module is specified) or the default set of modules (if --main-jar is specified) are used. |
--module-path or -p <module path>... | A :- or ;-separated list of paths. Each path is either a directory of modules or the path to a modular jar. (Each path is absolute or relative to the current directory.) |
--runtime-image <file path> | Path of the predefined runtime image that will be copied into the application image (absolute path or relative to the current directory). If --runtime-image is not specified, jpackage will run jlink to create the runtime image using options: --strip-debug, --no-header-files, --no-man-pages, and --strip-native-commands. --bind-services will also be added if --add-modules is not specified. |
jpackage – options for creating the application image
--icon <icon file path> | Path of the icon of the application bundle (absolute path or relative to the current directory). |
--input or -i <input path> | Path of the input directory that contains the files to be packaged (absolute path or relative to the current directory). All files in the input directory will be packaged into the application image. |
jpackage – options for creating the application launcher(s)
--add-launcher <file path> | Path to a properties file that contains a list of key-value pairs (absolute path or relative to the current directory). The keys “name” (required), “module,” “add-modules,” “main-jar,” “main-class,” “arguments,” “java-options,” “app-version,” “icon,” and “win-console” can be used. These options are added to, or used to overwrite, the original command line options to build an additional alternative launcher. The main application launcher will be built from the command line options. Additional alternative launchers may be built using this option. |
--arguments <main class arguments> | Command line arguments to pass to the main class if no command line arguments are given to the launcher. |
--java-options <java options> | Options to pass to the Java runtime. |
--main-class <class name> | Qualified name of the application main class to execute. This option can only be used if --main-jar is specified. |
--main-jar <main jar file> | The main JAR of the application, containing the main class (specified as a path relative to the input path). Either --module or --main-jar option can be specified but not both. |
--module or -m <module name>[/<main class>] | The main module (and optionally main class) of the application. This module must be located on the module path. When this option is specified, the main module will be linked in the Java runtime image. Either --module or --main-jar option can be specified but not both. |
jpackage – platform-dependent options for creating the application launcher
--win-console | Creates a console launcher for the application. Should be specified for the application which requires console interactions. |
--mac-bundle-identifier <ID string> | An identifier that uniquely identifies the application for Mac OS X. Defaults to the value of --identifier option. May only use alphanumeric (A-Z,a-z,0-9), hyphen (-), and period (.) characters. |
--mac-bundle-name <name string> | Name of the application as it appears in the menu bar. This can be different from the application name. This name must be less than 16 characters long and be suitable for displaying in the menu bar and the application info window. Defaults to the application name. |
--mac-bundle-signing-prefix <prefix string> | When signing the application bundle, this value is prefixed to all components that need to be signed that don’t have an existing bundle identifier. |
--mac-sign | Request that the bundle be signed. |
--mac-signing-keychain <file path> | Path of the keychain to use (absolute path or relative to the current directory). If not specified, the standard keychains are used. |
--mac-signing-key-user-name <user name> | Username portion of the typical “Mac Developer ID Application:” signing key. |
jpackage – options for creating the application installer(s)
--app-image <file path> | Location of the predefined application image that is used to build an installable package (absolute path or relative to the current directory). See create-app-image mode options to create the application image. |
--file-associations <file path> | Path to a properties file that contains a list of key-value pairs (absolute path or relative to the current directory). The keys “extension,” “mime-type,” “icon,” and “description” can be used to describe the association. |
--identifier <id string> | An identifier that uniquely identifies the application. Defaults to the main class name. The value should be a valid DNS name. |
--install-dir <file path> | Absolute path of the installation directory of the application on OS X or Linux. Relative sub-path of the installation location of the application such as “Program Files” or “AppData” on Windows. |
--license-file <file path> | Path to the license file (absolute path or relative to the current directory). |
--resource-dir <path> | Path to override jpackage resources. Icons, template files, and other resources of jpackage can be overridden by adding replacement resources to this directory (absolute path or relative to the current directory). |
--runtime-image <file-path> | Path of the predefined runtime image to install (absolute path or relative to the current directory). Option is required when creating a runtime package. |
jpackage – platform-dependent options for creating the application installer(s)
--win-dir-chooser | Adds a dialog to enable the user to choose a directory in which the product is installed. |
--win-menu | Adds the application to the system menu. |
--win-menu-group <menu group name> | Start Menu group this application is placed in. |
--win-per-user-install | Request to perform an install on a per-user basis. |
--win-shortcut | Creates a desktop shortcut for the application. |
--win-upgrade-uuid <id string> | UUID associated with upgrades for this package. |
--linux-bundle-name <bundle name> | Name for Linux bundle. Defaults to the application name. |
--linux-deb-maintainer <email address> | Maintainer for .deb bundle. |
--linux-menu-group <menu-group-name> | Menu group this application is placed in. |
--linux-package-deps | Required packages or capabilities for the application. |
--linux-rpm-license-type <type string> | Type of the license (“License:” of the RPM spec). |
--linux-app-release <release value> | Release value of the RPM .spec file or DEB control file. |
--linux-app-category <category value> | Group value of the RPM .spec file or Section value of the DEB control file. |
--linux-deb-copyright-file <file path> | Path to custom copyright file for Debian packaging (absolute path or relative to the current directory). |
Requirements
The images created by jpackage are not different from other applications developers create for native platforms. Therefore, the same tools that are used to generate native applications for a specific operating system are used by jpackage as well.
exe: Inno Setup, a third-party tool, is required to generate exe installers.
msi: Wix, a third-party tool, is required to generate msi installers.
Inno Setup
Samples
We will now show a few samples that explain how jpackage can be used. The samples themselves are very simple JavaFX applications, and we will not discuss their functionality in this chapter.
Let’s start using jpackage from a terminal window, without build tools or plugins. Since the usage of jpackage is slightly different depending on what platform we are running on, we have to distinguish between using jpackage on Windows, Mac OS, and Linux.
Non-modular Application: Sample1
As a first sample, we explain how to package a Java application that itself is not a module. The application still uses the JavaFX modules, but there is no specific module-info.java in the application itself.
- 1.
Define a number of environment variables.
- 2.
Compile the JavaFX application into Java bytecode.
- 3.
Run and test the application.
- 4.
Create a single jar file, containing the application.
- 5.
Create an installer using jpackage.
Instructions for Windows
These are the required steps if you want to create an installer for a non-modular application, like this one:
https://github.com/modernclientjava/mcj-samples/tree/master/ch10-packaging/Sample1
Clone the sample and, from a terminal, cd into the root.
- 1.Export these environment variables:set JAVA_HOME="C:Users<user>Downloadsjdk-14"set PATH_TO_FX="C:Users<user>Downloadsjavafx-sdk-13lib"set PATH_TO_FX_MODS="C:Users<user>Downloadsjavafx-jmods-13"
Note that if you have a different JDK added to the PATH environmental variable, this will take precedence.
- 2.Compile your application:dir /s /b src*.java > sources.txt & javac --module-path %PATH_TO_FX% --add-modules javafx.controls,javafx.fxml -d out @sources.txt & del sources.txtcopy srcorgmodernclientsscene.fxml outorgmodernclients & copy srcorgmodernclientsstyles.css outorgmodernclients
- 3.Run and test:java --module-path %PATH_TO_FX% --add-modules javafx.controls,javafx.fxml -cp out org.modernclients.Main
- 4.Create a jar:mkdir libsjar --create --file=libssample1.jar --main-class=org.modernclients.Main -C out .
- 5.
Create the installer.
Modifying the Installer
We can add more options to the jpackage command. For instance, we can add the application to the system menu, to create a desktop shortcut, to let the user choose the installation directory, and to use a custom icon based on the Duke image from
https://hg.openjdk.java.net/duke/duke/raw-file/e71b60779736/3D/Duke%20Waving/openduke.png
Mac OS X
These are the required steps if you want to create an installer for a non-modular application, like this one:
- 1.Export these environment variables:export JAVA_HOME=/Users/<user>/Downloads/jdk-14.jdk/Contents/Home/export PATH_TO_FX=/Users/<user>/Downloads/javafx-sdk-13/lib/export PATH_TO_FX_MODS=/Users/<user>/Downloads/javafx-jmods-13/
- 2.Compile your application:javac --module-path $PATH_TO_FX --add-modules javafx.controls,javafx.fxml -d out $(find src -name "*.java")cp src/org/modernclients/scene.fxml src/org/modernclients/styles.css out/org/modernclients/
- 3.Run and test:java --module-path $PATH_TO_FX --add-modules javafx.controls,javafx.fxml -cp out org.modernclients.Main
- 4.Create a jar:mkdir libsjar --create --file=libs/sample1.jar --main-class=org.modernclients.Main -C out .
- 5.Create the installer:$JAVA_HOME/bin/jpackage --package-type dmg -o installer -i libs --main-jar sample1.jar -n Sample1 --module-path $PATH_TO_FX_MODS --add-modules javafx.controls,javafx.fxml --main-class org.modernclients.Main
As a result, you will get Sample1-1.0.dmg (53.6 MB), as shown in Figure 10-8, that can be distributed, and it just requires a double-click to install the application.
Modifying the Installer
Linux
These are the required steps if you want to create an installer for a non-modular application, like this one:
https://github.com/modernclientjava/mcj-samples/tree/master/ch10-packaging/Sample1
- 1.Export these environment variables:export JAVA_HOME=/home/<user>/Downloads/jdk-14/export PATH_TO_FX=/home/<user>/Downloads/javafx-sdk-13/lib/export PATH_TO_FX_MODS=/Users/<user>/Downloads/javafx-jmods-13/
- 2.Compile your application:javac --module-path $PATH_TO_FX --add-modules javafx.controls,javafx.fxml -d out $(find src -name "*.java")cp src/org/modernclients/scene.fxml src/org/modernclients/styles.css out/org/modernclients/
- 3.Run and test:java --module-path $PATH_TO_FX --add-modules javafx.controls,javafx.fxml -cp out org.modernclients.Main
- 4.Create a jar:mkdir libsjar --create --file=libs/sample1.jar --main-class=org.modernclients.Main -C out .
- 5.Create the installer:$JAVA_HOME/bin/jpackage --package-type deb -o installer -i libs --main-jar sample1.jar -n Sample1 --module-path $PATH_TO_FX_MODS --add-modules javafx.controls,javafx.fxml --main-class org.modernclients.Main
Modular Application: Sample2
Our second application is a modular application. The source code can be found at https://github.com/modernclientjava/mcj-samples/tree/master/ch10-packaging/Sample2.
It contains a module-info.java file, and the jpackage tool can process this to deal with the modular dependencies. The module-info file is very simple: it declares dependencies on the javafx.controls and javafx.fxml modules, and it exports the module org.modernclients. Also, the latter module is opened to the javafx.fxml module.
Windows
- 1.Export these environment variables:set JAVA_HOME="C:Users<user>Downloadsjdk-14"set PATH_TO_FX="C:Users<user>Downloadsjavafx-sdk-13lib"set PATH_TO_FX_MODS="C:Users<user>Downloadsjavafx-jmods-13"
- 2.Compile your application:dir /s /b src*.java > sources.txt & javac --module-path %PATH_TO_FX% --add-modules javafx.controls,javafx.fxml -d modsmodernclients @sources.txt & del sources.txtcopy srcorgmodernclientsscene.fxml modsmodernclientsorgmodernclients & copy srcorgmodernclientsstyles.css modsmodernclientsorgmodernclients
- 3.Run and test:java --module-path %PATH_TO_FX%;mods -m modernclients/org.modernclients.Main
- 4.Create custom image:%JAVA_HOME%injlink --module-path %PATH_TO_FX_MODS%;mods --add-modules modernclients --output image
- 5.
Run and test the image:
- 6.Create the installer:%JAVA_HOME%injpackage --package-type exe -o installer -n Sample2 -m modernclients/org.modernclients.Main --runtime-image image
As a result, you will get Sample2-1.0.dmg (19.9 MB) that can be distributed, and it just requires a double-click to install the application.
Mac OS X
- 1.Export these environment variables:export JAVA_HOME=/Users/<user>/Downloads/jdk-14.jdk/Contents/Home/export PATH_TO_FX=/Users/<user>/Downloads/javafx-sdk-13/lib/export PATH_TO_FX_MODS=/Users/<user>/Downloads/javafx-jmods-13/
- 2.Compile your application:javac --module-path $PATH_TO_FX -d mods/modernclients $(find src -name "*.java")cp src/org/modernclients/scene.fxml src/org/modernclients/styles.css mods/modernclients/org/modernclients/
- 3.Run and test:java --module-path $PATH_TO_FX:mods -m modernclients/org.modernclients.Main
- 4.Create custom image:$JAVA_HOME/bin/jlink --module-path $PATH_TO_FX_MODS:mods --add-modules modernclients --output image
- 5.
Run and test the image:
- 6.Create the installer:$JAVA_HOME/bin/jpackage --package-type dmg -o installer -n Sample2 -m modernclients/org.modernclients.Main --runtime-image image
As a result, you will get Sample2-1.0.dmg (65.6 MB) that can be distributed, and it just requires a double-click to install the application.
Linux
- 1.Export these environment variables:export JAVA_HOME=/home/<user>/Downloads/jdk-14/export PATH_TO_FX=/home/<user>/Downloads/javafx-sdk-13/lib/export PATH_TO_FX_MODS=/Users/<user>/Downloads/javafx-jmods-13/
- 2.Compile your application:javac --module-path $PATH_TO_FX -d mods/modernclients $(find src -name "*.java")cp src/org/modernclients/scene.fxml src/org/modernclients/styles.css mods/modernclients/org/modernclients/
- 3.Run and test:java --module-path $PATH_TO_FX:mods -m modernclients/org.modernclients.Main
- 4.Create custom image:$JAVA_HOME/bin/jlink --module-path $PATH_TO_FX_MODS:mods --add-modules modernclients --output image
- 5.
Run and test the image:
- 6.Create the installer:$JAVA_HOME/bin/jpackage --package-type deb -o installer -n Sample2 -m modernclients/org.modernclients.Main --runtime-image image
As a result, you will get Sample2-1.0.deb that can be distributed or installed.
Gradle Projects
The previous samples explained how to use the command line jpackage tool. As with most commands, it often makes sense to use them in existing built tools, for example, Maven or Gradle.
While you can create a task to your build.gradle file, with the required options to run the jpackage tool, there is a plugin that does it already: the org.beryx.jlink plugin, by Serban Iordache (see https://badass-jlink-plugin.beryx.org/).
These are the required steps if you want to create an installer for a modular application with Gradle, like this one:
- 1.
Edit the build.gradle and review the required JDK 13 path:
- 2.Run and test:./gradlew run (Mac OS or Linux)gradlew run (Windows)
- 3.Create custom image:./gradlew jlink (Mac OS or Linux)gradlew jlink (Windows)
- 4.Run and test the image:build/image/bin/sample3 (Mac OS or Linux)buildimageinsample3 (Windows)
- 5.Create the installer:./gradlew jpackage (Mac or Linux)gradlew jpackage (Windows)
As a result, you will get Sample3-1.0.dmg (40.6 MB) on Mac OS X or Sample3-1.0.exe (38.6 MB) on Windows or Sample3-1.0.deb on Linux that can be distributed, and it just requires a double-click to install the application.
Using Graal’s Native
Using jpackage, you can build a native application for a specific operating system. The Java runtime is bundled with the application, and when the native application is executed, it will internally use the Java runtime to execute the bytecodes. Typically, the Java runtime contains a Just In Time compiler (JIT) that compiles Java bytecode to native code.
Another option for building a native application moves that compilation step to build time. With GraalVM’s Native Image, the Java code is compiled Ahead Of Time (AOT), which means that the java bytecode is compiled to native code before it is executed and before it is bundled in an application.
Although the GraalVM project has been active for many years, it only recently became available as a product. It is still in evolution, and parts of it are integrated with the OpenJDK project and vice versa. We recommend to regularly keep an eye on the https://graalvm.org web site and on the GitHub site for the open source code at https://github.com/oracle/graal.
While GraalVM provides the AOT compiler that translates Java bytecode into native code for a given platform, there are more actions needed in order to link the program code into an executable. Fortunately, there already are open source tools available that help developers achieve this. The Gluon Client plugin allows developers to create native images for Linux and Mac OS X (and soon Windows) based on existing Java code.
This same plugin also allows to generate mobile apps, which will be discussed in the next chapter.
We will now show how you can convert a HelloFX application into a native executable.
Requirements
To use the plugin to develop and deploy native applications on Mac OS X platforms, you need a Mac with Mac OS X 10.13.2 or superior and Xcode 9.2 or superior, available from the Mac App Store. Once Xcode is downloaded and installed, open it to accept the license terms.
- AdoptOpenJDK 11.0.3 that can be downloaded from the following:
The Code
HelloFX.java
File styles.css
Maven Project
If you have a Java or JavaFX project and you are using Maven as a build tool, you can easily include the plugin to start creating native applications.
The plugin can be found here: https://github.com/gluonhq/client-maven-plugin.
Pom.xml file
Gradle Project
If you have a Java or JavaFX project and you are using Gradle as a build tool, you can easily include the plugin to start creating native applications.
The plugin can be found here: https://github.com/gluonhq/client-gradle-plugin.
File build.gradle
File settings.gradle
Build the Project
The first step is to build and run the project as a regular Java project (on a regular JVM that you use for your local development, e.g., hotspot).
We will now compile, package, and run the native desktop application.
Compile
Output during the native compilation phase
As a result, HelloFX.o, with 86.7 MB, can be found under build/client/macos-x86_64/gvm/tmp/SVM-15***/HelloFX.o.
If that is not the case, check for any possible failure in the log files under build/client/macos-x86_64/gvm/log.
Link
Now that the Java code for the application is compiled to native code, we can package the generated code with the required libraries and resources using the nativeLink task .
Run
Note that you can distribute this native application to any Mac OS X machine and run it directly as any other regular application.
Conclusion
Packaging application code with all required dependencies, Java runtime, and resources is becoming increasingly popular on desktop, mobile, and embedded devices.
The old disadvantages, including larger size and longer download times, are becoming less important due to increasing bandwidth and storage.
The advantages of packaging include much less hassle for the end user (he only needs to install a single application), who can use an approach that is very similar to other applications.
JavaFX applications are regular Java applications, and the emerging packaging tools that exist and are being developed for Java can be used for JavaFX applications as well.
Since these tools are evolving rapidly, we recommend the reader to keep an eye on the samples in our GitHub repository, as they will be updated to the latest version once available.