CONTENTS |
In this book, we (in this case, I, Pat) have avoided talking about many third-party tools that aren't part of the standard SDK. I'm going to make an exception here to mention 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 and expressions—on the command line and see the results immediately. 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 good companion to have along on your journey through Java. BeanShell is an open source software project, so the source code is included on the CD-ROM that accompanies this book (view CD content online at http://examples.oreilly.com/learnjava2/CD-ROM/ ). And you can always find the latest updates and more information at its official home: http://www.beanshell.org. In recent years BeanShell has become fairly popular. It is included with Emacs as part of the Java Development Environment (the JDE), with the NetBeans and Forte IDEs, and with BEA's WebLogic application server. I hope you find it both useful and fun!
All you need to run BeanShell is the Java runtime system (Version 1.1 or greater) and the bsh JAR file. Under Windows you can launch a graphical desktop for BeanShell by simply double-clicking the JAR file icon. More generally, you can add the JAR to your classpath:
Unix: export CLASSPATH=$CLASSPATH:bsh.jar Windows: set classpath %classpath%;bsh.jar
Or just drop the JAR file in the jre/lib/ext directory of your Java installation. 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 the NetBeans and Forte 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.
At the prompt, 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 type these exactly as they would appear in Java. You also have the option of working with "loosely typed" variables and arguments. 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 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 );
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.*;
BeanShell can even automatically import all classes in your classpath, using the following special declaration:
import *;
But this can take quite some time if there are a lot of directories in your path.
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 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:
Reads a bsh script into this interpreter, or runs it in a new interpreter
Displays an AWT or Swing component in a frame
Loads or saves serializable objects (such as JavaBeans)
Unix-like shell commands
Runs a native application
Modifies the classpath or reload classes
See the BeanShell user's manual for a full list of commands.
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 take the place of scripted objects. 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.
One of the most powerful features of BeanShell is that you can "script" any interface type (provided you are running Java 1.3 or greater). BeanShell-scripted objects can automatically implement any required interface type. All you have 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 okay 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.
Within BeanShell you can add to your classpath and even reload classes:
addClassPath("mystuff.jar"); addClassPath(http://examples.oreilly.com/learnjava/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.
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 the GNU Library General Public License and the Sun Public License. You can learn more by checking out the full user's manual and FAQ on the web site. If you have ideas, bug fixes, or improvements, please consider joining the developer's mailing list.
As a final caveat, I should say that you do get what you pay for, and BeanShell is still somewhat experimental. So you will certainly find bugs. Please feel free to send feedback, using the book's web page, http://www.oreilly.com/catalog/learnjava2. Enjoy!
CONTENTS |