One of the projects I am currently on is to create a Java based headless browser that I can easily control programmatically.  I have wanted this for a couple of reasons over the years, one for navigating websites when trying to scrape information and secondly for use in automated functional testing.  I love the Selenium IDE but I find that it is impossible to automate your Selenium scripts into an automated build because it requires an actual browser (e.g. Firefox, Internet Explorer, etc.) to be started and executed each time.  So I want a headless browser that will do navigation, manage the DOM model of the page, and execute JavaScript to handle AJAX calls.  (SIDENOTE:  I am planning on open sourcing this and am waiting for approval from SourceForge to move the code out there).

To support JavaScript, I wanted to just use the ScriptEngineManager found in Java 6 to execute JavaScript.  Well, one problem that I found is that it is a real pain to get at objects that have been created in an executed script.  Consider the following JavaScript example:

sample.js

var foo = [“a”,”b”];

To execute that in Java 6, you would do the following:

ScriptEngineManager sm = new ScriptEngineManager();
ScriptEngine e = sm.getEngineByExtension(“js”);

String script = readFromFile(“sample.js”);

e.eval(script);

OK, so far so good.  Now let’s say you want to retrieve the value of foo from the ScriptEngine environment and iterate over the values.  The ScriptEngine class has a get method, however, this returns an Object of type sun.org.mozilla.javascript.internal.NativeArray, not a Java Array.  So to access values, you will need to cast the Object to a NativeArray.  Then, as you iterate the array, you might have to cast each value to a NativeObject as the get method on NativeArray only returns an object.

NativeArray a = (sun.org.mozilla.javascript.internal.NativeArray)e.get(“foo”);

for (int i = 0; i < a.getLength(); i++) {

  System.out.println(a.get(i, a));

}

Now your code is relying on a Sun internal class and not a base JDK class.  The appropriate classes exist in the $JAVA_HOME/lib/rt.jar file.  Unfortunately, this jar file is NOT used by default when running javac.  So to get my code to compile, I had to use the rt.jar in my bootclasspath.  Since I am using Maven 2.0 as my build system, I had to add the following to the pom.xml where I am defining my compiler

  maven-compiler-plugin           1.6         1.6                             ${java.home}/lib/rt.jar          

This put the rt.jar into the bootclasspath when building and allowed my code to compile.  What I had hoped would be a simple task turned out to be a real ordeal.

Using the sun.org.mozilla.javascript.internal classes is very, very ugly and has the added distraction that it is hard to build and will be hard to maintain.  I do not see a way around the problem and I have found other people having similar problems working with the scripting API.  If anyone has a better approach, I am all ears.