Applets: Missing Information about LiveConnect and Deployment
filed under: Applets, JavaScript, LiveConnectRecently, I created several Applets because they were the only satisfying tool for requirements put on me. Several issues arised because of rudimentary, stale, and unsatisfactory applet documentation, and buggy browsers. This post describes missing information about Java-to-JavaScript interaction and deployment regarding Java Applets.
LiveConnect
The glue between Java and JavaScript
Did you know Java-to-JavaScript interaction is possible? This is known as LiveConnect. LiveConnect makes it possible to use Java for things not otherwise possible in browser environments, while still keeping display logic in the web page in HTML/JavaScript.
LiveConnect is in most cases straightforward. Two examples:
document.getElementById('applet').uploadFile();
Above the JavaScript code calls the java uploadFile
method.
import netscape.javascript.JSObject;
public class Applet{
public void init(){
JSObject.getWindow(this).eval("alert('applet:init')");
// alternative way
// (JSOBject)(JSObject.getWindow(this).getMember('alert')).call('applet:init');
}
}
Above the Applet upon initialization calls
JavaScript alert. JSObject is located
in $JAVA_HOME/jre/lib/plugin.jar on Linux/Windows,
which must be included on the classpath when compiling.
The least common denominator for Cross-browser LiveConnect support
LiveConnect has been around for ages (since Netscape Navigator 3.0 and IE4); however, some browsers do still not fully support LiveConnect! The table underneath gives an overview of LiveConnect support in major browsers and OSes. The table is based on a LiveConnect feature test. The test is available at jdams.org where you can test your own browser. In the table (J) means Java-to-JavaScript, and (JS) means JavaScript-to-Java.
| Linux | Mac | Windows | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Test / Browser | Chrome | Firefox 3.6 | Opera 9.8 | Chrome | Firefox 3.6 | Safari 5 | Chrome | Firefox 3.6 | IE 6 | Safari 5 |
| Mayscript param not required | ||||||||||
| JSObject != null | ||||||||||
(J) getMember |
||||||||||
(J) setMember |
||||||||||
(J) getMember nested object call fct on it |
||||||||||
(J) call null args |
||||||||||
(J) call empty args |
||||||||||
(J) call prim. arg |
||||||||||
(J) call java object arg |
||||||||||
(J) call with prim. return value |
||||||||||
(J) call with obj. return value |
||||||||||
(J) call with eval |
||||||||||
(J) call with eval return value |
||||||||||
| (JS) get prim. java member | ||||||||||
| (JS) Java Strings are converted to JavaScript strings | ||||||||||
| (JS) get object java member | ||||||||||
The tests show that applets using LiveConnect must avoid parsing
complex objects back and forth and using call. If
complex objects are needed they should be converted to a JSON string
or similar before returning them to JavaScript from Java or vice
versa. Luckily, the call method is superfluous
since eval is available and working.
Plugin2: Maybe someday it gets easier
Signed applets
Applets, like all other client-code in browsers, is running under restrictions of the browser security model (Browser Security Handbook, part 2). For example, applets can't access the file system, or make requests to cross-domains. That is, unless the applet is signed.
LiveConnect, however, degrades the security status of signed applet to unsigned. This is because unsigned code (JavaScript) interacts with signed code (Java). Fortunately, it is possible to elevate the privileges again:
try {
AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws Exception {
// do stuff
return null;
}
});
} catch (PrivilegedActionException e) {
Exception ex = e.getException();
// ...
}
Deploying Applets
Goto jdams.org for different deployment examples
There are numerous different applet deployment tags:
the applet tag; the Mozilla-only embed
tag; or the object tag. The applet tag was
deprecated in HTML4.01
(spec) in favor for the generic object inclusion
tag object.
Oracle claims that Applets deployed with object is
not widely supported
(Java
plug-in developer guide). This is, however, not the case for
modern browsers. The only tag needed to deploy applets
is object!
Three parameters are essential for the object tag:
archive: space separated list of jar files;code: fully qualified name of the applet class; andcodebase: base URI for archive and code.
type(required): must be the mimetype of applets, i.d.,application/x-java-appletwidthandheight: specifies the display size of the applet.
With these things in mind, there are two deployment options for deploying applets with static HTML:
<!-- with class files located individually under the applet directory. -->
<object type="application/x-java-applet">
<param name="codebase" value="/applet" />
<param name="code" value="AppletTest" />
<param name="mayscript" value="true" />
</applet>
<!-- with class files jarred. -->
<object type="application/x-java-applet" >
<param name="code" value="AppletTest" />
<param name="archive" value="/applet.jar" />
<param name="mayscript" value="true" />
</object>
Dependencies to third-party libraries are specified either in
the archive parameter or in the manifest of the JARed
applet, e.g.,
Manifest-Version: 1.0 Class-Path: lib.jar lib2.jar
Third-party JARs are resolved relative to the URI of the JAR referencing them. In this case that means all JAR files (lib.jar and lib2.jar) must be located in the same directory as the applet jar.
Deployment caveats- The
mayscriptparameter must be present in Firefox Mac since LiveConnect otherwise is disabled. - On Firefox Mac LiveConnect is initialized when your make the
first reference to any applet, e.g.,
by
document.getElementById('appid'). An unfortunate side effect of that is the following: If the applet is included in the page via plain HTML, references to JSObject in the applet are initiallynull! The easy fix is to always inject the applet into the DOM via a script. - In Chrome if the size of the applet is set to zero (
width="0" height="0") the applet is not loaded (bug). - Chrome issues an superfluous erroneous request for the Java
class specified via the
codeparameter (bug).
<script>
document.getElementById('someid').innerHTML += [
'<object type="application/x-java-applet" width="1" height="1">',
'<param name="code" value="AppletTest"></param>',
'<param name="archive" value="/applet.jar"></param>',
'<param name="id" value="1"></param>',
'</object>'].join('\n');
</script>
During development I recommend bypassing all the caching mechanisms by
adding a time stamp to the jar URL. The deployment code then becomes:
<script>
document.getElementById('someid').innerHTML += [
'<object type="application/x-java-applet" width="1" height="1">',
'<param name="code" value="AppletTest"></param>',
'<param name="archive" value="/applet.jar?v=' + new Date().getTime() + '"></param>',
'<param name="id" value="1"></param>',
'</object>'].join('\n');
</script>
Note: Deployment is also possible with a script from
oracle: deployJava.js. Two things are suboptimal
with deployJava.js
- it uses
document.writeto write applet tags to the DOM. That makes it impossible to lazy load the applet; and, - it injects the applet into the DOM with the
deprecated
applettag.


