Building a bevy of beans

JavaBeans has transformed Java from a simple language to a powerful RAD platform; learn how you can get the most from this new Java paradigm

When the JavaBeans specification was finalized in time for inclusion in JDK 1.1, a whole lot of Java programmers heaved a big sigh of relief. The JavaBeans specification describes a component architecture for Java -- not unlike Microsoft's COM -- which allows Java to be used in a RAD environment. The RAD paradigm separates programmers into two types: component developers, who write small modules of code at the source level, and component users, who create large applications by visually combining these modules (components, or beans in Java parlance). Application development is significantly eased because the component user does not need to write large amounts of source code, and can easily make use of new, existing, and third-party components.

The coming storm

In this series of articles we are going to focus on developing beans at the source level, which we'll test in the

BeanBox

, a rudimentary JavaBeans test environment supplied with the Beans Development Kit (BDK). Writing beans is similar in many ways to writing custom GUI components, but with a number of caveats. For this reason, we will not step thoroughly through a single bean, but will instead work through the significant features of a small number of beans and conclude by linking these all together.

Prerequisites

As a prerequisite, I strongly suggest you review the following JavaWorld articles:

  • "JavaBeans and ActiveX go head to head" -- This article is a technical comparison of JavaBeans and ActiveX and provides a useful general introduction to the concept of a component architecture. In addition, the article lays out the JavaBeans specification in more detail than I can provide here.

  • "Moving to JDK 1.1: Using the delegation event model to create custom AWT components" -- This article discusses building a graphical component using the JDK 1.1 event model, which closely parallels many of the features of the JavaBeans event model. The article also covers important features of custom components under 1.1 which we will not discuss in this series.

The beans

Although we'll cover seven beans over the course of this series, a few are simply the building blocks of other, more complex beans. One important feature of beans is that they may themselves be constructed from other beans. What this means is that we can leverage existing code and conveniently modularize more significant component development efforts, resulting in a more useful, structured development approach.

As you will see, even the building-block beans are useful for other purposes, which demonstrates another benefit of the JavaBeans approach: As you develop with JavaBeans you build up a library of components that can be reused in future development efforts, saving the time it would have taken to extract or rewrite them.

Let's take a quick look at the beans we'll be implementing in this series.

AlarmBean
AlarmBean
A non-graphical bean that fires off an event after a specified delay.
ArrowBean
ArrowBean
A graphical left-arrow/right-arrow bean.
NumberFieldBean
NumberFieldBean
A graphical numeric TextField bean with roll buttons.
ProgressBean
ProgressBean
A graphical progress-display bean.
FontChooserBean
FontChooserBean
A graphical font-chooser bean.
FontSelectorBean
FontSelectorBean
A graphical font-chooser bean that displays a sample of the chosen font and provides OK/Cancel buttons.
FontDialogBean
FontDialogBean
A graphical font-chooser bean that pops up the font selector in a separate dialog.

We'll only tackle the first two this month. If you want a head start, check out the complete source code for these two beans.

Our scope

For the sake of brevity, we will only be discussing those parts of the classes that are relevant to JavaBeans; internal details related to threading and the AWT are omitted in all cases. You are invited to read the included source code and the aformentioned articles if you desire that information.

This month we'll be covering the general principles of JavaBeans programming, including properties and custom events, bean information classes, bean customizers, and the BeanBox. Next month we'll detail the creation of beans from other beans, property listeners, and the programmatic use of JavaBeans in an example application.

For convenience, I've highlighted all the code fragments specific to beans (what I call "beanisms") in red to help direct your focus as you peruse this article.

Building the AlarmBean bean As I mentioned earlier, the AlarmBean is an invisible (non-graphical) bean that represents an alarm. The bean fires an event after a specified delay has elapsed. Most common beans are graphical components that constitute part of a user interface, however invisible beans may also be visually manipulated in a BeanBox in order to create the behind-the-scenes function of an application. This visual representation of the AlarmBean bean is shown in the figure to the right.

The AlarmBean bean

This bean introduces two important JavaBeans concepts: properties and events. Properties are the mechanism by which a bean may be customized for a particular application. In this case, the AlarmBean has a "timeout" property that corresponds to a delay: after the alarm is started, it will sleep for this delay before firing its event. Events are the mechanism by which beans are hooked together. An event represents a significant occurrence from a bean (in this case, the alarm going off). A JavaBeans application is created by hooking events up to actions. For example, when the alarm goes off an event is fired, which can start an action like an animation sequence.

The following classes comprise this bean:

Class AlarmBean

The AlarmBean class is the basic bean class. We'll start off with a quick overview of how the class is constructed, then we'll look at the beanisms:

public class AlarmBean implements Runnable, Serializable ...

We inherit from Object and implement Runnable to make use of a timer thread.

protected int timeout = 1000;
public void setTimeout (int t) ...
public int getTimeout () ...

An internal variable timeout holds the alarm timeout value, which is accessible through the setTimeout() and getTimeout() methods.

transient protected Thread alarm;
public synchronized void start () ...
public synchronized void stop () ...
public void run () ...

The start() method creates a new Thread alarm which enters the run() method, sleeps for the specified period, and then fires an alarm event. We can call the stop() method to abort the alarm prematurely.

protected Vector listeners = new Vector ();
public void addAlarmListener (AlarmListener listener) ...
public void removeAlarmListener (AlarmListener listener) ...
protected void dispatchAlarmCall () ...

