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:
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.
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.
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.<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.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.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.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:
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.
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)
We recommend either:
<applet>
tag to implicitly specify the codebase.<jnlp>
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 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:
-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.
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:
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.<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.
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.
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.-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.
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.
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.