August 08, 2004

Embedding Javascript

I thought knowing how to expose my Java classes via Javascript would be a useful trick to know in the near future. (Why? More later...) So before we left for vacation (and out of constant net access) I grabbed the latest Rhino tarball. Following the examples bundled with the distribution was simple enough, but I kept having a problem importing my classes.

As the docs mention I used the org.mozilla.javascript.ImporterTopLevel class to bring in the importClass() and importPackage() functions like this:

    Context cx = Context.enter();
    Scriptable scope = new ImporterTopLevel( cx, true );

(By the way, the docs don't mention it but you can skip the standard call to cx.initStandardObjects() because the scope returned by the ImporterTopLevel() constructor includes that call.)

So my first test javascript looked like this, referencing the simple Java objects 'Author', 'Publisher', and 'Book' in the 'com.cwinters.js' package (and 'out' wired to System.out):

importPackage( com.cwinters.js );
var author = new Author( 'Douglas', 'Adams', 'Longtime science fiction author' );
var pub = new Publisher( 'Tweedledee Books', 'London' );
var book = new Book( 'So Long and Thanks for All the Fish', author, pub );
out.println( "The book is: " + book.toString() );

just like the docs for ImporterTopLevel() say. But when running this I got:

Exception in thread "main" ReferenceError: "com" is not defined.
  (scripts/create_book.js; line 4)
	at org.mozilla.javascript.ScriptRuntime.constructError(ScriptRuntime.java:2253)
    ....

I couldn't figure out what the hell this was -- single/double-quoting the package name didn't work, nor did using a fully-qualified package in the 'new' operations. and because I was Google-less I felt crippled in my debugging efforts. Finally I scoped out some of the other examples and in the SwingApplication.js saw:

importPackage(Packages.javax.swing);
importPackage(Packages.java.awt);
importPackage(Packages.java.awt.event);
...

A-ha! So 'Packages' looks like a top-level variable referencing all the Java packages. I prefixed 'com.cwinters.js' with 'Packages.' in the earlier example and everything worked exactly as expected. Times like this are when I realize how much I depend on google for debugging...

Next: Useful words on usability
Previous: Out for a week