InformIT

Employing Rexx as a Scripting Language for Java

Date: Oct 28, 2005

Return to the article

Rony Flatscher introduces the open source and free Bean Scripting Framework (BSF), which allows any Java application to deploy scripts in other languages. Any Java application developer can use BSF to supply scriptability in a user-friendly scripting language such as Rexx.

Rexx is a scripting language that's easy to learn and easy to use. It originated in the IBM mainframe world, where it replaced an awkward and arcane scripting language. Due to its philosophy of aiming at being a "human friendly" language, the syntax and functionality of Rexx were deliberately designed to be as easy and simple as possible—yet remain powerful enough to be able to create standalone programs.

Over the course of the past 25 years, Rexx has been a very popular language at times, and it has been deployed as a scripting language for quite a few operating systems, even non-IBM systems such as the Amiga OS. The biggest exposure to the non-business world was certainly realized via OS/2.

Independent of the faith of various operating systems, today there are quite a few Rexx interpreters available for different platforms; of the free and open source versions, the most popular are Regina Rexx and Open Object Rexx (ooRexx). A part of the Rexx community is organized in the non-commercial SIG Rexx Language Association, which puts together a yearly International Rexx symposium.

Java, Scripting Languages, and the Bean Scripting Framework (BSF)

In the past, the need for scripting languages for Java applications has been neglected by many companies, most notably by Sun itself. Although Sun started a JCP process—Java specification request 223 (JSR-223)—in the summer of 2003 to define and create a standard Java interface to scripting languages, it still will be some time until it will be made widely available with the next major release of the Java language.

One major company that has embraced Java is IBM. For business application development in Java, IBM wanted to include the capability of invoking scripting languages from Java in the context of Java Server Pages (JSP) in their Java application server WebSphere. Drawing from an IBM-funded open source development project, Bean Scripting Framework (BSF), IBM incorporated that into their application server. Because BSF has been handed over to the Apache organization, everyone can use that open sourced infrastructure for free. Jakarta BSF allows deploying scripts from Java in many languages, among them JavaScript (by virtue of using Rhino, the Java implementation of JavaScript) and Netrexx, a Rexx-like language implemented in Java.

The BSF framework is aimed at making it easy for Java programmers to employ scripting languages that execute in the context of their Java applications. The package BSF4Rexx adds the scripting language ooRexx to BSF, so that Rexx and ooRexx scripts can be invoked by Java.

Employing BSF: Three Fundamental Nutshell Examples

The BSF framework makes it extremely easy for Java programmers to execute scripts. The following sections demonstrate a few little Java programs that employ Rexx scripts with the help of the BSF framework.

Simple Script Execution

Listing 1 shows the Java TestExecRexx.java program that uses BSF to execute a Rexx program, which does nothing else but display the following text:

Rexx was here!

Listing 1 TestExecRexx.java

import org.apache.bsf.*; // get Jakarta-BSF support

/* Test Java program which demonstrates how easy it is to invoke Rexx via BSF. */
public class TestExecRexx {

 /* Running an in-line defined Rexx script. */
 public static void main (String[] args) throws java.io.IOException
 {
 try
 {
  BSFManager mgr = new BSFManager ();
  BSFEngine rexx = mgr.loadScriptingEngine("rexx");
  String  rexxCode = "SAY 'Rexx was here!'"; // Rexx code

  rexx.exec ("rexx", 0, 0, rexxCode); // execute the script
 }
 catch (BSFException e)
 {
  e.printStackTrace();
 }
 }
}

Here's a short walkthrough of Listing 1.

import org.apache.bsf.* makes all the BSF Java classes available, most notably BSFManager and BSFEngine. If you want to use the IBM version of BSF, you would need to import com.ibm.bsf.* instead. As all of the Java class names and interface definitions are the same in both versions of BSF, the rest of the program doesn't need to be changed.

Instances of the BSFManager class allow for the loading of BSF engines that know how to invoke script engines, cache BSF scripting engines, and offer some utility methods.

For each scripting engine to be usable in the BSF framework, you must supply an appropriate implementation. The BSF framework helps a lot by having most of the out-of-the-box functionality implemented already; script engine suppliers just need to focus on their particular interface from and to Java. The BSFEngines are used to invoke the appropriate script engines by supplying the code (a String object, which may contain carriage returns and/or linefeed characters from any source) and arguments, if any. For example, the exec method of BSFEngine expects the code without any arguments, whereas the apply method of BSFEngine expects arguments (a Vector of Objects) and a result value (a value of type Object).

