Deploying Software with JNLP and Java Web Start

   
   
Articles Index



August 2002

Delivering client-side Java technology-based programs has recently been a daunting task. Browser differences -- both in versions of the Java Runtime Environment (JRE) and security architecture -- have caused many developers to abandon rich client-side solutions in favor of using Java technology on the server, with JavaServer Pages (JSP TM ) and servlets. But now that Java Web Start -- and the underlying Java Network Launch Protocol (JNLP) -- are a standard part of the Java 2 platform (as of version 1.4), it's far easier to deploy cross-platform, client-side systems.

In this article, you'll take a look at what Java Web Start and JNLP are, learn how to use Java Web Start to run JNLP-packaged applications, and learn how to build and deploy your own.

Introducing Java Web Start

Quite simply, Java Web Start is a mechanism for program delivery through a standard Web server. Typically initiated through the browser, these programs are deployed to the client and executed outside the scope of the browser. Once deployed, the programs do not need to be downloaded again, and they can automatically download updates on startup without requiring the user to go through the whole installation process again.

Since these programs run outside the browser, you might think they can freely perform operations that are restricted within an applet. But that is not the case. They continue to run within a restricted container, or sandbox. Since Java Web Start runs on top of the Java 2 platform, you also have access to the underlying security architecture.

To use Java Web Start, you only need to install the client piece. For Microsoft Windows platforms, if you (as a developer) installed the Java 2 Software Development Kit (SDK) version 1.4, Java Web Start should already be installed (look in C:\Program Files\Java Web Start). Solaris/Linux users will need to run an additional install script that comes with the SDK. See the README notes that come with SDK for additional information.

While developers may have Java Web Start previously installed on their systems, users probably don't. In that case, when someone without Java Web Start tries to initiate a JNLP-packaged application, the user is prompted to install Java Web Start. Once installed, with a possible reboot, the user can then go on and run the desired program.

Java Web Start Demonstration

To see Java Web Start in action, you can visit a set of five demonstration applications that Sun has available: Draw, The SwingSet Demo, Military Game, A Simple Notepad, and Application Manager. If you don't have Java Web Start previously installed, click on any of the Launch buttons to be redirected to a download page where you can download Web Start for your platform. Java Web Start itself is only about 1 MB. However, it requires the Java Runtime Environment (JRE), so if a you (or a user) don't have a JRE, the download size increases accordingly, to just under 6 MB on Microsoft Windows platforms.

Once Java Web Start is installed, click Launch to initiate the downloading of the application. For instance, if you launch the Military Game, you'll see a window similar to the one below as the application downloads.

 

If you start either the Draw or Notepad program, a program loads that will try to break out of the sandbox when you try to load or save a file locally (from/to the client machine). When such an action occurs, a Security Advisory appears, similar to the one shown here.

 

At least on the Microsoft Windows platform, Java Web Start also integrates the program startup with system facilities. When a user starts a program with Java Web Start multiple times, Java Web Start will in turn ask the user if he or she wishes to install shortcuts for program startup on the Desktop and Start Menus.

 

The multiple times count before the prompt is configurable, but defaults to twice. Once installed, users can bypass the browser and the Application Manager and just start up the application from the Desktop or Start Menu.

Creating Your First Program

To truly understand what's happening with Java Web Start, let's create a program and try to deploy it through Java Web Start. Tools such as Borland's JBuilder automate many of the tasks, but until you do these tasks manually at least once, you'll never truly have a grasp of all that is being done.

The example program will display the time as reported by the United States National Institute of Standards and Technology (NIST) Internet Time Service (ITS). They offer a service that permits you to connect to their server to get the official time in the United States. You're just going to connect and then display.

You'll simply connect to their server over port 13, the DAYTIME service, and just read the response. You don't even have to send a request; connecting to the port is the request. The response that comes back is a blank line, followed by the date and time on the second line.

  Socket socket = new Socket("time.nist.gov", 13);
  InputStream is = socket.getInputStream();
  InputStreamReader isr = new InputStreamReader(is);
  Buffered reader = new BufferedReader(isr);
  reader.readLine(); // skip blank line
  String message = reader.readLine();

