BeanShell User's Manual

Table of Contents


Quick Start

This is just the crash course to get you going. I'm leaving out some important options and details. Please see the user's guide (which is not very long in its entirety) for all of the details.

Download and Run BeanShell

Download the latest jar file and start up BeanShell either in the graphical desktop mode or on the command line.

If you just want to start playing around you may be able to to launch the BeanShell desktop by simply double clicking on the bsh JAR file. More generally however you'll want to add the jar to your classpath so that you can work with your own classes and applications easily.


unix:     export CLASSPATH=$CLASSPATH:bsh-xx.jar
windows:  set classpath %classpath%;bsh-xx.jar

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

Note: If you are running under JDK 1.1 and want to use the GUI you'll also have to add the swing JAR file to your classpath.

Once BeanShell is running you can also add to the classpath using the addClassPath() command.

The above examples show how to run bsh interactively. It's also possible to run bsh in a server mode and to embed bsh for non-interactive use in your applications. You can even make executable bsh scripts under Unix using the standard "#!" syntax. See the user's manual for more information on those topics.

The BeanShell Desktop and Console Windows

Upon starting BeanShell one console window will open. By right clicking on the desktop background can also open additional console windows or other tools such as the BeanShell class browser.

Each console window effectively runs a separate bsh interpreter. Within the graphical console you have basic command history, line editing, and cut and paste. From any console window you can open a simple editor window for that console. In it you can write scripts and hit the 'eval' button to evaluate them in the attached workspace.

Java Statements and Expressions

At the prompt you can type standard Java statements and expressions. Statements and expressions are all of the normal things that you'd say inside a Java method: e.g. variable declarations and assignments, method calls, loops, conditionals, etc.

You can use these exactly as they would appear in Java, however in BeanShell you have the option of working with "loosely typed" variables. That is, you can simply 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 type of the variable.

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 a frame with a button in it
    b = new JButton("My Button");
    f = new JFrame("My Frame");
    f.getContentPane().add(b, "Center");
    f.pack();
    f.show();

Useful BeanShell Commands

In the previous example we used a convenient "built-in" BeanShell command called print(), to display values. print() does pretty much the same thing as System.out.println() except that it insures that the ouput always goes to the command line. 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 print()ing of the result of every line you type.

Here are a few other examples of BeanShell commands:

See the complete list of BeanShell commands for more information.

BeanShell commands are simply methods which are implemented by bsh scripts supplied in the bsh jar file. You can, of course, define your own methods in bsh and also add your own scripts to the classpath to extend the basic command set.

Scripted Methods

You can declare and use methods in bsh just as you would in a Java class.

    int addTwoNumbers( int a, int b ) {
        return a + b;
    }

    sum = addTwoNumbers( 5, 7 );  // 12

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

    add( a, b ) {
        return a + b;
    }

    foo = add(1, 2);            // 3
    foo = add("Oh", " baby");   // "Oh baby"

Scripted Objects

In BeanShell, as in JavaScript and Perl, method "closures" allow you to create scripted objects. You can turn the results of a method call into an object reference by having the method return the special value this. You can then use that reference to refer to any variables set in the method call. To make useful objects you need instance methods of course so in BeanShell methods may also contain methods at any level.

    foo() {
        print("foo");
        x=5;

        bar() {
            print("bar");
        }

        return this;
    }

    myfoo = foo();    // prints "foo"
    print( myfoo.x ); // prints "5"
    myfoo.bar();      // prints "bar"

If this seems strange to you please see the user's manual for more details.

You may also cast your scripted objects (e.g. myFoo above) to any Java interface type and supply the necessary methods in the script.

Note: implementing interface types requires BeanShell be running under a Java 1.3 environment or higher

Inner Class Style

You may also use the Java anonymous inner class style syntax to implement an interface type with a script.

    ActionListener scriptedListener = new ActionListener() {
        actionPerformed( event ) { ... }
    }

