Scripting Languages For Java

Weiqi Gao
© 2001 Object Computing, Inc.

Introduction

This month's Java Technical Insight brings you to the world of scripting languages.  Since this is the Java News Brief, we'll take a Java centric view and focus on two scripting language implementations that interact nicely with Java: Rhino and Jython.

A Survey of the Field

Since the release of JDK 1.0 in 1995, many popular languages have been reimplemented in Java.  These reimplementations usually give its users the ability to access any Java classes in the JVM.  Some are even capable of compiling arbitrary scripts into Java class files.  At the same time, many languages gained the ability to access the JVM and all the classes in it in their original C implementations.  Here is a partial list of languages that I have found on the internet as free software:
 
 Language  C implementation / Java Access Layer  Java Implementation
 ECMAScript  JS Engine  / LiveConnect  Rhino
 Python  Python (a.k.a. CPython) / None  Jython (formerly JPython)
 Tcl/Tk  Tcl/Tk / TclBlend  Jacl
 Perl  Perl / JPL (bundled with Perl)  None
 Scheme  Guile and others / None  Kawa, Skij, and  and more ...
 Java itself  The JDK  BeanShell, and others

Getting Started

To start using the Java implementation of a scripting language, one downloads the jar files, put them into the CLASSPATH, and write a shell script or two to make using the language convenient.  Jython even comes with an installer.  A portion of my CLASSPATH reads:
... ~/rhino15R2pre/js.jar:~/jython-2.0/jython.jar: ...
And I have the following shell scripts:
rhino: java org.mozilla.javascript.tools.shell.Main $* (Rhino interactive shell)
rhinoc: java org.mozilla.javascript.tools.jsc.Main $*  (Compile JavaScript code to class files)
jython: ...  (Jython interactive shell, supplied by Jython)
jythonc: ...  (Compile Python code to class files, supplied by Jython)
Here's the transcript of a couple of sessions with Rhino and Jython respectively:
[weiqi@gao] 1 $ rhino
js> importPackage(java.awt,java.awt.event)
js> for (m in AWTEvent) print("AWTEvent." + m + ": " + AWTEvent[m])
AWTEvent.ADJUSTMENT_EVENT_MASK: 256
AWTEvent.RESERVED_ID_MAX: 1999
AWTEvent.TEXT_EVENT_MASK: 1024
  ...
AWTEvent.MOUSE_EVENT_MASK: 16
js> 3 * 3 + 4 * 4 == 5 * 5
true
js> quit()

[weiqi@gao] 2 $ jython
Jython 2.0 on java1.2.2 (JIT: sunwjit)
Type "copyright", "credits" or "license" for more information.
>>> from java.util import Random
>>> r = Random()
>>> r.nextInt()
-21966670
>>> for i in range(4, 7):
...     print r.nextDouble()
...
0.22142449772414396
0.3263682239532196
0.8371217576770793
>>> ^D

We are writing JavaScript and Python code while making use of standard Java classes.  The interactivity and the economy of expression in the scripting languages account for their first big use---Exploratory Programming and Testing.

All scripting languages mentioned above have an interpreter interface that can be embedded into applications to make them scriptable by end users.  The Netscape Navigator web browser and Xalan-J XSLT processor are good examples of applications that contains an embedded scripting language.  This is the second big use of scripting languages.

All the scripting languages mentioned above, in either their C or Java implementations, have other uses that are not directly related to the Java platform.  These uses include rapid GUI application development, CGI programming, testing frameworks, and many more.  We will not go into this other uses in this article.

Exploratory Programming and Testing

Obviously, one of the prerequisites for using a scripting language is the familiarity with the language.  Fortunately, good tutorials and users' guides exist for all of the above languages.  For the rest of this article I'll assume that you are familiar with JavaScript and Python.  And I'll explain how Rhino and Jython allows you to access arbitrary Java objects.

Rhino and LiveConnect

LiveConnect is the mechanism that JavaScript uses to access Java objects.  LiveConnect is used by both the C engine and Rhino, while Rhino contains a few more convenient functions.  Although LiveConnect relies on a set of wrapper classes to communicate between JavaScript and Java, to the user, it works almost transparently.  Let's look at some examples: Since Java arrays are not in direct correspondence to JavaScript arrays in Rhino, creating a Java array in Rhino is more complicated than in Java.  Java reflection is used to create a Java array in Rhino:
js> a = java.lang.reflect.Array.newInstance(Object, 3)
js> a.length
3

