The Artima Developer Community
Leading-Edge Java | Discuss | Print | Email | First Page | Previous | Next
This article is sponsored by the Java Community Process.

Leading-Edge Java
Scripting Java: The BeanShell JSR
A conversation with Patrick Niemeyer
by Frank Sommers
June 3, 2005

Page 1 of 3  >>

Advertisement

Summary
The BeanShell Scripting Language recently became a JCP JSR (274), a first step on the road to an official Java standard. In this article, Artima interviews Pat Niemeyer, BeanShell's creator and lead of the JSR's expert group, about the role of scripting languages in Java, BeanShell's dynamic programming features, how BeanShell compares with Groovy and other Java scripting efforts, and how the JCP helps or hinders language design.

The BeanShell scripting language has gained a large following since Patrick Niemeyer first started to experiment with scriptable Java[1]. Today, BeanShell is a thriving open-source project, and BeanShell scripting has been incorporated into dozens of commercial and open-source projects, such as BEA's WebLogic, the NetBeans IDE, Sun's Java Studio, OpenOffice, JEdit, and many others. The Java Community Process recently accepted BeanShell as a JSR, the first step for BeanShell to become an official Java standard.

Artima's Frank Sommers interviewed Patrick soon after the JCP vote. Patrick became involved with Oak (Java's predecessor) while working at Southwestern Bell, and gained fame in the Java community both as author of the best-selling O'Reilly book Learning Java, and as creator of BeanShell.

Dynamic scripting with BeanShell

Frank Sommers: What is BeanShell, and what does it offer to a developer?

Pat Niemeyer: BeanShell is a scripting language for Java. At a basic level, BeanShell is a Java source interpreter. The BeanShell syntax is based on, and is a superset of, the Java syntax. You can feed it standard Java code, the kind that you'd give to the Java compiler, and BeanShell will dynamically interpret that code for you at runtime.

More important, however, is that BeanShell extends the Java language syntax into the realm of scripting languages in a natural way, allowing you to "loosen" or "scale" Java from a static to a dynamic programming style. That, in turn, allows you to use Java in all sorts of new ways. BeanShell scripts can work with "live" Java objects and APIs just as any other Java code would, as part of your application, or with a stand-alone interpreter.

By a dynamic programming style, I mean that BeanShell offers features of a traditional scripting language: You can use "loosely" typed variables, without declaring them first. You can write simple, unstructured scripts consisting of just arbitrary statements and expressions, or toss in a few methods if you like. You can supply scripted or compiled "commands" which act just like Java methods, but are loaded on demand when called by the script. You can dynamically change the classpath and reload classes in BeanShell as well as create new classes on the fly. These are examples of using Java language constructs in "scripty" ways.

Beyond that, BeanShell offers additional, highly dynamic language features, such as the ability to implement a "meta-method" to handle method invocations, variables, methods, and namespaces that can be inspected programmatically, and the ability to manipulate the call stack and perform evaluations in powerful ways.

Those latter capabilities are not there so much for day-to-day use, as they are to enable powerful BeanShell commands. BeanShell commands are methods that are a loaded in dynamically. You can mix and match all of this loose syntax with the standard, fully typed Java syntax and classes. That makes cutting and pasting or migrating code between scripts and Java very easy.

Rapid prototyping and dynamic program configuration

Frank Sommers: What are some useful ways BeanShell can help developers in their daily work?

Pat Niemeyer: BeanShell started as a playground for experimenting with Java APIs and "live" objects. There is something immensely satisfying about typing new JButton(), and seeing the thing pop up right on the screen, then having a way to tweak its methods and watching what it does. I still use BeanShell that way for rapid prototyping.

When I try to remember how the Java regular expression API works, for instance, I start typing in BeanShell. When I'm curious what fonts are available in my current environment, I type a line in BeanShell. There is that prototyping aspect to it, and also there is a role for BeanShell in education. I include BeanShell with my book, Learning Java (O'Reilly & Associates), because BeanShell is an excellent companion for learning or teaching the Java language[2].

In addition, BeanShell as a scripting and extension language allows users of your Java application to customize application behavior with their own scripts. That's similar to how Emacs is extended via Lisp, for instance, with the difference that BeanShell and Java share a common syntax. Often, it's not reasonable to require a regular user of an application to understand Java and have a full blown development environment just to supply a simple macro or "if-then-else"-type customization for that application.

James Gosling famously noted once that all configuration files eventually become programming languages. BeanShell's simplified syntax is perfect for user scripts, expressions, rules engines, and the like. BeanShell scripts can be used as a general replacement for Java property files, allowing you to not only supply simple values, but to perform more complex logic, if needed. For instance, you can invoke or supply methods, construct objects, and perform procedural setup, all with the familiar Java syntax, but using that syntax in a scripting way.

BeanShell can also do fantastic things for dynamic deployment and debugging. With a couple of lines of code you can create a server from your application, access that server with a web browser, and get a shell inside your running application. You can poke at your app's internals live, even fix things in some cases that way. There are an endless number of things you can do with a dynamic language married to Java.

Frank Sommers: Can you give us a taste of a few dynamic BeanShell features?

Pat Niemeyer: Let me mention just three examples here, but there are many others. First, BeanShell has a handy "invoke" meta-method:

invoke( name, args ) { ... } 

// Or, equivalently, with all the type information... 
void invoke( String name, Object [] args ) { ... } 

Implementing a method with those signatures in your script allows you to handle method invocations for non-existent methods:

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

justmadeup( 1, 2, 3 ); // justmadeup invoked! 

That's very useful if you don't feel like implementing all methods of a class or interface, or if you wish to implement your own command loading scheme for your scripts. You can even use this pattern to implement mappings or variable argument list schemes for BeanShell methods, by looking up other methods and dispatching invocations to them.

Next, there is the magic "this.caller" reference that lets you refer to the namespace - the scope - of the caller of your method, to do whatever you'd like on their behalf.

callMe() { 
    this.caller.foo = 42; 
} 

callMe(); 
print( foo ); // 42 

That may not seem all that magical until you look at what's going on. The script that you invoked has access to the environment of the line of code that called it, enabling you to do all sorts of things on the caller's behalf. For instance, you could set or get variables, and define or look up methods. That capability is used in many standard BeanShell commands to do things such as taking into account the classes imported in the caller's namespace or, more generally, to evaluate code and leave the side-effects in the caller's scope. The general eval() method works in this way.

Another example of a dynamic BeanShell command takes advantage of the above feature. The importObject() command imports the methods and variables of a Java object into your scope. That's a lot like the Java 5 static import feature, but it works relative to a "live" object. For example:

map = new HashMap(); 
map.put("foo", "bar"); 

importObject( map ); 

print( get("foo") ); // bar 

This code snippet "mixes in" the methods and variables of the map instance into your current scope, and you can then use those methods and variables as if you had scripted them yourself, or if they were built-in commands. That feature can be very convenient when you want to script part of your application, as it allows your script to "step into" the scope of one of your objects.

These are just a few interesting things to give your BeanShell scripts more leverage and help you keep them both simple and Java-like. I also think it's interesting that the features I just highlighted sort of fell naturally out of the design, and didn't require any special effort to add to the language.

Page 1 of 3  >>

Leading-Edge Java | Discuss | Print | Email | First Page | Previous | Next

This article is sponsored by the Java Community Process.

Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2014 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us