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.

Class AlarmBeanBeanInfo

The AlarmBeanBeanInfo class is an informational class that describes the events, properties, and control methods, among other details, that our bean is exposing. Because we have followed the JavaBeans design patterns, we don't need to provide this class; the introspection mechanism can determine most of these details automatically. However, a BeanInfo class gives us some additional power and control. Let's take a look:

public class AlarmBeanBeanInfo extends SimpleBeanInfo ...

All bean informational classes must either implement the java.beans.BeanInfo interface or extend the java.beans.SimpleBeanInfo class, which implements the interface with dummy methods. Informational classes must always provide a public no-arg constructor and be named NameBeanInfo where Name is the bean name.

This interface declares a bunch of methods, but we're only going to implement a few; the rest follow essentially the same pattern. Returning null from any of these methods, as the SimpleBeanInfo class does, indicates that JavaBeans should use its usual introspection mechanism to determine the information.

public BeanDescriptor getBeanDescriptor () {
  return new BeanDescriptor (AlarmBean.class, AlarmBeanCustomizer.class);
}

The getBeanDescriptor() method returns a java.beans.BeanDescriptor, which describes the bean class, the bean name, and its customizer, if any. A customizer is a custom class that allows a bean to be visually configured: we'll discuss customizers in more detail a bit later on. We simply create and return a BeanDescriptor with the Class object of the bean (AlarmBean) and the Class object of its customizer (AlarmBeanCustomizer).

Note that we are using a new JDK 1.1 feature here: Type.class returns the Class object for the specified class Type. In this case, AlarmBean.class is equivalent to, but much shorter than, Class.forName("org.merlin.beans.alarm.AlarmBean"), which is the fully qualified name of this bean.

public PropertyDescriptor[] getPropertyDescriptors () {
  try {
    PropertyDescriptor[] descriptors = {
      new PropertyDescriptor ("timeout (ms)", AlarmBean.class,
                              "getTimeout", "setTimeout")
    };
    return descriptors;
  } catch (Exception ex) {
    return null;
  }
}

The getPropertyDescriptors method returns an array of java.beans.PropertyDescriptor objects, which describe the properties that our bean exposes. We provide this method for primarily pedantic purposes: introspection can determine our properties automatically, however this method allows us some more versatility. The getClass() method of Object follows the JavaBeans design patterns and so is usually exposed as a property; for purity, we don't want this. This method also allows us to give more descriptive names to our properties.

In this case, we expose one property called "timeout (ms)" for the bean AlarmBean, which is accessed through the methods getTimeout() and setTimeout(). The PropertyDescriptor class provides a couple of constructors, of which this is just one; consult the API for the rest.

The introspection process that the PropertyDescriptor class uses may throw a variety of exceptions (if, for example, we name a method that does not exist). For this reason, we catch any Exception that may occur and return null. If this happens, the default introspection process will kick in and identify our properties strictly from the JavaBeans design patterns.

public MethodDescriptor[] getMethodDescriptors () {
  try {
    MethodDescriptor[] descriptors = {
      new MethodDescriptor (AlarmBean.class.getMethod ("start", new Class[0])),
      new MethodDescriptor (AlarmBean.class.getMethod ("stop", new Class[0]))
    };
    return descriptors;
  } catch (Exception ex) {
    return null;
  }
}

The getMethodDescriptors() method returns an array of java.beans.MethodDescriptor objects, which describe the methods that our bean exposes. Again, this method is primarily pedantic -- we simply don't wish to expose the run() method or any methods inherited from Object.

In this case, we expose the start() and stop() methods. We use the MethodDescriptor constructor, which accepts a single Method argument, and the getMethod() method of our AlarmBean's Class object to obtain the Method objects.

Note that we are using the new reflection mechanism of JDK 1.1. The second parameter to the getMethods() method describes the parameters of the method being sought; an empty array indicates that the start() and stop() methods take no parameters.

