Java Network Launch Protocol (JNLP) Support

Introduction

The next-generation Java Plug-In technology (hereafter the "Java Plug-In") provides support for launching applets directly from JNLP files. Previously, only Java Web Start utilized JNLP files, for the purpose of launching Java applications. Now Java applets can be described using the same meta-descriptor.

This new functionality offers many benefits:

  • Access to JNLP extensions in applets. Powerful JNLP extensions have been written for Java Web Start applications, such as the Java binding to the OpenGL 3D API, the scene graph for the JavaFX run-time, and the NASA World Wind Java planetary and extra-planetary visualization system. It is now trivial to use these extensions from applets. (There have been workarounds to utilize some of these extensions before, but these workarounds have always carried severe restrictions.)
  • Access to the JNLP APIs. Sandboxed Java Web Start applications have access to the JNLP APIs to gain user-controlled access to persistent storage (the PersistenceService), lazily-downloaded parts (the DownloadService), the local file system (the FileOpenService and FileSaveService), and other useful functionality. Now all of this functionality is transparently available to applet developers.
  • Auto-download, version selection and more. Building on the new Java Plug-In's architecture, applets launched via JNLP may utilize all of the features previously available to Java Web Start applications, such as JRE version selection, auto-download of a particular version of the JRE, passing command-line arguments and system properties to the JVM, and much more.
  • Unification of Java deployment. Support for JNLP in the Java Plug-In unifies the deployment mechanisms for Java content in and out of the web browser, simplifying the decision making process for the developer. Programs written using a component model can be trivially deployed as an element on a web page, as a stand-alone application, or both.

This document highlights the slight semantic differences when using JNLP to describe Java applets in the browser, and provides examples of simple and advanced applet deployment via JNLP.

Usage

To launch an applet from a JNLP file, use the jnlp_href parameter in the <applet> tag:



    <applet width="300" height="300" code="com.mycompany.MyApplet">
      <param id="jnlp_href" value="my_applet.jnlp">
   </applet>
my_applet.jnlp
    <?xml version="1.0" encoding="UTF-8"?>
   <jnlp href="my_applet.jnlp">
     <information>
       <title>My Applet</title>
       <vendor>My Company, Inc.</vendor>
       <offline-allowed />
     </information>
     <resources>
       <j2se version="1.4+"
href="http://bit.ly/xyGWeN" />
       <jar href="my_applet.jar" main="true" />
       <extension id="SomeExtension"
  href="http://some.server.com/some_extension.jnlp" />
     </resources>
     <applet-desc 
         id="My Applet"
         main-class="com.mycompany.MyApplet"
         width="300"
         height="300">
     </applet-desc>
   </jnlp>

Developers who have used Java Web Start will already be familiar with the JNLP file format. For developers new to JNLP, it is specifed under JSR-56, and described in the context of applications in the Java Web Start Developers' Guide.

Semantics

The <applet> tag and the JNLP file have overlapping mechanisms for specifying certain parameters. These conflicts are resolved as follows:

  • width and height: these attributes are always taken from the <applet> tag and not from the JNLP file. The assumption is that the browser knows best how big the applet should be on the web page, and only the browser can support page-relative width and height (for example, width="50%").
  • codebase: if the JNLP file specifies an absolute codebase in the <jnlp> tag, then that is used. Otherwise, it is composed using the rules described in the codebase handling section.
  • code and archive: if a jnlp_href parameter is specified, any code or archive parameters will be ignored by the new Java Plug-In. This makes it possible to write one <applet> tag which has completely different behavior when run with the old and the new Java Plug-In; see the examples below.
  • Any applet parameters specified using the <param> tag are merged with those specified in the JNLP file. If the same parameter is specified via both the <applet> tag and the JNLP file, the <applet> tag's version overrides the JNLP file's version, except for the java_arguments and java_version parameters.
  • The new java_arguments and java_version parameters are unnecessary in JNLP applets. The mechanisms in the JNLP file for requesting a JRE version, or passing command-line arguments to the JVM, are used instead. Command-line arguments and JRE version requests from the JNLP file override those specified in the HTML for the applet.
  • It may be advantageous to specify certain parameters desired early on in the applet's startup process, such as image, boxbgcolor, etc. in the HTML instead of in the JNLP file, because these are available immediately upon loading the web page rather than requiring the JNLP file to be downloaded separately first.

