Recently, I wrote about how we solved the problem of Java 11 removing the Java Network Launching Protocol (JNLP), which was how an organization I work with distributed a critical business app. Because I maintain the former JNLP application on Linux but deploy to Windows desktops, as far as I could tell, that meant I had to carry out a packaging step, which involves creating a custom Java 11 runtime environment on an intermediate Windows computer.
The process looked like this:
The intermediate machine requirement was a pain; it meant I needed to carry two machines (not easy to manage when I'm on the road), or I needed to install a Windows virtual machine on my Linux laptop (not something I intend to do), or I needed remote access to a Windows desktop (the solution we eventually chose).
Alan Bateman over at Oracle very kindly wrote to me to share a fourth alternative: Put the Windows version of OpenJDK 11 on the Linux development platform and build the custom Java 11 runtime from that. According to Alan, this has been possible since JDK 9; he notes that "there are a few limitations and the versions [between the Linux utilities and the Windows OpenJDK] need to match."
Since this would eliminate an onerous step outlined above, including the need to have remote access to the Windows desktop, I had to investigate further. To my surprise and delight, I was not one of those "few limitations" Alan mentioned—the approach works just fine for me. However, I did discover that the version of OpenJDK 11 in my Linux distro's repositories was not "close enough" to the Windows OpenJDK 11 that I downloaded, so I also needed to obtain the same version of the Linux OpenJDK 11 and use its tools to create the Java 11 runtime.
The new solution
Here's my new recipe in detail:
- Create a project folder somewhere handy.
- I created mine in /home/me/src/MyOpenJDK11Project (sorta) and set a Bash environment variable called MYPROJ_HOME (sorta).
- Get the matching Windows and Linux OpenJDK 11 packages from AdoptOpenJDK.
- Install both of them somewhere handy (the Windows version is a .zip; the Linux, a tarball).
- I installed mine in $MYPROJ_HOME/windows and $MYPROJ_HOME/linux, respectively.
- Write a Bash script (or a makefile, or an Ant package step, or…) to do the packaging.
The key elements of my Bash script solution, which must run inside the dist folder created by NetBeans, are:
export DEPS=`jdeps --print-module-deps MyApplication.jar lib/*`
jlink --module-path $W64_JAVA_HOME/jmods --no-header-files --no-man-pages --compress=2 --strip-debug --add-modules $DEPS --output java-runtime
This leaves the custom Java 11 runtime in the dist folder, in a subfolder called java-runtime. To that folder, my Bash script adds the various Windows .cmd files for installing and running the application, which are of course also kept in the MYPROJ_HOME folder. Finally, my Bash script zips up the completed dist folder and uses scp to put it on the web server.
How well does it work? First and most importantly, the users don't see any difference. From my perspective, this solution is a huge win, simplifying the packaging process considerably and eliminating the need for an intermediate Windows machine for the packaging process. And even though I miss the coolness of JNLP, I actually prefer this solution since it keeps me in control of the Java runtime as well as the application itself, which is a win for all of us—users, sysadmins, and me.
With that, I'll repeat my thanks to Alan Bateman for sharing this excellent suggestion.