JavaScript: The Definitive GuideJavaScript: The Definitive GuideSearch this book

22.2. Using JavaScript from Java

Having explored how to control Java from JavaScript code, we now turn to the opposite problem: how to control JavaScript from Java code. This control is accomplished primarily through the Java netscape.javascript.JSObject class, which represents a JavaScript object within a Java program. The JavaScript-to-Java capabilities described in the previous section typically work well in both Netscape and Internet Explorer. In contrast, the Java-to-JavaScript techniques described here are not as robustly supported, and you may well encounter bugs in both Netscape and IE.

22.2.1. The JSObject Class

All Java interactions with JavaScript are handled through an instance of the netscape.javascript.JSObject class. An instance of this class is a wrapper around a single JavaScript object. The class defines methods that allow you to read and write property values and array elements of the JavaScript object and to invoke methods of the object. Here is a synopsis of this class:

public final class JSObject extends Object {
    // Static method to obtain initial JSObject for applet's browser window
    public static JSObject getWindow(java.applet.Applet applet);
    public Object getMember(String name);                    // Read object property
    public Object getSlot(int index);                        // Read array element
    public void setMember(String name, Object value);        // Set object property
    public void setSlot(int index, Object value);            // Set array element
    public void removeMember(String name);                   // Delete property
    public Object call(String methodName, Object args[]);    // Invoke method
    public Object eval(String s);                            // Evaluate string
    public String toString( );                              // Convert to string
    protected void finalize( );
}

Because all JavaScript objects appear in a hierarchy rooted in the current browser window, JSObject objects must also appear in a hierarchy. To interact with any JavaScript objects, a Java applet must first obtain a JSObject that represents the browser window (or frame) in which the applet appears. The JSObject class does not define a constructor method, so we cannot simply create an appropriate JSObject. Instead, we must call the static getWindow( ) method. When passed a reference to an applet, this method returns a JSObject that represents the browser window that contains the applet. Thus, every applet that interacts with JavaScript includes a line that looks something like this:

JSObject jsroot = JSObject.getWindow(this);  // "this" is the applet itself

Having obtained a JSObject that refers to the root window of the JavaScript object hierarchy, you can use instance methods of the JSObject to read the values of properties of the JavaScript object that it represents. Most of these properties have values that are themselves JavaScript objects, so you can continue the process and read their properties as well. The JSObject getMember( ) method returns the value of a named property, while the getSlot( ) method returns the value of a numbered array element of the specified JavaScript object. You might use these methods as follows:

import netscape.javascript.JSObject;  // This must be at the top of the file
    ...
JSObject jsroot = JSObject.getWindow(this);                   // self
JSObject document = (JSObject) jsroot.getMember("document");  //  .document
JSObject applets = (JSObject) document.getMember("applets");  //    .applets
Applet applet0 = (Applet) applets.getSlot(0);                 //      [0] 

You should note two things about this code fragment. First, getMember( ) and getSlot( ) both return a value of type "Object", which generally must be cast to some more specific value, such as a JSObject. Second, the value read from slot 0 of the applets array can be cast to an Applet, rather than a JSObject. This is because the elements of the JavaScript applets[] array are JavaObject objects that represent Java Applet objects. When Java reads a JavaScript JavaObject, it unwraps that object and returns the Java object that it contains (in this case, an Applet). The data conversion that occurs through the JSObject interface is documented later in this chapter.

The JSObject class also supports methods for setting properties and array elements of JavaScript objects. setMember( ) and setSlot( ) are analogous to the getMember( ) and getSlot( ) methods. These methods set the value of a named property or a numbered array element to a specified value. Note, however, that the value to be set must be a Java object. If you want to set a value of a primitive type, use the corresponding Java wrapper class: use an Integer object instead of an int value, for example. Finally, the removeMember( ) method allows you to delete the value of a named property from a JavaScript object.

In addition to reading and writing properties and array elements from JavaScript objects, the JSObject class allows you to invoke methods of JavaScript objects. The JSObject call( ) method invokes a named method of the specified JavaScript object and passes a specified array of Java objects as arguments to that method. As we saw when setting JavaScript properties, it is not possible to pass primitive Java values as arguments to a JavaScript method; instead you must use the corresponding Java object types. For example, you might use the call( ) method in Java code like the following to open a new browser window:

public JSObject newwin(String url, String window_name)
{
    Object[] args = { url, window_name };
    JSObject win = JSObject.getWindow(this);
    return (JSObject) win.call("open", args);
} 

