Topics and trends related to the Java ecosystem with occasional random rants.

  • March 5, 2019

A Brief Example Using the Early Access jpackage Utility

James Connors
Principal Solutions Consultant

JEP-343 (Java Enhancement Proposal 343) specifies the introduction and formal inclusion of an application packaging utility called jpackage into a future JDK.  Based on an earlier version of the JavaFX javapackager program, the goal of this tool is to provide native packaging options for Java applications on Windows, MacOS and Linux. From an end-user perspective, once Java applications have been packaged up with jpackage, installation experience should be no different than installing any other native app.  An early access, (and admittedly incomplete) build of OpenJDK 13 is now available for those who wish to experiment with this new feature.

As examples of this new utility are currently in scant supply, the video that follows walks through the process of taking a sample Java application and creating a native installer on MacOS for that program.

In case the video frame doesn't appear correctly in this article, here's the link: https://www.youtube.com/watch?v=f8ytBeCEepU

In a few short steps, the application source code is pulled from GitHub, built and packaged.  For those that wish to duplicate this demonstration, they can do so with the following prerequisites:

  1. Have a version of JDK 11 or later (either Oracle JDK or OpenJDK) installed and situated in your PATH such that it will be the first Java version found.
  2. If not already available, download and install Apache Maven and have it placed in your PATH also
  3. Likewise, if not already installed, download and install git and have it in your PATH too
  4. Download and install an early access version of OpenJDK that includes the jpackage utility.  You can find a version here.  Note, you currently must have both Java versions available.  You cannot attempt to take the jpacakage utility from the EA version of OpenJDK 13 and move it into JDK 11.  It won’t work.
Assuming your environment is set up correctly, there's only a few steps needed to get to the point of creating an application image, via jpackage, for our sample application:
  • git clone https://github.com/jtconnors/SocketClientFX.git
  • cd SocketClientFX
  • mvn package
  • sh/link.sh -e (on MacOS) or powershell ps1\link.ps1 -e (on Windows) - this creates a runtime image via the jlink utility in the image/ directory.
With this done you can run either version of the script below which invokes jpackage to create a native application installer:
  • MacOS: sh/create-appimage.sh -e
  • Windows: powershell ps1\create-appimage.ps1 -e
Here's what a sample jpackage incantation on MacOS looks like:

    /Users/jtconnor/Downloads/jdk-13.jdk/Contents/Home/bin/jpackage create-image --runtime-image image --input target --output appimage --name SocketClientFX --main-jar SocketClientFX-11.0.jar

Lets take a look at each individual component of the command:
  • /Users/jtconnor/Downloads/jdk-13.jdk/Contents/Home/bin/jpackage - The jpackage utility is not part of the latest (JDK 11) distribution.  Currently an early access version of Open JDK 13 is available which contains this utility.  So we need this version of Open JDK just to run jpackage.  Note: if attempting to recreate this demonstration, you may need to edit the sh/env.sh or ps1/env.ps1 file and change the value of the JPACKAGE_HOME variable.  It is, by default, set to reside under user's Downloads directory.
  • create-image - one of three different modes that must be supplied on the command-line (create-image, create-installer or create-jre-installer), create-image instructs jpackage to create a platform-specific application image for our program.
  • --runtime-image image - directory containing the jlink'ed runtime image created by the sh/link.sh script.
  • --input target - this points to the directory containing the modules(s) comprising this application.  As this application is built by Apache maven, that directory is, by default, target/.
  • --output appimage - this is the directory under which the generated application image will be placed.
  • --name SocketClientFX - this is what the application image will be named.
  • --main-jar SocketClientFX-11.0.jar - this is the main jar file containing the main class.  It is located inside the directory specified by the --input option (in this case target/).
When complete, the single appimage/ directory contains the platform-specifc application directory.  In the video, the MacOS-genertated appimage/ directory has a SocketClientFX.app icon which is in reality a directory with multiple subdirectories.  To the MacOS finder application though, it looks like a properly formatted application and by simply dragging the SocketClientFX.app icon to the Applications folder, the SocketClientFX application is formally installed.

