The Scripting Engine
As with the simple example earlier, our code will create a BSFManager 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 implements SimFrame and
allocates the scripting manager and engine in the constructor, just like
our simple example earlier. Whenever initParticle is called by
the simulator, the JavascriptFrame will execute the contents
of the init text area, which contains the code that the user typed
into the top editor of the application. It'll do the same for
updateParticle 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, and Graphics2D) 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 JavascriptFrame constructor creates a new
Context variable and then adds it to the script environment
with the declareBean method. 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. The
init 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 current x, 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
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 the 50 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 by 2 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
Josh Marinacci first tried Java in 1995 at the request of his favorite TA and has never looked back.