The goal of bean-based software development is to use beans to quickly and easily assemble applets and applications. To accomplish this, you need a suitable collection of beans and an approach to integrating them into your programs. The InfoBus, developed by Lotus Development Corporation and JavaSoft, provides a mechanism for bean integration. InfoBus supports a standard interface for communication between beans and allows information to be exchanged between beans in a structured way.
In this chapter, you'll be introduced to the InfoBus and learn how it works. You'll learn how InfoBus simplifies and standardizes bean communication, and about the classes and interfaces of the InfoBus API. You'll then develop an applet that uses InfoBus to exchange data between beans. When you finish this chapter, you'll be able to use InfoBus to simplify communication between the beans that you develop.
Normally, all beans that are loaded from the same classloader are visible to each other. Beans can find each other by searching the container-component hierarchy or their bean context. They can then use reflection and design patterns to determine which services are provided by other beans. However, this approach is often cumbersome and prone to error. The software engineers at Lotus Development Corporation and JavaSoft recognized that a standard approach to data exchange between beans was needed and collaborated to simplify inter-bean communication. The InfoBus is the result of this effort.
The InfoBus is analogous to a PC system bus. Data producers and consumers connect to an InfoBus in the same way that PC cards connect to a PC's system bus. Data producers use the bus to send data items to data consumers. The InfoBus is asynchronous and symmetric. This means that the producer and consumer do not have to synchronize to exchange data, and any member of the bus can send data to any other member of the bus.
The InfoBus operates as follows:
NOTE: The unit of data exchanged on an InfoBus is referred to as a data item. A data item can be any object.
This list summarizes the typical usage of the InfoBus. However, the InfoBus is flexible and provides additional usage options, which you'll learn about in the next section. The advantage of InfoBus is that it eliminates the need for inference and discovery on the part of beans. Instead, it provides a standard, structured mechanism for named data items to be exchanged.
The InfoBus is a standard extension API consisting of the javax.infobus package, which defines 14 classes and 17 interfaces that support all aspects of InfoBus operation. The InfoBus class is the primary class of the package, supporting bus membership and communication between bus members. The InfoBusMember interface is the interface required of all bus members. The InfoBusMemberSupport class provides a default implementation of this interface.
NOTE: The InfoBus API is a standard extension API. It can be downloaded from the JavaBeans home page at http://www.javasoft.com/products/beans/.
Data producers implement InfoBusDataProducer, and consumers implement InfoBusDataConsumer. The InfoBusDataController interface is implemented by members that control the operation of the InfoBus. By default, no bus controllers are required.
The DataItem interface is used to provide descriptive information about a data item. The data provided by a data item can be accessed through the following InfoBus access interfaces:
The DataItemView interface provides a two-dimensional database view. The RowsetValidate is used to validate the contents of a row of a database.
The DefaultPolicy class provides a default InfoBus security policy implementation. It implements the InfoBusPolicyHelper interface, which is required of InfoBus security policies.
The InfoBus supports two event hierarchies. The InfoBusEvent class is the base event class used with InfoBus communication. It is extended by InfoBusItemAvailableEvent, InfoBusItemRequestedEvent, and InfoBusItemRevokedEvent. The InfoBusEventListener interface is used to handle these events. The DataItemChangeEvent class is used to inform bus members about the availability and changes to a data item. It is extended by DataItemAddedEvent, DataItemDeletedEvent, DataItemRevokedEvent, DataItemValueChangedEvent, and RowsetCursorMovedEvent. The DataItemChangeListener interface handles these events.
The InfoBusPropertyMap interface is used with InfoBus 1.1 to support the DataItemChangeEvent. The DataItemChangeManager interface is used to manage multiple DataItemChangeListeners.
In this section, we'll use the InfoBus to enable communication between the following three beans:
The TimeGenerator and TimeZoneList beans are data producers, and the TimeDisplay bean is a data consumer. All three beans are members of the Time Bus InfoBus.
Listing 28.1 shows the source code of the TimeGenerator bean. TimeGenerator extends the InfoBusMemberSupport class and implements InfoBusDataProducer, Serializable, and Runnable. The Runnable interface is required to run a TimeGenerator object as a separate thread.
Two constructors are provided. The first constructor has no parameters and passes a null value to the second constructor. The second constructor takes an InfoBusMember object as an argument and passes it to the superclass (InfoBusMemberSupport) constructor. This object is used to designate an alternative object to handle InfoBus-related events. A null value is used to let the InfoBusMemberSupport object handle these events.
The constructor invokes the joinInfoBus() method to join the Time Bus. The getInfoBus() and addDataProducer() methods add the object being constructed as a data producer on the InfoBus.
The run() method implements the Runnable interface by simply invoking generateTime(). The generateTime() method uses a while statement to loop forever and put Time data items in the InfoBus. The fireItemAvailable() method informs bus members that the Time data item is available. The thread then goes to sleep for a second to allow other threads to execute.
The dateItemRequested() method implements the InfoBusDataProducer interface. It creates a GregorianCalendar object for the GMT time zone and the current time. This object is then set into the InfoBusItemRequestedEvent, which is propagated back onto the data consumer.
import java.util.*; import java.io.*; import javax.infobus.*; import java.beans.*; public class TimeGenerator extends InfoBusMemberSupport implements InfoBusDataProducer, Serializable, Runnable { public TimeGenerator() { this(null); } public TimeGenerator(InfoBusMember infoBusMember) { super(infoBusMember); try { joinInfoBus("Time Bus"); getInfoBus().addDataProducer(this);
} catch (Exception e) {
System.out.println(e.toString()); } } public void run() { generateTime(); } void generateTime() { while(true) { // Notify consumers getInfoBus().fireItemAvailable("Time",null,this); // Wait a second try{ Thread.currentThread().sleep(1000); }catch(Exception ex) { } } } public void dataItemRequested(InfoBusItemRequestedEvent e) { // Get GMT time zone IDs String[] tzID = TimeZone.getAvailableIDs(0); SimpleTimeZone tz = new SimpleTimeZone(0,tzID[0]); GregorianCalendar calendar = new GregorianCalendar(tz); calendar.setTime(new Date()); e.setDataItem(calendar); } public void propertyChange(PropertyChangeEvent e) { }
}
The TimeZoneList bean, shown in Listing 28.2, extends the List class and implements the InfoBusMember, InfoBusDataProducer, ItemListener, and Serializable interfaces. The ItemListener class is implemented so that TimeZoneList can handle its own events.
An InfoBusMemberSupport field variable is used to simplify the implementation of the InfoBusMember interface. The TimeZoneList methods pass on requests to the ims variable. The tz variable is used to reference the TimeZone object produced by objects of this class. The tzIDs array consists of a list of time zone identifiers that are known to the JDK.
The TimeZoneList constructor takes an argument that identifies the number of rows to be made visible in the list's display. The object being constructed is added as a member and data producer to the Time Bus. The time zone IDs are added to the list, and the addItemListener() method is invoked to allow the object being constructed to handle its own ItemEvents.
The itemStateChanged() method handles the selection of a list item. It constructs a TimeZone object based on the time zone ID selected by the user. The availability of this object is signaled by the fireItemAvailable() method. The data item is named Zone.
The setInfoBus(), getInfoBus(), addInfoBusVetoableListener(), removeVetoableListener(), addInfoBusPropertyListener(), and removeInfoBusPropertyListener() method implement the InfoBusMember interface and are redirected to the InfoBusMemberSupport object referenced by the ims variable.
The dataItemRequested() method implements the InfoBusDataProducer interface by setting the generated TimeZone object in the InfoBusItemRequestedEvent.
import java.awt.*; import java.awt.event.*; import java.util.*; import java.io.*; import javax.infobus.*; import java.beans.*; public class TimeZoneList extends java.awt.List implements InfoBusMember, InfoBusDataProducer, ItemListener, Serializable { InfoBusMemberSupport ims; TimeZone tz = TimeZone.getDefault(); String[] tzIDs = TimeZone.getAvailableIDs(); public TimeZoneList(int rows) { super(rows); ims = new InfoBusMemberSupport(null); try { ims.joinInfoBus("Time Bus"); ims.getInfoBus().addDataProducer(this); }catch (Exception e) { System.out.println(e.toString()); } for(int i=0;i<tzIDs.length;++i) add(tzIDs[i]); addItemListener(this); } public void itemStateChanged(ItemEvent e) { int index = ((Integer) e.getItem()).intValue(); String tzID = tzIDs[index]; tz = TimeZone.getTimeZone(tzID); getInfoBus().fireItemAvailable("Zone",null,this); } public void setInfoBus(InfoBus newInfoBus) throws PropertyVetoException { ims.setInfoBus(newInfoBus); } public InfoBus getInfoBus() { return ims.getInfoBus(); } public void addInfoBusVetoableListener(VetoableChangeListener vcl) { ims.addInfoBusVetoableListener(vcl); } public void removeInfoBusVetoableListener(VetoableChangeListener vcl) { ims.removeInfoBusVetoableListener(vcl); } public void addInfoBusPropertyListener(PropertyChangeListener pcl) { ims.addInfoBusPropertyListener(pcl); } public void removeInfoBusPropertyListener(PropertyChangeListener pcl) { ims.removeInfoBusPropertyListener(pcl);
}
public void propertyChange(PropertyChangeEvent e) { } public void dataItemRequested(InfoBusItemRequestedEvent e) { e.setDataItem(tz); }
}
The TimeDisplay bean, shown in Listing 28.3, is an example of a data consumer. The TimeDisplay class extends Canvas and implements the InfoBusMember, InfoBusDataConsumer, and Serializable interfaces. The ims field variable is used in the same way as in TimeZoneList, to reference an InfoBusMemberSupport object that helps implement the InfoBusMember interface. The time1 and time2 variables reference String objects that are displayed on the canvas. The tz variable references the TimeZone object selected by the user. It is initially set to the current time zone.
The TimeDisplay constructor adds the object being constructed as a member and consumer to the Time Bus.
The getPreferredSize() and paint() methods are used to size and draw the component interface, which consists of two time displays. The first time display is based on the GregorianCalendar object received (via the InfoBus) from the TimeGenerator bean. The second time display is based on the TimeZone object received from the TimeZoneList bean.
The dataItemAvailable() method implements the InfoBusDataConsumer interface and allows the TimeDisplay object to receive data inputs from the other beans on the InfoBus. The InfoBusItemAvailableEvent passed as an argument to dataItemAvailable() contains the data item and information about the data item. The getDataItemName() method retrieves the data item's name. This name is used to determine whether the data item is from TimeGenerator or TimeZoneList.
If the data item is from TimeGenerator, the requestDataItem() method is used to return the GregorianCalendar object. This object is then used to update the time1 string. The time2 string is updated based on the current value of the tz variable. The repaint() method is invoked to cause the canvas to be repainted.
If the data item is from TimeZoneList, the requestDataItem() method is used to return the TimeZone object. This object is then used to update the tz variable.
The getTimeString() is used to retrieve the information to be displayed from a Calendar object.
import java.awt.*; import java.util.*; import java.io.*; import javax.infobus.*; import java.beans.*; public class TimeDisplay extends Canvas implements InfoBusMember, InfoBusDataConsumer, Serializable { InfoBusMemberSupport ims; String time1 = ""; String time2 = ""; TimeZone tz = TimeZone.getDefault(); public TimeDisplay() { ims = new InfoBusMemberSupport(null); try { ims.joinInfoBus("Time Bus"); ims.getInfoBus().addDataConsumer(this); }catch (Exception e) { System.out.println(e.toString()); } } public Dimension getPreferredSize() { return new Dimension(120,100); } public void paint(Graphics g) { g.drawString(time1,10,25); g.drawString(time2,10,75); } public void setInfoBus(InfoBus newInfoBus) throws PropertyVetoException { ims.setInfoBus(newInfoBus); } public InfoBus getInfoBus() { return ims.getInfoBus(); } public void addInfoBusVetoableListener(VetoableChangeListener vcl) { ims.addInfoBusVetoableListener(vcl); } public void removeInfoBusVetoableListener(VetoableChangeListener vcl) { ims.removeInfoBusVetoableListener(vcl); } public void addInfoBusPropertyListener(PropertyChangeListener pcl) { ims.addInfoBusPropertyListener(pcl); } public void removeInfoBusPropertyListener(PropertyChangeListener pcl) { ims.removeInfoBusPropertyListener(pcl); } public void propertyChange(PropertyChangeEvent e) { } public void dataItemAvailable(InfoBusItemAvailableEvent e) { String name = e.getDataItemName(); if(name.equals("Time")) { GregorianCalendar calendar1 = (GregorianCalendar) e.requestDataItem(this,null); GregorianCalendar calendar2 = new GregorianCalendar(tz); time1 = getTimeString(calendar1); time2 = getTimeString(calendar2); repaint(); }else if(name.equals("Zone")) { tz = (TimeZone) e.requestDataItem(this,null); } } String getTimeString(GregorianCalendar calendar) { String hour = String.valueOf(calendar.get(Calendar.HOUR_OF_DAY)); if(hour.length()==1) hour = "0"+hour; String minute = String.valueOf(calendar.get(Calendar.MINUTE)); if(minute.length()==1) minute = "0"+minute; String second = String.valueOf(calendar.get(Calendar.SECOND)); if(second.length()==1) second = "0"+second; TimeZone timeZone = calendar.getTimeZone();
return hour+":"+minute+":"+second+" "+timeZone.getID();
} public void dataItemRevoked(InfoBusItemRevokedEvent e) { }
}
Now that we have three InfoBus-enabled beans, let's combine them in an applet that will demonstrate their operation. The TimeApplet (Listing 28.4) shows how easy this is. This applet creates a new thread from the TimeGenerator bean and instances of the TimeDisplay and TimeZoneList beans. The applet's init() method adds the two visible beans as GUI components and then invokes the TimeGenerator's start() method.
Listing 28.5 provides an HTML file that can be used to run the applet. When you run this file in appletviewer, the window shown in Figure 28.1, is displayed. Note that the time is displayed in GMT and in your local time zone. Select a new time zone from the list and the second time string displays time in the newly selected time zone, as shown in Figure 28.2.
FIGURE 28.1. The TimeApplet initial display.
FIGURE 28.2. The time is displayed using the newly selected time zone.
import java.applet.*; import java.awt.*; import java.awt.event.*; import javax.infobus.*; public class TimeApplet extends Applet { Thread generator = new Thread(new TimeGenerator()); TimeDisplay display = new TimeDisplay(); TimeZoneList tzl = new TimeZoneList(5); public void init() { add(tzl); add(display); generator.start(); }
}
<HTML> <HEAD> <TITLE>Using the InfoBus</TITLE> </HEAD> <BODY> <APPLET CODE="TimeApplet.class" HEIGHT=300 WIDTH=300> </APPLET> </BODY>
</HTML>
In this chapter, you were introduced to the InfoBus. You learned how InfoBus simplifies and standardizes bean communication and about the classes and interfaces of the InfoBus API. You then developed an applet that uses InfoBus to exchange data between beans. In the next chapter, you'll study other facilities for integrating beans that were introduced with the Glasgow JavaBeans specification.
© Copyright, Macmillan Computer Publishing. All rights reserved.