Carrying Out a Script, Supplying Arguments, Retrieving the Result

Listing 2 shows a Java program that invokes a Rexx script, supplying a few arguments, which the Rexx script will concatenate in reverse order using a carriage return and linefeed character. This new value is then returned to Java and printed to the System.out stream. Listing 3 depicts the output of the Java program in Listing 2.

Listing 2 TestApplyRexx.java

import org.apache.bsf.*; // get Jakarta-BSF support

/* Test Java program which demonstrates how easy it is to invoke Rexx via BSF. */
public class TestApplyRexx {

 /* Running an in-line defined Rexx script. */
 public static void main (String[] args) throws java.io.IOException
 {
 try
 {
  BSFManager mgr = new BSFManager ();
  BSFEngine rexx = mgr.loadScriptingEngine("rexx");
  // define the Rexx program
  String rexxCode = "tmpStr=''       \n" +
      "crlf='0d0a'x      \n" +
      "do i=arg() to 1 by -1    \n" +
      " tmpStr=tmpStr crlf arg(i)  \n" +
      "end        \n" +
      "return tmpStr      \n";

  // define the arguments for the Rexx program
  java.util.Vector vArgs=new java.util.Vector();
  vArgs.addElement( new String("an argument of type String"));
  vArgs.addElement( new Integer(17) );
  vArgs.addElement( new Float(17.71f));
  vArgs.addElement( "This is the last argument.");

  // execute Rexx script, supplying the Java arguments, showing the result
  System.out.println(rexx.apply("rexx", 0, 0, rexxCode, null, vArgs));
 }
 catch (BSFException e)
 {
  e.printStackTrace();
 }
 }
}

Listing 3 TestApplyRexx.out

This is the last argument.
17.71
17
an argument of type String

Here's a short walkthrough:

  1. As in the earlier exec example, only an instance of BSFManager is needed, which we use to load the appropriate BSF engine, in this case the Rexx engine.
  2. A Rexx script is defined in which each statement is ended by the newline character. (Alternatively, you could use the optional semicolon to end the statement.) The script creates a string that concatenates the received arguments in reverse order. In Rexx, you concatenate strings merely by writing them in the desired order, where the blanks between the strings—and variables that contain strings—serve as concatenating characters. Therefore, arguments are delimited from each other with a blank, a carriage return, a linefeed, and another blank character.
  3. The Java program creates a Vector that will be used to store the Java arguments meant for the script.
  4. The Rexx script is carried out with the given arguments and returns a string from Rexx that gets displayed (refer to Listing 3).

It's interesting to note that it's possible to pass Java objects as arguments to a non-Java program. It depends on the support of the respective BSF engine whether and how those scripts are able to interact with Java objects.

In the case of the Rexx BSF engine, a particular feature of BSFManager is employed: the BSF registry. For the Rexx BSF engine, the registry serves as a directory of Java objects, any of which can be referred to by the Rexx scripts transparently by its index name, which is always a String object. In addition, conversion routines are implemented that transparently carry out any needed coercions—Rexx programmers usually don't have to care about Java datatypes at all. This makes the usage of Rexx as a scripting language for Java applications especially useful, as you can totally forgo any type hinting.

Whether this interaction with Java objects is simple or complicated depends very much on the implementation of the BSF engine. In the case of the Rexx BSF engine this has been carried out to the point that, in practice, Rexx coders don't need to know about Java datatypes at all.

Interacting with Java Objects

Listing 4 depicts a Java program that creates a simple GUI and then calls a script, stored in an external file, to interact with the Java frame. In this case, the BSF engine employed to execute the script is determined by the file extension.

Listing 4 ScriptedUI.java

/* This example shows how a Java app can allow a script to customize a UI */
/* Part of the BSF distribution (e.g. cf. http://jakarta.apache.org/bsf) */

import java.awt.*;
import java.awt.event.*;
import java.io.*;

import org.apache.bsf.*;
import org.apache.bsf.util.*;

public class ScriptedUI {
 BSFManager mgr = new BSFManager ();

