Process XML with JavaBeans, Part 1

Interconnect JavaBeans to process XML

The expression "eating your own dog food" has gained currency over the last few years. It means taking the product you're selling in your daily business and using it yourself, so that you understand it from the consumer's point of view. I've been churning out columns on JavaBeans (my particular brand of dog food) for the last couple of years, teaching readers how to create new JavaBeans and use them in novel ways. But I haven't focused on using JavaBeans in applications -- I haven't been eating my own dog food. And it's about time I sat down to a big chunky bowl of it.

With that in mind, this month I am going to cover not how to create JavaBeans, but how to use them. Using JavaBeans to create applications in an integrated development environment (IDE) is a great way to learn how to think in components. Component designers and implementers who are forced to chow down on what they've been dishing up quickly learn what makes a component useful -- or useless. Many developers who assemble JavaBeans into running applications have experience primarily with GUI components; thus, in this series, I'll particularly focus on components that do data processing and have no runtime user interface.

The software package I'll be using for this discussion is IBM's XML Bean Suite, available for free from IBM's alphaBeans site (see Resources for a link). This package is very different from the XML JavaBeans and BML I've covered in the past. Those discussions dealt with converting JavaBean components to XML, or creating JavaBeans from XML. The XML Bean Suite, on the other hand, is a set of JavaBean components designed for processing XML data. The suite contains JavaBeans that a developer interconnects visually in an IDE in order to read, write, display, search, and filter XML data. Many of these JavaBeans have no user interface at runtime; they do most of the application's work internally. They're also excellent design examples of how to encapsulate functionality into a component.

This article assumes that you're familiar with the basics of JavaBeans and XML. Links to background material for this article appear in the Resources section.

This month's column is mostly an overview of the XML Bean Suite, which contains a large number of classes for processing XML. I'll also discuss how IDEs interconnect JavaBeans in response to your input, and I'll point out useful design principles as we go along. Columns to follow will use the XML bean classes to create applications (such as an XML file editor) that process XML data.

Contents of the XML Bean Suite

The alphaBeans site (see

Resources

) is the JavaBeans section of IBM's alphaWorks site, which provides "early adopter developers direct access to IBM's emerging 'alpha-code' technologies." This means that the code is freely downloadable from the site. Some of the code is even available for free commercial use, but the licensing restrictions vary by package. The designation

alpha

also means that the software is not ready for prime time. APIs are not guaranteed to be stable, the software may be updated erratically, and IBM makes no guarantees about ever turning the material on alphaWorks (and alphaBeans) into commercial products. Still, several projects that began on alphaWorks have graduated to full commercial status. Most, if not all, alphaWorks technologies have online discussion forums where users can get advice from the developers creating the software, and can make suggestions for improving the products.

The XML Bean Suite is a set of 39 Java classes available for free from the alphaBeans site. Since it's alpha software, it doesn't yet work with the latest version of Swing (it requires Swing 1.0.2), and doesn't even work with the newest version of IBM's XML processor xml4j (it requires version 1.1.4). The license agreement that appears at download time grants redistribution rights to the code (though you shouldn't take my word for it -- read the license yourself).

The 39 classes in the suite are divided into five sets of related JavaBeans. Many of these beans are nonvisual; that is, they may have a design-time user interface (such as a property sheet), but have no user interface at runtime. The five sets of XML beans appear in Table 1.

Table 1. Five sets of XML beans in the XML Bean Suite
Bean Set# of BeansDescription
XMLCoreBean4Nonvisual beans that convert XML between text and DOM representations and manage DOM Nodes
XMLViewer5Visual beans that display XML documents or DTDs in various ways
XMLEditor12Nonvisual operator beans that allow construction of DTD-directed XML editors
XMLProcessing5Nonvisual beans that provide filtering, tokenizing, searching, and other operations on XML data
XMLConvenience13Beans that implement common XML editing subfunctions by combining XMLEditor beans and java.awt GUI objects

Each of these bean sets provides a domain of XML processing. You can wire instances of the beans from these sets together to create XML applications. Let's look at the XMLCoreBean set first.

XMLCoreBean set

The most basic set of XML beans is

XMLCoreBean

. This set of beans lets you convert XML text to a Document Object Model (DOM) representation of the XML and convert the DOM to XML text. These XML beans all operate on DOM documents or parts of documents, so they act as the gateway between the DOM and XML. The most central of these is the XML parser

DOMGenerator

.

Talkin' 'bout DOM generation

The

DOMGenerator

bean is a JavaBean encapsulation of an XML parser, as shown in Figure 1. The bean has three properties:

inputXmlFileLocation

(a string),

inputXmlText

(also a string), and

inputXmlURLLocation

(a

URL

, which may specify the source of the XML data to be parsed). When any of these properties are set,

DOMGenerator

immediately reads the text from the XML source and produces a result of type

org.w3c.dom.Document

, which is the root of a DOM-object tree that represents the input XML. This

Document

can then be passed for processing to any object that receives a

Document

as input.

Figure 1. DOMGenerator produces a DOM result from XML input

DOMGenerator also fires several events to let any interested listener know how the parsing is coming along. DOMGenerator fires a DOMGenerationEvent before it starts parsing, as well as after it completes parsing, or after an error occurs. The event contains a code that indicates which type it is. An object that needs to know what a DOMGenerator is up to implements the interface DOMGenerationListener, which has methods generationStarted(), generationError(), and generationOver(). The object registers itself as a listener with the DOMGenerator in which it's interested, and the DOMGenerator then fires events at the listening object(s) by calling the appropriate DOMGenerationListener interface methods.

What does it mean for the DOMGenerator to fire an event? And what does its firing an event provide to the application developer? The answers to these questions lie in an explanation of how IDEs hook objects together.

Event listener interfaces

This is a good opportunity to discuss how IDEs interconnect objects. When you're writing a class, it's common to want an instance of your class to be notified when an event source fires an event. A good example is a label that changes when a

DOMGenerator

object begins parsing, which we'll set up shortly. To catch an event (because you're interested in the object firing the event), you write the class to implement the