Codebase Handling

Specification

In a JNLP file, the codebase is an optional parameter to the <jnlp> tag. It is used both to locate resources described in that JNLP file, as well as to find the JNLP file itself. For technical reasons, Java Web Start is not able to refresh the contents of the JNLP file from the server unless an absolute codebase is specified.

In the Java Plug-In, a codebase is always provided by the browser, either because it was explicitly specified, or implicitly obtained from the location of the HTML document. This allows relative URLs to be used in JNLP files, which is very useful for moving an entire tree of content from one server to another.

JNLP files reference other JNLP files in a tree structure. The root JNLP file for a JNLP applet is referenced by an <applet> tag. The applet tag's codebase helps define the location of the root JNLP file.

The rules for codebase computation are as follows:

  • If an absolute codebase is specified in the JNLP file, it is used. This is required for backward compatibility reasons.
  • If the JNLP codebase is not specified, the directory containing the JNLP file is used.
  • Otherwise, merge the JNLP's codebase into the directory containing the JNLP file.

In simple Java terms, this can be expressed as

URL new_codebase = new URL(current_jnlp_dir, current_jnlp_codebase);

This codebase computation is not an extension of JSR-56. JSR-56 does not restrict the codebase to be absolute, and therefore it may be relative.

Examples

Example 1:



    this JNLP's location:   http://www.sun.com/this.jnlp
    this JNLP's codebase:   http://www.foo.com/test/
    resulting codebase for
     parsing this JNLP:    http://www.foo.com/test/

Example 2:



   this JNLP's location:   http://www.sun.com/test2/this.jnlp
   this JNLP's codebase:   <none>
   resulting codebase for
     parsing this JNLP:    http://www.sun.com/test2/

Example 3:



   this JNLP's location:   http://www.sun.com/this.jnlp
   this JNLP's codebase:   codebasedir
   resulting codebase for
     parsing this JNLP:    http://www.sun.com/codebasedir

Example 4:

Relative paths are used to refer to each nested JNLP, just as in a tree of HTML files.



    www.sun.com/html/my_applet.html
     refers to: my_applet.jnlp
     codebase:  www.sun.com/html

     my_applet.jnlp:
       codebase not specified
       inherits "www.sun.com/html"
       references JNLP extension "jogl/jogl.jnlp"

     jogl/
       jogl.jnlp
       codebase not specified
       inherits "www.sun.com/html/jogl"
         (the directory containing jogl.jnlp)
       references gluegen-rt/gluegen-rt.jnlp

       gluegen-rt/
         gluegen-rt.jnlp
         codebase not specified
         inherits "www.sun.com/html/jogl/gluegen-rt"
           (the directory containing gluegen-rt.jnlp)

Best Practices

We recommend either:

  • Leaving the codebase empty for both the main or extension JNLP file, allowing the referencing JNLP file or <applet> tag to implicitly specify the codebase.
  • Specifying an absolute URL for the codebase of any main or extension JNLP file.
<jnlp>

Command Line Arguments

The JNLP file syntax supports multiple mechanisms for passing command line arguments to the JVM. The java-vm-args attribute of the <java> tag may be used, the max-heap-size attribute of the same tag may cause implicit specification of an -Xmx argument, or a system property may be specified with the <property> tag.

The new Java Plug-In supports specification of JVM command-line arguments on a per-applet basis, so all of these features of the JNLP file format are supported, with some rules and restrictions.

A set of "secure" JVM command-line arguments and system properties is defined in the JNLP File Syntax section of the Java Web Start Developers' Guide. In the new Java Plug-In, by default, only these secure command-line arguments may be specified on a per-applet basis.