The format of the time returned follows, both in its symbolic form and with a real value. The most important pieces of information are at the start, including the Modified Julian Date, two digits each for year, month, and day, along with the time in hours, minutes, and seconds.

JJJJJ YR-MO-DA HH:MM:SS TT L H msADV UTC(NIST) OTM
52453 02-06-28 07:41:36 50 0 0 872.9 UTC(NIST) *

The following code sample packages the date/time information so that it is displayed within its own window. The JFrame.EXIT_ON_CLOSE constant requires that you compile this program with version 1.3 or later of the Java 2 platform.

import java.awt.*;
import javax.swing.*;
import java.io.*;
import java.net.*;

public class TheTime {
  public static void main(String args[]) {
    JFrame frame =  new JFrame("Time Check"); 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    JLabel label = new JLabel();
    Container content = frame.getContentPane();  
    content.add(label, BorderLayout.CENTER);
    String message = "missing";
    BufferedReader reader = null;
    try {
      Socket socket = new Socket("time.nist.gov", 13);
      InputStream is = socket.getInputStream();
      InputStreamReader isr = new InputStreamReader(is);
      reader = new BufferedReader(isr);
      reader.readLine(); // skip blank line
      message = reader.readLine();
    } catch (MalformedURLException e) {
      System.err.println("Malformed: " + e);
    } catch (IOException e) {
      System.err.println("I/O Exception: " + e);
    } finally {
      if (reader != null) {
        try {
          reader.close();
        } catch (IOException ignored) {
        }
      }
    }
    label.setText(message);
    frame.pack();
    frame.show();
  }
}

Note: If you are unable to connect to time.nist.gov, see the NIST Internet Time Service (ITS) link in the Resources section for other host names (at the end of this article).

At this point, you can compile and run the program to make sure it works. Notice that this program is not an applet; it doesn't subclass from java.applet.Applet (or javax.swing.JApplet).

Packaging

Creating the program is the easy part. Deploying requires you to do a little bit of work. Of course, all the work is meant to make the user's life easier, so in the end it's worth it.

The task of packaging for deployment is where the Java Network Launch Protocol (JNLP) comes into play. In addition to a JAR file for the application classes, JNLP requires you to create a descriptor file on how to start up the application. The file format is fully described in the Java Network Launching Protocol & API Specification (JSR-56), with a subset shown in the Java Web Start Technology Developer's Guide. Since this is just an XML file, the DTD is what's in the specification. You're required to provide the location of the application resources, what information should appear in the popup that appears while the application loads, and what the application resources are. Other pieces (like security) are optional and only required when you need to break out of the sandbox, as in this example. Details like which class to start when the program loads can be specified either in the JAR file or from the JNLP descriptor file. You'll now look at each block separately.

The first tag simply describes the XML version and encoding. Technically speaking, the line is optional. However, it's good practice to include it, in case the encoding ever defaults to a different version or the XML version changes.

<?xml version="1.0" encoding="UTF-8"?>

Next up is the start of the <jnlp> tag. From here, you specify the JNLP specification version supported and where to locate the resources for the applications. Essentially, this is saying where the JAR file is for the application.

<jnlp spec="1.0+"
  codebase="file:///c:/jdc/jnlp/"
>

The <information> tag provides the content shown in the popup while the application is loading.

<information>
  <title>Time Check</title>
  <vendor>Java Developer Connection</vendor>
  <homepage href="/jdc" />
  <description>Demonstration of JNLP</description>
</information>

The optional <offline-allowed> tag allows the application to run while not connected to the network.

<offline-allowed/>

The <security> tag is only required when extra permissions are needed. While files will load and save without signed code (they simply generate the Security Advisory), tasks such as the network access in this sample program require that the code be signed. The security element supports two options, either <all-permissions/> or <j2ee-application-client-permissions/>. J2EE TM permissions are usually sufficient, since they include socket permissions, clipboard access permission, and printing permission, among a set of ten.

<security>
  <j2ee-application-client-permissions/>
</security>

The resources element allows you to specify the runtime version required, and identify the JAR files that contain the application classes. The location of this JAR file is specified by the prior codebase

