Java Sketchbook: Getting Started With Scripting Blog

    {cs.r.title}




              
                                       

    Contents
    Why Scripting?
    Why JavaScript?
    Getting Started
    A Simple Example
    A Useful Example
    The Java Side
    The Scripting Engine
    Writing Some JavaScript
    Playing with the Numbers
    Conclusion
    Resources

    What do you think of when I say "scripting?" If you are a programmer, you might think of using Bash or Perl to automate command-line programs. If you are a designer, you may think of DHTML and JavaScript for building web pages. Either way, you probably don't think of graphical desktop applications, and you certainly don't think of Java. In years past, very few desktop applications had any support for internal scripting. Word had VBScript and Emacs had Lisp macros, but that was pretty much it. Today as Java developers, however, we have a wide variety of languages available to be easily embedded into our applications. By the end of this article, you will have a fully running Java program with an embedded JavaScript interpreter, and know enough to add scripting to your own applications.

    Why Scripting?

    The key to scripting languages is that they are designed for ad hoc programming: building small programs, or parts of programs, with quick turnaround. Common scripting language traits like loose typing, interpreting instead of compiling, and easy binding to other languages are all in support of ad hoc programming.

    Scripting languages are most commonly used in two scenarios. One is to tie different components written in different languages together. These components could be modules like in Perl and TCL, or entire programs (like the many Unix command-line utilities) tied together with shell scripts. The second use is to extend a single program in a controlled manner. This second use is more commonly done by a person with lesser programming skills or without access to the source of the underlying application, and it is this scenario that we will discuss.

    By creating a scripting layer to your application, you can allow semi-skilled developers to extend your program in ways that you don't have the time or expertise to do. Providing scripting support to your users isn't the easiest thing in the world; after all, you are potentially exposing the internals of your program to your users. But doing so can give your users great flexibility and power. Scripting will let you provide a better product for your customers and make them happy, and in the end, happier customers can only be a good thing.

    Why JavaScript?

    If you are a regular reader of java.net, you may have noticed arecent poll asking readers what scripting language they would like to see embedded in Java. The most popular was JavaScript, closely followed by Python/Jython. I chose JavaScript because it fits our task--to extend a single program cleanly--very well. There are more people writing in JavaScript than probably any other language (except maybe Excel functions). Though Perl is more commonly used by professional programmers, there are many more non-professional programmers doing simple but useful things with JavaScript.

    Don't believe me? Think of all of the web sites that use JavaScript. Not just the professional sites that use prefab libraries, but also the one-off pages done by complete novices. A little validation here, a bit of animation there; it's all done in JavaScript. Now think of Konfabulator and Apple's upcoming Dashboard. These are applications designed from the ground up to allow novice programmers to build simple but attractive systems. And their language of choice is JavaScript. Simple snippets of JavaScript are used everywhere, and by supporting JavaScript we can instantly take advantage of that shared knowledge among our users. It may not be the choice of skilled programmers (after all, I wouldn't use JavaScript to parse my web server logs or do automated backups), but for our target audience, non-professional programmers, it's the best thing out there.

    Getting Started

    There is pretty much only one JavaScript library for Java: Mozilla's Rhino. Rhino was spun off from a canceled project to rewrite Netscape Navigator entirely in Java. As their web page says: "When Netscape stopped work on 'Javagator', as it was called, somehow Rhino escaped the axe (rumor had it that the executives 'forgot' it existed).".

    Though there is only one library, there are two ways to use it. You can write directly to the Rhino API or you can use the Bean Scripting Framework, also known as BSF. BSF is a generic framework built by IBM's AlphaWorks team to provide support for scripting Java using any language. This means that if you code against BSF, then you can swap in any scripting language you want with no code modifications. Currently BSF has built-in support for JavaScript, Python, Tcl, NetRexx, and XSLT, in addition to support for Java, Ruby, JudoScript, Groovy, and ObjectScript via third party libraries. BSF was originally built as a research project at IBM, but was donated to the Apache Jakarta project in 2002, where it resides today. We will be using BSF for this project so that you can swap in another language after the program is built.

    To get started with scripting, you will first need to download the latest versions of BSF and Rhino. BSF is available here. The latest version of Rhino does not work with the latest release version of BSF (v2.3.0rc1), so you should download Rhino 1.5R3 (or check out a newer version of BSF from CVS). Drop the .jars from Rhino and BSF (bsf.jar and js.jar) into your classpath, and you are good to go.

    A Simple Example

    Let's start with a simple example. Create a new class calledBSFJavascriptTest that looks like this:

    import org.apache.bsf.*; public class BSFJavascriptTest { public static void main(String[] args) throws Exception { BSFManager mgr = new BSFManager(); mgr.registerScriptingEngine("javascript", "org.apache.bsf.engines.javascript.JavaScriptEngine", null); BSFEngine engine = mgr.loadScriptingEngine("javascript"); Object return_value = engine.eval("asdf",10,10,"4+5"); System.out.println("got: " + return_value); } }
    

    As you can see, it's ridiculously simple. The first line ofmain creates a new BSFManager. The second line uses the BSFManager to register a specific scripting language implementation, much as you would specify a JDBC driver. In this case, we are loading the JavaScript framework and naming it "javascript". The next line creates aBSFEngine, the object that actually interprets the scripts. Finally, we evaluate our expression "4+5" and print out the return value: '9'. The other three arguments to eval: "asdf",'10', and '10', are to specify the name of the code and the line and column numbers in the source code, which are used for printing out errors. Since we are loading code from a script file, we don't really care about these values. Just put in whatever you want. If you run this program you will get the value : 9.

    A Useful Example

    Let's try applying this to something more useful. When planning this article, I wanted to build an application that was fun and would let a novice programmer do something useful with small amounts of scripting. Thinking back to my days with graphing calculators, I came up with the idea of a particle simulator.

    A particle simulator is a piece of software used to simulate a dynamic system, like a volcano or birds in flight. Originally invented for scientific use, they can also be amusing toys for playing with algorithms and creating pretty pictures. Though they vary greatly, all particle simulators work basically like this:

    1. Create a particle, or a set of particles, with certain properties (position on screen, color, velocity).
    2. Update each particle as time moves forward.
    3. Draw each particle.
    4. Loop.

    Very simple rules (like particle.x = Sin(time)) can create very complex and beautiful behavior, not to mention be entertaining for hours.

    Simulators are usually written in two parts. One is the infrastructure that takes care of looping, drawing, and managing the objects. The other part consists of the actual rules that control how the particles move. These rules are usually simple, but require lots of tweaking, making them a perfect place to use scripting. In our program, the Java code handles all of the grunt work and leaves just the particle rules to JavaScript.

    The Java Side

    To combine the scripting and Java sides, I have created a program called SimEditor, which is shown in Figure 1.

    Figure 1: SimEditor running
    Figure 1. SimEditor running

    SimEditor displays the running simulation and includes a few controls on the left. On the right are three text areas for writing scripts: one for creating new particles, one for updating the particles, and one for drawing the particles. A more sophisticated version of SimEditor would provide a real code editor with syntax highlighting and code completion, but to keep this simple, I've just used JTextAreas in scroll panes. The underlying Java code manages the drawing canvas, starting and stopping the threads, and loading code samples. To communicate with the scripting side, there is a SimFrame interface with three methods for creating, updating, an drawing particles. It is this interface that will be our gateway to the scripting side.

    The Scripting Engine

    As with the simple example earlier, our code will create aBSFManager and a scripting engine, and then execute the scripts at the right time. Here is the basic outline:

    package org.joshy.oreilly.scripting.sim; import org.apache.bsf.util.*; import java.awt.*; import org.apache.bsf.*; public class JavascriptFrame implements SimFrame { public SimEditor editor; public BSFEngine engine; public BSFManager mgr; public JavascriptFrame(SimEditor editor) { this.editor = editor; try { mgr = new BSFManager(); mgr.registerScriptingEngine("javascript", "org.apache.bsf.engines.javascript.JavaScriptEngine", null); engine = mgr.loadScriptingEngine("javascript"); } catch (Exception ex) { ex.printStackTrace(); } } public void initParticle(Particle part, double clock) { try { String text = this.editor.init.getText(); engine.eval("Executing",1,1,text); } catch (Exception ex) { ex.printStackTrace(); } } public void updateParticle(Particle part, double clock) { // same as above, but from the update code editor ... } public void drawParticle(Particle part, double clock, Graphics2D g) { // same as above, but from the draw code editor ... } }
    

    The JavascriptFrame implementsSimFrame and allocates the scripting manager and engine in the constructor, just like our simple example earlier. Whenever initParticle is called by the simulator, theJavascriptFrame will execute the contents of theinit text area, which contains the code that the user typed into the top editor of the application. It'll do the same forupdateParticle and drawParticle, except using the code from the other two text areas.

    What we have so far will work. It will execute the JavaScript and let the user make calculations, but since the script has no access to Java objects (or anything else), it's pretty useless. We need to move the arguments of each function above (Particle, clock, andGraphics2D) into the JavaScript environment so that the scripts have something to work with. BSF enables this kind of integration with the ObjectRegistry or directly from the engine. The key is to add each object you want to be accessible using the declareBean method. In the code below, you can see the modified JavascriptFrame constructor.

    public JavascriptFrame(SimEditor editor) { this.editor = editor; try { mgr = new BSFManager(); mgr.registerScriptingEngine("javascript", "org.apache.bsf.engines.javascript.JavaScriptEngine",null); engine = mgr.loadScriptingEngine("javascript"); ctx = new Context(); mgr.declareBean("ctx",ctx, Context.class); } catch (Exception ex) { p(ex.toString()); ex.printStackTrace(); } } class Context { public double clock; public Particle particle; public Graphics2D graphics; public Color color; public Map map = new HashMap(); }
    

    I have also created a class called Context, which has some public variables for accessing the current clock, particle, and drawing graphics. The map lets the script author add custom variables at runtime. The JavascriptFrameconstructor creates a new Context variable and then adds it to the script environment with the declareBeanmethod. The first argument is the name for the object within the environment (ctx), the second is the object itself, and the third is the type the object should represent in the scripting environment. Now, the script writer could write something like this:

    ctx.graphics.drawLine(0,0,50,50);
    

    to draw a diagonal line.

    Now we must update the three particle functions to set the members of the context variable with their current values. This is the new drawParticle method, which sets the current particle, clock, and graphics object.

    public void drawParticle(Particle part, double clock, Graphics2D g) { try { String text = this.editor.draw.getText(); ctx.particle = part; ctx.clock = clock; ctx.graphics = g; engine.exec("Executing",1,1,text); } catch (Exception ex) { p(ex.toString()); ex.printStackTrace(); } }
    

    Writing Some JavaScript

    Now that our framework is up and running, let's take it for a spin. If you have Java Web Start installed on your computer, you can launch SimEditor here. Select Simple Circle under the Demos menu item and you will see the three editors fill with code:

    init()
    
     ctx.particle.startx = 100; ctx.particle.starty = 100; ctx.particle.startclock = ctx.clock;
    
    update()
    
     // reduce spacing by 1/4 // slow down time by 1/3 var t = ctx.particle.startclock+ctx.clock; t = t/5; var rad = 50; // radius var cx = 100; // center x var cy = 100; // center y // parametric equation of a circle ctx.particle.currentx =Math.cos(t)*rad + cx; ctx.particle.currenty = Math.sin(t)*rad + cy;
    
    draw();
    
     ctx.graphics.fillOval( ctx.particle.currentx, ctx.particle.currenty, 10,10);
    

    This set draws a circle using a parametric equation. Theinit method sets up each particle, including saving the starting clock tick. The update method creates a slowed-down time (to one-fifth), then calculates the currentx, y location based on the sine and cosine of the clock tick. Finally the draw method draws the particle as a circle on screen. (Figure 2)

    Figure 2. SimEditor running circles.xml
    Figure 2. SimEditor running circles.xml

    Playing with the Numbers

    Hit the play button and watch the particles move. The really cool thing about doing this project with a scripting language is that we can see immediate results. In the update window, change the50 in the first line to 20. Since our code reloads based on the current contents of the window, the simulation will immediately change on each keystroke. When you type in the 20, the circle will immediately become smaller. This is the power of interpreted code. Now divide time by2 for the x coordinate by changing the line:

    ctx.particle.currentx =Math.cos(t)*rad + cx;
    

    to

    ctx.particle.currentx =Math.cos(t/2)*rad + cx;
    

    The circle quickly changes to a figure eight. Interpreting the code each time through the loop allows you to continually tweak the algorithm with immediate feedback. I have included three other demonstration algorithms that show off complicated variations of the circle equation, a gravity simulation that ejects colored circles like a volcano, and a rainfall of spinning lines.

    Conclusion

    Integrating scripting languages with Java is very easy using the Bean Shell Framework. I hope you have seen how combining a simple language with immediate feedback provides a powerful and flexible user interface for a variety of applications. I also hope you enjoy playing with the simulator and post your own algorithms to the forum.

    Resources

      
    http://today.java.net/im/a.gif