We have all heard heated arguments between developers who use scripting languages and developers who use Java. One of the reasons for the war between these two factions is that the process of integrating the two was so difficult that developers on both sides were almost forced to choose one or the other. Java Specification Request (JSR) 223 addresses this issue. In this article, I present examples of how the standard framework and API defined by JSR 223 makes it easy for Java programmers to take advantage of the benefits of using a scripting language while retaining the benefits of using Java.

The Scripting API

The entire scripting API is contained in the package javax.script, which was first delivered with Java 6. This lightweight package contains six interfaces:

Bindings

Compilable

Invocable

ScriptContext

ScriptEngine

ScriptEngineFactory

five classes:

AbstractScriptEngine

CompiledScript

ScriptEnginemanager

SimpleBindings

SimpleScriptContext

and a single exception:

ScriptException

Rather than describing each of these interfaces, classes and exceptions, I present a series of five Java programs and five scripts in three languages to show just how little effort is required to make them work together.

Scripting Engines

The ScriptEngine interface, which lies at the heart of any implementation of JSR 223, defines methods that provide basic scripting functionality. These methods provide mechanisms for executing scripts and for getting and setting values that are shared between Java and a scripting language. The methods defined by the ScriptEngine interface form the framework that brings uniformity to interoperating with different scripting languages.

Although it does not mandate any specific scripting engine, Java 6 includes the Mozilla Rhino engine for the JavaScript language. If you visit https://scripting.dev.java.net, the home of the scripting project, you will see an impressive list of more than two dozen scripting engines that have been implemented to date.

ListScriptingtEngines.java (Listing One) displays a list of scripting engines available to the JVM in which it runs. I start by creating an instance of ScriptEngineManager . The documentation for this class states that it implements a discovery and instantiation mechanism for ScriptEngine classes and also maintains a collection of key/value pairs storing state shared by all engines created by the Manager . It does this by using the service provider mechanism to enumerate all implementations of ScriptEngineFactory . The service provider mechanism is described in the JAR file Specification.

package ca.tremblett; import java.util.List; import javax.script.ScriptEngineFactory; import javax.script.ScriptEngineManager; /** * * @author ptremblett */ public class ListScriptingEngines { public static void main(String[] args) { ScriptEngineManager manager = new ScriptEngineManager(); List<ScriptEngineFactory> engines = manager.getEngineFactories(); if (engines.isEmpty()) { System.out.println("No scripting engines were found"); return; } System.out.println("The following " + engines.size() + " scripting engines were found"); System.out.println(); for (ScriptEngineFactory engine : engines) { System.out.println("Engine name: " + engine.getEngineName()); System.out.println("\tVersion: " + engine.getEngineVersion()); System.out.println("\tLanguage: " + engine.getLanguageName()); List<String> extensions = engine.getExtensions(); if (extensions.size() > 0) { System.out.println("\tEngine supports the following extensions:"); for (String e : extensions) { System.out.println("\t\t" + e); } } List<String> shortNames = engine.getNames(); if (shortNames.size() > 0) { System.out.println("\tEngine has the following short names:"); for (String n : engine.getNames()) { System.out.println("\t\t" + n); } } System.out.println("========================="); } } }

Listing One

After creating the instance of EngineManager , I invoke its getEngineFactories() method. This method returns a List of all the ScriptEngineFactory classes found by the discovery mechanism. For each ScriptEngineFactory in the list, I obtain metadata describing the engine class by calling methods such as getEngineName() , getEngineVersion() , getLangugeName() , getExtensions() , and getNames() . I then simply display this metadata.

If you run the ListScriptingEngines program, you will see the following output displayed:

The following 1 scripting engines were found: Engine name: Mozilla Rhino Version: 1.6 release 2 Language: ECMAScript Engine supports the following extensions: js Engine has the following short names: js rhino JavaScript javascript ECMAScript ecmascript =========================

If you are using OS-X and have Java 6 from Apple installed, the output from the program looks like this:

The following 1 scripting engines were found: Engine name: AppleScriptEngine _Version: 1.0 _Language: AppleScript _Engine supports the following extensions: __scpt __applescript __app _Engine has the following short names: __AppleScriptEngine __AppleScript __OSA =========================

Remember, Java 6 does not mandate any specific scripting engine and Apple has chosen to deliver an engine for their AppleScript.

Since the examples I present in subsequent sections of this article use Ruby, Python, and Groovy, I needed to make these engines available also. To do so, I went to the scripting project site previously mentioned and download jsr223-engines.tar.gz from the "Documents & files" link (there is also a .zip file if you are using a platform that doesn't deal with tar balls). When expanding the file, I observe that there is one directory for each language; this directory contains a build subdirectory that contains a JAR file that is the engine for the language. I add this jar to my classpath. In addition to making the engine available, I must also make all of the JAR files used by the scripting language implementation available on my classpath. The README.TXT file in each language's directory contains a URL from which this implementation can be downloaded if you do not already have the language installed. The following is the content of the README.TXT for groovy:

This is JSR-223 script engine for the <a href="http://groovy.codehaus.org">Groovy language</a>. We have built and tested Groovy version 1.5.6.

If you examine the "Libraries" section of the NetBeans project JavaScriptingDemo (see Listing Six), you will see that supporting the three languages I'm using requires these JAR files:

antlr-2.7.6.jar

asm-2.2.jar

groovy-1.5.7.jar

groovy-engine.jar

jruby-engine.jar



jruby.jar jython-engine.jar

jython.jar

As a convenience, I have included the aforementioned JAR files in the JavaScriptingDemo NetBeans project in the required_jars directory. These are snapshot of the files at the time this article was being written. I recommend that you obtain the latest versions of these JAR files.

If you run ListScriptingEngines with the appropriate JAR files on the classpath, the program generates output that is too lengthy to include here but which you can see in Listing Two. Since I ran the code under OS-X, notice that the AppleScript engine is present.

The following 4 scripting engines were found Engine name: JRuby Engine Version: 1.1.4 Language: ruby Engine supports the following extensions: rb Engine has the following short names: jruby ruby ========================= Engine name: AppleScriptEngine Version: 1.0 Language: AppleScript Engine supports the following extensions: scpt applescript app Engine has the following short names: AppleScriptEngine AppleScript OSA ========================= Engine name: groovy Version: 1.5.7 Language: groovy Engine supports the following extensions: groovy Engine has the following short names: groovy ========================= Engine name: jython Version: 2.2.1 Language: python Engine supports the following extensions: jy py Engine has the following short names: jython python =========================

Listing Two