/ JAVA WEB START, JWS, LEGACY, PUBLISH, WEB START

Web Start your legacy applications

In this article, we’ll see how to make an old-but-useful legacy Java application into a brand new Java Web Start (JWS for short) application.

JWS is an often overlooked technology that works miracles. With it, you can deploy any Java application, as easily as you would a web one: with the only update server-side and still your application running client-side, with access to all its resources. JWS has two main features: the downloading of the application to the client and the launching of said application. It’s a technology available in the Java Runtime Environment since version 1.4.

JWS is based on a most specific deployment descriptor in JNLP format. This DD takes place along the many JARs of the application on the server. JNLP files are XML although there are no schemas published by Sun, either in DTD or XSD format. You may find some schemas made by people on the web.

In order to publish your application through JWS, you’ll need to perform the following steps.

Step 1: Make your code available as a web resource

I sold you that any Java application can be made available on the web. This means you’ll have to publish both your JAR itself and the associated JNLP.

  • /jnlp/@codebase specifies where the root directory lies
  • /jnlp/@href the name of the JNLP file under this root
  • /jnlp/resources/jar/@href the path to your JAR
<?xml version="1.0" encoding="utf-8"?>
<jnlp codebase="http://blog.frankel.ch/demo" href="launch.jnlp">
    <information>
        <title>Legacy Application</title>
        <vendor>Nicolas Frankel</vendor>
    </information>
    <resources>
        <jar href="myMainJar.jar" />
    </resources>
</jnlp>

Both /jnlp/information/title and /jnlp/information/vendor are required and will be shown in the JWS dialog menu.

Step 2: Adress a version of the JRE

Since you will make your application available, ask yourself which version will be used on the client’s computer. In essence, it will amount to 2 things:

  • /jnlp/@spec specifies the version of the JNLP XML format
  • /jnlp/resources/j2se/@version the needed version of the client JRE
<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.5+" codebase="http://blog.frankel.ch/demo" href="launch.jnlp">
    <information>
        <title>Legacy Application</title>
        <vendor>Nicolas Frankel</vendor>
    </information>
    <resources>
        <j2se version="1.5+" />
        <jar href="myMainJar.jar" />
    </resources>
</jnlp>

See how both can use the plus sign in order to indicate a minimum version.

You can even tell to JWS to redirect the browser to a JRE download page or download it automatically. Both are done through a combination of JavaScript and HTML.

Step 3: Reference dependent libraries and the Main Class

Usually, informations about the main class - the one containing the main method - and the JAR dependencies are specified either on the command line or in the JAR’s deployment descriptor.

Informations specified on the command line:

java -cp myMainJar.jar;lib/myFirstDependency.jar;lib/mySecondDependency.jar ch.frankel.blog.jws.MainClass

Informations specified in the MANIFEST.MF:

java -jar myMainJar.jar

META-INF/MANIFEST.MF in the JAR:

Manifest-Version: 1.0
Class-Path: lib/myFirstDependency.jar lib/mySecondDependency.jar
Main-Class: ch.frankel.blog.jws.MainClass

The first thing to understand is that JWS ignores what is written is the manifest. So, in either case, you have to tell Java Web Start the main class and your dependencies manually in the JNLP.

<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.5+" codebase="http://blog.frankel.ch/demo" href="launch.jnlp">
    <information>
        <title>Legacy Application</title>
        <vendor>Nicolas Frankel</vendor>
    </information>
    <resources>
        <j2se version="1.5+" />
        <jar href="myMainJar.jar" />
        <jar href="lib/myFirstDependency.jar" />
        <jar href="lib/mySecondDependency.jar" />
    </resources>
    <application-desc main-class="ch.frankel.blog.jws.MainClass" />
</jnlp>

Notice how both the JAR containing the main class and dependents JAR are referenced in the same fashion. The lib folder is for my own purpose only: JWS doesn’t differentiate based on where the JARs are located.

Step 4: Passing system properties to the JVM

Usually, system properties are passed to the JVM on the command line like this:

java -Dch.frankel.firstProp -Dch.frankel.secondProp=aValue -jar myMainJar.jar

This is done likewise in the JNLP:

<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.5+" codebase="http://blog.frankel.ch/demo" href="launch.jnlp">
    <information>
        <title>Legacy Application</title>
        <vendor>Nicolas Frankel</vendor>
    </information>
    <resources>
        <j2se version="1.5+" />
        <property name="ch.frankel.firstProp" />
        <property name="ch.frankel.secondProp" value="aValue" />
        <jar href="myMainJar.jar" />
        <jar href="lib/myFirstDependency.jar" />
        <jar href="lib/mySecondDependency.jar" />
    </resources>
    <application-desc main-class="ch.frankel.blog.jws.MainClass" />