<resources>
  <j2se version="1.2+" />
  <jar href="/developer/technicalArticles/Programming/jnlp/JNLPTime.jar"/>
</resources>

If you've specified a main-class in the manifest for the JAR, you don't need to identify one in the <application-desc> element. (Otherwise, you do). As this is the last tag, you can close up the <jnlp> tag, too.

<application-desc main-class="TheTime" />
</jnlp>

Putting all these pieces together provides the following time.jnlp file:

<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+"
  codebase="file:///c:/jdc/jnlp/"
>
<information>
  <title>Time Check</title>
  <vendor>Java Developer Connection</vendor>
  <homepage href="/jdc" />
  <description>Demonstration of JNLP</description>
</information>
<offline-allowed/>
<security>
  <j2ee-application-client-permissions/>
</security>
<resources>
  <j2se version="1.2+" />
  <jar href="/developer/technicalArticles/Programming/jnlp/JNLPTime.jar"/>
</resources>
<application-desc main-class="TheTime" />
</jnlp>

You'll notice a reference to JNLPTime.jar in the resources section. This is the JAR file for the application. It only contains one class, but must be created and signed. For signing, the keytool/ jarsigner combination of tools that come withe SDK is sufficient.

  1. First, create the JAR file:
    jar cf JNLPTime.jar TheTime.class
  2. Second, create a key in the keystore (or use one you already have). You'll be prompted for information like first name and last. You should at least fill in that information.
    keytool -genkey -keystore myKeys -alias jdc
  3. Third, sign the JAR. Be sure to remember your password from the previous step.
    jarsigner -keystore myKeys JNLPTime.jar jdc

Running Locally

At this point, your program is ready to run. The .jnlp file will be loaded from a FILE URL; loading through a Web server (HTTP URL) will be described shortly. After you enter the filename and press Enter, you'll see the first initialization screen:

 

After a couple more similar screens go by as the JAR file is loaded, the program will attempt to start. However, since the program requires additional security permissions, it won't start just yet. Since the JAR file is signed, you'll be prompted to give additional security permissions:

 

If you confirm the question, you won't be asked again. If you don't, you'll be asked each time you attempt to run the application.

Assuming you grant permissions, the program will then run, get the current time from NIST, and display the time in a label on a frame.

 

Notice that not only are the <information> elements from the JNLP file shown in the initialization popup, but you'll also see the information displayed in the Application Manager.

 

The manager is one of the applications from Sun's demo area. It allows you to remove unwanted applications and control such tasks as when to show the Java Console or send output to a log file.

Web Server Setup

While Java Web Start and JNLP rely on existing technologies like HTTP and a Web server, there is one configuration issue that must be addressed for JNLP to work properly. It isn't always sufficient to just create a .jnlp file and put it on your Web server with the rest of the application; the Web server must be configured to return the proper MIME type for the JNLP content -- and that MIME type must be application/x-java-jnlp-file.

If you are using the latest version of Tomcat -- 4.0.x -- this is already configured for you. If you look in the web.xml file in the conf directory under the installation location, you'll find the following piece of information:

  <mime-mapping>
    <extension>jnlp</extension>
    <mime-type>application/x-java-jnlp-file</mime-type>
  </mime-mapping>

Prior versions required you to manually add this information. (Other Web servers support adding MIME types in different fashions.)

To try out your new JNLP-based application, copy the time.jnlp file and JNLPTime.jar to a directory under the Web server where files are loaded from (for Tomcat, under webapps/ROOT). You'll also need to change the codebase in the time.jnlp file to reference the Web server (most likely http://localhost:8080) instead of the previous file URL. Then, make sure Tomcat is started, and load the application.

To demonstrate the updating capabilities of JNLP, change the JAR file on the Web server to do something else. The next time you start up the application, the new version will be downloaded.

JNLP API

You might think that that's all there is to JNLP and Java Web Start -- but there's more. Packed in the javaws.jar file that comes with Java Web Start is a collection of interfaces and classes for working locally on the client machine, even from an untrusted environment. For instance, tasks like printing are available, and the user is prompted for confirmation when the application is untrusted. With an applet, such tasks are always unavailable in an untrusted environment.