Classes that wish to be notified when an alarm goes off must implement the AlarmListener interface and register/deregister through the addAlarmListener() and removeAlarmListener() methods, which maintain the Vector listeners. The dispatchAlarmCall() method iterates through this list, calling each AlarmListener's alarmCalled() method.

Now that you have seen how the class is constructed, let's turn our attention to several important JavaBeans features.

Serializability

All beans must be serializable, as this is the mechanism that is usually used to store JavaBeans applications. You must ensure that each bean follows the principles of serializability: implement java.io.Serializable (or java.io.Externalizable), mark fields that should not be serialized as transient, and provide appropriate custom serialization methods, if necessary.

Let's see how this works:

public class AlarmBean implements Runnable, Serializable ...

The AlarmBean class implements Serializable to make use of the default serialization process.

transient protected Thread alarm;

We mark the alarm Thread as transient, so that it will not be serialized. The AlarmBean class is always serialized into an inactive state; it makes no sense to serialize an active Thread.

No-arg constructor

All beans must provide a public no-arg constructor, so that they can be automatically instantiated by a development environment. The normal programmatic instantiation process (Class.newInstance()) does not allow arguments to be passed to a constructor.

public AlarmBean () ...

We don't actually perform any tasks in the constructor, however we provide an empty implementation for the purpose of clarity.

Design patterns

JavaBeans obtains much of its simplicity from the fact that it can programmatically examine the methods that a bean provides (this is termed introspection) and from just the names and signatures of those methods, it can determine the properties, events, and control methods that are exposed by the bean. To this end, the bean must obey design patterns, which are essentially naming conventions. Let's take a look at several design patterns:

public void setTimeout (int t) ...

If a bean provides a method of the form setProperty(Type x), JavaBeans assumes that the bean is exposing a writable property named Property of type Type. JavaBeans can then easily provide the beans user with a visual means to modify this property. In this case, the int timeout property is being exposed for writing.

public int getTimeout () ...

If a bean provides a method of the form Type getProperty(), JavaBeans assumes that the bean is exposing a readable property named Property of type Type. In this case, the int timeout property is being exposed for reading.

public void addAlarmListener (AlarmListener listener) ...
public void removeAlarmListener (AlarmListener listener) ...

If a bean provides methods of the form addTypeListener (TypeListener l) and removeTypeListener (TypeListener l), JavaBeans assumes that the bean is declaring that it can fire events of type TypeEvent. JavaBeans can then easily provide the beans user with a visual means to connect these events to the appropriate actions. In this case, we are exposing that we fire events of type AlarmEvent, which will be delivered through the AlarmListener interface.

These are just a few of the JavaBeans design patterns. Additional patterns for boolean properties, indexed properties, vetoable properties, and the like are also available. Consult the JavaBeans specification for more details.

In addition to these methods, all other public methods that a bean exposes (including those that it inherits from superclasses) are presented as control methods to the beans user.

Thread safety

Finally, all beans must be threadsafe. For a bean to be threadsafe, it must be able to be used in complex multithreaded applications that are not predicated by the implementor, and must not misbehave if manipulated by multiple threads concurrently. Let's see how this feature works:

public synchronized void start () ...
public synchronized void stop () ...

We synchronize the start() and stop() methods to ensure threadsafe control of the AlarmBean. Note that as public methods, start() and stop() are exposed as control methods to the beans user.

Vector listeners = (Vector) this.listeners.clone ();

The Vector class is internally threadsafe, so we don't need any explicit synchronization for access to the list of listeners. Instead, in the dispatchAlarmCall() method we just make a copy of the list of event listeners so that we can easily iterate through the clone without concern for listeners being added to or removed from the original list as we do so.

Class AlarmEvent

The AlarmEvent class implements the actual alarm event that is passed to each AlarmListener, indicating that an alarm has occurred. You can reuse existing events (such as those provided by the AWT); however, in this case, we wish to use a custom event to carry alarm signals.

The AlarmBean bean fires an AlarmEvent

Let's take a closer look at this class.

public class AlarmEvent extends EventObject ...

All JavaBeans events must inherit from java.util.EventObject or a subclass, and the JavaBeans design patterns require that all event names have the form TypeEvent.

public AlarmEvent (AlarmBean source) {
  super (source);
}

The constructor accepts the AlarmBean that was the origin of the event and passes this on to the superconstructor. Recipients of an AlarmEvent can call the inherited getSource() method to determine the origin of the alarm.

AlarmEvent class contains no additional code. We could have added a payload to this event -- for example, a String that indicates the meaning of the alarm and is a property of the AlarmBean class. Instead, we keep things simple and clear. Take a look at the ColorEvent class from the earlier "Moving to JDK 1.1" article for an example of an event with a payload.

Class AlarmListener

The AlarmListener class implements the interface through which alarm events will be notified.

AlarmEvents are delivered through the AlarmListener interface

Here's how this class works:

public interface AlarmListener extends EventListener ...

All JavaBeans listener interfaces must extend java.util.EventListener or a subinterface, and the JavaBeans design patterns require that their name have the form TypeListener, corresponding to an event TypeEvent.

public void alarmCalled (AlarmEvent e);

The listener interface can declare whatever methods it desires, and usually accepts an event object as a parameter. The bean itself will determine the appropriate method to call when it fires its event. In this case, we need only one method, alarmCalled(), which indicates when an alarm has been fired.

1 2 3 Page
Recommended
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more