</jnlp>

Step 5: Passing parameters to your application

Parameters are passed to your main method on the command line like this:

java -jar myMainJar.jar myFirstParam mySecondParam

This is done likewise in the JNLP:

<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.5+" codebase="http://blog.frankel.ch/demo" href="launch.jnlp">
    <information>
        <title>Legacy Application</title>
        <vendor>Nicolas Frankel</vendor>
    </information>
    <resources>
        <j2se version="1.5+" />
        <property name="ch.frankel.firstProp" />
        <property name="ch.frankel.secondProp" value="aValue" />
        <jar href="myMainJar.jar" />
        <jar href="lib/myFirstDependency.jar" />
        <jar href="lib/mySecondDependency.jar" />
    </resources>
    <application-desc main-class="ch.frankel.blog.jws.MainClass">
        <argument>myFirstParam</argument>
        <argument>mySecondParam</argument>
    </application-desc>
</jnlp>

Step 6: Signing your JARs

Java Web Start applications are meant to run in a sandbox since they are downloaded on the web where, by definition, all resources are unsafe. If your application doesn’t use potentially unsafe operations, well, that’s fine. If it does, you’ll have to explicitly ask for special permissions in the JNLP. Unsafe operations include: file access, clipboard use and so on.

<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.5+" codebase="http://blog.frankel.ch/demo" href="launch.jnlp">
    <information>
        <title>Legacy Application</title>
        <vendor>Nicolas Frankel</vendor>
    </information>
    <resources>
        <j2se version="1.5+" />
        <property name="ch.frankel.firstProp" />
        <property name="ch.frankel.secondProp" value="aValue" />
        <security>
            <all-permissions/>
        </security>
        <jar href="myMainJar.jar" />
        <jar href="lib/myFirstDependency.jar" />
        <jar href="lib/mySecondDependency.jar" />
    </resources>
    <application-desc main-class="ch.frankel.blog.jws.MainClass">
        <argument>myFirstParam</argument>
        <argument>mySecondParam</argument>
    </application-desc>
</jnlp>

There’s one big but: all all of your JARs must then be signed by the same certificate! It means that if you reference already-signed JARs, you must unsign them first and then sign them using an unique certificate.

If that is too much hassle for you, then you will need to change the litigious parts of your code and replace them with calls to the JNLP API. In that case, kiss good bye to your application’s ability to run outside a Web Start-context. Beware, the API is quite small.

Step 7: Enhance your user experience

Shortcuts, splash screen, ability to run off-line, Java Web Start provides them all. The following JNLP adds these features to the previous one:

<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.5+" codebase="http://blog.frankel.ch/demo" href="launch.jnlp">
    <information>
        <title>Legacy Application</title>
        <vendor>Nicolas Frankel</vendor>
        <offline-allowed />
        <icon kind="splash" href="image/welcome.jpg" />
        <shortcut online="false">
            <desktop />
            <menu submenu="frankel dot ch"/>
        </shortcut>
    </information>
    <resources>
        <j2se version="1.5+" />
        <property name="ch.frankel.firstProp" />
        <property name="ch.frankel.secondProp" value="aValue" />
        <security>
            <all-permissions/>
        </security>
        <jar href="myMainJar.jar" />
        <jar href="lib/myFirstDependency.jar" />
        <jar href="lib/mySecondDependency.jar" />
    </resources>
    <application-desc main-class="ch.frankel.blog.jws.MainClass">
        <argument>myFirstParam</argument>
        <argument>mySecondParam</argument>
    </application-desc>
</jnlp>

Step 8: Remove bugs from your legacy code

In the context of Java Web Start, some legacy code may not run. For example, forget ClassLoader.getSystemResource(): replace it with the following snippet Thread.currentThread().getContextClassLoader().getResource() which will run in both JWS and local contexts. Remember, if you run into a problem, chances are someone did before you: Google is your friend here.

I hope this article convinced you that your legacy applications can be brought online with only minimal effort. Java Web Start is a standardized, cost-effective and technically sound solution to do this.

Nicolas Fränkel

Nicolas Fränkel

Developer Advocate with 15+ years experience consulting for many different customers, in a wide range of contexts (such as telecoms, banking, insurances, large retail and public sector). Usually working on Java/Java EE and Spring technologies, but with focused interests like Rich Internet Applications, Testing, CI/CD and DevOps. Also double as a trainer and triples as a book author.

Read More
Web Start your legacy applications
Share this