public Image getIcon (int iconKind) {
  if (iconKind == ICON_COLOR_16x16) {
    return loadImage ("AlarmBeanIconColor16.gif");
  } else {
    return null;
  }
}

The last method that we will implement is getIcon(), which returns an iconic representation of our bean (

AlarmBeanIconColor16
) for display in a BeanBox's list of beans. We use the inherited loadImage() method, which loads an image from the directory in which this class is stored; that is, from within the JAR file in which this bean is distributed. The iconKind parameter specifies the type of icon (16x16 or 32x32, color or monochrome); we're providing an implementation for a 16x16 color icon only.

To provide a complete implementation, we would also have to include images for ICON_COLOR_32x32, ICON_MONO_16x16, and ICON_MONO_32x32. It is important that the icon you provide has exactly the requested dimension; if not, a feeling of great loss will come over you when the BeanBox displays nothing.

To view this class in action, load the AlarmBean into the BeanBox (see Using beans in the BeanBox for a discussion of this process) and select Edit|Report. The BeanBox will display the properties, events (determined introspectively), and methods that we are exposing. Compare this to an AlarmBean without a BeanInfo class, where all methods inherited from Object and Runnable are exposed as methods or properties; the resulting JavaBeans interface is much more cluttered.

The AlarmBean customizer

Class AlarmBeanCustomizer

The AlarmBeanCustomizer class implements a customizer. Ordinarily, implementing a customizer is not necessary -- the BeanBox will list the properties of a bean in a property sheet, which allows the user to visually manipulate them. In some cases, however, a bean may be sufficiently complex that a custom configuration tool is necessary. The AlarmBean is one of those cases: Java exists in a digital timeline that is hard for us biological humans to comprehend, hence the need for an analog control utility.

This class is significantly more complicated than the actual AlarmBean itself; it is essentially a graphical analog clock custom AWT component. An analog dial at the top (provided as a GIF that is shipped with the bean) allows the timeout to be set, as does a NumberFieldComponent (described later). We will, however, ignore these details and simply consider the features of relevance to a generic customizer:

public class AlarmBeanCustomizer extends Panel implements Customizer ...

All customizers must extend Component or a subclass (they will be added to a customization panel or dialog within the BeanBox) and implement the Customizer interface. In this case, we extend Panel because we will use other components inside this customizer.

public AlarmBeanCustomizer () ...

Customizers must provide a public no-arg constructor that will be called by the BeanBox. Most initialization will be performed here.

public void setObject (Object bean) ...

The setObject() method, declared by the Customizer interface, will be called by the BeanBox to specify the bean that this customizer will be manipulating. In this case, it will be an AlarmBean, which we can store in an internal variable.

When the user configures the bean through this customizer, we can then alter the bean's properties by simply calling its various property accessors and manipulators.

public void addPropertyChangeListener (PropertyChangeListener l) ...
public void removePropertyChangeListener (PropertyChangeListener l) ...

The Customizer class also requires that we provide methods to notify the BeanBox when we modify a bean. The BeanBox will register PropertyChangeListeners, which must be notified whenever we manipulate the bean. Through this mechanism, the BeanBox can update its own display of the bean's state.

protected void firePropertyChange () {
  listeners.firePropertyChange ("", null, null);
}

We provide the firePropertyChange() method to notify the registered PropertyChangeListeners, which are stored in the listeners list, of any updates that we make. The code>listeners list is actually a PropertyChangeSupport list, which we'll get into next month). We simply fire an empty property change event whenever we alter the bean.

To see this customizer in action, add the AlarmBean into the BeanBox and select Edit|Customize to display the customization dialog. Again, we'll briefly discuss how to add beans to the BeanBox later in this article.

Building the ArrowBean bean The ArrowBean bean is a graphical left/right arrow button, and a much more sedate chap than the previous example. It is intended primarily for use as a building block in the NumberFieldBean, however as we will see it is also useful for other purposes -- including its own customizer.