Non-secure command-line arguments may only be specified on a per-applet basis if:

  • the applet is launched via a JNLP file, and
  • the command-line arguments are specified in the JNLP file, and
  • the applet's JNLP file is signed.

The concept of a signed JNLP file is described in the JNLP specification, section 5.4.1, "Signing of JNLP Files".

These restrictions are necessary for security reasons. The requirement that the JNLP file be signed in order to specify non-secure command-line arguments ensures that the original developer of the applet selected the command-line arguments for the applet.

Note that command-line arguments may still be specified for all applets via the Java Applet Runtime Settings in the Java Control Panel. Any command-line arguments specified there are not subject to the same restrictions as per-applet command-line arguments. In particular, non-secure arguments like -Xdebug may be specified in the Java Control Panel without affecting the execution of applets. This is useful for debugging applets during development. The per-applet command-line arguments are added to any specified via the Java Control Panel, and do not completely replace them.

When per-applet JVM command-line arguments are specified, it is likely that the new Java Plug-In will need to launch another JVM instance in order to satisfy them. In other words, it is unlikely that a preexisting JVM instance will have been started with the correct set of command-line arguments to satisfy the current applet's request. The rules for exactly when a new JVM instance is launched to start a given applet are deliberately left unspecified, as this functionality is brand new in the 6u10 release and may need to change in subsequent releases. Here is a rough set of guidelines for the sharing and creation of new JVM instances:

  • If the command-line arguments used to start a preexisting JVM instance are a superset of the requested arguments, the preexisting JVM instance will be used.
  • If a JVM instance is launched for the "default" set of command-line arguments (i.e., those specified in the Java Control Panel, with no per-applet arguments specified), then this JVM instance will never be used to launch any applet that has even one per-applet command-line argument.
  • -Xmx is handled specially: if a preexisting JVM instance was started with for example -Xmx256m, and a new applet requests -Xmx128m, then new applet will very likely be run in the preexisting JVM instance. In other words, -Xmx specifications are matched with a greater-than-or-equal test.

There is no way to "name" a JVM instance used to launch a particular applet and "force" subsequent applets into that JVM instance.

See the section on the separate_jvm parameter to isolate a particular applet in its own JVM instance, separate from all other applets.

Please provide your feedback if you have comments or questions about this functionality.

Examples

Scene Graph Demos

The Scene Graph Demo Applets, courtesy of the Scene Graph development team, illustrate how to use a JNLP extension from within an applet, and how to structure code for easy reuse in both applications and applets.

Each of the Scene Graph demos is simply a Component which can be placed in any kind of Container. When run as an application, the demo is placed in a top-level Frame. When run as an applet, the demo is placed in the Applet, which is itself a Container.

Most of the Scene Graph demos are also written in a particular style (having a public no-argument constructor) that allows them to be deployed using a generic applet and JNLP file template, specifying which demonstration should be run as a parameter to the applet.

Here is how the <applet> tag is written for the Egg Timer demo:



    <applet width="200" height="200"
           codebase="http://download.java.net/javadesktop/plugin2/scenegraph/"
           code="CompatibilityApplet" archive="CompatibilityApplet.jar">
     <param id="jnlp_href" value="scenario-applet.jnlp">
     <param id="demo.classname" value="demo.alarm.EggTimer">
   </applet>

The CompatibilityApplet.jar contains a tiny applet (the source code of which can be downloaded here) which simply displays a notice to users of an older version of the Java Plug-In that this content requires the new Java Plug-In, and directs them to the download page. Older versions of the Java Plug-In will use the code and archive parameters from the <applet> tag. The new Java Plug-In will use scenario-applet.jnlp to launch the applet and will ignore the code and archive parameters.

The resources of the applet are described in scenario-applet.jnlp. Here is an excerpt:



    <jnlp href="scenario-applet.jnlp">
     <resources>
        <!-- Want some extra RAM since we aim to run
