A quick look at jpackage

tkuenneth

Thomas Künneth

Posted on December 26, 2019

A quick look at jpackage

With Java 14, we will get back a tool to create native installation archives for our Java applications. While Java on the client unfortunately is not considered a hot topic these days, it is very important that the current gap is closed - again. There used to be a commandline tool called javafxpackager which was later renamed javapackager and even later removed from the JDK. java(fx)packager performed tasks related to packaging and signing Java and JavaFX applications, so when JavaFX was unbundled from the JDK, it sort of made sense to temporarily remove related tools as well. With jpackage a fresh, re-thought and more general implementation returns.
Java 14 will be released in March 2020 so what is available today is preliminary (early access). Further, jpackage is considered an incubator feature, so even what we will see in March may change over time.
Installing Java on a Mac is a matter of a few minutes. After downloading and unzipping the tarball just move the base directory to /Library/Java/JavaVirtualMachines/.

cd ~/Downloads
tar xzf openjdk-14-ea+28_osx-x64_bin.tar.gz
sudo mv jdk-14.jdk /Library/Java/JavaVirtualMachines
Enter fullscreen mode Exit fullscreen mode

Did you know you can list your installed Java versions with /usr/libexec/java_home -V? By default the Mac will use the latest version, so after this installation java -version will print something like openjdk version "14-ea" 2020-03-17. Still, typing jpackage will produce a command not found. To fix this, you could add /Library/Java/JavaVirtualMachines/jdk-14.jdk/Contents/Home/bin to PATH. For now I suggest to just use the absolute path when invoking the tool, because you will pass quite a few arguments, so creating a small shell script will make your life much easier.

Take a look at this one:

#!/bin/sh
JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-14.jdk/Contents/Home
BASEDIR=/Users/thomas/Entwicklung/Bitbucket/clip4moni
VERSION=`sed -n -e 's/.*VERSION = \"\(.*\)\".*/\1/p' < $BASEDIR/src/main/classes/com/thomaskuenneth/clip4moni/Clip4Moni.java`

$JAVA_HOME/bin/jpackage --name Clip4Moni --icon $BASEDIR/artwork/Clip4Moni.icns --app-version $VERSION --type app-image --module-path $BASEDIR/build/modules -m main/com.thomaskuenneth.clip4moni.Clip4Moni
Enter fullscreen mode Exit fullscreen mode

I use it to create the application bundle for my app Clip4Moni, a small text snippet tool that resides in the system tray (Windows, Linux) or menu bar (on a Mac). It is based upon Java modules. While jpackage can work with plain jars as well, I strongly suggest bringing your code to recent Java versions. So many nice features have gone into the language, it is more than a pity not to use them. Now, let's go through the script, starting with the last line. --name is the name of the application. --icon references the icon file. --type defines what kind of archive shall be created, in this case it is just the app bundle. --module-path specifies where the module files are located. -m identifies the main class.

--app-version sets, well, the version number of the app, for example 1.3.3. If you look at my script I do a funny looking sed call. Let me explain. I define the app version as a String constant. So I go to the source file in which the definition resides, and read the version number. This is the input:

...
public static final String VERSION = "1.3.3";
...
Enter fullscreen mode Exit fullscreen mode

jpackage is easy to use. Although it is still in preview it worked flawlessly. There is just a minor thing I noticed. Part of a Mac app bundle is a file called info.plist that contains important information about the app. It is created by jpackage. Currently it contains these lines:

<!-- See https://developer.apple.com/app-store/categories/ for list of AppStore categories -->
<key>LSApplicationCategoryType</key>
<string>Unknown</string>
Enter fullscreen mode Exit fullscreen mode

The underlying idea seems to be that developer edits the file. But using the syntax in my script, it is created on the fly. One solution might be to edit it after the invocation of jpackage. But this may not work if you sign your app (which can be done through jpackage). Another thing I would love to see is the ability to bring in additional entries. My app resides in the menu bar so I would like to hide the Dock icon. This is done as follows:

<key>LSUIElement</key>
<string>true</string>
Enter fullscreen mode Exit fullscreen mode

It would be cool to specify such entries at the commandline. There may already be solutions to this, so if you, dear reader, know how to do this, please tell me in the comments.

💖 💪 🙅 🚩
tkuenneth
Thomas Künneth

Posted on December 26, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related