How to access Java code from JavaScript (the easy way)
Tutorial March 22nd, 2007There are a few ways to invoke Java code from within Javascript, and when I made Screengrab, I used the easiest:
var list = new Packages.java.util.ArrayList();
Basically, to use any standard Java packages from within Javascript, you just use the fully qualified package name and append “Packages” to the front of it. This is kind of cumbersome, but you can ‘include’ classes by just assigning them to a local variable:
var ArrayList = Packages.java.util.ArrayList;
var list = new ArrayList();
Now, this is all fine and it will get you through most Java work in Javascript (these concepts have worked fine for Screengrab!), but what if you need to implement a Java interface from within Javascript?
Well, I’ve tried. When I was trying to get clipboard support for the Java-based grabber in Screengrab I looked for answers and tried a few things, but I just couldn’t do it. I eventually gave up and currently Screengrab will only copy images to the clipboard using the Canvas-based grabber.
The only way to do this is to somehow create a Java class externally and then load it from within Firefox. This is possible, because Firefox lets you create components in other languages and link them in using some magic. The problem with components is that there is a massive amount of crap to write to get one to work.
Here is a tutorial on creating a Java component for extension development that I found. This is the Java class it is trying to get access to. And this monolithic file is the Javascript code that you have to write to get Firefox to understand it. I haven’t read it. I stopped about 2 pages in and thought “there has got to be a way to do this that doesn’t suck”.
And there is…
From this page at Mozillazine we find “the easy way” (and actually, as it turns out, the easy way comes from the same guy that did the hard way.
As you can see from the tutorial, it boils down to using a ClassLoader. Essentially, since we can access most standard Java from inside Javascript, it stands to reason that we can create ClassLoaders and use reflection. And that’s what this code does. What it doesn’t do is tell you how to do this with a local url, pointing to a jar within your extension. Here’s how…
var id = "booksmarts@getbooksmarts.org";
var ext = Components.classes["@mozilla.org/extensions/manager;1"]
.getService(Components.interfaces.nsIExtensionManager)
.getInstallLocation(id)
.getItemLocation(id);
var cl = new Packages.java.net.URLClassLoader(
[new Packages.java.net.URL(
new Packages.java.io.File(ext.path + "/components/tiny.jar").toURL())]);
var aClass = Packages.java.lang.Class.forName("Tiny", true, cl);
var instance = aClass.newInstance();
ftst.logger.debug(instance.getMessage());
I’m pointing it to the components directory in my extension folder. When the extension is installed, the xpi is unzipped, but the chrome.jar isn’t, so if you want to load the your classes, you’ll need to put them somewhere that gets extracted. “components” seems like a good place for that, since it contains libs and the like.
Now obviously this is a bit ugly, but it could easily be wrapped in some code that does most of the work for you. To the point where you should be able to write code that looks like the following (or much much better):
var tiny =
jarLoader.using("tiny.jar")
.createNew("org.getbooksmarts.Tiny", arg1, arg2, arg3);