The ArrowBean bean

The most important feature of this example is that we will reuse an existing AWT event and can therefore make use of some AWT features.

Let's briefly review the classes that comprise this bean:

Class ArrowBean

The ArrowBean class is a custom AWT component that displays a left or right arrow button. When the bean user clicks the arrow, it fires an ActionEvent, which it repeats until the the user releases the mouse button. I will only detail the code that is relevant to JavaBeans, which allows us to bypass a lot of AWT componentry detail.

The ArrowBean bean fires an ActionEvent

Let's walk through this class:

public class ArrowBean extends Canvas ...

The ArrowBean class extends Canvas, which is basically a blank page for custom components. We don't need to implement Serializable because we automatically inherit this from the Component superclass.

public static final String LEFT = "left", RIGHT = "right";

The direction of the arrow is specified as a String: either "left" or "right".

public ArrowBean () ...

As a bean, we provide a public no-arg constructor that performs our basic initialization.

protected String direction = LEFT;
public void setDirection (String d) {
  direction = LEFT.equals (d) ? LEFT : RIGHT;
  repaint ();
}
public String getDirection () {
  return direction;
}

Here, we expose a String property (direction) that specifies whether the arrow is left-pointing or right-pointing. We cannot throw an IllegalArgumentException if an invalid direction is chosen because it conflicts with how the BeanBox operates. Instead, we simply translate whatever is passed in into either "left" or "right". As you will see, the customizer makes the configuration process easier.

In addition to this property, our bean will expose all the properties that it inherits from Component and Object: "foreground", "background", "visible", "enabled", and so forth.

protected ActionListener listeners;
public synchronized void addActionListener (ActionListener l) {
  listeners = AWTEventMulticaster.add (l, listeners);
}
public synchronized void removeActionListener (ActionListener l) {
  listeners = AWTEventMulticaster.remove (l, listeners);
}

Here, we expose that we fire ActionEvents by providing the expected addActionListener() and removeActionListener() methods. In addition to this event, our bean will expose all the events that it inherits from Component: mouse events, focus events, and so forth.

The noteworthy code here is our use of the java.awt.AWTEventMulticaster class to maintain the list of listeners. The aforementioned article on JDK 1.1 components provides details on how this is implemented, but for now, all you need to know is that it essentially implements all of the listener interfaces for the standard AWT events and provides add() and remove() methods, which can be used to implement a list of any of these listeners in this manner.

protected synchronized void fireActionEvent () {
  if (listeners != null)
    listeners.actionPerformed (
      new ActionEvent (this, ActionEvent.ACTION_PERFORMED, direction));
}

Here, we fire an ActionEvent to indicate that the arrow has been pressed or is repeating.

We create an ActionEvent with this as the source of the event, ACTION_PERFORMED as the AWT identifier of the event, and the arrow direction as the command code of the event. Each AWT event is assigned an identifier that must be correctly passed to the constructor; the value is usually obvious.

We then pass this new ActionEvent to the actionPerformed() method of our listeners list. The AWTEventMulticaster class automatically takes care of propagating the event to all registered listeners.

Class ArrowRoller

The internal ArrowRoller class represents the thread that keeps an arrow firing ActionEvents as long as the mouse is depressed. The details of this thread are of no interest in the context of JavaBeans; however, our use of threads has one noteworthy aspect: Some Java virtual machines have significant memory leak problems associated with the creation of Threads. Creating a new Thread every time an arrow is pressed could cause problems (for example, a 400K permanent memory loss for every click). Instead, we use a single static ArrowRoller thread for the entire ArrowBean class. This thread sleeps until an arrow is clicked, then proceeds to periodically notify the arrow until it is deregistered, at which point it goes back to sleep. For further details, peruse the source; most of this logic lies in the ArrowBean class.

Class ArrowBeanBeanInfo

The ArrowBeanBeanInfo class is a BeanInfo class for the ArrowBean bean that specifies our customizer class and occludes some events from the outside world.