all of these in the same JVM -->
        <j2se version="1.6+"
 href="http://bit.ly/xyGWeN"
 max-heap-size="128m" />
        <extension id="Scenario-0.5" 
           href="http://download.java.net/javadesktop/scenario/releases/0.5/Scenario-0.5.jnlp"/>
        <extension id="AppFramework"
           href="http://download.java.net/javadesktop/scenario/demos/lib/AppFramework.jnlp"/>
        <jar href="Scenario-Demos.jar" main="true" />
     </resources>
     <applet-desc 
         id="Scenario Demo Applet"
         main-class="demo.applet.DemoApplet"
         width="300"   <!-- overridden by applet tag -->
         height="300"> <!-- overridden by applet tag -->
     </applet-desc>
   </jnlp>

Some elements to note:

  • The max-heap-size parameter to the j2se tag is used to give the JVM some extra heap space. In this example multiple resource-intensive applets are being run on the same web page, and experimentation showed that the default heap size was not enough to reliably run all of the applets. Note that even though a non-standard heap size is being requested, the new Java Plug-In will still run all of the applets in the same JVM instance because they are all requesting the same command-line options.
  • The Scenario and AppFramework extension JNLPs are trivially pulled in to the applets in exactly the same way they would be for Java Web Started applications.
  • The width and height are ignored; the specification in the <applet> tag overrides them.

With this infrastructure, the actual DemoApplet class is trivial:



    public class DemoApplet extends JApplet {
       public void init() {
           try {
  Class c = Class.forName(getParameter("demo.classname"));
  Object o = c.newInstance();
  add((Component) o);
           } catch (Exception e) {
  throw new RuntimeException(e);
           }
       }
   }

It simply takes in the demo.classname parameter, instantiates the named class reflectively, and adds it to itself. Each of the demos was written to be a Component, which is the key design element which allows the code to be reused trivially in both an applet and an application.

JavaFX Script Applet Demo

The JavaFX applet demonstration, courtesy of JavaFX Technical Lead Chris Oliver, illustrates how to place JavaFX Script content in an applet. The base FXApplet class contains some "magic" that places arbitrary JavaFX Script content in an applet. TimerApplet subclasses FXApplet, overriding the getContent() method to return a Timer1 instance. Timer1 contains the application code. These sources are compiled with the JavaFX Script Compiler.

Once compiled to bytecode, the deployment of the applet is straightforward. The <applet> refers to the JNLP file for the applet:



    <applet width="250" height="280" code="CompatibilityApplet" archive="CompatibilityApplet.jar">
     <param id="jnlp_href" value="TimerApplet.jnlp">
   </applet>

(See the above example for a description of the backward compatibility applet.)

The applet's JNLP file simply refers to the resources for the applet:



    <jnlp href="TimerApplet.jnlp">
     <resources>
        <jar href="TimerApplet.jar" main="true" />
        <jar href="javafxrt.jar" />
        <jar href="Scenario-0.5.jar" />
     </resources>
     <applet-desc 
         id="JavaFX Timer Applet"
         main-class="TimerApplet"
         width="300"
         height="300">
     </applet-desc>
   </jnlp>

Note that both the javafxrt.jar and the Scenario-0.5.jar will have standardized locations in the near future; due to the current rapid development of the JavaFX project, version skew prevents the use of the published Scenario jar with the JavaFX runtime classes for this example.

NASA World Wind Java

The NASA World Wind Java applet example, by Patrick Murris of the World Wind Java development team, illustrates how to deploy leading-edge libraries like NASA World Wind Java, as well as how to effectively combine HTML and Java content in a web page using JavaScript.

The web page contains information about the Cascade mountain range (thanks to Wikipedia) and embeds World Wind Java as an applet to illustrate the locations of the mountains in the range. Incorporating World Wind in a web page is remarkably easy. Here is the <applet> tag embedding it on the page:



    <applet id="wwjApplet" width=600 height=380
           code="CompatibilityApplet"
           archive="CompatibilityApplet.jar">
     <param id="jnlp_href" value="WWJApplet.jnlp">
   </applet>