The following table shows the different classes and interfaces available. Before permitting the various operations, each of the interface implementations will warn the user.

Class/Interface Description
BasicService Permits display of document in the user's browser.
ClipboardService Permits access to the system clipboard
DownloadService Used for cache control of local resources.
DownloadServiceListener Used to monitor progress of downloads.
ExtensionInstallerService Used to communicate with the JNLP client.
FileContents Provides read-write access to a file's contents.
FileOpenService Permits prompting of filename input for opening.
FileSaveService Permits prompting of filename input for saving.
JNLPRandomAccessFile Provides random read-write access to a file's contents.
PersistenceService Permits local storage of information on the client.
PrintService Permits access to printer.
ServiceManager Permits lookup of JNLP services.
ServiceManagerStub Describes how to lookup JNLP services.
UnavailableServiceException Thrown with lookup of non-existent or Unavailable service.

To demonstrate the use of services, you'll add a button to the TheTime program which, when pressed, will send the browser to a particular URL.

Lookup services work by asking the ServiceManager for the implementation of the specific interface. The interface name is passed as a string, and lookup returns that implementation. Thus, to look up the BasicService, you would have the following line of code:

  BasicService basicService = (BasicService)
    ServiceManager.lookup("javax.jnlp.BasicService");

Once you've looked up the service with the ServiceManager, you are free to use the implementation of the interface that is returned. What follows is the updated example that does just that.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.net.*;
import javax.jnlp.*;

public class TheTime {
  static BasicService basicService = null;
  public static void main(String args[]) {
    JFrame frame = new JFrame("Time Check");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    JLabel label = new JLabel();
    Container content = frame.getContentPane();
    content.add(label, BorderLayout.CENTER);
    String message = "missing";
    BufferedReader reader = null;
    try {
      Socket socket = new Socket("time.nist.gov", 13);
      InputStream is = socket.getInputStream();
      InputStreamReader isr = new InputStreamReader(is);
      reader = new BufferedReader(isr);
      reader.readLine(); // skip blank line
      message = reader.readLine();
    } catch (MalformedURLException e) {
      System.err.println("Malformed: " + e);
    } catch (IOException e) {
      System.err.println("I/O Exception: " + e);
    } finally {
      if (reader != null) {
        try {
          reader.close();
        } catch (IOException ignored) {
        }
      }
    }
    label.setText(message);

    try {
      basicService = (BasicService)
        ServiceManager.lookup("javax.jnlp.BasicService");
    } catch (UnavailableServiceException e) {
      System.err.println("Lookup failed: " + e);
    }

    JButton button = new JButton("http://www.oracle.com/technetwork/java/index.html");

    ActionListener listener = new ActionListener() {
      public void actionPerformed(ActionEvent actionEvent) {
        try {
          URL url = new URL(actionEvent.getActionCommand());
          basicService.showDocument(url);
        } catch (MalformedURLException ignored) {
        }
      }
    };

    button.addActionListener(listener);

    content.add(button, BorderLayout.SOUTH);
    frame.pack();
    frame.show();
  }
}

Here's what the new and improved interface looks like.

 

There is one issue with JNLP services: your programs can now only run inside of Java Web Start. The only time the services are valid -- that is, when lookup will succeed -- is when Java Web Start is running. This is just something to be aware of as you delve into the JNLP API.

Conclusion

Now that Java Web Start is a standard feature of the Java 2 Standard Edition version 1.4, look for its popularity to continue to grow. The ability to deploy rich clients easily without having to deal with browser incompatibilities and provide automatic caching are all the more reason to consider it as an installation option.

Note: Java Web Start version 1.0 ships with the Java 2 Standard Edition (J2SE TM ) version 1.4. Java Web Start version 1.2 ships with J2SE version 1.4.1.

For More Information

About the Author

John Zukowski conducts strategic Java consulting with JZ Ventures, Inc. His latest books are Learn Java with JBuilder 6 from Apress and Mastering Java 2, J2SE v. 1.4 from Sybex. Contact John at jaz@zukowski.net.

1 As used on this web site, the terms Java virtual machine or Java VM mean a virtual machine for the Java platform.

Have a question about programming? Use Java Online Support.