 public ScriptedUI (String fileName) {
 Frame f = new Frame ("Application's Main Frame");
 f.addWindowListener (new WindowAdapter () {
  public void windowClosing (WindowEvent e) {
  System.exit (0);
  }
 });

 Panel p = new Panel ();
 f.add ("Center", p);
 f.add ("North", new Button ("North Button"));
 f.add ("South", new Button ("South Button"));

 mgr.registerBean ("centerPanel", p);

 // exec script engine code to do its thing for this
 try {
  String language = BSFManager.getLangFromFilename (fileName);
  FileReader in = new FileReader (fileName);
  String script = IOUtils.getStringFromReader (in);

  mgr.exec (language, fileName, -1, -1, script);
 } catch (BSFException e) {
  System.err.println ("Ouch: " + e.getMessage ());
  e.printStackTrace ();
 } catch (IOException e) {
  System.err.println ("Ouch: " + e.getMessage ());
  e.printStackTrace ();
 }

 // now pack and show the frame
 f.pack ();
 f.show ();
 }

 public static void main (String[] args) throws Exception {
 if (args.length != 1) {
  System.err.println ("Usage: java ScriptedUI filename");
  System.err.println ("  where filename is the name of the script");
  System.exit (1);
 }
 new ScriptedUI (args[0]);
 }
}

The interesting part of this nutshell example is the fact that the Java program creates a Panel object and stores it in the BSF registry for use by other programs. The invoked scripts therefore are able to retrieve/refer to those Java objects and interact with them.

The Rexx BSF engine makes a function BSF() available to Rexx; this function is used to interact directly with Java using the BSF framework. In this nutshell example, the BSF() subfunctions lookupBean, invoke, registerBean, and getStaticValue are needed:

The Rexx program in Listing 5 will apply an instance of a Java BorderLayout to the panel, which allows it to address the panel's canvas with cardinal points. Then the Rexx program will create Java objects, notably a Label, two TextFields, and two Buttons. The panel's background will then be turned to yellow and the panel's parent (the frame object) will get a title indicating that its value stems from Rexx.

Listing 5 ui.rex

/* "ui.rex" - a Rexx script interacting with a Java objet */
/* pick up the center panel bean, Rexx program modeled after ui.nrx */
p = bsf("lookupBean", "centerPanel") /* reference the entry in BSF registry, put there by
           "ScriptedUI.class" */

/* set the layout manager to border */
call bsf "invoke", p, "setLayout", bsf("registerBean", "", "java.awt.BorderLayout")

/* add a few things */
call bsf "invoke", p, "add", "Center", bsf("registerBean", "", "java.awt.Label",  "Middle from Rexx")
call bsf "invoke", p, "add", "North", bsf("registerBean", "", "java.awt.TextField", "North text from Rexx")
call bsf "invoke", p, "add", "South", bsf("registerBean", "", "java.awt.TextField", "South from Rexx")
call bsf "invoke", p, "add", "East", bsf("registerBean", "", "java.awt.Button", "Inner east from Rexx")
call bsf "invoke", p, "add", "West", bsf("registerBean", "", "java.awt.Button", "Inner west from Rexx")

/* configure p a bit */
call bsf "invoke", p, "setBackground", bsf("getStaticValue", "java.awt.Color", "yellow")

/* configure the frame that p is in */
f = bsf("invoke", p, "getParent")
call bsf "invoke", f, "setTitle", "Hello from REXX (title reset from Rexx)"

Earlier, Listing 4 depicted the Java program ScriptedUI.java, which comes with the samples in the BSF distribution. You've just seen Listing 5, which displays the Rexx program. Listing 6 gives the command line to start the program (in this case, the name of a file where the script is stored). Figure 1 shows the result of running the Java program.

Listing 6 Command line to start the Rexx program.

java ScriptedUI ui.rex
Figure 1

Figure 1 Using Java host objects and Java classes from Rexx.

Roundup and Outlook

The BSF infrastructure and the functionality of the BSF engines make it possible to fully interact with Java objects from scripts, even in programming languages that are not based on Java themselves, as is the case for Rexx. The IBM version of BSF even runs on Java 1.1, whereas the Apache (Jakarta) version needs at least Java 1.3.

The forthcoming Java scripting standard (JSR-223) will restrict the scripting abilities to the next version of Java, so application developers won't be able to deploy scriptable Java applications on earlier versions. In this respect, BSF can be of great practical help, as it's a proven Java framework that gets deployed in industry-strength application systems such as IBM's WebSphere.

The Jakarta BSF group plans to work on a JSR-223 implementation of BSF, which would bring a compatible set of scripting interfaces to BSF users. Such a version would relieve Java application developers from having to force their customer base to the next version of Java, and rather allow them to deploy their applications in customer environments that deploy older versions of Java—yet allow them to take full advantage of scripting Java applications.

800 East 96th Street, Indianapolis, Indiana 46240