What to do when Java SE 11 removes JNLP

When Java SE discontinued JNLP, my organization had to decide the best way to continue delivering a critical business app. Here's how we got there.
150 readers like this.
JBoss source code (Java) in Atom

Alex Sanchez. CC BY-SA 4.0.

For about the past 15 years, an organization I work with has used the Java Network Launching Protocol (JNLP) to internally distribute a Java Swing desktop application to its users.

To access the Java Swing application, users had to install a specific version of Java Platform, Standard Edition (Java SE) that contained support for JNLP. When they wanted to use the application, they would click on the link in the organization's intranet, and the Java Web Start (javaws) program would download the XML at the link, interpret it, download the current version of the application if necessary, and run it, all while managing the security sandbox where the application ran. In other words, it was a nice, low-ceremony way to distribute a completely configured application to everyone's desktop.

However, because JNLP is no longer a part of Java SE (from version 11 onward), the organization was faced with a decision: discontinue its use of JNLP as a part of Java SE or eliminate its policy of staying reasonably up to date with Java SE releases. This relatively simple-sounding choice turned out to be somewhat complex for various reasons.

Option 1: Stay with current Java SE and find a substitute for JNLP

IcedTea-Web is an open source project that provides JNLP capabilities. This would imply a two-step pre-configuration process: 1) install Java SE and 2) install IcedTea-Web, which would increase client-side maintenance. Unfortunately, when we were evaluating the options, there was not a working IcedTea-Web facility compatible with Java 11, so this would mean using Java 8. However, Java SE 8 was reaching end-of-life in March 2019, so we would have to create a Java 8 runtime environment from OpenJDK 8 for installation on the client machines and move to a more manual process of tracking OpenJDK 8 updates. Neither of these approaches seemed very appealing.

Option 2: Move away from JNLP

At first, this approach seemed like the worst possible scenario—we would need to modify the application to provide configuration information in some other fashion; all the nice automated-update stuff in JNLP would be discarded; and we would need some kind of per-workstation installation process. Ugh! Or we could rewrite the existing fat client application, liked by its user community, as a web app. Double ugh!!!

All this negativity was surmounted by a few interesting discoveries and a bit of careful thinking:

  1. The configuration information could be packaged as metadata in a metadata table that could be selected as a "configuration" by the end user.
  2. The application could fairly easily check its version number against the currently distributed version number and refuse to run, or at least complain to any user running an out-of-date version.
  3. The application could be packaged along with a customized Java runtime created with the jdeps and jlink commands provided in Open JDK 11, thus ensuring the application always runs with the correct version of Java.
  4. The whole installation and execution business could be handled on the client side with Windows .cmd files, eliminating the need for an installer (perhaps one day…).
  5. The application and the customized Java runtime distribution could still be handled as a download from the intranet.

With this detail, the attentive reader may be able to guess…

The chosen solution

In the end, the organization decided to move away from JNLP because the amount of end-user management seemed reasonably straightforward compared to maintaining the existing JNLP environment.

Here is the detailed process for getting to the solution:

  1. Building the application:
    1. The app was always developed and maintained using the NetBeans IDE running in a Linux desktop environment; this is still the case, using OpenJDK 11 (from the distro) and NetBeans 10 (downloaded from the new Apache NetBeans site).
    2. The build process creates an operating system-agnostic dist folder containing the application .jar file and all required libraries
  2. Customizing the Java 11 runtime:
    1. Unfortunately, there's no way to customize cross-platform Java runtimes, so we need an intermediate Windows computer—let's call it the IWC. (I'm going to come right out and say this is the major drawback of our approach.)
    2. OpenJDK 11 prebuilt binaries are installed on the IWC.
    3. The application dist folder is copied to the IWC.
    4. Inside the dist folder, the jdeps command from OpenJDK 11 determines which Java runtime components are needed to support the application:
      jdeps --print-module-deps TheApplication.jar lib\*
    5. Using the output of the above command, the jlink command (also from OpenJDK 11) builds the customized runtime:
      jlink --no-header-files --no-man-pages --compress=2 \
      --strip-debug –add-modules \ java.base,java.desktop,java.sql,java.security.jgss,java.xml \
      --output customjre
    6. At this point, the application can be run (tested) from this folder using the command:
      .\customjre\bin\java -jar TheApplication.jar
    7. The Install.cmd and RunTheApplication.cmd files are copied into the dist folder.
  3. Bundling the application for distribution:
    1. The dist folder, containing the application, .cmd files and the custom runtime, is zipped and put on the intranet server; a link pointing to the .zip file allows users to download it.
  4. Downloading, installing, and running the application:
    1. The user navigates to the page containing the link pointing to the .zip file.
    2. Clicking on the link downloads the .zip file.
    3. The user unzips the .zip file, navigates to the dist folder, and double-clicks Install.cmd.
    4. When Install.cmd executes, it copies the dist folder into a known place on the user's computer and installs the RunTheApplication.cmd link on the user's desktop.
    5. The user will need to have certain permissions to install the application.
    6. The user does not need Java SE installed on the computer.

The application was modified to:

  • Read the configuration parameters from a database table (the organization thinks of these as "project parameters," so the user is "selecting a project").
  • The VERSION=123 string is extracted from the RELEASE_NOTES.TXT locally and on the server to make sure the versions match (and the end user is harassed if they do not).

That's it. Of course, it's possible to use an installer rather than the .cmd files for a "more professional" installation experience. It's also possible to find other ways to install OpenJDK 11 on the IWC, which could even be a virtual machine (making it the VIWC). It's too bad the jlink command doesn't have a --target-platform option or many other improvements that might be desirable or necessary depending on the nature of the deployment environment.

How well does it work?

Early reports indicate users are happy with the results, and sysadmins are no longer responsible for watching over the Java SE version installed. As for the guy supporting the application, he's more or less OK with the extra build steps—but would rather deal with two machines than run a VIWC on his beautiful System76 laptop.

What to read next
Chris Hermansen portrait Temuco Chile
Seldom without a computer of some sort since graduating from the University of British Columbia in 1978, I have been a full-time Linux user since 2005, a full-time Solaris and SunOS user from 1986 through 2005, and UNIX System V user before that.


Big problem with loss of JNLP support is working with IPMI/BMC management tools on remote servers. We have a number of these that use JavaWebstart as the means of seeing the system console. Short of throwing the hardware out and starting over (because you know the vendors will never be fixing the firmware even IF we were allowed to update it) there isn't much option.

Did you end up having to convert your code base to the JPMS?

Our code base uses 3rd party libraries that I haven't been able to convert to JPMS.

Is it possible to use jlink to package an application that has jar files that haven't been converted?

Thanks for the comment, mv133.

I haven't done anything intentional to convert any existing code to anything else, other than upgrading NetBeans and OpenJDK and building in that new environment. It's possible that alone was enough to handle this JPMS conversion; but certainly I did not do anything active to promote that.


It may help you to know that I'm using a VERY old instance of a library called "glazed lists" (I need to upgrade this one day), and it's working fine. Also I rely on PostgreSQL JDBC and SwingSet, which didn't need any fiddling.

In reply to by mv133

Creative Commons LicenseThis work is licensed under a Creative Commons Attribution-Share Alike 4.0 International License.