Using Jython

For the most part, accessing Java using Jython has the same feel as using Rhino.  However, since Python (the language implemented by Jython) has a richer set of features than JavaScript, Jython provides more convenience for the exploratory Java programmer.  Let's try out our Rhino examples in Jython: Jython makes creating Java arrays easier through the jarray module:
>>> from jarray import array, zeros
>>> array([1,2,3], 'i')
array([1, 2, 3], int)
>>> from java.util import HashMap
>>> a = array([HashMap(), HashMap()], HashMap)
array([{}, {}], java.util.HashMap)
Jython goes one step further than Rhino in JavaBeans scripting.  All properties and event listener methods can be assigned at object construction time using keyword arguments:
>>> f = Frame("A Java Frame in Jython", visible=1, windowClosing=exit)
This is roughly equivalent to the following Java code
Frame f = new Frame("A java Frame in Jython");
f.setVisible(true);
f.addWindowListener(new WindowAdapter() {
  public void windowClosing(WindowEvent e) {
    exit();
  }
});
I have found this style of exploratory program useful in rapid prototyping of ideas, in getting acquainted with new Java APIs, and in informal testing of newly developed Java classes and packages.

User Level Scripting of Applications

Embedding a scripting language into an application has been a winning strategy for application designers for a long time.  With the proliferation of scripting languages for the JVM, embedding one into an application becomes as easy as choosing a language and instantiating an interpreter object of the chosen language.

Issues such as the execution environment, the scoping of objects, threading and synchronization need to be considered carefully when you embed a scripting language into an application.  We will not go into detailed discussion about these issues in this article.  Instead I will restrict my attention to a very simple Java class and write a Java application around it, giving the user the power to drive the application through scripts.

The Java class I'll script is called Turtle.  It represents a point that moves on a plane, tracing its path:

[weiqi@gao] 1 $ javap Turtle
Compiled from Turtle.java
public class Turtle extends java.awt.Canvas {
    public void reset();
    public void move(int);
    public void turn(double);
    ...
}
The methods of this class do the obvious things.  However merely using objects of this class in a plain Java application isn't very exciting.  You probably prescribe a series of move() and turn() actions in Java code, compile and execute the code, and observe the resulting pattern.

To make it more interesting, I'll write an AWT application containing a Turtle, a TextArea for the user to enter scripts, and an OK button to send scripts to the interpreter.  I have chosen to use the Jython interpreter to do the job.  However, Rhino would do the job equally well.

import org.python.util.PythonInterpreter;
public class EtchSketch extends Frame implements ActionListener {
  // I want a Turtle ...
  private Turtle turtle = new Turtle();
  // ... and a TextArea, ...
  private TextArea ta = new TextArea(10, 40);
  // ... and a Button ... (register an ActionListener)
  private Button b = new Button("OK"); { b.addActionListener(this); }
  // ... and a Jython interpreter!
  private PythonInterpreter interp = new PythonInterpreter();

  public EtchSketch() {
    // Do the GUI stuff
    ...
    // Add a variable named 'turtle' to the toplevel scope of the Jython interpreter,
    // bound to the Java object turtle
    interp.set("turtle", turtle);
  }

  public void actionPerformed(ActionEvent e) {
    // When the OK button is clicked, we grab the text from the TextArea, ...
    String script = ta.getText();
    // ... execute it in the Jython interpreter, ...
    interp.exec(script);
    // ... and prepare for the next user command.
    ta.selectAll(); ta.requestFocus();
  }

  public static void main(String[] args) {
    EtchSketch f = new EtchSketch();
  }
}

In this application, the user can interactively enter commands to direct the turtle's movement on the Canvas.  Since the user has the turtle object, and the whole Python language at his disposal, he can do things that may surprise the original designer.  Putting myself into my user's shoes, I immediately concocted a script that, well, surprised myself!

Summary

In this article, I illustrated how to use Rhino and Jython for exploraroty programming and testing, and for embedding

Resources

  1. Rhino is a subproject of the Mozilla project:  http://mozilla.org/rhino .
  2. Jython has it's own web site:  http://jython.org .
  3. Of course, Python information can be found at  http://python.org .