Bean Markup Language, Part 2

Create event-driven applications with BML

Any system made up of components needs a way to connect those components to one another. In an automobile, that framework is the auto frame (itself a component), to which other components are attached. In electronics, it's the chassis, the circuit board, or the substrate, depending on what level of component-use you mean.

Last month, a reader commented that BML didn't work as well as an IDE, or integrated development environment, for creating user interfaces, and I agree. But the power of and interest in BML derives from its ability to create structures of arbitrary classes -- not just user interfaces or JavaBeans -- to build an application. More importantly, BML can define an application declaratively, usually without procedural code. This makes it much easier for (potential) builder tools to compose functionality into applications, much in the same way a dialog editor composes GUI elements into a user interface. In fact, since BML defines applications structurally rather than procedurally, it can be used to automatically generate interactive interfaces for arbitrary datastructures.

IBM's Bean Markup Language provides just such a software chassis for Java object instances, particularly instances of JavaBean components. As I demonstrated in August (see Part 1 of this two-part series in Resources below), you can use BML to create configured instances of JavaBeans.

This month, I'll show you how to create instances of several objects in BML and then use BML to wire these objects together. I'll also show you how to execute any method of an object from BML, extend BML to handle conversions between data types, and explore binding Java objects created in BML to a scripting language (in this case, JavaScript). Since I assume you've read the first article, let's dive right into this month's sample code.

A BML application

I'm going to extend last month's ColorFadeBean by transforming it into the interactive ColorFadeEditor.bml (click the link to see the file). I've added an AWT text field to the original ColorFadeBean, which sets the text in the ColorFadeBean, and three ColorChooser objects, which set the text color and the start and end colors for the color gradient. A picture of the ColorFadeEditor appears in Figure 1.

Figure 1. ColorFadeEditor defined entirely in BML

This application is actually a single-nested hierarchy of GUI elements, as it must be, since an XML document may have only one top-level element. You'll notice that the BML file contains no Java code. That's because BML is a language used for wiring JavaBeans together, not for creating new JavaBeans (although defining JavaBeans in BML will be a feature of an upcoming BML release). The color gradient in the ColorFadeBean, for example, is created by the bytecodes in ColorFadeBean.class, which can be considered an off-the-shelf JavaBean. BML sets the bean's exposed properties and so on, but it can't add new properties or behavior.