The WWJApplet ships with the standard World Wind Java distribution. You might choose to write your own applet class and embed World Wind inside it; how to do so is described below. The backward compatibility applet is described above.

Here are the relevant portions of the WWJApplet.jnlp file:



    <jnlp href="WWJApplet.jnlp">
     <resources os="Windows">
       <property id="sun.java2d.noddraw" value="true"/>
     </resources>
     <resources>
       <j2se href="http://bit.ly/xyGWeN" version="1.4+"/>
       <jar href="worldwind.jar" main="true" />
       <extension id="jogl"
          href="http://download.java.net/media/jogl/builds/archive/jsr-231-webstart-current/jogl.jnlp" />
     </resources>
     <applet-desc 
         id="WWJ Applet"
         main-class="gov.nasa.worldwind.examples.applet.WWJApplet"
         <!-- Overwritten by the surrounding web page -->
         width="100" 
         height="100">
     </applet-desc>
   </jnlp>

Some items to note:

  • worldwind.jar is used as the main jar in this example. Ideally, it would be referenced as an extension JNLP from NASA's web site, which allows many different applications or applets all embedding World Wind to share the same downloaded jar file. See below for more details.
  • World Wind Java uses the Java Binding to the OpenGL API, JOGL, for its hardware-accelerated 3D graphics. Note that the JOGL JNLP extension is incorporated into the application with a single line of code. Note also that on the Windows platform it is necessary to specify the system property -Dsun.java2d.noddraw=true due to driver-level conflicts between the OpenGL API and the DirectDraw/Direct3D APIs used by the Java 2D implementation by default on the Windows platform. This system property is needed on Windows for all applications and applets using JOGL.

The HTML links on the web page call JavaScript functions which interact with the applet to navigate it to the appropriate mountain. Here is an example of one of these links:



<a href="javascript:gotoLocation(MOUNT_RAINIER);">Mount Rainier</a>
   (southeast of Tacoma, Washington) ...

When the link is clicked, the JavaScript function gotoLocation is called. It is defined on the same web page:



    function gotoLocation(locationString) {
      var params = locationString.split(';');
      if(params.length == 3)    // Lat/lon
         getWWJApplet().gotoLatLon(parseFloat(params[1]),
   parseFloat(params[2]));
      ...
   }

The locations of the mountains are encoded as JavaScript strings in the HTML of the web page. The latitude, longitude, and other viewing information is parsed out of these strings and passed into the applet. The gotoLatLon method is defined on the WWJApplet class; the above call to this method initiates a JavaScript-to-Java call, passing the parameters from the JavaScript engine to Java. The World Wind applet takes in this notification and animates the viewpoint to the appropriate place. Note that the gotoLatLon method returns quickly, so that the browser is not waiting for it to complete; the animation is performed on a separate Java thread.

View the page's HTML source code for the full details of this example.

As mentioned above, the preferred method of incorporating World Wind Java into your application or applet is as a JNLP extension. This allows the World Wind code resources to be shared among many applications or applets from around the web which incorporate it. To reference the World Wind JNLP extension, you would add the following lines to your application's or applet's JNLP file in the <resources> section:



<extension id="worldwind" href="http://worldwind.arc.nasa.gov/java/0.4.1/webstart/worldwind.jnlp"/>
<extension id="jogl"
     href="http://download.java.net/media/jogl/builds/archive/jsr-231-webstart-current/jogl.jnlp"/>

Note that the World Wind extension JNLP is versioned, so you would need to consult the World Wind documentation. The World Wind Central site is a useful source of up-to-date information on World Wind.

Using World Wind as an extension implies that you can not use the WWJApplet as your main-class directly. Due to the semantics of the JNLP file format, the main jar (main="true") must be defined in the main JNLP file. This is an easy restriction to satisfy, however. You can simply create your own subclass of WWJApplet (call it MyWWJApplet) which does nothing:

