Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Recently, we've explored the interface between Java and XML. More specifically, we've been looking at ways to use Java's ability to dynamically load code to improve our ability to process XML -- in particular, to handle XML tags we didn't specifically design our application to handle.
"XML and Java: A Potent Partnership": Read the whole series!
The Java-based framework I developed over the preceding two columns was capable of examining tags and their attributes, and even analyzing the context in which tags occur in order to properly handle them. As an application developed within this framework encountered novel tags in its XML input, it located and loaded code to handle those tags. The one feature it lacked was scripting.
Adding scripting to the framework brings a number of advantages. First, scripting languages provide a natural and concise method of specifying behavior (scripting languages are programming languages, after all). Second, since scripts are text, they can be incorporated into the XML itself. This is essential for XML applications such as the GUI definition application we've been developing.
Here's the roadmap for what lies ahead.
Because of the sheer volume of what I'm about to cover, I'll present this material in two parts. This month, I will cover the JavaScript language and discuss how to integrate the Rhino JavaScript implementation into Java. Next month, I'll demonstrate how to integrate JavaScript into our XML framework.
There are several capable scripting languages available for Java, covering a wide feature space, from compiled to interpreted and from procedural to declarative. The list includes versions of many of our favorites from the days before Java: TCL (Jacl), Python (JPython), JavaScript (Rhino), Scheme (at least a dozen different implementations), and so on. For an exhaustive list, take a look at Robert Tolksdorf's Web page, "Languages for the Java VM," in the Resources section at the bottom of this article.
For this exercise, I've selected the Rhino JavaScript implementation. I chose JavaScript because it's reasonably easy to use and learn, it's powerful, it's already both a de facto and a de jure standard (see ECMA-262 in the Resources), and it's extensible.
JavaScript was developed by Netscape for use in its Web browser. Originally called LiveScript, the name was changed to JavaScript at roughly the same time that Java was incorporated into Netscape Navigator 2.0. Since then, JavaScript (or a derivative) has found its way into most major browsers.
In late 1996, the ECMA international standards body began a standardization process to define a scripting language based on a number of existing technologies, the most significant being JavaScript. The ECMA standardized the core portion of the language (the generic, nonbrowser pieces). The first version of the standard appeared in 1997; the second, called ECMA-262, followed in June 1998. The language that was thus defined is technically called ECMAScript, but it looks and feels just like JavaScript -- minus the browser-specific parts.
There are at least two Java-based scripting languages that implement JavaScript in Java: FESI (which actually attempts to implement the ECMA-262 specification) and Rhino. Rhino is one component of the Mozilla project. (See the Resources for links to both languages.) Please note that, from here on in, I'll use the term JavaScript throughout this article to refer to the JavaScript scripting language in general and Rhino when I need to refer to or emphasize the Rhino implementation of that language in particular.
There are so many introductions to JavaScript programming freely available via the Web that I can't bring myself to take up the space to cover the language here. Instead, consult the Resources section for references.
JavaScript is object-based. Just as they do in Java, objects in JavaScript have properties (member data) and methods (member functions). If you want to build applications, you do so by creating objects.
Let's create an object directly in JavaScript:
x = {
a : 5,
b : function() {
print(this.a)
}
}
We can now access the properties and invoke the methods of object x:
$ x.a 5 $ x.b() 5 $ x.a = 4 4 $ x.b() 4
Now let's create a JavaScript object in Java. This is how you extend JavaScript's built-in functionality. I've numbered the lines of code, and I'll refer to them by number in the following sections.
001 public
002 class Y
003 extends ScriptableObject {
004
005 public
006 String
007 getClassName() { return "Y"; }
008
009 public
010 void
011 b() {
012 System.out.println(get("a", this));
013 }
014
015 public
016 static
017 void
018 main(String [] rgstring) {
019 Context context = Context.enter();
020 Scriptable scriptable = context.initStandardObjects(null);
021 Y y = new Y();
022 scriptable.put("y", scriptable, y);
023 y.put("a", y, new Integer(5));
024 Method [] rgmethod = FunctionObject.findMethods(y.getClass(),
"b");
025 FunctionObject functionobject = new FunctionObject(null,
rgmethod[0], y);
026 y.put("b", y, functionobject);
027 context.exit();
028 }
029 }
JavaScript objects must implement the Scriptable interface. The easiest way to create a new JavaScript object is to implement the ScriptableObject abstract class, which implements the Scriptable interface and provides definitions for its methods. The only method we must define is the getClassName() method.
All JavaScript activity occurs within the bounds of a valid runtime context. The Context.enter() method associates a Context instance with the currently executing thread. The Context instance stores execution information, such as the JavaScript call stack, and provides methods for interacting with the JavaScript
engine. At any place within a valid context you can obtain a reference to the context via a call to the static method getCurrentContext(). Lines 019 and 027 in the listing above demonstrate how to enter and exit a context.
The ECMA-262 specification details the existence of a handful of standard objects, called built-in objects. These objects are guaranteed to be available when a JavaScript program begins execution. The important members of
this group are the objects Object, Function, Array, String, Boolean, Number, Date, and Math. The Rhino implementation supports these standard objects.
Line 020 demonstrates how to initialize these objects and return a reference to the global object. The eight built-in objects are properties of the global object. We can make use of that fact if we need to access them from Java.
In lines 021 and 022, I create an instance of the Y class and make it a property of the global object. You can use this object exactly like any other JavaScript object, as in
the example below:
$ y [object Y] $ y.foo = "bar" bar
The put() method adds a property to an object. It requires three parameters:
In line 023, I add the first of two properties -- property a; in lines 024, 025, and 026, I add another, property b. Recall from the first JavaScript example that b is a method that returns the value of property a. I am going to build this JavaScript method as a Java method. Rhino uses the Reflection API to find the Method instance associated with a method. It wraps the Reflection API in the method findMethods(), which searches a class for all methods of a given name. In line 025, I use the instance of the Method class to create an instance of the FunctionObject class. A function object is a first-class value in JavaScript and can be manipulated like any other value. Finally, I add
the property to y.
You can now use this object in exactly the same way that you used the native JavaScript object:
$ y.a 5 $ y.b() 5 $ y.a = 4 4 $ y.b() 4
In addition to providing general contextual information, the Context class also provides methods for evaluating scripts. The code in the listing below evaluates the strings passed in on the
command line. See if you can figure out how to extend this method in order to evaluate code interactively or from a file.
public
static
void
main(String [] rgstring) {
Context context = Context.enter();
Scriptable scriptable = (ScriptableObject)context.initStandardObjects(null);
for (int i = 0; i < rgstring.length; i++) {
context.evaluateString(scriptable, rgstring[i], "<>", i, null);
context.exit();
}
These techniques cover only part of Rhino's functionality, but by mastering them, you'll have the background necessary to
understand next month's column. In that installment of our series, I'll use these techniques to reflect the XML tag elements
into the JavaScript environment as properties so that you can access them from JavaScript scripts. I'll also show you how
to attach scripts to the BUTTON tag, and how to evaluate them when the button is pressed.
Read more about Enterprise Java in JavaWorld's Enterprise Java section.