# JnlpDownloadServlet Guide

## Table of Contents

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

## Introduction

Java Web Start includes a servlet in the `sample/jnlp` directory of the JDK. It can be used on either a JNOP file and its associated resources packaged in a Web Archive (`.war`) file, or on an unarchived directory. The purpose of the servlet is to provide a simple and convenient packaging format for JNLP applications, so they can be easily deployed 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
- Pack200 support

The packaging support consists of one servlet: `JnlpDownloadServlet`. The servlet is packaged into the `jnlp-servlet.jar` file, which can be found in the SDK under `samples/jnlp/servlet/`.

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/<jar files for XML parser> (not needed if the servlet container is running J2SE 1.4+)

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>jnlp.sample.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. If your servlet container is running J2SE 1.4+, there is already a XML parser integrated in it. Otherwise please add jar files that implement a Java XML parser to the `WEB-INF/lib` directory. 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/<jar files for XML parser> (not needed if the servlet container is running J2SE 1.4+)

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>jnlp.sample.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. If your servlet container is running J2SE 1.4+, there is already a XML parser integrated in it. Otherwise 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>jnlp.sample.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>
          jnlp.sample.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>JRE</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 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 J2SE Runtime Environment (JRE). This request is generated by Java Web Start, when a JNLP file has requested a particular version of the JRE 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

## Pack200 Support

You can now host `*.jar.pack.gz` or `*.jar.gz` files together with your original `*.jar` files. If the client supports the pack200-gzip or gzip file formats, the servlet will return the compressed file if it is available on the server. Java Web Start 5.0 supports both compression formats. You must host both the original jar file and the pack file in the server, otherwise the servlet will not pick up the pack file. The pack file can be created with the pack200.exe command included in J2SE 5.0. For more information on the pack200 compression technology, please see Pack200 and Compression for Network Deployment.