public class ArrowBeanBeanInfo extends SimpleBeanInfo ...

As expected, we follow the design patterns for the class name and extend the SimpleBeanInfo class.

public BeanDescriptor getBeanDescriptor () ...

The getBeanDescriptor() method follows the form of the AlarmBean, specifying a customizer for this bean.

public EventSetDescriptor[] getEventSetDescriptors () {
  try {
    EventSetDescriptor[] events = {
      new EventSetDescriptor (ArrowBean.class, "action", ActionListener.class,
                              "actionPerformed")
    };
    return events;
  } catch (IntrospectionException ex) {
    return null;
  }
}

The getEventSetDescriptors() method returns an array of java.beans.EventSetDescriptor objects that describe the events that this bean produces. For clarity, we obscure all the events that we inherit from Component, exposing only the ActionEvent that we create.

We use the EventSetDescriptor constructor, which accepts the Class of the bean, the name of the event, the Class of the listener interface, and the name of the single method that declares; other constructors provide more variety. As before, we must catch any offending exceptions and return null.

public Image getIcon (int iconKind) ...

The getIcon() method follows the form of the AlarmBean, specifying an icon (

ArrowBeanIconColor16
) for this bean.

Class ArrowBeanCustomizer

The ArrowBeanCustomizer class implements a customizer for the ArrowBean. We add a left and a right ArrowBean to a Panel. Clicking on either arrow directs the bean being customized accordingly.

The ArrowBean customizer

In many ways, the ArrowBeanCustomizer class is similar to the AlarmBeanCustomizer class. I have excised all code except what is strictly relevant to JavaBeans.

public class ArrowBeanCustomizer extends Panel implements Customizer ...

As a customizer, we sublass Component (or, in this case, Panel) and implement Customizer.

public ArrowBeanCustomizer () ...

The public no-arg constructor performs the basic initialization for our customer, setting up the components of the user interface.

public void setObject (Object bean) ...

The setObject() method will be called to specify the target ArrowBean. We simply store a reference in an instance variable.

public void addPropertyChangeListener (PropertyChangeListener l) ...
public void removePropertyChangeListener (PropertyChangeListener l) ...

The addPropertyChangeListener and removePropertyChangeListener methods register and deregister PropertyChangeListeners that must be notified whenever we manipulate a property of the bean being customized.

Our beans are complete, so let's test them out.

Tasting the beans of our labor

Because JavaBeans mandates that developers follow new creation and access idioms when writing a JavaBeans application from scratch -- idioms that we have not yet discussed -- we will use the BeanBox to test out our new beans. We will examine the new access mechanisms in next month's article. You impatient folks can satiate your need to know by reviewing the

AlarmBeanCustomizer

and

ArrowBeanCustomizer

classes, which make use of the JavaBeans access mechanisms.

The BeanBox is a simple visual JavaBeans development environment. Within its confines, you can visually create, configure and, to a certain extent, hook together beans. The configuration part of the BeanBox examines each bean's properties and makes them editable in a property sheet. The hookup part of the BeanBox examines each bean's events and allows them to be connected to the control methods of other beans.

First, you'll need to install the BeanBox. The URL for the BeanBox download page is listed in the Resources section of this article. Once you've got that part out of the way, you can install and use your beans. We'll cover these last two steps in detail in this section.

Packaging beans in JARs

Beans are distributed and installed in a beans development environment, such as the BeanBox, by packaging them in a JAR (Java ARchive) file. These files are essentially ZIP files, and are created using the jar utility supplied with the BDK.

The syntax of the jar command, for the purposes of this process, is:

jar cfm <jar file> <manifest file> <bean files>

The jar file parameter specifies the JAR file to create; for example, AlarmBean.jar.

The manifest file parameter specifies a partial manifest file; for example, AlarmBean.manifest.

