With the basics of beans in the bag, you're now ready to see how easy it is to develop more advanced beans
In this short series, we are examining the development of JavaBeans software components. Ultimately, most beans will be manipulated in a beans development environment; however, we are only concerned here with the source-level aspects of the framework. The advantages of developing JavaBeans โ that is, developing to the JavaBeans specification โ are severalfold, among them:
Beans can be easily manipulated in visual development environments by users who need not be technically skilled in source-level Java development.
Because of the standard interface, beans are readily distributable, which allows third-party components to be more easily integrated into development efforts.
- Developers can easily transfer code that has been developed for one project into a reusable library of components, which can be accessed in future development efforts.
The eye of the storm
In the
first part of this series
, we developed two simple beans: a non-visual alarm bean and a graphical left-arrow/right-arrow bean. Both were augmented with visual
customizer
and
bean information
classes. In the beans we cover this month, we wonโt be providing customizers; instead, weโll concentrate on using existing beans and components to create bigger, better beans.
Prerequisites
As the continuation of a two-part series, I will assume familiarity with the issues discussed in the previous installment, including the supplementary articles and resources.
The beans
From start to finish of this series, we develop the following beans:
| AlarmBean | A non-graphical bean that fires off an event after a specified delay. |
| ArrowBean | A graphical left-arrow/right-arrow bean. |
A graphical progress-display bean. | ||
A graphical numeric | ||
A graphical font-chooser bean. This bean makes use of the NumberFieldBean bean. | ||
A graphical font-chooser bean that displays the current font and provides OK/Cancel buttons. This bean makes use of the FontChooserBean bean. | ||
A graphical font-chooser bean that pops up the font selector in a separate dialog. This bean makes use of the FontSelectorBean bean. |
We discussed the AlarmBean and ArrowBean beans in detail last month; in this episode, we will discuss the remaining beans in varying levels of detail.
You may be wondering why weโre building three font beans. The ultimate goal is simply to produce a font selector bean that pops up a font dialog when the user clicks on a button. This task very naturally divides into the three beans weโll produce: The first is the user interface for the font selection, the second adds dialog controls and a font sample, and the third introduces a button to pop up the dialog and contains the basic dialog-handling code.
Without beans, we would have to develop these items as specialized AWT components or as a single monolithic class; using beans, we can develop the three parts as independent beans that are reusable in their own right.
Our scope
As with the first installment of this series, we are only concerned with the beanisms of these classes and not the actual nuts and bolts that make them tick. As a result, we will discuss the beans in skeletal form, highlighting in red the fragments that are of particular relevance, and leaving the other details for you to peruse in your spare time. Neither will we concern ourselves with the customizers, which we covered in sufficient detail with our discussion of the first two beans.
To see the forced labor behind the beans, check out the complete source code.
Building the ProgressBean bean
ProgressBean
is a simple progress display bean. It is a custom AWT component that displays a percentage value and graphical bar-representation of this value, as shown in the figure below. It exposes two properties: the current and maximum bar values.