The JSObject class has one more important method: eval( ). This Java method works just like the JavaScript function of the same name -- it executes a string that contains JavaScript code. You'll find that using eval( ) is often much easier than using the various other methods of the JSObject class. Since all the code is passed as a string, you can use string representations of the data types you want -- you do not have to convert Java primitive types to their corresponding object types. For example, compare the following two lines of code that set properties of the main browser window:

jsroot.setMember("i", new Integer(0));
jsroot.eval("self.i = 0"); 

The second line is obviously easier to understand. As another example, consider the following use of eval( ) to write a particular frame being displayed in the browser window:

JSObject jsroot = JSObject.getWindow(this);
jsroot.eval("parent.frames[1].document.write('Hello from Java!')"); 

To do the equivalent without the eval( ) method is a lot harder:

JSObject jsroot = JSObject.getWindow(this);
JSObject parent = (JSObject) jsroot.getMember("parent");
JSObject frames = (JSObject) parent.getMember("frames");
JSObject frame1 = (JSObject) frames.getSlot(1);
JSObject document = (JSObject) frame1.getMember("document");
Object[] args = { "Hello from Java!" };
document.call("write", args); 

22.2.2. Using JSObjects in Applets

Example 22-1 shows the init( ) method of an applet that uses LiveConnect to interact with JavaScript.

Example 22-1. Using JavaScript from an applet method

import netscape.javascript.*

public void init( )
{
    // Get the JSObject representing the applet's browser window.
    JSObject win = JSObject.getWindow(this);

    // Run JavaScript with eval( ). Careful with those nested quotes!
    win.eval("alert('The CPUHog applet is now running on your computer. " +
             "You may find that your system slows down a bit.');");
}

In order to use any applet, you must compile it and then embed it in an HTML file. When the applet interacts with JavaScript, special instructions are required for both of these steps.

22.2.2.1. Compiling applets that use the JSObject class

Any applet that interacts with JavaScript uses the netscape.javascript.JSObject class. To compile such an applet, therefore, your Java compiler must know where to find a definition of this class. Because the class is defined and shipped by Netscape and not by Sun, the javac compiler from Sun does not know about it. This section explains how to enable your compiler to find this required class. If you are not using the JDK from Sun, you may have to do something a little different -- see the documentation from the vendor of your Java compiler or Java development environment.

To tell the JDK compiler where to find classes, you set the CLASSPATH environment variable. This environment variable specifies a list of directories and JAR files (or ZIP files) that the compiler should search for class definitions (in addition to its standard directory of system classes). The trick is to figure out which JAR file on your system holds the definition of the netscape.javascript.JSObject class. In Netscape 6.1, the file is plugins/java2/javaplugin.jar, under the Netscape installation directory. In Netscape 4, the file is java/classes/java40.jar, under the installation directory. For Netscape 4 on a Windows system, for example, you would probably find java40.jar at C:\Program Files\Netscape\Communicator\Program\ Java\Classes\ java40.jar.

For Internet Explorer, the class definition you need is usually in one of the ZIP files in c:\Windows\ Java\Packages. The trouble is that this directory contains a bunch of ZIP files, all of whose names are gibberish and change from release to release! The largest of the files is typically the one you need. You can use an unzip utility to verify that it contains the file netscape/javascript/JSObject.class.

Once you have found the JAR or ZIP file you need, you can tell the compiler about it by setting the CLASSPATH environment variable. For a Unix system, set a path like this:

setenv CLASSPATH .:/usr/local/netscape/plugins/java2/javaplugin.jar 

And for a Windows system, set a path like this:

set CLASSPATH=.;C:\Windows\Java\Packages\5fpnnz7t.zip 

With CLASSPATH set, you should be able to compile your applet with javac as you would normally.

22.2.2.2. The mayscript attribute

There is an additional requirement for running an applet that interacts with JavaScript. As a security precaution, an applet is not allowed to use JavaScript unless the web page author (who may not be the applet author) explicitly gives the applet permission to do so. To give this permission, you must include the new mayscript attribute in the applet's <applet> tag in the HTML file.

Example 22-1 showed a fragment of an applet that used JavaScript to display an alert dialog box. Once you have successfully compiled this applet, you might include it in an HTML file as follows:

<applet code="CPUHog.class" width="300" height="300" mayscript></applet> 

If you do not remember to include the mayscript attribute, the applet is not allowed to use the JSObject class.



Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.