Java developers know that the Java language isn't always the best language for every task. This year's releases of 1.0 versions of JRuby and Groovy have heightened interest in adding dynamic languages to Java applications. Groovy, JRuby, Rhino, Jython, and other open source projects make it possible to write code in a so-called scripting language and run it within the JVM (see Resources). Yet integrating these languages with Java code typically has meant learning each interpreter's unique API and features.
The javax.script package added to Java SE 6 makes
integrating dynamic languages easier. It provides a single, simple way to invoke dozens
of scripting languages using a small set of interfaces and concrete classes. But the
Java scripting API is about more than making it easier to script parts of an application; the scripting package lets you read and invoke external scripts at run time, which means you can alter those scripts dynamically to change a running application's behavior.
This article, the first in a two-part series, introduces the features and key classes of the Java scripting API using a Hello World style application. Part 2 presents a more realistic sample application that shows off more of the scripting API's power. That application uses the scripting API to create a dynamic rules engine in which the rules are encoded as external scripts written in Groovy, JavaScript, and Ruby. The rules decide whether applicants for a home loan qualify for particular mortgage products. Externalizing the rules with the Java scripting API allows the rules to change and new mortgage products to be added at run time.
The scripting package was added to the Java language in December 2006 to provide a unified way to integrate scripting languages into a Java application. For language developers, the package provides a way to write the necessary glue code for letting their languages be called dynamically from a Java application. For Java developers, the scripting package provides a small set of classes and interfaces that let scripts written in any number of languages be invoked using a common API. The scripting package is thus similar to the Java Database Connectivity (JDBC) package in that different languages -- like different databases -- can be integrated into the Java platform using a consistent interface.
Previously, invoking a scripting language dynamically from Java code involved using unique classes provided by each language's distribution or using Apache's Jakarta Bean Scripting Framework (BSF). BSF unifies a handful of scripting languages behind a single API (see Resources). More than two dozen scripting languages, including AppleScript, Groovy, JavaScript, Jelly, PHP, Python, Ruby, and Velocity, can be integrated into Java code using the Java SE 6 scripting API, which is based largely on BSF.
The scripting API provides two-way visibility between Java applications and external scripts. Your Java code can not only invoke external scripts, but it can also give those scripts access to selected Java objects. An external Ruby script, for example, can invoke methods on Java objects and access their properties, allowing those scripts to add behavior to a running application not anticipated at development time.
Invoking external scripts can be used for run-time application enhancement, configuration, monitoring, or other run-time manipulation, such as changing business rules without stopping the application. Possible uses of the scripting package include:
- Writing business rules in a language simpler than the Java language without resorting to a full-blown rules engine.
- Creating a plug-in architecture to let users customize an application on the fly.
- Integrating an existing script into your Java application, such as a script that processes or transforms text files.
- Externally configuring an application's run-time behavior using a full-blown programming language instead of a properties file.
- Adding a domain-specific language to a Java application.
- Using a scripting language while prototyping a Java application.
- Writing application test code in a scripting language.
The HelloScriptingWorld class, which you can download along
with all the code for this article (see Download), demonstrates key
features of the Java scripting package. It uses hard-coded snippets of JavaScript as the
sample scripting language. The class's main() method, shown
in Listing 1, creates a JavaScript script engine and then calls five methods (shown in
later listings) that highlight features of the scripting package:
Listing 1. HelloScriptingWorld main method
public static void main(String[] args) throws ScriptException, NoSuchMethodException {
ScriptEngineManager scriptEngineMgr = new ScriptEngineManager();
ScriptEngine jsEngine = scriptEngineMgr.getEngineByName("JavaScript");
if (jsEngine == null) {
System.err.println("No script engine found for JavaScript");
System.exit(1);
}
System.out.println("Calling invokeHelloScript...");
invokeHelloScript(jsEngine);
System.out.println("\nCalling defineScriptFunction...");
defineScriptFunction(jsEngine);
System.out.println("\nCalling invokeScriptFunctionFromEngine...");
invokeScriptFunctionFromEngine(jsEngine);
System.out.println("\nCalling invokeScriptFunctionFromJava...");
invokeScriptFunctionFromJava(jsEngine);
System.out.println("\nCalling invokeJavaFromScriptFunction...");
invokeJavaFromScriptFunction(jsEngine);
}
|
The main() method's chief function is to get an instance of a javax.script.ScriptEngine (the first two statements in Listing 1). A script engine loads and executes scripts in a particular language. It is the most frequently used and vital class in the Java scripting package. You obtain a script engine from a javax.script.ScriptEngineManager (first statement). A typical program needs to obtain only one instance of a script engine, unless many scripting languages are used.
ScriptEngineManager is probably the only concrete class in the
scripting package you'll use regularly; most of the rest are interfaces. And it may be the only class from the scripting package you'll instantiate directly (or indirectly through a dependency-injection mechanism such as the Spring Framework.) A ScriptEngineManager can return a script engine in one of three ways:
- By engine or language name, as in Listing 1 requesting a
JavaScriptengine. - By a file extension commonly used for scripts in that language, such as .rb for a Ruby script.
- By a MIME type the script engine has declared it knows how to process.
ScriptEngineManagers find and create script engines indirectly. That is, when script-engine managers are instantiated, they use a service-discovery mechanism added in Java 6 to find all registered javax.script.ScriptEngineFactory implementations in the classpath. These factory classes come packaged with Java scripting API implementations; you may never need to deal with these factory classes directly.
Once the ScriptEngineManager has found all the script-engine factory classes, it queries each one to find out whether it can create a script engine of the requested type -- in the case of Listing 1, a JavaScript engine. If a factory says it can create a script engine for the desired language, the manager asks the factory to create one, which it returns to the caller. The manager returns null if it finds no factory for the requested language, which the code in Listing 1 guards against by checking for a null return value.
As I mentioned, your code uses a ScriptEngine instance to execute your scripts. A script engine acts as a mediator between your scripting code and an underlying language interpreter or compiler that ultimately executes the code. That way, you don't need to know which classes each interpreter uses to execute code. For example, a script engine for JRuby might pass your code to an instance of JRuby's org.jruby.Ruby class first to compile the script into an intermediate form, then call it again to evaluate the script and process return values. The script-engine implementation hides the details, including how the interpreter shares class definitions, application objects, and input/output streams with Java code.
Figure 1 shows the general relationship among your application, the Java scripting API, a ScriptEngine implementation, and a scripting-language interpreter. You can see that your application relies only on the scripting API, which provides the ScriptEngineManager class and the ScriptEngine interface. The ScriptEngine implementation component handles the specifics of using a particular scripting-language interpreter.
Figure 1: Scripting API component relationships
You might be wondering where you get the necessary JAR files for the script-engine implementation and language interpreters. The best place to look first for a script-engine implementation is the open source Scripting project hosted by java.net (see Resources). There you'll find script-engine implementations for many languages and links to script-engine implementations hosted elsewhere. The Scripting project also provides links to download the interpreters for scripting languages it supports.
In Listing 1, the main() method
passes the ScriptEngine to each method for use in evaluating
that method's JavaScript code. The first method is shown in Listing 2. The invokeHelloScript() method calls the script engine's eval method to evaluate and execute the given string of JavaScript
code. The ScriptEngine interface defines six overloaded eval() methods that accept a script to evaluate as either a string or a java.io.Reader object, which is commonly used for reading scripts from external sources such as files.
Listing 2. The invokeHelloScript method
private static void invokeHelloScript(ScriptEngine jsEngine) throws ScriptException {
jsEngine.eval("println('Hello from JavaScript')");
}
|
The JavaScript in the invokeHelloScript() method outputs Hello from JavaScript to the standard output stream, which in this case is the console window. (Listing 6 contains the complete output from running the HelloScriptingWorldApplication.)
Note that this and other methods in the class declare that they throw javax.script.ScriptException. This checked exception -- the only one
defined by the scripting package -- indicates that the engine failed to parse or execute the given code. All script-engine eval() methods declare they throw a ScriptException, so your code needs to handle it appropriately.
Listing 3 shows two related methods: defineScriptFunction()
and invokeScriptFunctionFromEngine(). The defineScriptFunction() method also invokes the script engine's eval() method with a hard-coded snippet of JavaScript. But note that
this method does nothing more than define a JavaScript function, sayHello(). No code is being executed. The sayHello() function takes one parameter, which it outputs to the
console in the following println() statement. The script
engine's JavaScript interpreter adds this function to its global environment, making it
available in subsequent eval calls, which occurs (not surprisingly) in the invokeScriptFunctionFromEngine() method.
Listing 3. The defineScriptFunction and invokeScriptFunctionFromEngine methods
private static void defineScriptFunction(ScriptEngine engine) throws ScriptException {
// Define a function in the script engine
engine.eval(
"function sayHello(name) {" +
" println('Hello, ' + name)" +
"}"
);
}
private static void invokeScriptFunctionFromEngine(ScriptEngine engine)
throws ScriptException
{
engine.eval("sayHello('World!')");
}
|
This pair of methods demonstrates that script engines can maintain a state of
application components and make that state available during subsequent calls to the
engine's eval() method. The invokeScriptFunctionFromEngine() method takes advantage of the
maintained state by invoking the sayHello() JavaScript
function defined in the previous call to eval().
Many script engines maintain state of global variables and functions between calls to
eval(). However, it is important to note that the Java
scripting API does not require script engines to supply this feature. The JavaScript,
Groovy, and JRuby script engines used in this article do maintain state between calls to eval().
Listing 4 is a variation of the preceding example. The invokeScriptFunctionFromJava() method differs in that it calls the
sayHello() JavaScript function without using ScriptEngine's eval() method or JavaScript
code. Instead, it uses the Java scripting API's javax.script.Invocable interface to call a function maintained by the
script engine. The invokeScriptFunctionFromJava() method
casts the script-engine object to the Invocable interface and
then calls the invokeFunction() method on that interface to
call the sayHello() JavaScript function with the given
parameter. If the called function returns a value, the invokeFunction() method returns it wrapped as a Java Object type.
Listing 4. The invokeScriptFunctionFromJava method
private static void invokeScriptFunctionFromJava(ScriptEngine engine)
throws ScriptException, NoSuchMethodException
{
Invocable invocableEngine = (Invocable) engine;
invocableEngine.invokeFunction("sayHello", "from Java");
}
|
Notice that Listing 4 contains no JavaScript. The Invocable interface allows Java code to invoke a script function
without knowing its implementation language. The invokeFunction() method throws a java.lang.NoSuchMethodException if the script engine can't find a function with the given name or parameter types.
The Java scripting API doesn't require script engines to implement the Invocable interface. Realistically, the code in Listing 4 should have used the instanceof operator to ensure the script engine implements the Invocable interface before making the cast.
Invoking Java methods from scripting code
The examples in Listing 3 and Listing 4
show how Java code can invoke functions or methods defined in a scripting language.
You're probably wondering whether code written in a scripting language can in turn
invoke methods on Java objects. It can. The invokeJavaFromScriptFunction() method in Listing 5 shows how to give
a script engine access to Java objects and how scripting code can invoke methods on
that Java object. Specifically, the invokeJavaFromScriptFunction() method uses the script engine's put() method to supply an instance of the HelloScriptingWorld class itself to the engine. Once the engine has
access to the Java object using the name provided in the call to put(), scripting code in the call to the eval() method uses it.
Listing 5. The invokeJavaFromScriptFunction and getHelloReply methods
private static void invokeJavaFromScriptFunction(ScriptEngine engine)
throws ScriptException
{
engine.put("helloScriptingWorld", new HelloScriptingWorld());
engine.eval(
"println('Invoking getHelloReply method from JavaScript...');" +
"var msg = helloScriptingWorld.getHelloReply(vJavaScript');" +
"println('Java returned: ' + msg)"
);
}
/** Method invoked from the above script to return a string. */
public String getHelloReply(String name) {
return "Java method getHelloReply says, 'Hello, " + name + "'";
}
|
The JavaScript code contained in Listing 5's call to the eval() method uses the HelloScriptingWorld
Java object by accessing it with the variable name helloScriptingWorld provided in the call to the script engine's
put() method. The second line of JavaScript code invokes the
getHelloReply() public Java method, also shown in Listing 5. The getHelloReply() method
returns the Java method getHelloReply says, 'Hello,
<parameter>' string. The JavaScript code in the eval() method assigns the Java return value to the msg variable, then prints the value to the console.
ScriptEngine.put and its associated get() method are the primary ways of sharing objects and data between
Java code and scripts running within the script engine. (See Script-execution scope later in this article for an
expanded discussion of this topic.) When you call the engine's put() method, the script engine associates the second parameter (any Java object) with the given string key. Most script engines make those Java objects accessible to scripts under the given variable name. Script engines are free to take liberties with the names you pass to the put() method. For instance, the JRuby script engine makes helloScriptingWorld available to Ruby code under the global $helloScriptingWorld variable to fit Ruby syntax for global variables.
The script engine's get() method retrieves values available
within the scripting environment. Generally, every global variable and function in the
script environment is accessible from Java code through the get() method. But only those Java objects explicitly shared with the
script engine using put() are accessible to scripts.
This ability for external scripts to access and manipulate Java objects in a running application is a powerful technique for extending your Java programs' functionality. (The example in Part 2 exploits this technique.)
Running the HelloScriptingWorld application
You can run the HelloScriptingWorld application by downloading and building the source code. The .zip file contains both an Ant script and a Maven build file to help compile and run the sample application. Follow these steps:
- Download the .zip file.
- Create a new directory, such as java-scripting, and unzip the file you downloaded in Step 1 into that directory.
- Open a command-line shell and change to that directory.
- Run
ant run-hello.
You should see console output from Ant similar to that shown in Listing 6. Notice that the defineScriptFunction() method produces no output because it defines but doesn't call the JavaScript function.
Listing 6. Output from running HelloScriptingWorld
Calling invokeHelloScript...
Hello from JavaScript
Calling defineScriptFunction...
Calling invokeScriptFunctionFromEngine...
Hello, World!
Calling invokeScriptFunctionFromJava...
Hello, from Java
Calling invokeJavaFromScriptFunction...
Invoking getHelloReply method from JavaScript...
Java returned: Java method getHelloReply says, 'Hello, JavaScript'
|
Java SE 6 introduced the Java scripting API, but you can also run the API with Java SE 5. You just need to supply an implementation of the missing javax.script package classes. Fortunately, an implementation is available from the Java Specification Request 223 reference implementation (see Resources for a download link). JSR 223 defines the Java scripting API.
If you download the JSR 223 reference implementation, unzip the file and place the script-api.jar, script-js.jar, and js.jar files in your classpath. These files supply the script API, the JavaScript script-engine interface, and the JavaScript script engine that come bundled with Java SE 6.
How you expose Java objects to scripts running inside a script engine is more
configurable than just calling the engine's get() and put() methods. When you call get() or
put() on a script engine, the engine retrieves or stores the requested key in a default instance of the javax.script.Bindings interface. (The Bindings interface is just a Map interface that enforces keys to be strings.)
When your code calls a script engine's eval() method, the
engine's default bindings of keys and values are used. You can, however, supply your own
Bindings object on an eval() call
to restrict which variables and objects are visible to that specific script. The call
would look like eval(String, Bindings) or eval(Reader, Bindings). To help you create customized Bindings, script engines supply a createBindings() method that returns an empty Bindings object. Calling eval with a Bindings object temporarily hides Java objects previously stored in the engine's default bindings.
To add to the story, script engines contain two default bindings: the "engine scope"
bindings used by calls to get() and put() and "global scope" bindings the engine can use to look up objects if they're not found in the "engine scope" bindings. The word can is operative. Script engines are not required to make the global bindings accessible to scripts. Most script engines do.
The design purpose behind the "global scope" bindings is to share objects among different script engines. Every script engine returned by a ScriptEngineManager instance is seeded with the same "global scope" bindings object. You can retrieve an engine's global bindings using the getBindings(ScriptContext.GLOBAL_SCOPE) method and set the global bindings for an engine using setBindings(Bindings, ScriptContext.GLOBAL_SCOPE).
ScriptContext is an interface that defines and controls a
script engine's run-time context. A script engine's ScriptContext contains the "engine" and "global" scope bindings, as well as the input and output streams the engine uses for standard input and output operations. You can get and manipulate a script engine's context using the engine's getContext() method.
Scripting API concepts such as scope, bindings, and context can be confusing at first because of their overlapping meanings. The source-code download file for this article contains a JUnit test file called ScriptApiRhinoTest in the src/test/java directory to help explain these concepts through Java code.
Now that you have a basic grounding in the Java scripting API, Part 2 of this article refines and expand upon that knowledge using a more realistic sample application. That application uses external script files written in a combination of Groovy, Ruby, and JavaScript to define business logic that can be changed at run time. As you'll see, defining the business rules in a scripting language makes the rules easier to write, and probably easier for a nonprogrammer, such as a business analyst or rule writer, to read.
| Description | Name | Size | Download method |
|---|---|---|---|
| Source code and JAR file | j-javascripting1.zip | 116KB | HTTP |
Information about download methods
Learn
- "Invoke dynamic languages dynamically, Part 2": The second half of this series demonstrates how external scripts written in Ruby, Groovy, and JavaScript can be executed and altered at run time to change business logic without stopping and restarting the application.
-
JSR-223: Scripting for the Java Platform: This Java specification request defines the Java scripting API added to Java SE 6.
-
Java Scripting Programmer's Guide: Sun's JDK 6 documentation includes a programmer's guide to the Java scripting API.
-
Jakarta Bean Scripting Framework: The BSF project provides the foundation for the Java scripting API.
-
"Making Scripting Languages JSR-223-Aware" (Thomas Künneth, java.net, September 2006): This article shows how to make a scripting language available to the Java scripting API when no existing script engine is available.
-
Groovy: Learn more about Groovy, an agile dynamic language for the Java platform, from the project Web site.
-
Practically Groovy
: Dig deeper into Groovy with this developerWorks series.
-
Mozilla Rhino: Documentation and other resources for learning more about the JavaScript engine that comes bundled with Java runtimes available from Sun Microsystems and BEA Systems.
-
JRuby: JRuby is a pure-Java implementation of the Ruby programming language. The project Web site features the latest project news and other resources for using JRuby.
- Browse the technology bookstore for books on these and other technical topics.
-
developerWorks Java technology zone: Hundreds of articles about every aspect of Java programming.
Get products and technologies
-
Groovy: Download the latest Groovy release.
-
Java SE 6 and BEA JRockit: Development kits and runtime environments that natively support the Java scripting API and include a slimmed-down version of the Mozilla Rhino JavaScript engine.
-
Scripting project: The open source Scripting project at java.net provides script-engine interfaces for about two dozen languages and links to other known Java scripting engines. To use one of these scripting languages, install the script-engine implementation JAR file from this project along with the scripting-language interpreter JAR file itself.
-
Scripting for the Java Platform 1.0 Reference Implementation: The JSR-223 reference implementation provides three JAR files that allow the Java scripting API to run with Java SE 5. Download and unzip the sjp-1_0-fr-ri.zip file and place the js.jar, script-api.jar, and script-js.jar files in your classpath.
Discuss
-
Check out developerWorks blogs and get involved in the developerWorks community.

Tom McQueeney is a Java developer and application architect for Idea Integration, a national consulting firm. He enjoys integrating dynamic languages such as Ruby and Groovy into Java projects to make development faster, more efficient, and more fun. He is a past speaker at O'Reilly's OSCON and ApacheCon Europe, and he formerly served as president of the Denver Java Users Group. He and his wife, also a certified Java architect, live in Washington, D.C.