Normally, if you leave out methods required by the interface the calling code will throw an exception. However if you wish to override this behavior you can implement the special method invoke(name, args) in your scripted object to handle undefined method invocations:

        ml = new MouseListener() {
            mousePressed( event ) { ... }
            // handle the rest
            invoke( name, args ) { print("Method: "+name+" invoked!");
        }

BeanShell Commands

BeanShell comes with a bunch of commands to make your life easier. These are, for the most part, just very short bsh scripts, supplied in the bsh.jar file. See Adding bsh commands for more details on adding to the "built-in" bsh command set.

addClassPathvoid addClassPath( string | URL )
bgThread bg( String script )
browseClassvoid browseClass( String | Object | Class )
catvoid cat( String filename )
cdvoid cd( String dirname )
classBrowservoid classBrowser()
debugvoid debug()
desktopvoid desktop()
dirvoid dir( String dirname )
errorvoid error( string )
evalObject eval( String expression )
execexec( String process )
exitvoid exit()
extendThis extend( This object )
frameFrame | JFrame | JInternalFrame frame( Component component )
getClassClass getClass( String )
getClassPathURL [] getClassPath()
getResourceURL getResource( String path )
javapvoid javap( String | Object | Class )
loadObject load( String filename )
objectThis object()
pathToFileFile pathToFile( String filename )
printvoid print( arg )
pwdvoid pwd()
reloadClassesvoid reloadClasses( [ package name ] )
rmvoid rm( String pathname )
runThis run( String script )
savevoid save( Component component, String filename )
servervoid server( int port )
setClassPathvoid setClassPath( URL [] )
setFontFont setFont( Component comp, int ptsize )
setNameCompletionvoid setNameCompletion( boolean )
showvoid show()
sourcevoid source( String filename )
superThis super( String scopename )
unsetvoid unset(String name)

print
void print( arg )
Print the string value of the argument, which may be of any type. If beanshell is running interactively, the output will always go to the command line, otherwise it will go to System.out.

Most often the printed value of an object will simply be the Java toString() of the object. However if the argument is an array the contents of the array will be (recursively) listed in a verbose way.

Note that you are always free to use System.out.println() instead of print().

show
void show()
Toggle on or off displaying the results of expressions (off by default). When show mode is on bsh will print() the value returned by each expression you type on the command line.

frame
Frame | JFrame | JInternalFrame frame( Component component )
Display the component, centered and packed, in a Frame, JFrame, or JInternalFrame. Returns the frame. If the GUI desktop is running then a JInternaFrame will be used and automatically added to the desktop. Otherwise if Swing is available a top level JFrame will be created. Otherwise a plain AWT Frame will be created.

load
Object load( String filename )
Load a serialized Java object from filename. Returns the object.

save
void save( Component component, String filename )
Save a serializable Java object to filename.

cat
void cat( String filename )
Print the contents of filename (like Unix cat)

exec
exec( String process )
Get the Java Runtime and start the external process, display any output. Note: process handling will get more sophisticated in the future

pwd
void pwd()
Print the bsh working directory. This is the cwd obeyed by all the unix like bsh comands.

rm
void rm( String pathname )
Remove the file (like Unix rm)

unset
void unset(String name)
"undefine" the variable specifed by 'name' (So that it tests == void).

Note: there will be a better way to do this in the future. This is currently equivalent to doing namespace.setVariable(name, null);

cd
void cd( String dirname )
Change working directory for the dir() command (like Unix cd)

dir
void dir( String dirname )
Display the contets of directory dirname. The format is similar to the Unix ls -l command. dir void dir() Display the contents of the current working directory. The format is similar to the Unix ls -l

pathToFile
File pathToFile( String filename )
Create a File object corresponding to the specified file path name, taking into account the bsh current working directory (bsh.cwd)

source
void source( String filename )
Read filename into the interpreter and evaluate it in the current namespace. Like Bourne Shell "." command.

eval
Object eval( String expression )
Evaluate the string in the current interpreter (see source() above). Returns the result of the evaluation or null.

server
void server( int port )
Create a Server Mode server attached to the current interpreter, listening on the specified port.

debug
void debug()
Toggle on and off debug mode... Note: debug output is verbose and gross.

exit
void exit()
Call System.exit(0);

getResource
URL getResource( String path )
The equivalent of calling getResource() on the interpreter class in the bsh package. Use absolute paths to get stuff in the classpath.

setFont
Font setFont( Component comp, int ptsize )
Change the point size of the font on the specified component, to ptsize.

run
This run( String script )
run() is like source() except that it runs the command in a new, subordinate and prune()'d namespace. So it's like "running" a command instead of "sourcing" it. Returns the object context in which the command was run.

bg
Thread bg( String script )
This is like run() except that it runs the command in its own thread. Returns the thread object (for stop()ing, join()ing, etc.)

object
This object()
object() Returns a new bsh object which you can use to hold data items. e.g.

  myStuff = object();
  myStuff.foo = 42;
  myStuff.bar = "blah";

extend
This extend( This object )
Note: this command will likely change along with a better inheritance mechanism for bsh in the next release.

extend() is like the object() command, which creates a new bsh scripted object, except that the namespace of the new object is a child of the parent object.

For example:

    foo=object();
    bar=extend(foo);

is equivalent to:
  
    foo() { 
        bar() {
            return this; 
        }
    }

    foo=foo();
    bar=foo.bar();

and also:
 
   foo=object();
   bar=object();
   bar.namespace.bind( foo.namespace );

The last example above is exactly what the extend() command does. In each case the bar object inherits variables from foo in the usual way.

super
This super( String scopename )
super( name ) Returns a bsh This reference to the enclosing scope (method scope) of the specified name. e.g.

    foo() {
        x=1;
        bar() {
            x=2;
            gee() {
                x=3;
                print( x ); // 3
                print( super.x ); // 2
                print( super("foo").x ); // 1
            }
        }
    }

addClassPath
void addClassPath( string | URL )
Add the specified directory or JAR file to the class path. e.g.

     addClassPath( "/home/pat/java/classes" );
     addClassPath( "/home/pat/java/mystuff.jar" );
     addClassPath( new URL("http://myserver/~pat/somebeans.jar") );

See Class Path Management

setClassPath
void setClassPath( URL [] )

Change the classpath to the specified array of directories and/or archives.

See Class Path Management

reloadClasses
void reloadClasses( [ package name ] )
Reload the specified class, package name, or all classes if no name is given. e.g.

        reloadClasses();
        reloadClasses("mypackage.*");
        reloadClasses(".*")  // reload unpackaged classes
        reloadClasses("mypackage.MyClass") 

See Class Path Management

browseClass
void browseClass( String | Object | Class )
Open the class browser to view the specified class.

If the argument is a string it is considered to be a class name. If the argument is an object, the class of the object is used. If the arg is a class, the class is used.

classBrowser
void classBrowser()
Open the class browser.

desktop
void desktop()
Start the BeanShell GUI desktop.

error
void error( string )
Print the item as an error. If in the GUI console the text will show up in red, else it will be printed to stderr

getClassPath
URL [] getClassPath()
Get the current classpath including all user path, extended path, and the boostrap JAR file if possible.

javap
void javap( String | Object | Class )
print the public fields and methods of the specified class (output similar to the JDK javap command).

If the argument is a string it is considered to be a class name. If the argument is an object, the class of the object is used. If the arg is a class, the class is used.

setNameCompletion
void setNameCompletion( boolean )
Turn name completion in the GUI console on or off. Name competion is on by default. Explicitly setting it to true however can be used to prompt bsh to read the classpath and provide immediate feedback. (Otherwise this may happen behind the scenes the first time name completion is attempted). Setting it to false will disable name completion.

getClass
Class getClass( String )
This is equivalent to the standard Class.forName() method for class loading, however it takes advantage of the BeanShell class manager so that added classpath will be taken into account. You can also use Class.forName(), however if you have modified the classpath or reloaded classes from within your script the modifications will only appear if you use the getClass() command.


BeanShell Syntax

BeanShell is primarily a Java interpreter, so you probably already know most of what you need to start using and scripting in bsh. This section will describe specifically what portion of the Java langauge bsh interprets and how bsh extends it - loosens it up - to be more scripting language like.

Standard Java Syntax

At the prompt you can type normal Java statements and expressions and display the results. Statements and expressions are the kinds of things you normally find inside of a Java method - variable assignments, method calls, math expressions, for-loops, etc., etc.

Here are some examples:

    // Use a hashtable
    Hashtable h = new Hashtable();
    Date d = new Date();
    h.put("today", d);

    // Print the current clock value
    print( System.currentTimeMillis() );

    // Loop
    for (int i=0; i<5; i++)
        print(i);

    // Pop up an AWT frame with a button in it
    JButton b = new JButton("My Button");
    JFrame f = new JFrame("My Frame");
    f.getContentPane()add(b, "Center");
    f.pack();
    f.show();

Loose Java Syntax

In the examples above, all of our variables have declared types - e.g. 'JButton b'. Beanshell will enforce these types, as you will see if you later try to assign something other than a JButton to the variable 'b' (you will get an error message). However bsh also supports dynamically typed variables - that is, you can refer to variables without declaring them first and without specifying any type. In this case bsh will do type checking where appropriate at "runtime". So, for example, we could have left off the types in the above example and simply said something like:

    foo = new JButton("Another Button");

We are then free to assign 'foo' to another type of Java object later. Untyped bsh variables can also freely hold Java primitive values like int and boolean. Don't worry, bsh always knows the real types and only lets you use the values where appropriate. For primitive types this includes doing the correct numeric promotion that the real Java language would do when you use them in an expression.

Convenience Syntax

In bsh you may access JavaBean properties as if they were fields:

    b = new java.awt.Button();
    b.label = "my button";  // Equivalent to: b.setLabel("my button");
Of course if there is a field of the same name (e.g. label in the above example) then it would take precedence.

If you wish to avoid ambiguity Bsh provides an additional, uniform syntax for accessing Java Bean properties and Hashtable entries. You may use the "{}" curly brace construct with a String identifier as a qualifier on any variable of the appropriate type:

    b = new java.awt.Button();
    b{"label"} = "my button";  // Equivalent to: b.setLabel("my button");

    h = new Hashtable();
    h{"foo"} = "bar";          // Equivalent to: h.put("foo", "bar");

Importing classes and packages

In BeanShell as in Java, you can either refer to classes by their fully qualified names, or you can import one or more classes from a Java package.

    import mypackage.MyClass;
or
    import mypackage.*;

You may also automatically import the entire classpath using:

    import *;

See Class Path Management for details.

By default, several Java core and extension packages are imported for you. They are:


Methods

You can define define methods in bsh, just as they would appear in Java:

    int addTwoNumbers( int a, int b ) {
        return a + b;
    }

and use them immediately as you would any bsh command:

    sum = addTwoNumbers( 5, 7 );

Just as bsh variables may be dynamically typed, bsh methods may have dynamic argument and return types. We could, for example, have declared our method like so:

    add( a, b ) {
        return a + b;
    }

In this case, bsh would dynamically determine the types when the method is called and "do the right thing":

    foo = add(1, 2);
    print( foo ); // 3

    foo = add("Oh", " baby");
    print( foo ); // Oh baby

In the first case Java performed arithmetic addition on the integers 1 and 2. By the way, if we had passed in numbers of other types bsh would have performed the appropriate numeric promotion and returned the right type. In the second case bsh performed the usual string concatenation for String types and returned a String object.

Methods with unspecified return types may return any type of object (as in the previous example). Alternatively they may also simply issue a "return;" without a value, in which case the effective type of the method is "void" (no type). In either case, the return statement is optional. If the method does not perform an explicit "return" statement, the value of the last statement or expression in the method body becomes the return value (and must adhere to any declared return typing).

Note:

The namespaces for methods and variables are separate, so it is ok to have a method named "foo()" and variable named "foo" within the same scope.

Scope modifiers: this, super, global

Within a method, it is possible to explicitly qualify a variable or method reference with the values 'this', 'super', and 'global' to refer to, respectively, the current scope, the calling method's scope, or the global scope.

    a = 42;
    foo() {
        a = 97;
        print( a );
        print( this.a );
        print( super.a );
    }

    foo();  // prints 97, 97, 42

In the case above, 'global' and 'super' both refer to the top level scope.

We'll refer to instances of the special 'this', 'super', and 'global' references as "This" type references.


Scripted Objects

In bsh you can script objects as "method closures", similar to those in Perl5.0 and JavaScript.

As in most languages, executing bsh methods have their own "local" scope that holds argument variables and locally declared variables. For example, any variables that we might have declared within our add() method above would normally only be visible within the scope of and for the lifetime of a particular method invocation:

    foo() {
        bar = 42;
        print( bar );
    }   

    foo();  // prints 42
    print( bar ); // Error, var is undefined here 

However, in BeanShell you can "hang on" to this scope after exiting the method by returning the special value: "this". As in Java, "this" refers to the current object context. By saving the "this" reference, you can continue to refer to variables defined within the method, using the standard Java "." notation:

    foo() {
        bar = 42;
        return this;
    }

    obj = foo();
    print( obj.bar ); // 42

In the above, the value returned by the foo() method is effectively an instance of a "foo" object. In this case foo is really just acting like a structure, but bsh methods are also allowed to contain their own methods:

    foo() {
        bar() {
            ...
        }
    }

Method may define any number of local methods in this way, to an arbitrary depth.

Statements and expressions within a Bsh method can call their own "local" methods just like any other method. (Locally declared methods shadow outer-more methods like local variables shadow outer variables.) And we can invoke local methods of objects through an appropriate 'this' reference for that object:

    foo() {
        int a = 42;
        bar() {
            print("The bar is open!");
        }
        
        bar();
        return this;
    }

    obj = foo();     // prints "the bar is open!"
    print ( obj.a )  // 42
    obj.bar();       // prints "the bar is open!"

Within a nested method, locally declared variables at each level of scope shadow methods of the same name in outer-more scopes. Otherwise, variables are visible to an arbitrary depth of scope.


Scripted Event Handling

One of the most powerful features of Bsh is the ability to script event handlers and other interface types. To do this simply define the appropriate methods in your script or scripted object and use the corresponding 'this' reference. For example, we could could script an event handler for a button like so:

    button = new java.awt.Button("foo!");

    actionPerformed( event ) {
        print( event );
    }

    button.addActionListener( this );
    frame( button );  // show it

Now when ActionEvents are fired by the button, your actionPerformed() method will be invoked. The bsh 'this' reference implements the interface and directs them to the appropriately named method, if it exists.

But you don't have to define all of your interface methods globally. You can get callbacks in any bsh object scope. For example, the following method creates a button that displays a message when pushed:

    messageButton( message ) {
        b = new Button("Press Me");
        b.addActionListener( this );
        frame(b);
     
        actionPerformed( e ) {
            print( message );
        }
    }

    messageButton("Hey you!");
    messageButton("Another message...");

The above will create two buttons and each will display its own message when pushed. Each has a separate instance of the event handler object. Note too that we could return a 'this' reference from the handler method and use it in other contexts to register listeners...

Anonymous inner class style

More generally you can use the Java anonymous inner class style syntax to construct a scripted object implementing an interface:

    buttonHandler = new ActionListener() {
        actionPerformed( event ) { 
            print(event);
        }
    };

See also the dragText example.

Details

What happens if I don't implement all of the methods of an interface?

You can implement the special method invoke() which will be called when a method of the interface is not found. e.g.

    mouseHandler = new MouseListener() {
        mousePressed( event ) { 
            print("mouse button pressed");  
        }

        invoke( method, args ) { 
            print("Undefined method of MouseListener interface invoked:"
                + name +", with args: "+args
            );
        }
    };

What kind of events can bsh handle in this way?

When running under JDK1.3 or greater BeanShell can handle any kind of event (serve as any kind of event listener) because bsh scripts are capable of implementing arbitrary Java interfaces. However when running under JDK1.2 (or JDK1.1 + Swing) only the core AWT and Swing interfaces are available. In the pre-JDK1.3 case the bsh.This object implements these interfaces (along with Runnable, etc.) directly.

Scripting Threads

BeanShell 'This' type references also implement the standard java.lang.Runnable interface, so you can declare a "run()" method in your bsh objects and make it the target of a Thread.

    foo() {
        run() {
            // do work...
        }
        return this;
    }
    
    foo = foo();
    new Thread( foo ).start();

You can also use the bg() command to run an external script in a separate thread. example... FINISH


Special Variables and Values

See also BOGUSLINKthis, super, and global.

Special variables

Undefined variables

You can test to see if a variable is "undefined" with the value "void", e.g.:

    if ( foobar == void )
        // undefined

You can return a defined variable to the undefined state using the unset() command:

    a == void;  // true
    a=5;
    unset("a"); // note the quotes
    a == void;  // true

Special features of 'This' type references

'This' type references have four special "magic" members:

These are mainly for internal use by bsh commands. Note that there are certain special situations in which the ".interpreter" reference may not be available, such as in AWT event handlers (see the next section).


Class Path Management

BeanShell is capable of some very fine grained and sophisticated class reloading and modifications to the class path. BeanShell can even map the entire class path to allow for automatic importing of classes.

Changing the Class Path

addClassPath( URL | path )

Add the specified directory or archive to the classpath. Archives may be located by URL, allowing them to be loaded over the network.

Examples:

    addClassPath( "/home/pat/java/classes" );
    addClassPath( "/home/pat/java/mystuff.jar" );
    addClassPath( new URL("http://myserver/~pat/somebeans.jar") );
Note that if you add class path that overlaps with the existing Java user classpath then the new path will effectively reload the classes in that area.

setClassPath( URL [] )

Change the entire classpath to the specified array of directories and/or archives.

This command has some important side effects. It effectively causes all classes to be reloaded (including any in the Java user class path at startup). Please see "Class Reloading" below for further details.

Note: setClassPath() cannot currently be used to make the classpath smaller than the Java user path at startup.

Auto-Importing from the Classpath

As an alternative to explicitly importing class names you may use the following statement to trigger automatic importing:
    import *;
There may be a significant delay while the class path is mapped. This is why auto-importing is not turned on by default. When run interactively, Bsh will report the areas that it is mapping.

It is only necessary to issue the auto-import command once. Thereafter changes in the classpath via the addClassPath() and setClassPath() commands will remap as necessary.

Note: As of BeanShell 1.1alpha new class files added to the classpath (from outside of BeanShell) after mapping will not be seen in imports.

Reloading Classes

BeanShell provides an easy to use mechanism for reloading classes from the classpath. It is possible in BeanShell to reload arbitary subsets of classes down to a single class file. However There are subtle issues to be understood with respect to what it means to reload a class in the Java environment. Please see the discussion of class loading detail below. But in a nutshell, it is important that classes which work together be reloaded together at the same time, unless you know what you are doing.

reloadClasses( [ package name ] )

The most course level of class reloading is accomplished by issuing the reloadClasses() command with no arguments.

    reloadClasses();
This will effectively reload all classes in the current classpath (including any changes you have made through addClassPath()).

Note: that reloading the full path is actually a light weight operation that simply replaces the class loader - normal style class loading is done as classes are subsequently referenced.

Be aware that any object instances which you have previously created may not function with new objects created by the new class loader. Please see the discussion of class loading details below.

You can also reload all of the classes in a specified package:

    reloadClasses("mypackage.*");
This will reload only the classes in the specified package. The classes will be reloaded even if they are located in different places in the claspath (e.g. if you have some of the package in one directory and some in another).

As a special case for reloading unpackaged classes the following commands are equivalent:

    reloadClasses(".*") 
    reloadClasses("")

You can also reload just an individual class file:

    reloadClasses("mypackage.MyClass") 
Note: As of alpha1.1 classes contained in archives (jar files) cannot be reloaded. i.e. jar files cannot be swapped.

Mapping the path

Unlike the reloadClases() command which reloads the entire class path, when you issue a command to reload a package or individual class name BeanShell must map some portions of the classpath to find the location of those class files. This operation can be time consuming, but it is only done once. If running in interactive mode feedback will be given on the progress of the mapping.

Class Loading in Java

A fundamental Java security proposition is that classes may only be loaded through a class loader once and that classes loaded through different class loaders live in different name spaces. By different name spaces I mean that they are not considered to be of the same type, even if they came from the very same class file.

You can think of this in the following way: When you load classes through a new class loader imagine that every class name is prefixed with the identifier "FromClassLoaderXXX" and that all internal references to other classes loaded through that class loader are similarly rewritten. Now if you attempt to pass a reference to a class instance loaded through another class loader to one of your newly loaded objects, it will not recognize it as the same type of class.

BeanShell works with objects dynamically through the reflection API, so your scripts will not have a problem recognizing reloaded class objects. However any objects which have you already created might not like them. More, etc...

Class Loading in BeanShell

The following is a discussion of the BeanShell class loader architecture, which allows both course class path extension and fine grained individual class reloading...

Thriftiness - Abiding by the BeanShell thriftiness proposition: no class loading code is exercised unless directed by a command. BeanShell begins with no class loader and only adds class loading in layers as necessay to achieve desired effects.

The following diagram illustrates the two layer class loading scheme:

A "base" class loader is used to handle course changes to the classpath including added path. Unless directed by setClassPath() the base loader will only add path and will not cover existing Java user class path. This prevents unecessary class space changes for the existing classes.

Packages of classes and individual classes are mapped in sets by class loaders capable of handling discrete files. A mapping of reloaded classes is maintained. The discrete file class loaders will also use this mapping to resolve names outside there space, so when any individual class is reloaded it will see all previously reloaded classes as well.

The BshClassManager knows about all class loader changes and broadcasts notification of changes to registered listeners. BeanShell namespaces use this mechanism to dereference cached type information, however they do not remove existing object instances.

Type caching is extremely important to BeanShell performance. So changing the classloader, which necessitates clearing all type caches, should be considered an expensive operation.


BeanShell Desktop

The BeanShell Desktop is a simple GUI environment that provides multiple bsh shell windows (MDI), a simple text editor, and a simple class browser. The desktop is mostly implemented by bsh scripts.

Shell Windows

The bsh console windows provide simple command line editing, history, cut & paste, and variable and class name completion.

Editor Windows

The Class Browser

Using BeanShell in Your Application

    import bsh.Interpreter;
    ...

    Interpreter i = new Interpreter();
    i.set("foo", 5);
    i.eval("bar = foo*10");
    System.out.println("bar = "+i.get("bar") );

Note: The trailing ";" semi-colon in the string containing the bsh statement to be evaluated is optional in this mode (bsh simply always adds one).

Embedding in Server Mode is the same, coming...
More coming...

Server Mode

Note: this document is out of date. Check back soon for an update with 1.0beta

Server mode lets you access a bsh session inside of a remote VM. You can literally telnet into the application and type commands at the bsh shell prompt. Or even better you can use your a web browser to bring up a remote console.

After starting the bsh server you can connect your web browser to the specified port. Bsh will act as an httpd and send over a remote console applet. You will get a bsh session that looks like the regular console applet, but it will be running remotely in the originating VM. You can open as many sessions into that VM as you like in this way...

Special Notes: In server mode bsh actually runs *two* services: a (minimal) httpd at the port you specify and a session server at your port + 1. You can actually connect to the session server with telnet, if you want to try it out...(you have to type ";"s manually at the end of every line).

More coming...


Executable scripts under Unix

#!/usr/java/bin/java bsh.Interpreter 

print("foo");

#!/bin/sh
#! The following hack allows java to reside anywhere in the PATH.
//bin/sh -c "exec java bsh.Interpreter $0 $*"; exit

print("foo");

The bsh Package

Adding Commands to BeanShell

Adding to the set of "prefab" commands supplied with bsh can be about as easy as writing any other bsh methods. You simply have to place your bsh scripts in a bsh/commands/ directory in the classpath (or inside the JAR file). bsh commands directly in Java as .class files which are dynamically loaded when needed. The dir() commands are an example of a bsh command that is implemented in Java. (I did this originaly for speed).

More info coming


Learning More...


Learning More...

Almost all of the built-in bsh commands are simply bsh scripts stored in the bsh jar file. A good way to familiarize yourself with bsh is to take a look at those commands - simply unpack bsh/commands/*.bsh from the JAR file.


Credit and Acknowledgments

Many people have contributed substantially to BeanShell, including many who have contributed ideas, which can be just as important as code.

I will try to start listing as many as possible here, but I will forget, so please feel free to remind me (don't be modest, everyone who contributes deserves to be mentioned).

New Code and Utilities

Bug Fixes

Me

Finally, I will put in a plug for myself.

Pat Niemeyer (pat@pat.net)

If you like BeanShell check out my book: Exploring Java, O'Reilly & Associates, 2nd edition. Third edition coming soon!