Bean Markup Language, Part 2

Create event-driven applications with BML

1 2 3 Page 2
Page 2 of 3

Line 188 finds the System object; then line 189 finds that object's out field, giving that field the name OUT. Line 190 calls the println() method of the object called OUT, passing to it the result of calling getValue() on the TextColorEditor. Note that in line 188, the string class:java.lang.System indicates that the object you're looking up is a class object, not an object instance. This is a totally unnecessary addition to the application, but it does demonstrate how to execute a class method.

By the way, you can also look up static fields with a similar syntax; for example:

which looks up the constant BOLD from java.awt.Font.

The standard BML distribution won't run this script correctly, because standard BML doesn't know how to typecast a java.lang.Object (returned by getValue()) to a java.awt.Color (specified by the <cast> tag). Try running just the sample script with just the standard BML distribution. Changing a ColorChooser's value will result in a thrown exception, with a complaint that BML doesn't know how to perform the cast. Fortunately, you can fix that by writing a custom type converter that plugs in to the BML runtime.

It takes all types

Sometimes, as in the preceding example, you want to be able to use the <cast> tag between two types that BML doesn't know how to convert. BML lets you extend the types of casts that <cast> can handle by defining a custom TypeConverter. In addition to the object registry (which handles mapping id strings to objects for lookup), there is also a type-converter registry. When cast is going to typecast an object of class A to another object of class B, it asks the type-converter registry for an object that can do the job. The object returned by the registry implements the interface com.ibm.bml.TypeConverter, which has two methods:

  1. Object convert(Class from, Class to, Object obj): converts an object of class from to class to, and returns the result as an Object. The BML player uses this method to convert between types at runtime.

  2. String getCodeGenString(): returns a string that contains all the Java code (except the method name) for a method that would do what convert() does if it were included inline in a Java program. This lets the compiler generate code that performs the typecast.

To write your own type converter, simply implement convert() and getCodeGenString() and register an instance of your class with BML's TypeConverterRegistry. The code for a class that converts an Object (that you know to be a Color) into a Color appears in ObjectColorConvertor.java.

At this moment, you might be thinking, "Hey, wait a minute! I thought we were doing codeless programming here! Now you have to write code! This BML stuff isn't living up to its billing!" Notice the kind of code you're writing, though. This type-conversion code isn't specific to a particular BML script. You only have to write the type-conversion code for a particular class (or JavaBean) once. From then on, those conversions will work in all of your BML scripts. TypeConverter classes actually extend BML's capabilities. When you write a TypeConverter, you're extending what the BML tool can do, not writing code that's specific to one script. And in fact, if you compile your BML application with the BML compiler, you don't ship the TypeConverter class with the resulting code, because the compiler uses TypeConverter.getCodeGenString() to create code in the compiler output that does the typecast.

The BML system also lets you add functionality to the adder registry and the event-adapter registry, which extend the <add> and <event-binding> tags in much the same way as you've just extended the type-converter registry. See the BML reference manual that comes with BML for details.

Sometimes, when wiring beans together, the beans' arguments don't quite fit with one another. BML can't, for example, express if-then-else concepts; for that, you need a scripting language. The next section explains how to use JavaScript to script JavaBeans within a BML file.

Creating scripts with other scripting languages

