Scripting API for everyone

I definitely think that dynamic languages for Java platform is an important topic. In that context, a Scripting API as a standardized scripting framework has its own role for Java developers. It is definitely a good thing it is included in JDK 6, but the lack of proper support for earlier JDK’s, in my opinion, slows down its wider adoption.

Why is this important? On one side, there are developers that work on “in-house” applications and solutions. Although it may seem that JDK (or JRE) upgrade process in such environments is not a big deal, it is usually a process that is pretty inertial. Many arguments are used in discussions, from compatibility issues and testing needs to “don’t fix it if it ain’t broken”, but also the inertia factor could be often associated to plain laziness.

On other side, you have developers of Java libraries and general-purpose Java applications who try to have user base as wide as possible. In that manner, they usually keep their products either compatible with JDK 1.4 or provide a special “retrotranslated” version of it.

So, it is not hard to see why general scripting support (through JDK 6 exclusive Scripting API) in applications and libraries is not something that we can see on regular basis in Java projects today (especially when there is no official JDK 6 release for OS X yet).

So, let’s see what we can be done to enable Scripting API for Java applications of today. There are three artifacts you need to have in order to successfully use the API. The first is, of course, the API implementation. Next, you need a script engine API interface implementation for the specific language you want to use and finally you’ll need an interpreter (engine) of the same language. If you are using JDK 6, the Scripting API implementation is already integrated in your Java environment. You also have a full support for Rhino (JavaScript) script engine (both engine and API interface implementation). For other languages, you can find script engine API interface implementations in Scripting Project on java.net. For language interpreters (engines), you have to look at particular project’s site or general-purpose Maven repositories.

If you have your project developed on JDK 5, things are somewhat different. There is no problem with the first artifact needed. You can always download the reference implementation of the Scripting API. It is compiled with JDK 5 so you are free to use it in your project. The things are somewhat different with script engine interface implementations. All binaries distributed with the Scripting Project are built using JDK 6, so you cannot use them in your project as they are. There are two things you can do. The first, obvious one, is to get the source of the project and try to rebuild the engine interface implementation that you need. While this option is always viable, it is not automated enough for wider use. The second approach you can use is explained below and it is the same technique used with JDK 1.4.

So what if you want to use Scripting API (and appropriate engines) with JDK 1.4? Fortunately, good guys of the Mule fame have done some good work in that field, so all there is left for us to do is to use it. In the Maven repository of Mule dependencies (http://dist.codehaus.org/mule/dependencies/maven2/javax/script/), you can find JARs of Scripting API and some script engine implementations retrotranslated to JDK 1.4. So, you can download it or set up your maven build process to use those JARs in your projects and enable Scripting API even in your JDK 1.4 compatible projects (of course, the JDK 5 projects can also use this stuff).
In the rest of this post, I’ll explain Maven project setup needed for this and provide some notes on compatibility issues.

First of all, you have to add aforementioned repository to the list of repositories in your pom.xml file.

	<repository>
		<id>mule-deps</id>
		<name>Mule Dependencies</name>
		<url>http://dist.codehaus.org/mule/dependencies/maven2</url>
		<snapshots>
			<enabled>false</enabled>
		</snapshots>
	</repository>

Now, you can add necessary dependencies

	<dependency>
		<groupId>javax.script</groupId>
		<artifactId>script-api</artifactId>
		<version>1.0</version>
		<classifier>jdk14</classifier>
	</dependency>
	<dependency>
		<groupId>net.sf.retrotranslator</groupId>
		<artifactId>retrotranslator-runtime</artifactId>
		<version>1.2.1</version>
	</dependency>

You can notice, that Retrotranslator runtime engine is needed for successful usage of this JAR. And that’s it, now you can include script engines of your choice (or let your users/customers to do so) and start using scripts in your applications according to your needs.

For example, to add Groovy engine you’ll add something like this to your project’s configuration

	<dependency>
		<groupId>javax.script</groupId>
		<artifactId>groovy-engine</artifactId>
		<version>1.0</version>
		<classifier>jdk14</classifier>
	</dependency>
	<dependency>
		<groupId>groovy</groupId>
		<artifactId>groovy-all</artifactId>
		<version>1.0</version>
	</dependency>

Now you can write something like this in your application

	public void testScriptingAPI() throws Exception {
		ScriptEngineManager factory = new ScriptEngineManager();
		ScriptEngine engine = factory.getEngineByName("groovy");
		Object ret = engine.eval("return 'test'");
		assertEquals("test", ret);
	}

The important thing to note is that Scripting API specification is compatible with Java 5 syntax. This means that if you use this retrotranslated version with JDK 5 you’ll have no compatibility issues, but if you insist on using it with JDK 1.4 you must take care about these syntax differences. For example, the Invocable interface use varargs for function and method calls, so the calls like these are used:

Object result = ((Invocable)engine).invokeFunction(
	"hello", "Arg1", "Arg2"
);

On JDK 1.4, you must use the “older” syntax:

Object result = ((Invocable)engine).invokeFunction(
"hello", new Object[] {"Arg1", "Arg2"}
);

No compatibility will be broken when you upgrade your project to full JDK 6 support, so it’s small price to pay.

If we lived in a perfect world there would be Scripting API (and all script engines from the Scripting Project) accessible in mainstream Maven repositories (or even only in the java.net repository) in versions for all JDKs mentioned above (so you can choose). I guess it would lead to much higher adoption of this API and scripting support in Java projects. And after all, it seems to me that it is not too much to ask for.

Leave a Reply