Appendix B. BeanShell: Java Scripting

In this book, we (in this case, I, Pat) have avoided talking about many third-party tools that aren’t part of the standard JDK. I’m going to make an exception here to give a shout out to a nifty, free Java tool called BeanShell. As its name suggests, BeanShell can be used as a Java “shell.” It allows you to type standard Java syntax—statements, expressions, and even classes—on the command line or evaluate bits of source code from within your applications. With BeanShell, you can try out bits of code as you work through the book. You can access all Java APIs and even create graphical user interface components and manipulate them “live.” BeanShell uses only reflection, so there is no need to compile class files.

I wrote BeanShell while developing the examples for this book, and I think it makes a fun companion to have along on your journey through Java. BeanShell is an open source software project and at the time of this writing is in the process of being adopted by the Apache foundation. You can find the latest updates and more information at its current home. Over the years, BeanShell has been a popular tool and has been widely used in tools and projects that require scripting. However, today there are probably better options for serious scripting integration with Java. For example, you may wish to look at Jython, an implementation of the Python scripting language, as another option. Many scripting languages can be accessed in a plug-in/ provider fashion using the java.scripting API (JSR-223). The JDK 7 is bundled with the Mozilla Rhino implementation of JavaScript, as another option.

Running BeanShell

All you need to run BeanShell is the Java runtime system (version 1.1 or greater) and the bsh JAR file. Under Mac OS X and Windows, you can launch a graphical desktop for BeanShell by simply double-clicking the JAR file. More generally, you can add the JAR to your classpath:

    Unix:     export CLASSPATH=$CLASSPATH:bsh.jar
    Windows:  set classpath %classpath%;bsh.jar

You can then run BeanShell interactively in either a GUI or command-line mode:

    java bsh.Console       // run the graphical desktop
    java bsh.Interpreter   // run as text-only on the command line

Running BeanShell with the GUI console brings up a simple, Swing-based desktop that allows you to open multiple shell windows with basic command history, line editing, and cut-and-paste capability. There are some other GUI tools available as well, including a simple text editor and class browser. Alternately, you can run BeanShell on the command line, in text-only mode.

You can run BeanShell scripts from files, like so:

    % java bsh.Interpreter myfile.bsh

Within some versions of the NetBeans and Sun Java Studio IDEs, you can create BeanShell script files using the New File wizard or run any file with a .bsh extension just as you would execute Java code.

Java Statements and Expressions

At the prompt or in a BeanShell script, you can type standard Java statements and expressions. Statements and expressions are all of the normal things that you’d include in a Java method: variable declarations and assignments, method calls, loops, and conditionals. You can declare classes in the usual way if you want to, but BeanShell allows you to write statements outside of a class or method in an unstructured way as well.

You can type statements exactly as they would appear in Java. You also have the option of working in a more scripting-language-like fashion, with “loosely typed” variables and arguments. In other words, you can be lazy and not declare the types of variables that you use (both primitives and objects). BeanShell will still give you an error if you attempt to misuse the actual contents of the variable. If you do declare types of variables or primitives, BeanShell will enforce them.

Here are some examples:

    foo = "Foo";
    four = (2 + 2)*2/2;
    print( foo + " = " + four );   // print() is a bsh command
    // do a loop
    for (i=0; i<5; i++)
        print(i);
    // pop up an AWT frame with a button in it
    button = new JButton("My Button");
    frame = new JFrame("My Frame");
    frame.getContentPane().add( button, "Center" );
    frame.pack();
    frame.setVisible( true );

If you don’t like the idea of “loosening” Java syntax at all, you can turn off this feature of BeanShell with the following command:

    setStrictJava( true );

Imports

By default, BeanShell imports all of the core Java packages for you. You can import your own classes using the standard Java import declaration:

    import mypackage.*;

In addition to regular package, class, and static imports, BeanShell can also import the methods and variables of an object instance into the current context using the importObject() command. For example:

    Map map = new HashMap();
    importObject( map );
    put("foo", "bar");
    print( get("foo") ); // "bar"

BeanShell Commands

BeanShell comes with a number of useful built-in commands in the form of Java methods. These commands are implemented as BeanShell scripts, and are supplied in the bsh JAR file. You can make your own commands by defining methods in your own scripts or adding them to your classpath. See the BeanShell user’s manual for more information.

One important BeanShell command is print(), which displays values. print() does pretty much the same thing as System.out.println() except that it ensures the output always goes to the command line (if you have multiple windows open). print() also displays some types of objects (such as arrays) more verbosely than Java would. Another very useful command is show(), which toggles on and off automatic printing of the result of every line you type. (You can turn this on if you want to see every result value.)