listener interface

for that event, and then

register

yourself with the event source by calling

addEventtypeListener

, the event source's method.

So, some object (which object depends on your IDE) in the system implements DOMGenerationListener, and then calls DOMGenerator.addDOMGenerationListener(this). The DOMGenerator calls that object's generationStarted() method before it starts parsing, and that object sets the label's text value to reflect the fact that parsing has started.

A visual IDE sets up such a listener interface by letting you visually indicate the source and target objects, usually by drawing a line between them. Once the information about the event type, the event source, and the event target are indicated in the IDE, the IDE automatically generates the calls to addEventtypeListener and implementations for the listener interface.

Let's look at a quick example using DOMGenerator in IBM's VisualAge for Java. Note that these XML beans will work in any JavaBeans-compliant IDE, not just VisualAge. Any IDE worthy of the name will let users create event connections graphically. The diagrams may look a bit different, but the basic idea is the same across the different IDEs.

Figure 2 shows a tiny application that uses the DOMGenerator class to parse an XML file. This figure is a screen shot taken directly from the IDE (except that I added the letters A through D to aid discussion below.

Figure 2. Wiring up a DOMGenerator in an IDE to create an application

I created four event connections in this application, identified by four capital letters, corresponding to the letters in the list below:

A: When the text box produces an actionEvent (calling actionPerformed()), the DOMGenerator's property inputXmlFileLocation is set to the string in the text box.

B: When the DOMGenerator fires a DOMGenerationEvent to indicate that it has started parsing, the text label's value is set to Started parsing...

C: When the DOMGenerator fires a DOMGenerationEvent to indicate that it has finished parsing, the text label's value is set to Parsing complete.

D: If the DOMGenerator fires a DOMGenerationEvent indicating that an error occurred, the text label's value is set to Parse Error!

This means that, when the user types a filename into the text box and hits Return, the DOMGenerator gets a filename as its input, automatically starting a parse. The DOMGenerator fires an event to indicate that it is beginning to parse, parses the file, and throws an event to indicate either successful completion of the parse or an error condition. In either case, you can see the label changing as the parse proceeds. If the DOMGenerator succeeds in parsing, it makes the Document object it created available via its result property.

But how did just drawing a few lines cause the events to hook together? The lines I drew indicate an event listener relationship between the source (the object the line comes from) and the target (the object the line goes to). Let's look at case B for a simple example. In my IDE, I connected the DOMGenerator to the Label by selecting the DOMGenerator's generationStarted event (on a popup menu) and linking it to setting the Label's text property. I entered the Started Parsing... string as the value for the label text by editing the properties on the relationship (the line between elements) itself.

When I drew this relationship, the IDE did two things. First, it chose an object to be the DOMGenerationListener. VisualAge for Java makes the application class (called AWTDOMGeneratorDemo) be the listener, and then implements the listener methods. So, the IDE added a new implements clause to the class definition, like this:

public class AWTDOMGeneratorDemo
   extends Frame 
   implements com.ibm.xml.generator.event.DOMGenerationListener,  WindowListener, ...

Then, it implemented the methods for that listener interface, one of which was generationStarted():

/**
 * Method to handle events for the DOMGenerationListener interface.
 * @param arg1 com.ibm.xml.generator.event.DOMGenerationEvent
 */
/* WARNING: THIS METHOD WILL BE REGENERATED. */
public void generationStarted(com.ibm.xml.generator.event.DOMGenerationEvent arg1) {
    // user code begin {1}
    // user code end
    if ((arg1.getSource() == getDOMGenerator1()) ) {
        connEtoM2(arg1);
    }
    // user code begin {2}
    // user code end
}

In plain English, this method translates to, "When the

DOMGenerator

calls

generationStarted()

, call the method

connEtoM2()

." The latter method actually sets the label, as shown in the listing below. Now, the application class sets the label's

text

property (the string that the label displays) to

Started Parsing...

whenever the

DOMGenerator

fires a

generationStarted

event. This is the most common method IDEs use to create relationships between JavaBeans without modifying the their source code.

/**
 * connEtoM2:  (DOMGenerator1.domGeneration.generationStarted(
 * com.ibm.xml.generator.event.DOMGenerationEvent) --> Label2.text)
 * @param arg1 com.ibm.xml.generator.event.DOMGenerationEvent
 */
/* WARNING: THIS METHOD WILL BE REGENERATED. */
private void connEtoM2(com.ibm.xml.generator.event.DOMGenerationEvent arg1) {
    try {
        // user code begin {1}
        // user code end
        getLabel2().setText("Started parsing...");
        // user code begin {2}
        // user code end
    } catch (java.lang.Throwable ivjExc) {
        // user code begin {3}
        // user code end
        handleException(ivjExc);
    }
}

The event listener interface was set up in an

init

function called when the object was constructed. Your IDE may do things differently. Remember, I said above that some object must be able to respond to the incoming events; in VisualAge's case, that object is the application class itself. Other IDEs may generate tiny

adapter

1 2 Page 1