Web Start Services

by Richard Osbaldeston (www.osbald.co.uk)
Updated: 11th June 2003

Overview

Web Start Services is basically a suite of servlets and scripts designed to make it easier to serve and maintain Java Web Start applications. The solution has grown from our own company's experiences in providing a Web Start package to a customer that is installed onto their local intranet. Much of this document is written assuming a similar scenario. Some key features include:

Installation

Ideally Web Start Services should work as a normal out-of-the Web Application deployment. However the distribution does (naturally) lack the Java Runtime files should you wish to provide automatic Java Runtime updates. You'll need to unpack the application (your J2EE container will probably have already done this for you during the deployment) and follow the instructions below for preparing the runtime files and modifying the corresponding runtimes.xml configuration file so that Web Start services will be able to locate the necessary files.

Introduction

The majority of this document is about configuring the Web Start server, however I want to start talking about your average users experience. The first step before they can use a Web Start application is that they'll need to install a Java Runtime. The ideal solution is where somebody's already provided every user with the Runtime and Web Start client but unless the client has a shared company image (via Ghost or something similar), this isn't always possible. In the latter case you'll need to prompt the user to install the required components.

If you look into the index.html page supplied with the example site you'll see how it uses Sun's suggested JavaScript/VbScript JWS detection (albeit a modified version to support 1.4.2 changes). Where the script thinks Web Start is missing it redirects the user to the install.html page, which currently talks about installing 1.3 but could easily be modified to describe the same process for 1.4.

It's worth noting that Java Web Start can also initiate an ActiveX auto-download of both the Runtime and Web Start (see autodownload example under the js folder). This only works for users of Windows and Internet Explorer and only available for a restricted set of runtime versions, but your users may find it an preferable in terms of ease of use.
The reasoning behind the limited support for the runtimes seems to be Web Start brings the number of different Java runtime installers up to at least three. There's the normal .exe installer available in international or en_US versions (from 1.4.2 this choice switches to the web install with US & European locales and optionally international support or just the full international standalone exe). The autodownload script actually uses a separate .cab file distribution, while Web Start itself supports the ability to update or add new runtimes from extension installers delivered as JNLP and signed jars.

The remainder of this document goes onto detailing the steps and frustrations you may face when installing the Java Runtime, describing the basic JnlpService and macro expansion features before talking about the extension download protocol support for providing runtime updates from the client's local servers.

Web Start client installation

Before a user client can run a Web Start application they'll first need to install a Java Runtime and Java Web Start combination. For developers this doesn't present much of a problem, try rolling out a Web Start solution on a company's intranet however and it can prove arduous.

Installing J2RE 1.4 and Web Start

Currently installing Java Runtime 1.4.x and Web Start is the preferred route, when you install a 1.4 JRE you get the Java Runtime, Java Plugin and Web Start all installed by default - easy. You might want to move the Java Web Start desktop icon & it's start menu folder from your user (installer) profile into the all users profile - just to ensure any other users of this machine all be able to get into the Web Start application manager (1.4.2 should fix this). Typically you'll then need to check / reset the proxy setting under the Java Web Start preferences and if this is a Windows XP client you'll also need to reset access privileges (see below).