Finally, bean files lists all of the class files and image files that should be stored in the JAR file; for example, org/merlin/beans/arrow/*.class org/merlin/beans/arrow/*.gif.

The manifest file specifies which of the stored class files is actually the bean. The syntax of a manifest file is:

Name: file name
Java-Bean: true

The file name portion of the file specifies the actual class file; for example, org/merlin/beans/alarm/AlarmBean.class.

Note that all beans should be created within a Java package (that is, should use the package keyword). The beans that we developed are, respectively, stored in the org.merlin.beans.alarm and org.merlin.beans.arrow packages.

The manifest files for the beans that we developed are provided on the source page. The commands to package up the beans are:

jar cfm AlarmBean.jar AlarmBean.manifest org/merlin/beans/alarm/*.class     org/merlin/beans/alarm/*.gif org/merlin/beans/arrow/ArrowBean.class     org/merlin/beans/arrow/ArrowRoller.class     org/merlin/beans/numberfield/NumberFieldBean.class
jar cfm ArrowBean.jar ArrowBean.manifest org/merlin/beans/arrow/*.class     org/merlin/beans/arrow/*.gif

Note that we must pack up the ArrowBean and NumberFieldBean classes in the AlarmBean JAR file because they are used by its customizer.

Using beans in the BeanBox

To install your beans in the BeanBox, simply copy the new JAR files into the jars directory of the BeanBox and launch the BeanBox. It automatically examines all JAR files that it finds, and makes the enclosed beans available.

The BeanBox will display a list of beans, which will include our two new beans, as shown in the following figure.

Beans displayed in the BeanBox

Click on either of the new beans and drop it on the page. The invisible alarm bean is displayed as a label; the arrow bean is displayed as you would expect.

Select either bean and select Edit|Customize to view the customizer and Edit|Report to print a list of the bean's properties and events to the console. Select Edit|Events and click on a target bean to hook up an event; you will be presented with a choice of which control method should be executed when the event occurs.

Before we part ways, let's build an sample JavaBeans application:

  1. Add an AlarmBean, an ArrowBean, and a Juggler to the BeanBox.
  2. Hook an ActionEvent from the ArrowBean to the startJuggling() method of the Juggler.
  3. Hook an ActionEvent from the ArrowBean to the start() method of the AlarmBean.
  4. Hook an AlarmEvent from the AlarmBean to the stopJuggling() method of the Juggler.

The figure below shows off our creation. Of course, a live applet would be preferred, but only appletviewer/HotJava can fully "appreciate" JDK 1.1 AWT!

Our sample JavaBeans application

When you click on the arrow, the juggler will start juggling and the alarm will start its countdown; after its timeout, the alarm will automatically stop the juggler. You can experiment with more complex applications to your heart's content -- within the limits of the BeanBox.

Conclusion

We've covered a lot in this article: from the basics of JavaBeans through writing beans and their support classes, and on to actually packaging and using these beans in a development environment. What should be obvious is that JavaBeans is not intrusive: if we ignore the customizers (which are optional, and essentially just GUI components) then all that we really have to do is obey good programming practice and observe some naming conventions.

What this framework gains us, however, is the ability to develop components that are easy to use in current development efforts -- the JavaBeans specification does not restrict us in many ways -- and, more importantly, easy to reuse in future developments. As a source-level developer, you might not think to use a graphical development environment for your applications; however, JavaBeans gives you the opportunity to develop code that can automatically be used in such a way.

Next month, we'll go on to do some more advanced things with JavaBeans, including using beans within beans and writing JavaBeans applications from scratch. As you await next month's column, I suggest that you sit back and think about how JavaBeans affects you and how you can use JavaBeans in your own development efforts.

Merlin Hughes is a 12 cylinder V, liquid-cooled 1565hp Rolls-Royce aircraft engine, used to power the Mk. IX series Spitfire during World War II, and lead author of Java Network Programming.

Learn more about this topic

  • Previous Step by Step articles

Join the discussion
Be the first to comment on this article. Our Commenting Policies