The ColorFadeEditor is incomplete as a customizer, since it doesn't let you set all the bean's properties. The textDirection property (which should be called messageAlignment, but isn't) controls the message text alignment in the fade bar. It can take one of the three values: LEFT, RIGHT, and CENTER. The ColorFadeBar class, upon which the ColorFadeBean is based, has five constants that indicate direction:

    public static final int LEFT   = 0;
    public static final int RIGHT  = 1;
    public static final int UP     = 2;
    public static final int DOWN   = 3;
    public static final int CENTER = 4;

I decided to add a java.awt.Choice item to the editor to control the textDirection, but ran into a problem. The strings in the Choice element are Left, Right, and Center, but in BML, there's no way to indicate that these strings correspond to the integers 0, 1, and 4, respectively. I couldn't use Choice.getSelectedIndex(), because that wouldn't work when the number I wanted was 4. BML simply wasn't up to the task of performing this mapping.

Fortunately, you can use a BML <script> tag to define a script in a language other than BML. Two other scripting languages are supported: JavaScript and NetRexx. I don't know beans about NetRexx, so my example here is in JavaScript. You can read the sample source in the file ColorFadeEditorWithJS.bml.

Figure 3. The ColorFadeEditor modified to change text alignment

The only major difference between this second sample file and the first is the addition of the Choice item and its associated label. Adding the item and initializing its strings is easy enough, as shown in Listing 5:

The <bean> tag creates the bean, and the <call-method> tags add the three strings to the Choice item. The event-binding that updates the textDirection property of the fade bar in Listing 6 is also very short, but only because I hid the code.

Line 170 in Listing 6 binds the Choice item's item event to line 171's single-line script. Line 171 defines a <script> with the additional attribute language="javascript", which indicates that everything inside the script tag should be interpreted as JavaScript. The only thing the event-binding does is call the JavaScript function setTextDirection(), which is defined in Listing 7.

The entire script (lines 189 through 202) is enclosed in a <![CDATA[]]> section, which is an XML feature that protects everything within the script from interpretation by the XML processor. Everything in an XML CDATA section is treated as a long string by XML. This JavaScript function looks at the string selected in the Choice item and sets the fade bean's textDirection property with the appropriate constant. The calls to bsf.lookupBean() in lines 192 and 193 are special additions to JavaScript that let the JavaScript access the beans in the BML script as JavaScript objects and call methods on them.

All the JavaScript in a BML file shares one context, so, just as in Web pages, the code for the scripts may be defined anywhere and then used whenever it is needed (see Listing 6, line 171).

Listing 7 is essentially an adapter, written in JavaScript, that translates the state of the Choice item to the fade bean's textDirection property.

There is one small remaining task to do: ensuring that the Choice item reflects the state of the fade bean's textDirection the first time the window opens. The code in Listing 8 accomplishes that:

That code simply asks for the FadeBar's text direction as an integer, uses it to index into an array with appropriate strings, and then selects the corresponding string.

Note that this script isn't inside any event-binding. Scripts that are not inside an event-binding are executed as soon as they occur in the source. Scripts inside an event-binding don't execute until their corresponding event occurs.

This sample doesn't address the final BML concept I'll look at this month, but the tutorial and reference that come with BML do. My final topic is the modification of BML scripts -- while they continue to run!

Incremental application extension

One of the BML player's more surprising features is its ability to modify programs -- including the program's user interface -- while the program is running. The second example in the BML tutorial adds a scrollbar and a text widget to the Juggler applet seen in the August column, but those new widgets are added as the Juggler continues juggling! Be sure to download the BML package and try the demo code that does this.

You can give the BML Player new BML documents that edit the DOM (document object model) tree, which represents the current running program. In the standard BML juggler demo, you can give the running BML player a chunk of BML that adds a scrollbar to the panel and then tells the panel to lay itself out. The BML player executes those commands immediately, and a new scrollbar appears in the user interface. The received BML also binds the value of the scrollbar's adjustment event to the juggler's juggling speed property, so sliding the scrollbar up or down speeds up or slows down the juggler. The Juggler is a Java application consisting of cooperating classes running inside a JVM, but what precisely the application is capable of can be extended without even stopping the program, much less uninstalling and reinstalling. This capability has broad implications for program maintenance, automated upgrades, customization, and security. One limitation is that a new version of a class can't be loaded into a JVM while a previous version of that class is running.

It's pretty impressive to see a program add new functionality and reconfigure its user interface as it runs. But this ability is more than a "stupid applet trick": Leaving behind user interfaces for a moment, imagine a server to which you can add new enterprise functionality without even taking the server down to install the new code. Or even a server that, receiving a request it doesn't understand, contacts a class repository that specializes in downloading new functionality on command. Suddenly, the whole concept of what an application is becomes less obvious.

Deconstructing applications

Some growing pains have attended the growth of many software applications' complexity and capability. Witness user complaints about bloat in major software packages. Users want their applications to be lean and mean, but each user has differing requirements. A journalist, an editor, and a receptionist may all use a word processor, but they rely on different features. How can developers provide all the functions a user might want, without creating a program with enormous thickets of menus and dialog boxes, requiring tens of megabytes of memory, and tens or even hundreds of megabytes of disk space?

The mechanics of the compile-link-package-install cycle, and the executable structure common to most computer systems, have resulted in the current concept of an application -- a single, monolithic component that, when installed, may perform a fixed set of tasks. Of course, there are numerous exceptions to this model, but by and large, most software packages are distributed as programs or sets of programs that work together to solve some closed set of functions. Few applications are truly runtime-extensible, although scripting languages provide user-definable functionality that you can add at runtime.

1 2 3 Page 2
Page 2 of 3