The current value is exposed as an observable property. Observable properties are properties whose changes can be observed. Observers are registered with the bean in the same way that event listeners are, and they are notified whenever a property changes. Individual properties of a bean must be made explicitly observable by the bean; it is not possible to observe changes to just any property of any bean.
This bean is implemented with the following two classes:
ProgressBeanโ The main bean classProgressBeanBeanInfoโ The bean information class
ProgressBean class is the main bean class, a simple custom AWT component and Java bean.public class ProgressBean extends Component ...
This bean is a lightweight component, so we extend Component instead of Canvas, and provide an appropriate paint() method. The lightweight component framework is more efficient than the traditional custom-component framework, requiring fewer resources of the local windowing system. As a component, we automatically inherit the serializability mandated by JavaBeans, and we provide the default no-arg constructor.
public void setBarground (Color c) ...
public Color getBarground () ...
public synchronized void setMaximum (int m) ...
public int getMaximum () ...
Here, we expose the Color property barground (the color of the displayed bar) and the int property maximum (the maximum bar value).
public synchronized void setValue (int v) {
if (value != v) {
value = v;
repaint ();
fireValueChange ();
}
}
public int getValue () ...
The int property value is observable, which means that we must inform all interested listeners whenever its value changes. To this end, we call our fireValueChange() method to inform the listeners whenever setValue() is called.
protected PropertyChangeSupport listeners = new PropertyChangeSupport (this);
public void addPropertyChangeListener (PropertyChangeListener l) {
listeners.addPropertyChangeListener (l);
}
public void removePropertyChangeListener (PropertyChangeListener l) {
listeners.removePropertyChangeListener (l);
}
Here, we maintain a list of objects that are registered to be notified whenever an observable property changes. We use the class PropertyChangeSupport from the java.beans package to maintain this list. The constructor for this class requires us to specify the bean that will be the origin of property change events; in this case, it is this, and the methods that it provides allow us to maintain the list.
By exposing the methods addPropertyChangeListener() and removePropertyChangeListener(), we automatically indicate that this bean has observable properties. We do not, however, indicate which properties are observable. That information must be appropriately documented with the bean.
protected Integer oValue = new Integer (value);
protected void fireValueChange () {
listeners.firePropertyChange ("value", oValue, oValue = new Integer (value));
}
We call this method to notify listeners of a change in our value property; we use the firePropertyChange() method of our list to propagate this notification. The first parameter is the name of the property, which should match the name of an exposed property; the second parameter is the old value of the property; and the third property is the new value. The PropertyChangeSupport class returns without doing anything if the old and new values are the same.
ProgressBeanBeanInfo class simply describes the ProgressBean bean, obscuring any inherited information that we wish to obscure.Building the NumberFieldBean bean
This bean implements a common user-interface component, the rollable number entry field โ a numeric text field that provides increment and decrement arrows, as shown in the figure below. This bean brings up an important JavaBeans concept:
programmatic manipulation of beans
.