Here are a few other examples of BeanShell commands:

source(), run()

Reads a script into this interpreter, or runs it in a new interpreter

frame()

Displays an AWT or Swing component in a frame

load(), save()

Loads or saves serializable objects (such as JavaBeans)

cd(), cat(), dir(), pwd()

Unix-like shell commands

exec()

Runs a native application

addClassPath(), reloadClasses()

Modifies the classpath or reload classes

javap()

Prints a javap-style class description for the class or object specified

See the BeanShell user’s manual for a full list of commands.

Scripted Methods and Objects

You can declare and use methods in BeanShell, just as you would inside a Java class:

    int addTwoNumbers( int a, int b ) {
        return a + b;
    }
    sum = addTwoNumbers( 5, 7 );  // 12

BeanShell methods may also have dynamic (loose) argument and return types.

    add( a, b ) {
        return a + b;
    }
    foo = add(1, 2);                // 3
    foo = add("Hello ", "Kitty");   // "Hello Kitty"

In BeanShell, as in JavaScript and Perl, method closures can take the place of classes for scripting objects (but in BeanShell you can also use the regular class syntax). You can turn the context of a method call into an object reference by having the method return the special value this. You can then use the this reference to refer to any variables that were set during the method call. To be useful, an object may also need methods; so in BeanShell, methods may also contain methods at any level. Here is a simple example:

    user( n ) {
        name = n;
        reset() {
            print( "Reset user:"+name );
        }
        return this;  // return user as object
    }
    bob = user("Bob" );

    print( bob.name ); // "Bob"
    bob.reset();      // prints "Reset user: Bob"

This example assigns the context of the user() method to the variable bob and refers to the field bob.name and the method bob.reset().

If you find this strange, don’t worry. The most common reason you’d want to script an object is to implement a Java interface, and you can do that using the standard Java anonymous inner class syntax, as we’ll discuss next, or just use a regular class. BeanShell gives you a lot of options.

Scripting Interfaces and Adapters

One of the most powerful features of BeanShell is that you can “script” any interface type. BeanShell-scripted objects can automatically implement any required interface type. The only thing you need to do is implement the necessary method (or at least the ones that are going to be invoked). You can use this feature either by explicitly referring to a BeanShell script using a this-style reference as described earlier, or by using the standard Java anonymous inner class syntax. Here is an example:

    actionPerformed( event ) { print( event ); }
    button = new JButton("Press Me!");
    button.addActionListener( this );
    frame( button );

You can type this code right on the command line and press the button to see the events it generates. In this case, the this reference refers to the current context, just as in a method. BeanShell automatically implements the ActionListener interface and delegates calls to its actionPerformed() method to our scripted method.

Alternately, we could use the anonymous inner class syntax to create an ActionListener for our button:

    button = new JButton("Press Me!");
    button.addActionListener( new ActionListener() {
        actionPerformed( event ) { print( event ); }
    } );
    frame( button );

In this case the “anonymous inner class” is actually a BeanShell script that implements the ActionListener interface for us in the same way as the previous example.

One more thing: we hinted earlier that you only have to implement those methods of the interface that you want to use. If you don’t script a method, it’s OK as long as it’s not invoked (in which case, you’d get an exception). For convenience in implementing a large interface, you can define the special invoke() method, which handles calls to scripted methods that don’t exist:

    invoke( name, args ) { print("Method: "+name+" invoked!"); }

This invoke() method will handle method calls for methods that are not defined and simply print their names. See the user manual for more details.

Changing the Classpath

Within BeanShell, you can add to your classpath and even reload classes:

    addClassPath("mystuff.jar");
    addClassPath("http://examples.oreilly.com/learnjava3/magicbeans.jar");

To reload all classes in the classpath, simply use:

    reloadClasses();

You can do more elaborate things as well, such as reloading individual classes, if you know what you’re doing. See the user manual for more details.

Learning More . . .

BeanShell has many more features than I’ve described here. You can embed BeanShell into your applications as a lightweight scripting engine, passing live Java objects into and out of scripts. You can even run BeanShell in a remote server mode, which lets you work in a shell inside your running application, for debugging and experimentation. There is also a BeanShell servlet that can be used for running scripts inside an application server.

BeanShell is small (only about 200 KB) and it’s free, licensed under multiple open source licenses. You can learn more by checking out the full user’s manual and FAQ on the website.

Please feel free to send feedback using the book’s web page. So long until the next edition!