Packaging JNLP Applications in a Web Archive

Java TM Web Start

Version 1.2

 

Please send comments and feedback to javawebstart-feedback@sun.com

Table of Contents

Introduction
Examples
Servlet Configuration
Specifying Resources
Mapping Requests to Resources
Processing of JNLP Files

Introduction

Java Web Start includes a servlet in the developer's pack that can be used to package a JNLP file and its associated resources in a Web Archive (.war) file. The purpose of the servlet is to provide a simple and convenient packaging format for JNLP applications, so they can be easily deployment in a Web Container, such as Tomcat or a J2EE-compliant Application Server.

The download servlet supports the following features:

  • Automatic installation of the codebase URL into JNLP files, thus eliminating manual management of hard coded URLs into JNLP files.
  • Explicit specification of the timestamp for a JNLP file, independent of the file-system timestamp.
  • Supports all download protocols defined in the JNLP specification v1.0.1. These include basic download protocol, version-based download protocol, and extension download protocol.
  • Version-based information is specified per file or per directory in the Web archive. Thus, no centralized file needs to be managed for the entire archive.
  • Automatic generation of JARDiff files

The packaging support consists of one servlet: JnlpDownloadServlet. The servlet is packaged into the jnlp-servlet.jar file.

Below are two examples of how to use the servlet followed by a detailed description of the functionality of the servlet.

Examples

The first example shows how an application can be packaged into a WAR file without using the version-based download. The JnlpDownloadServlet is used to insert the exact URL into the JNLP file at request time. The second example shows how to add support for version-based download as well.

WAR file without use of version-download protocol

The example1.war contains:
   /index.html
   /app/launch.jnlp
   /app/application.jar
   /app/images/icon.gif
   /WEB-INF/web.xml
   /WEB-INF/lib/jnlp-servlet.jar
   /WEB-INF/lib/jaxp.jar
   /WEB-INF/lib/parser.jar
The JNLP file for the application would look like this:
   TS: 2002-04-23 19:21:05
   <?xml version="1.0" encoding="UTF-8"?>
   <jnlp codebase="$$codebase">
     <information>
         <title>Example 1</title>
         <vendor>Myself</vendor>
         <description>just an example</description>
         <icon href="images/icon.gif"/>
     </information>
     <resources>
       <j2se version="1.2+"/>
       <jar href="application.jar"/>
     </resources>
     <application-desc/>
   </jnlp>
The first line with the TS tag contains the timestamp that the servlet will return for the JNLP file. The format of the timestamp is in ISO 8601 format. If the line is omitted, the timestamp of the file in the WAR file is used. The $$codebase string is modified by the JnlpDownloadServlet to be the actual URL for the request.

The web.xml file instructs the Web container to invoke the JNLPDownloadServlet for all requests to a JNLP file.

  <web-app>
     <servlet>
        <servlet-name>JnlpDownloadServlet</servlet-name>
        <servlet-class>com.sun.javaws.servlet.JnlpDownloadServlet</servlet-class>
     </servlet>
     <servlet-mapping>
        <servlet-name>JnlpDownloadServlet</servlet-name>
        <url-pattern>*.jnlp</url-pattern>
     </servlet-mapping>
  </web-app>
The JnlpDownloadServlet requires an XML parser in order to work. The jaxp.jar and parser.jar archives in the WEB-INF/lib directory implement an XML parser. The reference implementation of the parser can be downloaded from http://java.sun.com/xml.

WAR file with use of version-download protocol

The example2.war contains:
   /index.html
   /app/version.xml
   /app/launch.jnlp
   /app/application.jar
   /app/lib__V2.1.jar
   /app/images/icon.gif
   /WEB-INF/web.xml
   /WEB-INF/lib/jnlp-servlet.jar
   /WEB-INF/lib/jaxp.jar
   /WEB-INF/lib/parser.jar
The /app directory contains two JAR resources: application.jar and lib.jar. The lib.jar uses a naming convention to associate the version-id 2.1, i.e., the version information is associated on a per-file basis. The version of the application.jar file is described in the version.xml file, i.e., on a per-directory basis. The version.xml file looks like this:
   <jnlp-versions>
      <resource>
         <pattern>
           <name>application.jar</name>
            <version-id>1.1</version-id>
         </pattern>
         <file>application.jar</file>
      </resource>
   </jnlp-versions>