For the final step, you can convert the application image into a proper platform-specific installer using the jpackage create-installer directive.  The SocketClientFX project contains the following scripts which aid in the creation of installers:
  • sh/create-dmg-installer.sh - creates a native MacOS dmg installer of this application using JEP-343 jpackage tool
  • ps1\create-exe-installer.ps1 - creates a native Windows EXE installer of this application using JEP-343 jpackage tool
  • ps1\create-msi-installer.ps1 - creates a native Windows MSI installer of this application using JEP-343 jpackage tool

As an example invocation on Windows, issuing

    powershell ps1\create-exe-installer.ps1 -e

would have a jpackage command-line which looks something like this:

    ~\Downloads\jdk-13\bin\jpackage.exe create-installer --installer-type exe --output "installer" --app-image "appimage/SocketClientFX" --name "SocketClientFX"

Please note, to create either an EXE or MSI installer on Windows does require third party applications (like, for example Inno or WiX toolkit respectively) to  be installed and available on your command-line PATH.

This article barely scratches the surface of the proposed jpackage tool.  For example, there are literally dozens of additional command options to provide image and installation customizations.  But it does at least provide one working example, which at this point is a bit of a rarity.  Expect more information to become available as this technology matures.

Join the discussion

Comments ( 13 )
  • John S Wednesday, March 27, 2019
    Thanks for writing this up. Have you tried following your instructions on a Windows machine?

    You include scripts for creating native Windows installers: create-exe-installer.ps1 and create-msi-installer.ps1 but they fail with this error:

    C:WorkJavajdk-13.b30binjpackage.exe create-installer --installer-type exe --output "installer" --app-image "appimage" --name "SocketClientFX-11.0" --verbose
    jdk.jpackage.internal.PackagerException: Bundler EXE Installer skipped because of a configuration problem: java.lang.RuntimeException: Error: Failed to compare version null with 5.0.
    at jdk.jpackage/jdk.jpackage.internal.Arguments.generateBundle(Arguments.java:712)
    at jdk.jpackage/jdk.jpackage.internal.Arguments.processArguments(Arguments.java:577)
    at jdk.jpackage/jdk.jpackage.main.Main.run(Main.java:91)
    at jdk.jpackage/jdk.jpackage.main.Main.main(Main.java:53)
    Caused by: jdk.jpackage.internal.ConfigException: java.lang.RuntimeException: Error: Failed to compare version null with 5.0.
    at jdk.jpackage/jdk.jpackage.internal.WinExeBundler.validate(WinExeBundler.java:284)
    at jdk.jpackage/jdk.jpackage.internal.Arguments.generateBundle(Arguments.java:682)
    ... 3 more
    Caused by: java.lang.RuntimeException: Error: Failed to compare version null with 5.0.
    at jdk.jpackage/jdk.jpackage.internal.VersionExtractor.isLessThan(VersionExtractor.java:65)
    at jdk.jpackage/jdk.jpackage.internal.WinExeBundler.validate(WinExeBundler.java:251)
    ... 4 more

    And after searching for this error it looks like you need to have the Inno Setup 5.5.9 installed on the system?

    And this isn't mentioned anywhere in your post?

  • John S Wednesday, March 27, 2019
    Thanks again for the post. You mention:

    "create-image - one of three different modes that must be supplied on the command-line (create-image, create-installer or create-jre-installer), create-image instructs jpackage to create a platform-specific application image for our program."

    But I'm seeing this error trying the "create-jre-installer" option:

    C:WorkJavajdk-13.b30binjpackage.exe create-jre-installer msi --output "installer" --app-image "appimage" --name "SocketClientFX-11.0" --verbose
    Error: Invalid Option: [create-jre-installer]

    In your post you link to https://jdk.java.net/jpackage/ and there's a link to JDK13 Build 30 (2019/3/18) which is what I'm using.

    Not sure if they broke this or if they took this out for this build? I do see references to it in recent bug reports. For exmaple: https://bugs.openjdk.java.net/browse/JDK-8215020

    Any help is appreciated.

    Really like JavaFX 11+ and Java11+ but there's such a big hole with packaging these type of desktop applications.

  • Jim Connors Wednesday, March 27, 2019
    Thanks for trying this out John, it looks like things have changed. First off, the original project you downloaded was based on a February 2019 build of jpackage. Since then a March version has been provided and updated. I noticed the JEP-343 spec has been updated too and no longer contains any reference to "create-jre-installer".
  • Jim Connors Wednesday, March 27, 2019
    Hi again John, can you try pulling down the latest source from github again and tell me if this version is any better?
  • Jim Connors Wednesday, March 27, 2019
    Additionally, the blog does mention in the last paragraph the following:

    "Please note, to create either an EXE or MSI installer on Windows does require third party applications (like, for example Inno or WiX toolkit respectively) to be installed and available on your command-line PATH."
  • Andrei Friday, March 29, 2019

    I'm trying to follow these steps on Windows, but when I want to build the package using mvn package I get:

    [ERROR] Plugin org.codehaus.mojo:exec-maven-plugin:1.6.0 or one of its dependencies could not be resolved: Failed to read artifact descriptor for org.codehaus.mojo:exec-maven-plugin:jar:1.6.0: Could not transfer artifact org.codehaus.mojo:exec-maven-plugin:pom:1.6.0 from/to central

    How to bypass this?

    Also, I do understand that this one works only for modular projects?
  • Jim Connors Friday, March 29, 2019
    I'm no Maven expert, but is it possible you're using an older version? Here's what I've got:

    > mvn -version
    Apache Maven 3.5.4 (1edded0938998edf8bf061f1ceb3cfdeccf443fe; 2018-06-17T14:33:14-04:00)
    Maven home: D:IDEmvn
    Java version: 11.0.2, vendor: Oracle Corporation, runtime: D:Oraclejdk-11
    Default locale: en_US, platform encoding: Cp1252
    OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

    As far as your second question goes, the jpackage tool will work on legacy applications. In this instance the tool will run jlink to create an entire JDK for the application.
  • Josh Sunday, March 31, 2019
    I'm doing this a little differently (without maven) and for Windows I get the following in the executables root directory:

    45 .dll files
    "app" folder
    "runtime" folder

    Inside /runtime/bin almost all of the .dll files are replicated (together with even more)

    Can anyone confirm if this is the structure they are seeing for Windows? It's working but I'm not too impressed with (a) the overall large number of dll's, (b) having so many files in the apps root directory and (c) the high level of apparent file duplication.
  • Neal Thursday, May 9, 2019
    Very Ossum article...
    In my case I can't have java on local pc
    I want the packaged stuff to work on a java command from outside. How do I interact with that embedded java in package to kick off the main in embedded app.
    Could use REST as an alternate and pack a springbootjar...but if I had to contact the java within exe how do I do it? possible?
  • James Elliott Monday, July 8, 2019
    I haven’t been able to figure out any way to communicate with the team working on jpackage, though I have been following it eagerly, but perhaps you know the answer to this: now that Apple is requiring installers to be notarized in order to run in current MacOS releases. Is jpackage being updated to perform this step in addition to the code signing that it already handles?
  • Jim Connors Tuesday, July 9, 2019
    The Open JDK jpackage page (https://jdk.java.net/jpackage/) mentions that you can send feedback via e-mail to core-libs-dev@openjdk.java.net

    Have you given that a shot?
  • Adam Walczak Sunday, November 17, 2019
    I recently experimented a bit with building installers and packages using jlink + jpackage. If anyone is interested how doing this looks today checkout my blog post: https://walczak.it/blog/distributing-javafx-desktop-applications-without-requiring-jvm-using-jlink-and-jpackage
    PS. I would be grateful if anyone could test the installer for Windows and PKG for Mac OS of this small app: http://pdf-decorator.walczak.it - ​​in case you have any problems, please contact me. I will try to fix it or pass a bug report to the OpenJDK devs.
  • Gautam Joshi Monday, April 20, 2020
    Hi Jim,

    I have tried to create installer for mac os using below command and see the result.

    $ sh/create-dmg-installer.sh

    WARNING: Using incubator modules: jdk.incubator.jpackage

    jlink failed with: Error: Module javafx.base not found, required by socketclientfx

    Any suggestions?

    FYI - I am able to create install for windows. Both msi & exe.
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.