Applets: missing information about liveconnect and deployment
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
Java Plugin 2 (introduced in 1.6_10) solves many of the issues above. Only problem: Plugin2 is not available on the Mac! Until then or until all browser vendors fix the bugs, we have to stick with the lowest common denominator working feature set.
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 live-connect-test for different deployment examples</p>
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 aplet class; andcodebase
: base URI for archive and code.
In addition, three attributes are essential:
type
(required): must be the mimetype of applets, i.d.,application/x-java-applet
width
andheight
: 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
mayscript
parameter 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
code
parameter (bug).
Taking the caveats into consideration makes the cross-browser LiveConnect and HTML valid approach to deploy applets the following:
<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.write
to 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
applet
tag.