The top-level object in the object tree is a java.awt.Frame, containing a panel above (to hold the TextField for editing the message text), a panel below (holding the color editors), and a ColorFadeBean (which we're editing) in the middle. The structure of the entire document appears in Figure 2.

 
Frame MainFrame
  • BorderLayout
  • Font LabelFont
  • ColorFadeBean FadeBar
  • Panel
    • GridLayout
    • Label
    • ColorChooser TextColorEditor
    • Label
    • ColorChooser FromColorEditor
    • Label
    • ColorChooser ToColorEditor
  • Panel
    • BorderLayout
    • TextField TextEditor
 
Figure 2. The structure of ColorFadeEditor.bml

In Figure 2, the class name of each nested object appears with its optional symbolic id in italics. Giving an object a symbolic id in a BML file allows that object to be looked up and used elsewhere in the script. (An example is given below in our next section.)

When this BML script is running, changing the colors or the text in the window immediately changes the properties of the ColorFadeBean. Note that this entire application was written in BML: no Java code was necessary beyond what already existed in the class files used to implement it. In fact, with just a little more work, you could use this editor as the customizer (as in java.beans.Customizer) for the ColorFadeBean class.

The ColorChooser objects you see in the application are a custom color editor I wrote as a support class for this article. These objects could have been defined as in-line BML, but creating a new JavaBean (ColorChooser) and wiring it into the application better demonstrates BML as a wiring language and makes the sample code shorter.

You'll notice that, when the ColorFadeEditor window appears, the color editors and the text editor are already initialized to the corresponding color and text values in the ColorFadeBean. And how was the font for the color editor labels set? These widgets got these initial values by way of the <property> tag, which you can use not only to set property values but also to look them up.

A question of values

In Part 1, I used the <property> tag to set the value of a property. You can use <property>, like many other BML tags, to look up values as well as set or create them. For example, look at how the TextField item at the top of the application receives a value of "Emeralds and Sapphires." The entire add operation that adds the text field appears in Listing 1.

Line 140 declares that whatever it contains should be added to the enclosing container (in this case, a java.awt.Panel). Line 141 creates java.awt.TextField and gives it the name TextEditor. Any bean can name itself upon creation by setting its id attribute. id registers the bean with the BML object registry, a table inside the BML processor. Later, BML code will be able to access this bean by name, as you're about to see.

Line 142 defines an argument to the TextField's constructor. The <string> tag creates a string, <cast> casts the string to an int, and <args> indicates to the enclosing <bean> tag the arguments to use for the constructor. Since the <args> tag contains a single integer (with a value of 80), the BML processor selects java.awt.TextField(int) as the constructor to use to create the text field.

Lines 143 through 145 set the TextField's text property to the value of ColorFadeBean's message property. Instead of using the <property name="..." value="..."/> syntax from Part 1, line 143 declares the property to be set, but the value is retrieved from that tag's contents.

The <property> tag's target attribute looks up the ColorFadeBean by its id (the string FadeBar) in the BML object registry, and then the value attribute selects the value to retrieve. The ColorFadeBean has the id FadeBar because it registered with the object registry under that name when it was created on line 29, shown in Listing 2:

 
029     <bean class="com.javaworld.JavaBeans.Oct99.ColorFadeBean " id="FadeBar">

Listing 2. Create a ColorFadeBean and assign it a name

The structure you see in lines 143 through 145 is equivalent to an assignment statement; it assigns the property value of one bean to another bean. Note that this assignment doesn't create a permanent link between the two properties; that is, the properties aren't bound together after this assignment. It simply assigns the TextField value at the time the field is created.

What if you wanted to bind properties together? Since the application is an editor, you want any change in the value of the ColorChooser to change the corresponding property in the ColorFadeBean (messageColor, fromColor, or toColor). In Java code, objects typically talk to each other using events. BML provides that capability in the form of event-bindings, which I'll discuss next.

Wiring beans together

Creating a running instance of a Java class, as you've seen so far, is a cool trick. It's pretty useless, though, without a way to use that object. The standard Java event model, introduced in JDK 1.1, provides a way for Java objects to communicate with one another by sending events, objects that encapsulate some sort of occurrence. (If you need to brush up on the Java event model, see my JavaWorld article, "Keep listening for upcoming events," in Resources.)

The <event-binding> BML tag establishes an event listener relationship between a target bean (the bean that produces the event) and some action performed when that event occurs.

When you're writing event code by hand, you'll typically set up an event relationship this way:

    eventSource.addEventtypeListener(eventReceiver);

This line of code means "notify the eventReceiver object whenever the eventSource object has an eventType event."

An example would be

 window.addWindowListener(windowEventProcessor); 

which means "notify the windowEventProcessor anytime the window has a windowEvent."

BML takes a slightly different approach. Instead of binding objects directly together, BML makes one bean listen for events on another bean: it binds event sources to scripts, which are chunks of code (in either BML or some other scripting language) defined by the BML <script> tag. An adapter object is the event listener, which listens for events on its event source and executes its script whenever the event occurs. An adapter object is created for each event-binding in a BML script.

The <event-binding> tag binds a BML script to a particular event type on a particular bean. Its syntax is as follows:

    <event-binding [target="id"] name="eventtype">

The optional target attribute defines which object is the source of the event. It's a little confusing that the event source is identified by a tag called target, but that's because target is the BML attribute that any tag uses to look up a bean -- target was probably a poor name choice for that attribute. The target attribute is optional because the default target for an event-binding is the <bean> tag in which the binding is enclosed.

In the sample code, I've chosen to locate all event-bindings at the bottom of the script, but I could have placed them anywhere. Listing 3 shows the event-binding in the example that updates the FadeBar's fromColor property in response to a change in the fromColorEditor's value.

In summary, the code above means:

  • When the FromColorEditor has a PropertyChangeEvent (line 205), execute the following <script> (line 206):
    • Set the colorFrom property of the object called FadeBar (line 207) to:
      • the result of typecasting to java.awt.Color (line 208)
        • the result of calling the FromColorEditor's getValue() method (line 209)
    • Call the FadeBar's repaint() method
    • (line 212)

When the PropertyChangeEvent in the object with id FromColorEditor fires, the adapter object listening for this event is notified, and the BML script attached to the adapter is executed. The FadeBar object's fromColor property is then updated.

That mechanism works for any event type. Values from the event object itself can be accessed by the script. See the details in the tutorial and reference manual that come with the BML package.

Calling methods

I've used a tag in the example above, <call-method>, that you haven't seen before. The <call-method> tag calls an arbitrary object or class method. The tag uses the target attribute to indicate the object or class upon which the method is to be invoked. The example in Listing 3 shows a call to the FadeBar object's repaint() method. You can also call static methods within classes, by using the class: modifier inside the target attribute's value. The example in Listing 4 shows how to do this.

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.

Java's class loader interface makes it possible for programs to add new classes -- and thus, new behavior -- at runtime, even while the program is running. The ability to dynamically extend program functionality, combined with the metadata provided by the JavaBeans component model, results in an enormously flexible architectural toolset. Imagine an application wherein new functions and features could be added lazily, being loaded only when they were required. Not only would load time be amortized through the entire run of the application, but unused functions would never be loaded, decreasing the total load time and causing less drain on system resources.

The growing popularity of software component systems, such as JavaBeans, points toward the eventual disappearance of the application as a monolithic unit. Applications will operate as loosely coupled, dynamic associations of components assembled by a user, or even by another system, to perform a particular set of tasks. Such an approach to software systems has many benefits, including improved testability, dynamic upgrading, finer licensing granularity, quicker application startup times, extensibility, customizability, modularity, and eventually the development of a competitive component market. (Of course, those benefits bring with them challenges -- not least of which are component versioning and compatibility.)

Running the sample code

To run the sample programs, download the appropriate archive (in jar, tar, or zip format) from Resources below. Extract the files from the archive into a directory. (Because of some oddity that I haven't yet figured out, the jar file doesn't work properly in the classpath -- you have to extract it. Extra credit for anyone who can tell me what the problem is.)

I used the BML compiler to create a BML-free, reflection-free application in the file CFE.class. You can experiment with the application by simply extracting the files from the archive, setting the classpath to include ".", and running CFE as a main program:

$ export CLASSPATH=".;$CLASSPATH"
$ java CFE

The application window should appear, and you'll have the running sample code. The second example, with JavaScript, won't run without the BML runtime, so I didn't provide a compiled version.

If you want to experiment with and modify the BML, you'll need to download the BML package and xml4j from alphaWorks (see the links in Resources.) Be sure the xml4j jar file (whose name depends on the release number), the jar file bmlall.jar and your current directory all appear in the classpath. Follow the instructions in the tutorial (included in the BML package) to get the BML player working, and then you can experiment with the sample BML from this article -- or write your own!

Conclusion

For the reasons outlined above, BML is one of the most exciting technologies I've experimented with. I urge anyone with an interest in XML and/or JavaBeans to consider experimenting with BML. BML may not change the world -- but something like it will in the near future. Remember, you heard it here first.

Mark Johnson has a BS in computer and electrical engineering from Purdue University (1986), and has been writing for JavaWorld since August 1997. By day, he works as a designer and developer for OrganicNet in Fort Collins, CO.

Learn more about this topic

  • Download the source code for this article:
  • Related JavaWorld resources
  • IBM BML and XML resources
  • The three great virtues of a programmer
  • According to Larry Wall, the creator of Perl (the second-best computer language in existence), the three great virtues of a programmer are laziness, impatience, and hubris. Read a description here
    http://hiro.protagonist.net/perl/virtue.html