by
Paul Jensen, Partner
Object Computing, Inc. (OCI)
The upcoming JDK 6.0 (Mustang) release does not approach the magnitude of enhancements in JDK 5.0, but does offer several interesting updates. Among these updates is the incorporation of JSR 223 Scripting for the JavaTM Platform. Essentially, this introduces a standard framework to allow scripting language programs to be executed from and have access to the Java platform. It provides many of the capabilities of BSF (Bean Scripting Framework). As with BSF, JSR 223 provides a common interface for integration of a variety of scripting langages. The specification includes examples incorporating PHP and Javascript. Eventual support for a wide range of scripting languages (BeanShell, Jython, Ruby, etc) is expected. At the time of this writing, the author could find support for only JavaScript and Pnuts languages.
The examples in this article are based on J2SE 1.6.0-rc-b69.
J2SE 1.6 includes a modified version of the Rhino JavaScript engine
integrated via JSR 223 and a command-line scripting shell, jrunscript
,
which leverages this support. The shell is of limited use in most
applications, but can be a valuable tool in learning and prototyping in
Java and arbitrary scripting languages. The jrunscript
syntax is:
Usage: jrunscript [options] [arguments...]
where [options] include:
-classpath, -cp <path> | Specify where to find user class files |
-D<name>=<value> | Set a system property |
-J<flag> | Pass <flag> directly to the runtime system |
-l <language> | Use specified scripting language |
-e <script> | Evaluate given script |
-encoding <encoding> | Specify character encoding used by script files |
-f <script file> | Evaluate given script file |
-f - | Interactive mode, read script from standard input |
-q | List all scripting engines available and exit |
The fundamental interface for all scripting engines is javax.script.ScriptEngine
. This interface
defines a set of methods expected to be supported by all
implementations. An implementation is obtained from the
ScriptEngineManager
. ScriptEngine
implementations are deployed as
Jar files (typically in lib/ext). Each such jar contains a text
file resource META-INF/services/javax.script.ScriptEngineFactory
defining one or more classes implementing ScriptEngineFactory
.
The ScriptEngineManager
utilizes these factories to create specific
implementations. The ScriptEngineManager
also provides metadata
defining the supported script type and version information which is
used to resolve requests for a scripting implementation.
Obtaining an engine is straightforward:
ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine jsEngine = manager.getEngineByName("js");
The primary method of the ScriptEngine, eval()
, executes a script
directly passed as a String or accessed through a Reader. These
two variations may be additionally parameterized with either a Bindings
or ScriptContext object.
Object eval(String script) Object eval(Reader reader) Object eval(String script, Bindings n) Object eval(Reader reader, Bindings n) Object eval(String script, ScriptContext context) Object eval(Reader reader, ScriptContext context)
A ScriptContext
associates a ScriptEngine with objects in the Java
application. Every engine has a default ScriptContext (which can
be altered through the API). It groups objects by scope.
Each scope has a related Bindings instance containing these
objects. Bindings
is simply a Map<String, Object>
which
associates scripting variable names with Java instances.
Two scopes are predefined, ScriptContext.GLOBAL_SCOPE
and
ScriptContext.ENGINE_SCOPE
. The first contains attributes
shared by all Engines created via a given factory, while the second is
specific to the engine implementation. The default context and
scope is used by the single argument eval() methods. The
Scripting API includes many instances of such convenience
methods. A binding or context passed to an eval
effectively replaces the
default bindings or context for the scope of the method call.
Additional scopes may be defined by contexts specific to
an Engine. For example, the HttpScriptContext
used in web-based
applications defines request, session, and applications scopes
corresponding to Servlet scopes.
The below example summarizes basic usage of the Scripting API:
// simple script execution jsEngine.eval("print('Hello')"); // bind scripting variables and use in a script jsEngine.put("a", 3); Integer b = 2; jsEngine.put("b", b); Object retval = jsEngine.eval("c = a + b; print('a + b = ' + c);"); // retrieve the bindings we previously added Bindings bindings = jsEngine.getBindings(ScriptContext.ENGINE_SCOPE); boolean containsKey = bindings.containsKey("a"); System.out.println(containsKey);
Results in:
Hello a + b = 5 true
The checked ScriptException is thrown by methods of the Scripting API. Depending on the Engine, the errant line and column number of the script will be included in the exception.
Beyond the based ScriptEngine capability, certain implementations may
advertise additional capability by implementing the Compilable
or
Invocable
interfaces.
An engine implementing Compilable
supports the re-execution of
intermediate (e.g. compiled) code from previous compilations. The
compile() method returns a CompiledScript
based on either a String or
Reader. The CompiledScript
may be executed repeatedly via its
eval() method.
Compilable eng = (Compilable)jsEngine; CompiledScript script = eng.compile("function test() { return 10; }; test();"); Object compiledOutput = script.eval(); System.out.println(compiledOutput); // prints 10.0
An engine implementing Invocable
supports the calling of individual
functions scripted in the engine via the invoke()
method.
Arguments and return values are converted between script and Java types
based on the engine implementation. Below is a simple example:
jsEngine.eval("function test(val) { return val; }"); Invocable jsInvoke = (Invocable)jsEngine; Object invoke = jsInvoke.invoke("test", new Object[] {111.0}); System.out.println(invoke); // prints 111.0
Scripts may be used to implement arbitrary interfaces without direct
dependence on the given interface. The following example
illustrates definition of a run()
method which is bound as the
implementation of a Runnable
interface at runtime based only on method
signature. Below, the invoke()
and run()
calls invoke the same
underlying scripted method.
jsEngine.eval("function run() { print ('running..'); }"); Object invoke2 = jsInvoke.invoke("run",null); // prints running... Runnable r = jsInvoke.getInterface(Runnable.class); r.run(); // prints running...
While not part of J2SE, the JSR 223 specification describes an optional Web Scripting Framework which includes an HttpScriptContext integrated with an HttpScriptServlet located a proposed javax.script.http package. In fact, PHP and JavaScript integration for web page generation was an initial impetus for the JSR. This scope was expanded over time to be the much more general solution described above. We can anticipate that future versions of J2EE will introduce the originally envisioned Web Scripting Framework.
So what is gained through JSR 223 and how can it be put to use? Certainly scripting is a convenient and efficient means to learn and prototype. In the context of production Java applications, it can also be useful to provide scriptable extension points for sophisticated users. It can also be useful for altering the state of running applications for debugging and monitoring where alternatives such as JMX are not available or sufficient. Naturally, security concerns will dictate reasonable usage for these scenarios.
However, the above only scratches the surface of the capabilities of scripting languages. In general, they offer greater flexibility and power than statically-typed languages like Java. Scripting languages continue to gain in popularity and capability, providing significant productivity gains over Java only solutions (witness Ruby on Rails). JSR 223 defines an integration standard, establishing a foundation for leveraging the strengths of both approaches.
OCI is the leading provider of Object Oriented technology training in the Midwest. More than 3,000 students participated in our training program over the last 12 months. Targeted toward Software Engineers and the development community, our extensive program of over 50 hands-on workshops is delivered to corporations and individuals throughout the U.S. and internationally. OCI's Educational Services include Group Training events and Open Enrollment classes.
For further information regarding OCI's Educational Services programs, please visit our Educational Services section on the web or contact us at training@ociweb.com.
Object Computing, Inc. is a Sun Authorized Java Center in St. Louis, MO and a Member of the Object Management Group, OMG. OCI specializes in distributed computing using object-oriented and web-enabled technologies and provides Consulting, Education and Product Development services to clients nation-wide. For more information contact us in St. Louis, MO (314)579-0066, Tempe, AZ (480)752-0042 or email info@ociweb.com.
Inquiries regarding Career Opportunities can be directed to: hr@ociweb.com.
The Java News Brief is a monthly newsletter. The purpose and intent of this publication is to advance Java, provide technical value and announce available OCI educational services. To subscribe or unsubscribe from this newsletter, simply click the desired link.
Copyright
© 2006.
Object Computing, Inc. All rights reserved.
Java and all
Java-based marks are trademarks or registered trademarks of Sun
Microsystems, Inc. in the United States and other countries.
Object Computing, Inc. is independent of Sun Microsystems, Inc.
.NET, C#, and .NET-based marks are trademarks or registered trademarks of Microsoft
Corporation in the United States and other countries.
Object Computing, Inc. is independent of Microsoft Corporation.