- Java, Scripting Languages, and the Bean Scripting Framework (BSF)
- Employing BSF: Three Fundamental Nutshell Examples
- Interacting with Java Objects
- Roundup and Outlook
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:
- 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.
- 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.
- The Java program creates a Vector that will be used to store the Java arguments meant for the script.
- 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.