NB: Windows XP machines will sometimes get the 'Installation refd memory that could not be read' error when trying to install the JRE, you might need to use the workarounds described in the bugreport (http://developer.java.sun.com/developer/bugParade/bugs/4799541.html) or follow the notes below for very similar problems that occur when installing 1.3.

As a side note if you take a peek under the runtime install directory 'C:\Program Files\Java\j2re1.4.1_02' here you'll find an installation file for Web Start 1.2 it'll be called something like javaws-1_2_0_02-windows-i586-i.exe, so if a user un-installs Web Start it could be re-installed from here.

Installing J2RE 1.3 and Web Start

Installing a Java Runtime 1.3 and Web Start 1.0.x combination is slightly more involved, the JRE and Web Start installations are provided by two separate files. You need to download and install the 1.3 JRE package followed by the Java Web Start installation (download from the Sun Java Web Start homepage).

If you are doing this on Windows XP box the chances are you haven't gotten this far. The version of InstallShield Sun used to deploy the 1.3 runtimes is incompatible with XP. When problems occur you'll be greeted by either 'Cannot find file ...' or scary-looking 'ikernel.exe application error' messages. To get around these you'll have to copy the installation file to your local drive, unpack the .exe with something like Winzip into a temporary folder, ensure you've got local admin rights and then execute the setup.exe from the extracted files (phew!).

Once Again when you've completed the install you should move the Java Web Start desktop icon & it's start menu folder from your user (installer) profile into the all users - just to ensure any other any other users of this machine all be able to get into the Web Start application manager. Typically you'll then need to check / reset the proxy setting under the Java Web Start preferences and if this is a Windows XP client you'll also need to reset access privileges (see below).

Problems with Access Rights

Windows XP continues to muddy the waters somewhat; default user privileges on XP prevent the user from writing to any location on the local hard disk except under their own user profile. The upshot is you need local Administrator rights to install just about anything (won't Palladium be fun). Insofar as wanting to give your users an easy time installing the runtime client and Web Start, this is a real dilemma. Can you really expect each and every user in your customers company to have local admin rights to their own PC?

A possible solution may exist within Microsoft Installer by utilizing a couple of new tricks; it has the ability both to perform administrative installation for whole workgroups or users, and more pertanent to our access rights problems - MSI installers can run with elevated privileges (remember Windows XP users run in locked-down mode as default). To quote from Microsoft's Windows Installer Service white paper:

Operation in Lockdown Environments

To decrease support costs, many organizations have locked down their desktops by controlling people's ability to write to the file system and registry. While this prevents a person from accidentally or intentionally modifying their configuration, it also requires administrator intervention whenever a new application needs to be installed.

Since the Windows Installer operates as a system service on Windows NT 4.0 and Windows 2000, it has the ability to run in one of two contexts:

    - As the Local System account, which has greater privileges than the user

    - As the user, which is the default behavior

In a Windows 2000 environment, using the Group Policy-based Change and Configuration Management, the administrator can approve certain applications, specifying that all configuration operations on those applications (installation, uninstall, and repair) run as the Local System account. In this manner, administrators can lock down the file system and registry as described above, and the Windows Installer service can still perform installations on the person's behalf. Only those applications approved by the administrator run with elevated privileges.

In a Windows NT 4.0 environment, administrators can specify that all Windows Installer transactions run with Local System privileges, but cannot granularly approve certain products.

These points would be ideal for enterprise runtime deployments; however we first need Sun to support these Microsoft Installer options, lobby Sun via the BugParade by voting for Bug ID 4854974. Otherwise administrators can attempt to re-package the Java Runtime setup as a MSI package with 3rd party tools such as Wise AppStudio (or similar OnDemand / InstallShield alternatives).


In the meantime we need to use the tools we're given. To this end I've seen local system administrators run around after-hours logging onto PC's with their own account privileges and installing the runtime & Web Start components for the user. However this approach is only partially effective, with Windows XP the directories created by the install (or any new directory) receive the default rights (Owner:Full, Everybody: Read, Execute), so this approach only gives Mr administrator full access (which they've got already!). This can lead to several access problems with the existing Web Start solutions:

Web Start 1.0.x is typically installed into 'c:\program files\java web start' and includes both the JWS application and the Web Start cache. Under XP users won't have sufficient privileges to write to the cache. They won't be able to install or run any applications, nor can they change Web Start preferences (this needs to modify the javaws.cfg configuration file in the JWS directory). The administrator will need to go back and allow either both modify and delete or just grant full access on the JWS directory :- particularly for the Web Start cache and cfg files.

Web Start 1.2 (j2re 1.4) tries to be more compatible; the Web Start cache has been moved under the users profile, no access problems here. However the application directory is still 'c:\program files\java web start' which still contains the vital javaws.cfg configuration file. While users will be able to use Web Start, they won't be able to change the Web Start preferences or allow Web Start to install extensions or update runtimes. Again the poor old administrator has to remember to weaken the access privileges on the directory or cfg files.

Web Start 1.4.2 (j2re 1.4.2) seems to have got it about right, while you must have local admin rights to install the JRE (awkward!). The Web Start application has been slipped under the JRE directory while all the configuration files, cache and extensions have been placed under a completely new location (and structure) under the user profile.

Problems with Proxies

By far the most common problem / support call we get is due to Web Start's admittedly poor history of proxy support. By default Web Start will attempt to find and then use your default browsers proxy settings. This can immediately cause problems if the users default browser isn't Internet Explorer or in situations where more than one browser is in use. In practise however this isn't much of an issue because Internet Explorer is the default in by far the greatest majority of cases, and Web Start supports several of the more popular browsers.

While using the browser settings will work for the for users literally using JWS from the web (such as home users with modems), when you're trying to deliver a Web Start package from a company intranet this default setting often (very often in my experience) won't work. Most companies will be using a proxy for external access - which Web Start will dutifully detect and use; but now JWS won't be able to connect to the internal network from an external facing proxy.

Users will have to change the proxy setting manually from the preferences dialog under the Java Web Start application manager, and probably change them again under the Control Panel, Java Plugin if they need applets (these settings don't appear to be shared). Due to a few inconvenient bugs and the inherent difficulty in explaining the process to novice users this process can be deeply frustrating.

The Web Start Preferences dialog has three basic proxy choices:

None (no proxy) - The most basic option is not to use any proxies and leave JWS to attempt to load everything from the local network. This is the option I tend to offer to the support call users, as it's by far the easiest choice to talk them through. It isn't strictly correct though; should they need to use a proxy for external access using this option will cripple Web Starts ability to work across the web (although for intranet workers this isn't much of a problem). The correct solution here may be to use the manual setting and exclude (No Proxy Hosts) the list of local servers.

Use Browser - This is the default setting and attempts to duplicate the proxy settings from your default browser. In theory this is should work but assumes all users will be connecting to the web to use Web Start and not the local intranet / LAN. When users need to use a proxy for external access and be able to access local network Web Start resources, the Use Browser setting is often inadequate. A key point when using a local Web Start server is to add this address to the list of exceptions. For Internet Explorer this is under browser options, connections, proxy setting, advanced; note that these addresses are separated by semi-colons and take care NOT to add any extra whitespace between hosts (or web start will then quietly ignore these addresses).

So the important trick to pain-free installations is to ensure that the local Web Start server is listed in the default browser's list of server addresses to exclude from the proxy BEFORE a user attempts to install Web Start for the first time.

This recurring issue is compounded with Windows Internet Explorer which has the ability to manually set proxies for each protocol but then sneakily sets the option "bypass proxy server for local addresses", this deafult avoids users ever having to enter a list of local servers to exclude from using the proxy. Unfortunately for us Web Start doesn't support this behaviour (Bug ID 4721082) which is why you'll often be able to view the local Web Start site and the JNLP files in question through your browser, but JWS can't load / run any of the applications.

Web Start is somewhat more adept when it comes to supporting other browser functions; automatic configuration - "use automatic configuration script". This points to the url for a pac script (JavaScript) file which can be shared amongst all the local network users and defines when and what proxy should be used for requests to which hosts. This makes it a very handy option to make the system administration easier. In an ideal world all we need to do here is make sure all the users are setup for automatic configuration and then modify the script to exclude our Local Web Start Server from using any external proxies. However the level of support Web Start offers may surprise you; see Automatic Proxy Configuration and Bug ID 4763344 -Proxy auto config script parsing still needs improvement. To summerise: Java Web Start only picks up the use of an external proxy from the first occurrence of the string PROXY in the pac script (as opposed to executing the javascript rules these scrits contain). This renders auto configuration effectively useless for telling Web Start when NOT to use proxies for local servers.

Manual - This lets the user enter the Http Proxy and Http Proxy Port and critically for our - Internet / Intranet duality problems above lets you specify a list of No Proxy Hosts (a list of local servers for which the external proxy shouldn't be used). Sadly Web Start slightly lets us down here once again, it's currently unwilling (or unable) to retrieve the list of proxy exceptions from the browser and have to be copied manually into the Web Start, Prefernecs, No Proxy Hosts list for each client (remembering to change the IE style semi-colons to commas in the process).

On Windows systems however the "bypass proxy server for local addresses" issue comes back to haunt us (above), this list of addresses that override the proxy setting will inevitably be empty because Internet Explorer doesn't particularly need to specify them. System administrators may well have to visit each machine and manually set the correct proxy options for each Web Start client.

If these support issues irritate you as much as they do us, you could try voting on the bugparade for the bug reports above, and given any spare votes the following RFE's may prove useful:

Bug ID 4433701 - Some proxy authentication flavours not supported.

Bug ID 4816800 - Web Start proxy settings not available to application.


The Web Start Server

JnlpService

The core aim of the Web Start Services package is to make available a simple out-of-the-box Web Start application provider. When we talk about application provider, I don't mean the actual application the users will see (you'll have to write that) I'm talking about the server from which clients download these applications. At a basic level this server need not be any more complicated than a normal web server; any old server that you can add a mime type for Web Start JNLP files will do, Apache is doubtless the most common web server in use, a single one line change to the http.conf (or .htaccess) configuration file is all that 's needed to turn Apache into a Web Start Server:

AddType application/x-java-jnlp-file JNLP

Within your web server the individual Web Start client applications are defined by JNLP files, these JNLP files act much like batch files or shell scripts would, setting the applications classpath and invoking the desired version of the java runtime. Web Start's JNLP files however are slightly more complicated beasts, not only are they responsible for launching client applications - but they will also update client applications (or individual components therein) when newer components are available on the server. To perform these update checks JNLP files need to contain references to their originating remote server. This is where maintaining JNLP files becomes a chore; when the server contains many different JNLP applications, or you're running different development, acceptance & production servers and need to swap applications between these environments, or even when you just want to deliver a packaged Web Start solution to a client for installation on their servers. You quickly realise how incredibly inconvenient it is to have all this hard coded information that needs changing in so many different configuration files.

Dynamic JNLP files

The obvious solution to this dilemma is to generate these JNLP files dynamically such that no host (or path) information is hard coded. A simple static page web server won't be much use at this point; although you could generate JNLP files yourself with your server scripting language of choice (PHP, ASP, Python, Perl etc..) as we all know Servlets & JSPs offer the most flexible and universally compatible scripting environment. Sun have already recognised these problems, the JnlpDownloadServlet is available as part of the developers Web Start pack. This provides three different services:

Firstly it allows JNLP files to be templates (much like Velocity or WebMacro for html pages), the host information is substituted within these files before they are sent to the client.

Second it provides support for the JNLP version-based download protocol as described by the Java(tm) Network Launching Protocol & API Specification (JSR-56). Basically this allows access to a specific version of a specific resource at the given URL.

Finally JnlpDownloadServlet provides automatic generation of JarDiff files, these can be used to provide incremental updates to cached JAR files. Typically, downloading an incremental update swill be much faster than downloading a new version of a resource.

Web Start Services contains the JnlpService servlet, which duplicates and extends upon the most useful aspect of macro expansion functions allowing JNLP files to be specified in templates. Sun's JnlpDownloadServlet only defines the three macros $$codebase, $$context and $$name while useful with just three substitutions they are of limited use.

JnlpService both fully supports these basic macros and adds an extended set of more generally useful macros. Additionally JNLP files often require the connection properties of external resources other than just the necessary host context & name used by Web Start itself. For example an application might need a JDBC hoststring for connecting to a database or a remote JNI service, mailhost, LDAP etc.. JnlpService allows administrators to define their own macros, passed as either servlet init-params for server wide resources or passed as http parameters along with the JNLP request.

Default Macro Usage

The default macros are defined as follows:

$$codebase Complete URL for request, except name of JNLP file
$$name Name of the JNLP file
$$href Alias for $$name (for backward compatibility)
$$context Base URL for the Web Application archive
$$host Host portion of the complete URL
$$contextPath Context Name of the Web Application
$$parent Parent directory URL of $$codebase
$$nameNoExt Name of JNLP file (without the filename extension)
Example:
$$codebase = http://www.myhost.com/jnlpExampleSite/jws/
$$context = http://www.myhost.com/jnlpExampleSite/
$$name = application.jnlp
$$href = application.jnlp
$$host = http://www.myhost.com
$$contextPath = /jnlpExampleSite
$$parent = http://www.myhost.com/jnlpExampleSite/
(if $$codebase was deeper this would not equal $$context)
$$nameNoExt = application

To use some of these macros you'll need to define a template JNLP file for launching your application, substituting any hard-coded host or path values with a combination of the suitable macros. The most essential of which are $$codebase and $$name which directly substitute the jnlp element's attributes codebase and href. For our example above this might look something like:

application.jnlp

<?xml version="1.0"?>
<jnlp spec="1.0+" codebase="$$codebase" href="$$name">
<information>
<title>My Application - Example</title>
<vendor>My Company</vendor>
<description>Lorem ipsum dolor sit amet</description>
<icon href="applicationIcon.gif"/>
<homepage href="$$context"/>
<offline-allowed/>
</information>

<resources>
<j2se version="1.2+"/>
<jar href="HelloWorldApp.jar"/>
</resources>

<application-desc main-class="HelloWorldApp">
<property name="JdbcHostString" value="$$jdbcHostString"/>
</application-desc>
</jnlp>

When this JNLP file is requested the JnlpService will read the template and substitute the correct values in place of these macros, using the values above (assuming application.jnlp is in the folder jnlpExampleSite/jws/) this will translate to something like:

<?xml version="1.0"?>
<jnlp spec="1.0+" codebase="http://www.myhost.com/jnlpExampleSite/jws/" href="application.jnlp">
...

Moving application.jnlp to different folders or even different hosts (with Web Start Services installed) requires no modification to the JNLP file itself. The values for the codebase etc.. will always reflect that of the originating request. Which is nice.

User Defined Macros

You may have noticed in the previous example that our application is passed via our JNLP a property called JdbcHostString which is assigned to the value of the $$JdbcHostString macro. Exceptionally eagle-eyed readers may have also noticed that $$JdbcHostString isn't from our eclectic list of default macros; it is in fact an example of a user defined macro.

User defined macros can be defined two ways; the most useful technique is to add either container context parameters or better still adding JnlpService servlet parameters (init-param) to your servers web.xml file. Any parameters passed to the service are treated as candidate macros and can be expanded with the $$<name> syntax. Effectively these user-defined macros act like a poor-mans JNDI server (any could hold the connection information to one). This provides a centralised point for administrators to modify any client-specific parameters that need to be passed to the hosted JNLP applications, which is much easier than trying to remember to modify each JNLP file in turn. Users are free to add whatever naming schemes and macros they require to fulfil any more advanced configuration options.

The other method of passing macro parameters is via the http request string, such as; http://www.myhost.com/ExampleSite/webstart/jws/MyApplication.jnlp?param=value&param2=value"
Note: Web Start will by default when installing a JNLP file for the first time will re-request the itself from <codebase><href> whenever it finds a valid href attribute. This second request will be missing these extra GET parameters. The upshot of which means the cached JNLP is left in an un-expanded form for the non-default, non-init-param macros. Users wanting to use this should omit the href (which has other side effects - applications won't appear in the Web Start application manager). It can still be useful for run once only installers, which shouldn't need to be re-used or re-run or when you don't mind about the App Launcher (applications are still cached, just not displayed).
I've read posts from the web start-engineering team on this very topic; it seems likely a future version of Web Start will support this feature (or something very similar).

Detailed Syntax Notes

Macro names can contain any number of alphanumeric characters (a-z and numbers) and including the following characters '.' '_' and '-'. A macro usage always begins with $$ and continues until a non-valid character, space or the end of the line is reached. When a definition for a macros value cannot be found, it is left intact without any substitution.

Occasionally you many find the basic macro usage insufficient to declare a substitution. For example 'icon=$$nameNoExt.gif' will actually look for a macro called 'nameNoExt.gif', rather than expand the 'nameNoExt' macro into 'icon=application.gif '. In cases such as this macros can be escaped with curly braces, so the correct syntax becomes 'icon={$$nameNoExt}.gif'.

Servlet Installation

JnlpService is pretty simple to setup manually if you wish (although it's easier by far to just deploy the .war package). The servlet definition is pretty average, the servlet mapping needs mapping to a file pattern so it gets invoked on a file request, additionally the extension of this file must be mapped to Web Start's mime type: application/x-java-jnlp-file. There are a couple of configuration parameters to take note of;

parse.querystring (true|false) enables or disables the parsing of http parameters as user macros.

parse.initparam (true|false) enables and disables the parsing of init-params as user defined macros.

The only reason for these options is to alter the normal precedence of init-params macros over http parameter macros, and they're could conceivably be a slight performance improvement to leaving parsing of http parameters disabled.

web.xml

<servlet>
<servlet-name>JnlpService</servlet-name>
<servlet-class>webstartservices.JnlpService</servlet-class>

<init-param>
<param-name>parse.initparam</param-name>
<param-value>true</param-value>
</init-param>

<init-param>
<param-name>parse.querystring</param-name>
<param-value>false</param-value>
</init-param>

<!-- example parse.initparam macro definition (safe to remove this) -->
<init-param>
<param-name>jdbcHostString</param-name>
<param-value>jdbc:oracle:thin:@dbhost:1521:dbsid</param-value>
</init-param>

<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>JnlpService</servlet-name>
<url-pattern>*.jnlp</url-pattern>
</servlet-mapping>

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

Notes

It should be noted that JnlpService supports neither the version-based download protocol nor the generation of JarDiff files unlike Sun's JnlpDownloadServlet. In doing client deployments we've found the versioning protocol requires more server side configuration & management when we're trying to reduce it. Plus the proving of a product across many different version combinations of sub-components is easier said than done. I'd suggest supplying a new JNLP file for new product versions, which use known and tested versions of external JAR components. Ie. Keep It Simple Stupid.

The JarDiff's might be useful to alleviate some of the world-wide wait should your application's download range into the ten's of megabytes (or your users only have slow modem connections) and/or where you're updating your application regularly enough for the updates to become a nuisance. In the majority of situations this simply isn't required, and the idea of automatically patching signed jars seldom seems to work terribly well in practise.


Automatic Java Runtime Installation

Java Web Start is capable of downloading the required runtime version on requested from the JNLP and not already installed in the client. Unfortunately to date this has been dependant on support from Sun the runtime versions available from download, unfortunate because their support has been pretty inconsistent only a few versions are on offer. One solution is to provide runtime installer extensions from a local server, especially useful when you're providing a Web Start backed applications on a customer intranet.

The only valid response to a Web Start client's request is a JNLP installer of the matching product version; these extension installers use a slightly modified kind of JNLP file to invoke a Java based installer for a particular runtime version.

You can specify that your application will require a particular product version of the Java Runtime Environment (JRE) in the j2se element of your JNLP file. It's important to understand how this specification works. Sun differentiate between platform versions and product versions.

Platform version generally defines a major Java version such as 1.2, 1.3 or 1.4. For example specifying platform version 1.3 would match the product versions 1.3.0, 1.3.0_01, 1.3.1, 1.3.1_01 etc.. Typically it will match to the highest installed product version that matches the platform version.

Product version denotes a particular runtime; moreover they specify a vendor specific runtime; to achieve this product versions need to identify the vendor by passing a unique href attribute. For Sun Java Runtimes products versions take the form 1.3.0, 1.3.1_07, 1.4.0_02 with the href=http://java.sun.com/products/autodl/j2se.

Strictly speaking the href attribute is supposed to be for JRE vendors to provide unique URL that name their particular implementation. But you can also point this to a local servlet that knows how to respond to Web Start requests for a given JRE. With Web Start Services the servlets DownloadService and InstallerDownloadService do just this. Whenever Web Start decides it needs to download a new runtime the url from the href attributes will be queried with additional parameters specifying the acceptable os and platform/products versions. For our above examples this might produce a request of the form:

http://java.sun.com/products/autodl/j2se?arch=x86&os=Windows+XP&
locale=en_US&version-id=1.3+1.4&known-platforms=1.2

To fulfil this request DownloadService will need to know which of the available runtime products will best satisfy these variables. The servlet uses a simple xml configuration file called runtimes.xml, which holds not only the filenames of the available contents but also encodes much of the order of preference for the runtime choice. The basic structure of the file contains a list of the runtime installation packages in the directory along with they're assigned platform and product versions. Given the downloads directory structure below here's what an corresponding runtimes.xml might look like:

../downloads/javaRuntime

j2re-1_3_1_04-solsparc.jar
j2re-1_3_1_04-linux-i386.jar
j2re-1_3_1_06-windows-i586.jar
j2re-1_3_1_06-windows-i586-i.jar
j2re-1_3_1_07-windows-i586.jar
j2re-1_3_1_07-windows-i586-i.jar
j2re-1_4_1_02-windows-i586-i.exe
j2re-1_4_0-windows-i586-i.exe
runtimes.xml

Runtime Configuration DTD (runtimes.xml)

<!ELEMENT jre EMPTY >
<!ATTLIST jre arch CDATA #REQUIRED >
<!ATTLIST jre international CDATA #IMPLIED >
<!ATTLIST jre name CDATA #REQUIRED >
<!ATTLIST jre os CDATA #REQUIRED >

<!ELEMENT platform ( product+ ) >
<!ATTLIST platform version CDATA #REQUIRED >

<!ELEMENT product ( jre+ ) >
<!ATTLIST product vendor CDATA #IMPLIED >
<!ATTLIST product version CDATA #REQUIRED >

<!ELEMENT runtimes ( platform+ ) >

Sample runtimes.xml

<runtimes>
<platform version="1.4">
<product version="1.4.0" vendor="Sun Microsystems Inc.">
<jre name="j2re-1_4_0-windows-i586-i.exe" os="Windows"
arch="x86" international="true"/>
</product>
<product version="1.4.1_02" vendor="Sun Microsystems Inc.">
<jre name="j2re-1_4_1_02-windows-i586-i.exe" os="Windows"
arch="x86" international="true"/>
</product>
</platform>

<platform version="1.3">
<product version="1.3.1_07" vendor="Sun Microsystems Inc.">
<jre name="j2re-1_3_1_07-windows-i586.jar" os="Windows"
arch="x86" international="false"/>
<jre name="j2re-1_3_1_07-windows-i586-i.jar" os="Windows"
arch="x86" international="true"/>
</product>
<product version="1.3.1_06" vendor="Sun Microsystems Inc.">
<jre name="j2re-1_3_1_06-windows-i586.jar" os="Windows"
arch="x86" international="false"/>
</product>
<product version="1.3.1_06" vendor="Sun Microsystems Inc.">
<jre name="j2re-1_3_1_06-windows-i586-i.jar" os="Windows"
arch="x86" international="true"/>
</product>
<product version="1.3.1_04" vendor="Sun Microsystems Inc.">
<jre name="j2re-1_3_1_04-solsparc.jar" os="Solaris"
arch="sparc" international="true"/>
</product>
<product version="1.3.1_04" vendor="Sun Microsystems Inc.">
<jre name="j2re-1_3_1_04-linux-i386.jar" os="Linux"
arch="x86" international="true"/>
</product>
</platform>
</runtimes>

In our example each of the runtime packages (*.jar) have are assigned into a <jre> element, this stipulates implicitly what platform version it is (from the parent platform element), and explicitly states the product version, what os, architecture this package applies to and whether or not it contains an international runtime (optional - defaults to true).

The most critical points about this XML description is that the platform versions MUST be valid numerical digits (1.2, 1.4, 1.3.1 etc..). This is necessary to satisfy request of the form version-id=1.2+ (java platform 1.2 or higher). While product versions are treated as normal character strings - here it's the order the JRE product versions are listed in the platform element that expresses the order of preference for the DownloadService. This latter point is partly a means to express custom preferences for your own local environment and partly because they're simply isn't a common numbering system of JRE products between vendors - and while Sun's own product versions largely consist of numbers and underscores this hasn't always been followed we've had version strings like '1.3.1_01a', '1.2.2_13/_013' and '1.2.1_004' in the past. So a request for version-id="1.3.1*" will return 1.3.1_07 (first matching product string), while a request for version-id=1.4 returns 1.4.0 (instead of 1.4.1_02) assuming both os and arch also match the values from the servlet request.

The international attribute can be used as a hint to the servlet when you need to make sure of providing an international runtime. Normally the deciding factor is the locale parameter from the runtime request, if this is 'en_US' you get the non-internationalised version otherwise the international one. This can be overridden by providing a second servlet mapping which has the word international in it's mapping (much like Sun's default autodl servlet will when you use the alias 'http://java.sun.com/products/autodl/international'. This can be better demonstrated by the servlet definition:


web.xml

<servlet>
<servlet-name>DownloadService</servlet-name>
<servlet-class>webstartservices.DownloadService</servlet-class>

<init-param>
<param-name>jre.directory</param-name>
<param-value>downloads/exeFiles/</param-value>
</init-param>

<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>DownloadService</servlet-name>
<url-pattern>/http/download</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>DownloadService</servlet-name>
<url-pattern>/http/international</url-pattern>
</servlet-mapping>

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

<mime-mapping>
<extension>jar</extension>
<mime-type>application/java-archive</mime-type>
</mime-mapping>

The only configuration parameter needed by DownloadService (and it's sister InstallerDownloadService) is the 'jre.directory' parameter. This indicates your downloads folder, the location of the runtime files & the all-important runtimes.xml. You can run several DownloadService servlets each with a different jre.directory (& servlet-mapping). You may well want to run a basic DownloadService to serve (.exe) installers as the initial Java client install and an InstallerDownloadService to provide automatic runtime updates once thereafter.

DownloadService and InstallerDownloadService

Web Start services comes with two different servlets capable of responding to and auto-download request from a client. DownloadService will process the client runtime request and choose the best match from the runtime.xml file (as explained above). If this file is a JNLP file it assumes it contains an extension protocol installer and will return the JNLP resource to the runtime setup routine for our client to invoke. If the returned file is of any other type, executable .exe for example; it returns that file as it's associated mime type for the client to handle - in this case the browser will invokes a file download and file save as.. dialog. In this way DownloadService performs two functions, returning extension installers for JNLP files or a basic runtime download servlet suited towards HTML/JSP pages serving the clients initial runtime installation.

This method of supplying runtime installers (rather than requesting http downloads) will require a JNLP file for each runtime. To make this less of a trial DownloadService makes use to JnlpSerivce macros and once a runtime request has been processed the additional macros (detailing the runtime chosen) will be made available:

$$runtime.name filename of the runtime (or JNLP installer) selected
$$runtime.platformVersion platform version chosen
$$runtime.productVersion product version
$$runtime.os client/runtime OS name
$$runtime.arch client/runtime OS architecture
$$runtime.locale clients locale
$$runtime.international international version (international) or US (en_US)
$$runtime.vendor runtime vendor information
$$runtime.url url to the chosen runtime/JNLP file
$$runtime.version-id copy of the clients request version-id
$$runtime.known-platforms copy of the java versions installed on the client
Example:
$$runtime.name = j2re-1_3_1_06-windows-i586-i.jnlp
$$runtime.platformVersion = 1.3.1_06
$$runtime.productVersion = 1.3
$$runtime.os = Windows
$$runtime.arch = x86
$$runtime.locale = en_GB
$$runtime.international = international
$$runtime.vendor = Sun Microsystems Inc.
$$runtime.url = http://www.myhost.com/jnlpExampleSite/downloads/javaRuntime/ j2re-1_3_1_06-windows-i586-i.jnlp
$$runtime.version-id = 1.3
$$runtime.known-platforms = 1.2.2

The extension protocol now requires the client to download the runtime files in question and then run (via Web Start) an installation program that will setup this Java runtime. Before we talk about a suitable installation program for DownloadService I'd like to talk about how it differs from the other download routine InstallerDownloadService.

As we've already mentioned, DownloadService must return a JNLP file from the runtimes.xml to comply with Web Starts extension protocol; this also means that Web Start requires a suitable JNLP file for each runtime. InstallerDownloadService is a subclassed version of DownloadService that takes a different slant on the problem. Rather than directly return the matched filename from the runtime.xml it automatically generates its own JNLP installer and passes this the url to the matching file instead. The setup file invoked from here will then initiates a download of the passed url (to the runtime files) and then continues to install the package. When InstallerDownloadService is passed either a Jar or Zip file, it will assume this contains the runtime files and will generate a suitable JNLP file to download and extract the contents. It also has rather more experimental support on Windows platforms for .exe files, where it will download and then execute the returned file; which should obviously be the usual runtime setup utility we all know and love. This makes InstallerDownloadService much similar to install and maintain, administrators need not worry about any further configuration or JNLP files other than placing the runtime files in the directory and modifying. Although it lacks the multi-purpose http downloader and extension protocol installer provided by DownloadService.


DownloadService & JREInstaller

Web Start Services comes with a modified version of Dale Searle's original JREInstaller servlet that can be used as a suitable runtime installation routine for DownloadService. This does however require a fair amount of manual configuration for each runtime you need to add. Firstly you need to define an extension JNLP file responsible for installing the particular runtime version. Although the macro expansions help, you still need to pass the runtime files to each installer as an additional signed jar; an example for a 1.3.1_07 runtime might look like this:

j2re-1_3_1_07-windows-i586.jnlp

<?xml version="1.0"?>
<jnlp spec="1.0+" codebase="$$codebase">
<information>
<title>Java $$runtime.platformVersion Installer</title>
<vendor>Web Start Services</vendor>
<description>$$runtime.vendor, Java Runtime $$runtime.productVersion ($$runtime.os)</description>
<homepage href="$$context"/>
</information>

<security>
<all-permissions/>
</security>

<resources>
<j2se version="1.2+"/>
<jar href="runtime-installer.jar"/>
<jar href="jre_1.3.1_07.jar"/>
<property name="jre.container" value="jre-1.3.1_07.jar"/>
<property name="jre.version" value="$$runtime.productVersion"/>
<property name="jre.execute" value="bin\\javaw.exe"/>
</resources>

<installer-desc main-class="webstartservices. JREInstaller "/>
</jnlp>

You'll notice that along with the jar file containing the JREInstaller classes (runtime-installer.jar) it also passes the runtime files in the Jar archive jre_1.3.1_07.jar. This runtime archive must also be contracted in a very particular way. To create a suitable runtime jar follow these steps:


InstallerDownloadService & RuntimeInstaller

RuntimeInstaller is the installation routine that will be called via InstallerDownloadService, however you shouldn't need to know this as InstallerDownloadService automatically generates the JNLP installer files on the fly. Adding a new Java Runtime is much eaiser too:

Alternatively if you're confient of only needing to support Windows clients, you can simply place the Java runtime executable (.exe file) into the download directory and modify the runtimes.xml to return this file rather than a .jar or .zip archive. Notice that current neither the runtime .jar (or .zips) need to be signed, in practise it's probably a good idea to sign them however - just to be sure they've not been tampered with.


I think you can see how much easier InstallerDownloadService is to use, I'd prefer to use DownloadService purely to provide http downloads on request (initial installs) and ignoring JREInstaller in favour of InstallerDownloadService to provide the automatic runtime updates thereafter.