The JNLP file for the application looks like this:
   TS: 2002-04-23 19:21:05
   <?xml version="1.0" encoding="UTF-8"?>
   <jnlp codebase="$$codebase" href="$$name">
     <information>
         <title>Example 2</title>
         <vendor>Myself</vendor>
         <description>just an example</description>
         <icon href="images/icon.gif"/>
     </information>
     <resources>
       <j2se version="1.2+"/>
       <jar href="application.jar" version="1.1"/>
       <jar href="lib.jar" version="2.1"/>
     </resources>
     <application-desc/>
   </jnlp>
Finally, the web.xml file configures the JnlpDownloadServlet to be invoked for all requests into the /app directory.
  <web-app>
     <servlet>
        <servlet-name>JnlpDownloadServlet</servlet-name>
        <servlet-class>com.sun.javaws.servlet.JnlpDownloadServlet</servlet-class>
     </servlet>
     <servlet-mapping>
        <servlet-name>JnlpDownloadServlet</servlet-name>
        <url-pattern>/app/*</url-pattern>
     </servlet-mapping>
  </web-app>

Servlet Configuration

This section shows how to configure a WAR file to include the JnlpDownloadServlet and also how the servlet can be configured.

Adding the Servlet to a WAR archive

First the necessary servlet code must be made available to the servlet container. This is done by populating the WEB-INF/lib directory. The servlet is added to a WAR archive by including the jnlp-servlet.jar file in the WEB-INF/lib/ directory. The servlet needs access to an XML parser, so JAR files implementing a Java XML parser must also be added. They can be downloaded from http://java.sun.com/xml.

Once the servlet code is available, the Web container must be told to invoke the servlet on the right set of JNLP and JAR files, or on a given subdirectory. This is all configured in the WEB-INF/web.xml file inside the <web-app> tag:

   <web-app>
      ...
   </web-app>
The first thing to do is to tell the Web container how to invoke the servlet. This is done by using the <servlet> tag:
   <servlet>
      <servlet-name>JnlpDownloadServlet</servlet-name>
      <servlet-class>com.sun.javaws.servlet.JnlpDownloadServlet</servlet-class>
   </servlet>
Secondly, the Web container must be told when to invoke the servlet. This can be done in several ways. It can be invoked for certain directories or for files with certain extensions. For example, to invoke it for JNLP files, add the following to the web.xml file:
   <servlet-mapping>
     <servlet-name>JnlpDownloadServlet</servlet-name>
     <url-pattern>*.jnlp</url-pattern>
   </servlet-mapping>
Or for a given subdirectory:
   <servlet-mapping>
     <servlet-name>JnlpDownloadServlet</servlet-name>
     <url-pattern>/app/*</url-pattern>
   </servlet-mapping>

See also example1 and example2 for examples on how to configure the servlet.

Logging

The servlet has built-in logging capabilities to monitor its behavior. Logging messages are generated in 4 different categories:

FATAL

A malfunction or internal error happened inside the servlet.

WARNING
An error processing some of the information in the WAR file, e.g., parsing the version.xml file.

INFORMATIONAL
Logging all requests and replies, re-scanning of directories, etc.

DEBUG
Detailed internal information about how a request is processed.
The logging output is controlled by two servlet initialization parameters, logLevel and logPath. The log level can be set to either NONE, FATAL, WARNING, INFORMATIONAL, or DEBUG. The log path specifies a file where the output will be written to. If no path is specified, logging is done to the standard log for servlets (using the ServletContext.log method). For example:
 <servlet>
    <servlet-name>
      JnlpDownloadServlet
    </servlet-name>
    <servlet-class>
      com.sun.javaws.servlet.JnlpDownloadServlet
    </servlet-class>

    <init-param>
      <param-name>
        logLevel
      </param-name>

      <param-value>
        DEBUG
      </param-value>
    </init-param>

    <init-param>
      <param-name>
        logPath
      </param-name>

      <param-value>
        /logs/jnlpdownloadservlet.log
      </param-value>
    </init-param>

  </servlet>

Configuring file extensions and MIME types

The servlet treats JNLP and JAR files specially. JNLP files will be macro-expanded as described in a section below. A version-based request for a JAR file might result in the generation for an incremental update. The servlet uses extensions to determine if a file is a JNLP or JAR file. The default extension of JNLP files is .jnlp and for JAR files is .jar. These default extensions can be overwritten by the initialization parameters: jnlp-extension and jar-extension. For example:
    <init-param>
      <param-name>
        jnlp-extension
      </param-name>

      <param-value>
        .xjnlp
      </param-value>
    </init-param>
The MIME type that is returned for a file is also based on its extension. The MIME type is looked up in the configuration files for the Web container and the WAR file. If no mapping is specified, the default MIME types are shown below:
     Extension      Default MIME type
     -------------------------------------------
       .jnlp        application/x-java-jnlp-file
       .jar         application/x-java-archive
       .jardiff     application/x-java-archive-diff
A mapping can be overwritten by using the <mime-type> element in the web.xml file. For example:
  <web-app>
     ...
     <mime-mapping>
        <extension>jnlp</extension>
        <mime-type>text/ascii</mime-type>
     </mime-mapping>
     ...
  </web-app>

Specifying Resources

Application resources such as images, JAR files, and JNLP files are stored in the WAR file. The WAR file itself is a hierarchical directory structure, and the location of a resource inside the WAR file determines what URL will be used to look it up.

Assume that the WAR file (or servlet) has been configured so it handles all URL requests that starts with http://www.mytool.com/tool/. If the following requests is made: http://www.mytool.com/tool/app/launch.jnlp. Then the JnlpDownloadServlet will look for the launch.jnlp resource in the app/ directory in the WAR file.

No version information

A resource that has no associated version information, such as a JNLP file for an application, is simply just added to the WAR file. For example, given the above example, then the WAR file should included the following file:
    /app/launch.jnlp
In general, the timestamp that will be returned for the file is the last-modified timestamp that the file has in the WAR file. The only exception is for JNLP files where it can be explicitly specified in the JNLP file (see below).

Version information and more

The version-based and extension-based download protocols in the JNLP specification allows a resource to be looked up based on version-id, operating system, system architecture, and locale. The JnlpDownloadServlet provides two mechanisms for associating this information with a resource. It can be done on a per-file basis, using a naming convention, or by a per-directory basis, using a configuration file. Both methods can be used for the same directory.

The following information can be associated with a resource:

  • A path to the resource, e.g., /app
  • A name for the resource, e.g., launch.jnlp
  • A version-id, e.g., 1.1.0
  • A list of supported operations systems, e.g., SunOS Linux
  • A list of supported architectures, e.g., x86
  • A list of supported locales, e.g., da da_DK
  • A flag indicating if the version-id is a product version or a platform version (only used for JRE downloading)

The path is specified by the location of the resource in the WAR archive. The rest of the information is either specified by the use of a naming convention or in the version.xml file.

Resource Naming

The file naming convention is used if a double underscore (__) marker is found in the filename. The filename is parsed according to the BNF notation shown below:
    file    ::= name __ options . ext
    options ::= option ( __ options ) *
    option  ::= V version-id |
                O os |
                    A arch |
                    L locale
Only one version-id can be specified per file. However, multiple os, arch, and locale fields can be specified. For example:
    application__V1.2__Len_US__Len.jar
will mean that the resource application.jar has a version-id of 1.2, and the following associated locales: en_US and en.

The version.xml file

In each directory, a version.xml file can be placed to describe the additional properties, such as a version-id, for files in that particular directory. This is an alternative to the file naming convention.

For example, placing the file application-1_2-us.jar in a directory, along with a version.xml with the following content:

       <jnlp-versions>
          <resource>
             <pattern>
                <name>application.jar</name>
                <version-id>1.2</version-id>
                <locale>en_US</locale>
                <locale>en</locale>
             </pattern>
             <file>application-1_2-us.jar</file>
         </resource>
       </jnlp-versions>
will be the same as having placed the file application__V1.2__Len_US__Len.jar in the directory.

A resource can also be specified with a platform version-id in the version.xml file. Such a resource is used to match a particular platform request for a JRE. A resource with a platform version-id is specified using the <platform> element. For example:

      <platform>
             <pattern>
                <name>J2RE</name>
                <version-id>1.3</version-id>
                <locale>en_US</locale>
                <locale>en</locale>
             </pattern>
             <file>j2re-1_3.0-us.jnlp</file>
             <product-version-id>1.3.0</product-version-id>
         </platform>
Platform version requests are generated internally by Java Web Start, when an application has requested a version of the Java 2 platform that is currently not installed on the local system.

The complete document type definition (DTD) for the version.xml is shown in the following:

   <!ELEMENT jnlp-versions <resource*, platform*)>
   <!ELEMENT resource (pattern, file)>
   <!ELEMENT platform (pattern, file, product-version-id)>
   <!ELEMENT pattern (name, version-id, os*, arch*, locale*)>
   <!ELEMENT name (#PCDATA)>
   <!ELEMENT version-id (#PCDATA)>
   <!ELEMENT os (#PCDATA)>
   <!ELEMENT arch (#PCDATA)>
   <!ELEMENT locale (#PCDATA)>
   <!ELEMENT file (#PCDATA)>
   <!ELEMENT product-version-id (#PCDATA)>

Mapping Requests to Resources

The JNLP specification defines four different kinds of download requests that Java Web Start (or more generally, a JNLP Client) can make to a server when requesting a resource:

  • Basic Download Request: This is an ordinary URL request to a specific file. The Last-Modified field of the response is used to determine if an update is available on the server. The basic download request can be used for all resource types.
  • Version-based Download Request: This request type can be used for JAR files and images, i.e., the following elements will generate a version-based download request: <jar>, <nativelib>, and <icon>, when the version attribute is included.
  • Extension Download Request: This request type is an extension of the version-based request type and is generated by either the <j2se> or <extension> elements. In addition to the version information, it also contains information about operating system, system architecture, and locale.
  • Platform-version Download Request: This is simliar to the extension download request, but used to request a particular version of the Java 2 SE Runtime Environment (J2RE). This request is generated by Java Web Start, when a JNLP file has requested a particular version of the J2RE that is not currently installed on the client system.

A request is initially processed by the JNLPDownloadServlet, and it extracts the following information from the request:

  • Path in WAR file,
  • Name of requested file
  • Version string (version-id parameter or platform-version-id parameter)
  • Current version-id (current-version-id parameter)
  • List of operation systems (os parameter)
  • List of architectures (arch parameter)
  • List of locales (locale parameter)

Consider example2, and assume that it is being hosted at http://www.mytool.com/tool2/. If the following requests is made: http://www.mytool.com/tool2/app/lib.jar&version-id=2.1. Then the path of the resource would be, app/, the name would be lib.jar, the version string would be 2.1, and the lists for os, architecture, and locales would be empty.

A request to a directory, e.g., http://www.mytool.com/tool2/app/, will get appended the default filename: launch.jnlp. Thus, it would be the same as http://www.mytool.com/tool2/app/launch.jnlp.

Handling a basic download request

A request for which no version-id is specified (neither version-id parameter or platform-version-id parameter is specified in the request) is handled as a basic download request.

The request is first checked to see if it contains a double underscore (__) or is a request to the version.xml file. If so, the request is rejected and a HTTP 404 error code is returned.

The JnlpDownloadServlet will then try to locate the resource with the given path and name in the WAR file, and if found return it. If the resource is not found, a HTTP 404 error code is returned for the request.

If a match is found and it is a JNLP file, then it is preprocessed as described below before returned.

Handling a version-based download request

The resource lookup for resources with a version-id is uniform across the version-based download protocol, the extension download protocol, and the platform-version download request.

First, the JnlpDownloadServlet will build a database of all the resources that are located in the WAR file directory that the URL request is accessing (based on the path in the request). The database is built by scanning the version.xml file (if present), and the list of files in the directory that is using the naming convention described above. The servlet caches the information internally. It only does a re-scan if the timestamp of the version.xml file is more recent than at the last scan. Thus, if you add a file using the naming convention, make sure to touch the version.xml file to force the servlet to do a re-scan.

Secondly, the servlet will scan through the entries in the database to find a match for the given request (the match rules are described below). For a non-platform request, first the resource entries in the version.xml file are scanned in the order they are specified, and then secondly the entries that are specified using the naming convention. For a platform-version request, the platform entries in the version.xml file is scanned in the order they are specified. If several entries matches the request, then the entry with the highest version-id is returned. If multiple matches is found with the same highest version-id, then the first one specified is returned.

The matching rules are as follows:

  1. The name of the resource must match the request
  2. The version-id of the resource must match the version string in the request
  3. For the os, arch, and locale lists the following rules are used:

    1. If an empty list is specified for the resource, then it is a match
    2. If a non-empty list is specified for the resource, then it is a match, if at least one of the values specified for the resource is a prefix of at least one of the values specified in the request.
The x-java-jnlp-version-id returned in the response is the version-id for the matching resource, except for a platform request where it is taken from the <product-version-id> field in the version.xml file.

If a match is found and it is a JNLP file, then it is preprocessed as described below before returned.

Automatic JARDiff generation

The servlet will automatically generate and return incremental updates to JAR files, if possible. If the current-version-id parameter is included in the request and the servlet can find both a match on the current-version-id and the requested version (given the above matching rules) and the request is for a JAR file (e.g., the target resource has the .jar extension), then a JARDiff file will be generated by the servlet. The JARDiff file is returned as long as its size is less than that of the requested version.

The JARDiff file is generated and stored in a temporary directory that is specific to the given Web container. The servlet locates the temporary working directory using the javax.servlet.context.tempdir context attribute.

Processing of JNLP files

The JnlpDownloadServlet will automatically macro-expand certain template values in a JNLP file and replace them with URLs that are specific to the current request. This will enable JNLP files to be written and deployed without containing hard-coded URLs.

Macro expansions

The servlet will automatically substitute certain fixed keys in the JNLP file (prefixed with $$) with URLs that are based on the current request. The keys are designed so location-independent WAR files can be created and deployed into a Web container.

The table below shows the 3 keys that the servlet will look for and substitute:

         Pattern       Value
         ----------------------------------------------------------------
         $$codebase    Complete URL for request, except name of JNLP file
     $$name        Name of the JNLP file
         $$context     Base URL for the Web Archive.
For example: Consider the example1 WAR file. Lets assume that it has been deployed at the following location: http://www.mytool.com/tool. Thus, a request to http://www.mytool.com/tool/app/launch.jnlp will return the JNLP file. The values of the macro-expanded keys would be:
      $$codebase = http://www.mytool.com/tool/app/
      $$name     = launch.jnlp
      $$context  = http://www.mytool.com/tool/

The servlet does not validate the format of the JNLP file nor that the XML is well-formed. The value substitution is purely textual.

Explicit Timestamps

An explicit timestamp can be included in a JNLP file to ensure that a consistent timestamp will be returned from the Web Server. This is especially useful if a JNLP file is replicated onto multiple Web servers that are serving the same URL in a round-robin or load-balancing fashion. Note that a similar method is not provided for JAR files. The version-based download protocol should be used instead.

An explicit timestamp is included in the JNLP if the first-line starts with TS:. If so, it is parsed accordingly to the ISO 8601 formatting of timestamps (see below). The first line is also removed from the contents.

See example1 and example2 above for sample JNLP files using the TS: element.

ISO 8601 Formatting

The general format of a timestamp is:
   YYYY-MM-DD hh:mm:ss
The dashes, colons, and seconds are optional:
   YYYYMMDDhhmm
The hh is in 24h notation. By default, the local time zone is used. A Universal Time (UTC) (also know as GMT time) can be specified by appending the capital letter Z to a time as in:
     23:59:59Z or 235959Z
The strings
     +hh:mm, +hhmm, or +hh
can be added to the time to indicate that the used local time zone is hh hours and mm minutes ahead of UTC. For time zones west of the zero meridian, which are behind UTC, the notation
     -hh:mm, -hhmm, or -hh
is used instead. For example, Central European Time (CET) is +0100 and U.S./Canadian Eastern Standard Time (EST) is -0500. The following strings all indicate the same point of time:
     12:00Z = 13:00+01:00 = 0700-0500