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.
Bean Markup Language: Read the whole series!
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.
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.
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.
|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
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 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
<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
text property to the value of
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.
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
FadeBar because it registered with the object registry under that name when it was created on line 29, shown in Listing 2:
|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
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.)
<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:
This line of code means "notify the
eventReceiver object whenever the
eventSource object has an
An example would be
which means "notify the
windowEventProcessor anytime the
window has a
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.
<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">
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
fromColor property in response to a change in the
In summary, the code above means:
- When the
PropertyChangeEvent(line 205), execute the following
- Set the
colorFromproperty of the object called
FadeBar(line 207) to:
- the result of typecasting to
- the result of calling the
getValue()method (line 209)
- the result of calling the
- the result of typecasting to
- Call the
- (line 212)
- Set the
PropertyChangeEvent in the object with
FromColorEditor fires, the adapter object listening for this event is notified, and the BML script attached to the adapter is executed. The
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.
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
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
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:
Object convert(Class from, Class to, Object obj): converts an object of class
to, and returns the result as an
Object. The BML player uses this method to convert between types at runtime.
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
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
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
<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.
Creating scripts with other scripting languages
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:
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
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
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:
<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.
|Listing 6. Setting up the event-binding for textDirection|
Line 170 in Listing 6 binds the
item event to line 171's single-line script. Line 171 defines a
<script> with the additional attribute
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
Choice item and sets the fade bean's
textDirection property with the appropriate constant. The calls to
Choice item to the fade bean's
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.
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
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!
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.
Learn more about this topic
- Download the source code for this article:
- You need to download the Beans Development Kit from JavaSoft in order for the sample code in this article to run correctly. Be sure to include the BDK installation directory in your classpath before you run the sample code
- In jar format (with class files)
- In gzipped tar format
- In zip format
- Related JavaWorld resources
- "Keep listening for upcoming events," Mark Johnson (JavaWorld, October 1997). Brush up on the JDK 1.1 event model
- "XML JavaBeans, Part 1," Mark Johnson (JavaWorld, February 1999)
- "XML JavaBeans, Part 2," Mark Johnson (JavaWorld, March 1999)
- "XML JavaBeans, Part 3," Mark Johnson (JavaWorld, July 1999)
- "Turn Java classes into JavaBeans," Mark Johnson (JavaWorld, June 1998). Describes the operation of the
ColorFadeBeanused in the example in this article
- "XML for the absolute beginner," Mark Johnson (JavaWorld, April 1998). If you want a gentle introduction to XML, this article is for you
- "Problems with Swing's new XML OutputStream class," Allen Holub (JavaWorld, August 1999). Interested in what's going on with XML and JavaBeans at Sun? If so, definitely read this insightful and provocative critique
- IBM BML and XML resources
- IBM's Bean Markup Language (BML)
- The parser from IBM's XML4J package is available free for noncommercial use. It's even free for commercial use, but be sure to read the license agreement
- IBM's alphaBeans site has a large number of high-quality JavaBeans you can play with
- 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