class MyWWJApplet extends WWJApplet {}

Compile this with worldwind.jar on the classpath, and then bundle this class into its own jar file. Reference that as your main jar and MyWWJApplet as your main-class, and then pull in World Wind as the extension JNLP.

Jake2: Quake II in Java

The Jake2 applet example shows the future of game distribution over the Internet. Jake2 is a port of id Software's Quake II to the Java platform developed by Bytonic Software. To date, Jake2 has been deployed very effectively as a Java Web Start application; one click on a JNLP link on the web page launches the game. With the new Java Plug-In, it is now possible to deploy the game directly into the web page with full hardware acceleration and rock-solid reliability.

This example shows not only the deployment of advanced 3D game content into the browser, but also how to fully customize the look and branding of the web page and applet, which is important to many web developers.

The <applet> tag is slightly more involved than in the previous examples:



    <applet id="jake2Applet" width="800" height="600"
           archive="CompatibilityApplet.jar"
           code="CompatibilityApplet">
     <param id="jnlp_href" value="jake2applet.jnlp">
     <param id="image" value="ajax-loader.gif">
     <param id="boxborder" value="false">
     <param id="centerimage" value="true">
     <param id="boxbgcolor" value="#000000">
     <param id="boxfgcolor" value="#ffffff">
     <!-- Require our own JVM instance for better robustness -->
     <param id="separate_jvm" value="true">
     <!-- Parameters for the backward compatibility applet -->
     <param id="compat_bgcolor" value="#000000">
     <param id="compat_fgcolor" value="#ffffff">
   </applet>

As before, the Jake2 resources are pulled from a JNLP file, jake2applet.jnlp. Again, the backward compatibility applet is described above.

In this web page we desire the background to be black, and to use a custom loading indicator before the applet is ready to run rather than the default Sun logo. The background color of the page is set in the HTML for the page. An Ajax-style animated GIF (white on black) is used as a loading progress indicator via the image parameter. The rendering behavior of this image is customized with the boxborder, centerimage, boxbgcolor, and boxfgcolor parameters. These are documented in more depth in the new Java Plug-In's release notes.

Jake2 was originally designed as an application and sets up some global state for example in its networking code. It does not work well when two copies of the code are launched in the same JVM. This is fine for Java Web Start deployment, but applets typically reuse the same JVM instance. To allow easy redeployment of Jake2 as an applet, we use the new separate_jvm applet parameter (described in the new Java Plug-In's release notes) to force a fresh JVM instance to be created each time we launch the Jake2 applet.

The contents of jake2applet.jnlp are remarkably straightforward:



    <jnlp href="jake2applet.jnlp">
     <resources os="Windows">
       <property id="sun.java2d.noddraw" value="true"/>
     </resources>
     <resources>
       <j2se href="http://bit.ly/xyGWeN" version="1.4+"/>
       <jar href="jake2.jar" main="true"/>
       <extension id="jogl"
          href="http://download.java.net/media/jogl/builds/archive/jsr-231-webstart-current/jogl.jnlp" />
       <extension id="joal" href="http://download.java.net/media/joal/webstart/joal.jnlp" />
     </resources>
     <applet-desc 
         id="Jake2"
         main-class="jake2.Jake2Applet"
         <!-- Overridden by the web page -->
         width="100" 
         height="100">
     </applet-desc>
   </jnlp>

As in the World Wind applet example, Jake2 uses JOGL and therefore specifies the -Dsun.java2d.noddraw=true system property.

Note that the JOGL extension for OpenGL-accelerated 3D graphics and the JOAL extension for spatialized audio via OpenAL are trivially pulled in with one line each in the applet's JNLP file.

Conclusion

The introduction of JNLP support in the Java Plug-In opens up many new possibilities for applet deployment and represents a large step forward in the unification of the deployment of Java content both in and out of the browser. Please provide your feedback on this new functionality on the Java Plug-In forum.