Programmatic manipulation of beans refers to the mechanisms that JavaBeans provides for programmatically creating and accessing beans. Although it is possible to access beans using the standard Java object creation (new X ()) and type-casting mechanisms ((Y) x), it is recommended that you use the provided JavaBeans mechanisms to allow for future extension of the JavaBeans framework.
This bean is implemented with the following two classes:
NumberFieldBeanโ The main bean classNumberFieldBeanBeanInfoโ The bean information class
The NumberFieldBean class, the main bean class, is an AWT container that adds three components: two ArrowBean beans and a TextField. Programmatic access to the ArrowBean class requires that we make use of the bean manipulation mechanisms I mentioned a moment ago.
The current numeric value is exposed as an observable property. Although it is a normal property that can be accessed and manipulated through the usual beans accessor methods, it is also observable, so listeners can register to be notified whenever its value changes. We do not fire an event when the user presses Return, although that would be an obvious extension to this class.
public class NumberFieldBean extends Container implements ActionListener ...
We extend Container and implement ActionListener in order to receive events from the beans and AWT components that we use. Extending Container instead of the more traditional Panel means that this bean, like the ProgressBean bean is a lightweight component.
public NumberFieldBean () ...
As a bean, we must provide a public no-arg constructor. Note that we should not provide other constructors for programmatic use; doing so would go against the JavaBeans access mechanism.
try {
down = (ArrowBean) Beans.instantiate (getClass ().getClassLoader (), "org.merlin.beans.arrow.ArrowBean");
} catch (Exception ex) {
ex.printStackTrace ();
}
Here, we create an ArrowBean using the programmatic beans instantiation mechanism. We donโt use the standard Java new operator; instead, we use the instantiate() method of class Beans. We specify the ClassLoader to use for loading the bean class; in this case, we use our own ClassLoader and the fully qualified name of the bean class ("org.merlin.beans.arrow.ArrowBean"), and cast the resulting Object to the appropriate class.
Note that the instantiate() method may throw a variety of exceptions (for example, if the specified bean could not be located). We simply catch and display any such exceptions, which, by the way, should not occur if the bean is appropriately installed.
add ("East", (Component) Beans.getInstanceOf (down, Component.class));
Here, we cast the ArrowBean to a Component and add it as a normal Component. We donโt use the standard (Component) type-casting mechanism, and we donโt use the fact that our AlarmBean is a subclass of Component; instead, we use the getInstanceOf() method of class Beans. We specify the bean that we wish to cast and the Class object to which we wish to cast it (in this case, Component.class).
Although this approach makes little sense right now, future versions of JavaBeans will support beans composed of multiple class files, as well as beans that can expose different aspects of themselves as the different classes. For example, a bean could appear to subclass both Component and RemoteObject by providing two coupled classes: a Component and a RemoteObject. Using the JavaBeans type-casting mechanism, the appropriate bean object can be returned automatically, so beans can have apparent multiple-inheritance, although Java does not natively support this. For details, see the โGlasgowโ JavaBeans specification. (A link to this spec is provided in the Resources section of this article.)
It is necessary for us to use these beans access mechanisms now, so we can transition our beans to future JavaBeans technologies without any problems.
down.setDirection (ArrowBean.LEFT);
down.addActionListener (this);
Here, we configure the ArrowBean using the setDirection() property accessor and the addActionListener() registration method. We can use these property accessors and listener registration methods directly on the bean we just created; it is only necessary to use the JavaBeans type-casting feature when we are accessing an aspect of a bean that is inherited from another class.
public synchronized void setValue (int v) {
field.setText (String.valueOf (v));
fireValueChange (getValue ());
}
public synchronized int getValue () ...
Here, we expose the int property value, which is the value of this field. This property is observable, so we must notify listeners whenever it is changed. We do this by calling our fireValueChange() method.
public void setColumns (int c) ...
public int getColumns () ...
public synchronized void setMinimum (int m) ...
public int getMinimum () ...
public synchronized void setMaximum (int m) ...
public int getMaximum () ...
public synchronized void setStep (int s) ...
public int getStep () ...
Here, we expose the int properties columns, minimum, maximum, and step, which are, respectively, the number of columns displayed in the TextField, the minimum and maximum values that this field should hold, and the amount by which the arrow buttons should alter the value. These properties are not observable.
Note that we use synchronization to ensure thread safety where appropriate.
public synchronized void actionPerformed (ActionEvent e) {
int value = getValue ();
if (e.getSource () == down) {
if (value > minimum) {
value = (value - step > value) ? minimum : clamp (value - step);
setValue (value);
}
} ...
This method is called when an action event occurs from either of our arrows or the TextField; we use the getSource() method of the event to determine its origin and proceed accordingly. In this case, the user is clicking on the down arrow, so we call the setValue() property accessor to change our value. the setValue() method automatically notifies registered listeners of the new value.
public void addPropertyChangeListener (PropertyChangeListener l) ...
public void removePropertyChangeListener (PropertyChangeListener l) ...
protected void fireValueChange (int v) ...
These methods maintain the list of property change listeners. We use the PropertyChangeSupport class in exactly the same manner as we did with the ProgressBean class.
The NumberFieldBeanBeanInfo class simply describes the NumberFieldBean bean, obscuring inherited information that we wish to obscure.
Building the FontChooserBean bean
The
FontChooserBean
bean, shown below, is the foundation of our full font-selector bean. It is a custom component that provides for selection of the various properties of a font: its name, size, and style. This bean provides only for selection of these properties; it does not display a sample of the chosen font.
The font represented by this bean is exposed as an observable property, which allows interested classes to easily display a sample of the currently chosen font.
This bean demonstrates another new JavaBeans feature: listening for property changes from an enclosed bean. We implement the FontChooserBean bean with the following two classes:
FontChooserBeanโ The main bean classFontChooserBeanBeanInfoโ The bean information class
The FontChooserBean class is the main bean class. The FontChooseBean bean is another lightweight AWT component, composed of a Choice to allow selection of the font name, a NumberFieldBean to allow selection of the font size, and two Checkboxes to allow selection of the font style.
public class FontChooserBean extends Container implements PropertyChangeListener ...
The NumberFieldBean that we use exposes its value as an observable property, so we must implement the PropertyChangeListener interface to be notified of updates to its value.
size = (NumberFieldBean) Beans.instantiate (getClass ().getClassLoader (), "org.merlin.beans.numberfield.NumberFieldBean");
add ((Component) Beans.getInstanceOf (size, Component.class));
We are using another bean within this bean, so we must again use the JavaBeans programmatic access mechanisms: we instantiate the NumberFieldBean with Beans.instantiate() and cast it to a Component with Beans.getInstanceOf().
size.setColumns (3);
size.setValue (oFont.getSize ());
size.setMinimum (1);
size.setMaximum (128);
size.setStep (1);
size.addPropertyChangeListener (this);
The NumberFieldBeanโs properties can be accessed with its usual property accessors. We also register as a listener for its property change events.
public void setStyle (Font f) ...
public Font getStyle () ...
This FontChooserBean bean exposes a single Font property, style. We donโt use the more obvious property name, font, because that is already used by the Component superclass.
This property automatically combines the chosen font name, size, and style into a single Font object for easy manipulation.
public void propertyChange (PropertyChangeEvent e) {
if ("value".equals (e.getPropertyName ()) || (e.getPropertyName () == null))
fireStateChange ();
}
This method is called whenever a property of the NumberFieldBean changes. We verify that the changed property is indeed value, calling fireStateChange() if so. This process notifies all registered listeners that the font represented by this bean has changed. Note that a property name of null means that any number of properties of the source have changed, so we also include this case.
We also include code to fire a property change event if the user selects a new typeface from the Choice, or changes the font style with the Checkboxes.
public void addPropertyChangeListener (PropertyChangeListener l) ...
public void removePropertyChangeListener (PropertyChangeListener l) ...
protected void fireStateChange () ...
These methods maintain the list of listeners to be notified whenever our Font property has changed.
The FontChooserBeanBeanInfo class simply describes the FontChooserBean bean, obscuring inherited information that we wish to obscure.
Building the FontSelectorBean bean
The
FontSelectorBean
bean extends the
FontChooserBean
with a sample display of the currently selected font, and OK and Cancel buttons that fire events of type
ItemEvent
to indicate a font selection or cancel by the user. The
FontChooserBean
is shown in the figure below.
In implementing this bean, we make use of inner classes to receive events of the enclosed beans and AWT components. (See the manifest file on the sources page for details of packaging up the inner class files with this bean.) We implement the FontSelectorBean bean with the following two classes:
FontSelectorBeanโ The main bean classFontSelectorBeanBeanInfoโ The bean information class
The FontSelectorBean class is the main bean class. This bean is an AWT container that encloses a FontChooserBean, two Buttons and a Label to display a font sample.
There is one small graphical nicety that I want to point out: clicking on the font sample changes the display to a TextField that allows the user to alter the sample message; pressing Return in the TextField redisplays the font sample. This is a simple demonstration of the improved event handling provided with JDK 1.1.
We reuse the existing AWT event ItemEvent to propagate events. We fire a selection event when the user clicks on OK, and a deselection event when the user clicks on Cancel.
public class FontSelectorBean extends Container implements ItemSelectable ...
We extend Containerto create a lightweight component and implement the ItemSelectable interface; this step is required if we wish to use the existing ItemEvent mechanisms.
chooser = (FontChooserBean) Beans.instantiate (getClass ().getClassLoader (), "org.merlin.beans.font.FontChooserBean");
p.add ("Center", (Component) Beans.getInstanceOf (chooser, Component.class));
We instantiate and manipulate the FontChooserBean using the JavaBeans programmatic access mechanisms.
chooser.addPropertyChangeListener (new PropertyChangeListener () {
public void propertyChange (PropertyChangeEvent e) {
if ("style".equals (e.getPropertyName ()))
label.setFont ((Font) e.getNewValue ());
}
});
Here, we register a property listener for the events of the FontChooser; whenever the user alters the font, we change the displayed sample accordingly. Note that we are using the new inner class mechanism of JDK 1.1. Instead of implementing the PropertyChangeListener interface and providing a propertyChange() method, we create a new anonymous class that implements the desired interface in the requisite manner. In this case, we provide an implementation of the propertyChange() method. Note that the inner class has easy access to variables of its enclosing class.
Using inner classes in this manner is convenient because it keeps the implementation of the specific handler method close to the event registration; however, this technique results in an extra class file that must be shipped with this bean. In this example, we use an inner class more for pedagogic reasons than stylistic; in a real-world implementation it would be better to just implement the interface ourselves.
public void setStyle (Font f) ...
public Font getStyle () ...
Here we expose the selected font as a property, calling the corresponding methods of the enclosed FontChooser.
public void addItemListener (ItemListener l) ...
public void removeItemListener (ItemListener l) ...
The addItemListener and removeItemListener methods allow classes to register interest in the ItemEvents that we generate. As these follow the JavaBeans design patterns, we automatically expose the events that we fire. Internally, we use the AWTEventMulticaster class to maintain the list of listeners, much as we used it in the ArrowBean example last month.
protected void fireSelection (boolean okay) {
if (listeners != null)
listeners.itemStateChanged (
new ItemEvent (this, ItemEvent.ITEM_STATE_CHANGED, getStyle (),
okay ? ItemEvent.SELECTED : ItemEvent.DESELECTED));
}
Here, we fire an ItemEvent to all registered listeners; we simply call the itemStateChanged() method of our list to propagate the event to all registered listeners. If the user clicks on OK, we send a selection event; otherwise, we send a deselection event.
Class FontSelectorBeanBeanInfo
TheFontSelectorBeanBeanInfo class simply describes the FontSelectorBean bean, obscuring inherited information that we wish to obscure.
Building the FontDialogBean bean
This is the final bean of our series, and the last on the font selector theme. The
FontDialogBean
bean displays a sample of the chosen font and provides a
Button
that pops up a FontSelectorBean in a separate dialog, as shown in the figure below. This provides all the function of the font selector, but requires significantly less user-interface space.
This approach allows us to fire a new FontEvent event that includes the selected font in its payload anytime the user selects a font. This custom event is an AWT event, and so obeys the conventions thereof.
This bean is implemented with the following four classes:
FontDialogBeanโ The main bean classFontEventโ The font event classFontListenerโ The event listener interfaceFontDialogBeanBeanInfoโ The bean information class
The FontDialogBean class is an AWT container that includes a Label sample of the selected font, and a Button that launches FontSelector in a dialog.
One internal item of interest is the creation of the dialog: We iterate through the ancestors of the FontDialogBean until we find the top-most Container. If we find a Frame, then we can open a dialog that blocks all user input; otherwise, we open a normal Frame.
public class FontDialogBean extends Container implements ActionListener, ItemListener ...
We implement the main FontDialog as a lightweight component. We must implement ActionListener to receive events from the Button and ItemListener to receive events from the FontSelector.
public void setStyle (Font f) ...
public Font getStyle () ...
Here, we expose the Font property style.
selector = (FontSelectorBean) Beans.instantiate (getClass ().getClassLoader (), "org.merlin.beans.font.FontSelectorBean");
dialog.add ("Center", (Component) Beans.getInstanceOf (selector, Component.class));
Creation of the FontSelectorBean follows the standard JavaBeans accessor mechanisms. As always, we ignore any exceptions that arise during this process. We create this bean only when the user first clicks on the change Button.
selector.addItemListener (this);
We register as a listener for events from the FontSelector through its addItemListener() listener registration method.
Window dialog;
public void itemStateChanged (ItemEvent e) {
dialog.dispose ();
if (e.getStateChange () == e.SELECTED) {
sample.setFont ((Font) e.getItem ());
fireFontEvent ();
}
}
When the user clicks on either OK or Cancel, we hide the dialog by calling its dispose() method; if the user clicks on OK, then we alter our sample font and fire an event indicating the change.
public void addFontListener (FontListener l) ...
public void removeFontListener (FontListener l) ...
These methods allow classes to be registered to listen for FontEvents that are fired by this FontDialogBean. We internally maintain the list of FontListeners in a Vector.
protected void fireFontEvent () {
FontEvent event = new FontEvent (this, FontEvent.FONT_SELECTED, getStyle ());
Vector listeners = (Vector) this.listeners.clone ();
for (int i = 0; i < listeners.size (); ++ i)
((FontListener) listeners.elementAt (i)).fontSelected (event);
}
This method iterates through our list of listeners, notifying them with a newly created FontEvent. We clone the list to maintain thread safety, as we did with the AlarmBean.
The FontEvent class is a custom AWT event that contains a newly selected Font as its payload.
public class FontEvent extends AWTEvent ...
All custom AWT events must subclass from AWTEvent.
public static final int FONT_SELECTED = RESERVED_ID_MAX + 1;
protected Font font;
public FontEvent (Component source, int id, Font font) {
super (source, id);
this.font = font;
}
The AWTEvent superconstructor requires the origin of the event, as well as a numeric identifier to be specified. This value can be any value above AWTEvent.RESERVED_ID_MAX, so we select an appropriate value for FONT_SELECTED. A more complex application would use various identifiers to distinguish among different types of FontEvent.
public Font getFont () {
return font;
}
We provide a simple accessor to extract the payload font from this event.
public String paramString () {
return "FONT_SELECTED,font=" + font;
}
The paramString() method provides a convenient textual description of an event; we provide an appropriate implementation.
The FontListener class is a simple listener interface for classes interested in receiving FontEvents.
public interface FontListener extends EventListener {
public void fontSelected (FontEvent e);
}
All listener interfaces must extend EventListener; no special superinterface exists for AWT event listeners.
We just declare the method through which font selections will be delivered.
The FontDialogBeanBeanInfo class simply describes the FontDialogBean bean, obscuring inherited information that we wish to obscure.
The smell of roasted beans
After that brisk trot through a bunch of beans, itโs time to sit back, smell the beans, and digest the result. In last monthโs episode, we packaged the beans up in JAR files and used them in the BeanBox. This time, weโll look at using them programmatically. This is not, in fact, far removed from the usage that weโve seen so far, so thereโs not much new here.
For completenessโ sake, however, manifest files and JAR files suitable for use with the BeanBox are all included in the accompanying sources page.
Sampling our wares โ an example program
Weโve got all the pieces to build a program, so letโs get down to business. The simple program weโll create pops up a Frame with two NumberFieldBeans, a FontDialogBean, a ProgressBean, and a Label, as shown in the figure below.

Once you select a font, an AlarmBean counts the ProgressBean up to its maximum value and then changes the Label. The two NumberFieldBeans control the rate of counting of the ProgressBean and its maximum value.
I encourage you to peruse the complete source file, BeanTest.java, as we work through the various pieces.
public class BeanTest {
public static void main (String[] args) throws Exception {
final Frame frame = new Frame ("BeanTest") {
public Insets getInsets () {
Insets i = super.getInsets ();
return new Insets (i.top + 10, i.left + 10, i.bottom + 10, i.right + 10);
}
};
frame.setLayout (new GridLayout (0, 1));
This whole application resides in the main() method. We use anonymous inner classes to perform all event handling and detail work.
In this case, we create a new Frame subclass that has a 10-pixel border around all sides, as returned by the overridden getInsets() method. This is a very convenient use of inner classes to customize a base class in a simple way.
We mark the frame variable as final (that is, immutable), so that we can use it in later inner classes; local variables can only be accessed by an inner class if they are so declared.
Panel top = new Panel ();
NumberFieldBean delay = (NumberFieldBean) Beans.instantiate
(null, "org.merlin.beans.numberfield.NumberFieldBean");
delay.setMinimum (1);
delay.setMaximum (1000);
delay.setStep (50);
delay.setValue (200);
top.add (new Label ("ms"));
top.add ((Component) Beans.getInstanceOf (delay, Component.class));
NumberFieldBean max = (NumberFieldBean) Beans.instantiate
(null, "org.merlin.beans.numberfield.NumberFieldBean");
max.setMinimum (1);
max.setMaximum (100);
max.setStep (5);
max.setValue (10);
top.add (new Label ("max"));
top.add ((Component) Beans.getInstanceOf (max, Component.class));
frame.add (top);
This piece of code creates and configures the two NumberFieldBeans that reside at the top of the frame. We use the standard programmatic access mechanisms that weโve already encountered, adding the NumberFieldBeans in a row with label descriptors.
Note that we use a null ClassLoader for loading the beans, which indicates that JavaBeans should use the default ClassLoader. This approach is appropriate for a standalone application of this type.
final FontDialogBean fontDialog = (FontDialogBean) Beans.instantiate
(null, "org.merlin.beans.font.FontDialogBean");
frame.add ((Component) Beans.getInstanceOf (fontDialog, Component.class));
final ProgressBean progress = (ProgressBean) Beans.instantiate
(null, "org.merlin.beans.progress.ProgressBean");
progress.setMaximum (max.getValue ());
progress.setBarground (new Color (0x9999cc));
frame.add ((Component) Beans.getInstanceOf (progress, Component.class));
final Label label = new Label ("waiting", Label.CENTER);
frame.add (label);
This piece of code creates, configures, and adds the FontDialogBean that sparks the reaction in this application, the ProgressBean that counts down to extinction, and the Label that documents the stages of execution.
final AlarmBean alarm = (AlarmBean) Beans.instantiate
(null, "org.merlin.beans.alarm.AlarmBean");
alarm.setTimeout (delay.getValue ());
This AlarmBean is used to periodically execute the tasks that form this application.
delay.addPropertyChangeListener (new PropertyChangeListener () {
public void propertyChange (PropertyChangeEvent e) {
alarm.setTimeout (((Integer) e.getNewValue ()).intValue ());
}
});
We first register a property change listener, in the form of an inner class, that sets the alarm timeout to be the value of the delay NumberFieldBean. Whenever the user alters delay, the AlarmBean is automatically updated.
max.addPropertyChangeListener (new PropertyChangeListener () {
public void propertyChange (PropertyChangeEvent e) {
progress.setMaximum (((Integer) e.getNewValue ()).intValue ());
}
});
This PropertyChangeListener inner class ties the max NumberFieldBean to the ProgressBeanโs maximum value.
fontDialog.addFontListener (new FontListener () {
public void fontSelected (FontEvent e) {
fontDialog.setEnabled (false);
label.setText ("started");
progress.setValue (0);
alarm.start ();
}
});
When the user selects a font, we disable the FontDialogBean, notify the Label, reset the ProgressBean, and start the AlarmBean. As we have already set up, the alarm delay will automatically reflect the value of the appropriate NumberFieldBean.
alarm.addAlarmListener (new AlarmListener () {
public void alarmCalled (AlarmEvent e) {
int v = progress.getValue (), m = progress.getMaximum ();
if (v < m)
progress.setValue (v + 1);
if (v + 1 < m)
alarm.start ();
}
});
This inner class handles the AlarmBean firing its event. We increment the ProgressBean value, and if it has not reached its maximum, we restart the alarm. This method will be repeated until the ProgressBean reaches its maximum.
progress.addPropertyChangeListener (new PropertyChangeListener () {
public void propertyChange (PropertyChangeEvent e) {
if (((Integer) e.getNewValue ()).intValue () >= progress.getMaximum ()) {
label.setText ("finished");
label.setFont (fontDialog.getStyle ());
fontDialog.setEnabled (true);
}
}
});
This inner class monitors the current value of the ProgressBean, and when it reaches its maximum it resets this system and adjusts the Label accordingly. This method will be called in response to the previous inner class incrementing the ProgressBeanโs value.
frame.addWindowListener (new WindowAdapter () {
public void windowClosing (WindowEvent e) {
System.exit (0);
}
});
frame.pack ();
frame.setVisible (true);
We finally add an inner class that exits when the user closes the frame, and then we pack and show the frame.
This entire sequence of code involves seven inner classes, so when you compile this code, there will be seven class files for the inner classes (named BeanTest.class through BeanTest.class) in addition to the main class file for the BeanTest class.
Conclusion
Weโve encountered much of the existing JavaBeans specification in this brief tour, including properties, events, listeners, customizers, descriptors, the BeanBox, and programmatic access to beans. As an ephemeral topic, however, weโre already behind the JavaBeans times. New features, including ActiveX bridges, multiple appearances, and so forth are either available now or are expected shortly. For details of these new features, Iโd advise becoming a regular reader of the
JavaBeans Advisor
. Iโve provided a link to this newsletter in the
section of this article.
For those who wish to stay on solid ground, I think youโll agree that there is a lot to work with in the preliminary JavaBeans specification. As long as you follow the various guidelines, especially those of programmatic access, then JavaBeans code that you write now should be fully future-proof. Of course, the same is not true of the AWT, so you must mind yourself on that. The Java Foundation Classes, or JFC, are a fully JavaBeansified replacement for the AWT, which will (hopefully) address many of the current AWT limitations later this year, but will also throw a monkey-wrench into much user-interface code. If you want to be safe, keep an eye out for that too. Iโve included a link to the latest release in the Resources section.


