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
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
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";
...
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>
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>
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.
Top comments (2)
You can create a custom Info.plist, then tell jpackage to use that with the --resource-dir option. The resource-dir is useful for a lot of platform-specific options.
More information is at: docs.oracle.com/en/java/javase/14/..., or try jpackage with --verbose to see what is copied from the JDK default that you could customize.
When packaging on macOS with OpenJDK 16: at runtime the menu bar disappears... Any idea ?
I have tried System.setProperty("apple.laf.useScreenMenuBar", "true") both in code and passed a System property.