[View INPRISE Home Page][View Product List][Search This Web Site][View Available Downloads][Join Inprise Membership][Enter Discussion Area][Send Email To Webmaster]
INPRISE Online And ZD Journals Present:



Easy chair JavaBeans
By Daniel Brookshier

JavaBeans is probably one of the most simplistic and powerful component APIs ever invented. Although the guts of a JavaBeans-compatible tool like JBuilder are fairly complicated, creating JavaBeans components in this environment (or even by hand) is easy for the programmer. JavaBeans are simply Java classes that follow a few basic programming patterns. In fact, most of your custom Java components may already be JavaBeans-compliant—or very close.

The surprisingly small list of requirements for a simple JavaBeans component dictate that it must

  • Have a default class constructor (a constructor that has no parameters).
  • Implement java.io.Serializable.
  • Have property access methods that conform to JavaBeans design signatures (set and get methods).
  • Use the 1.1 Delegation event model.

These requirements aren't complex and don't require a vast knowledge of the JavaBeans API. You'll add a few optional classes, but they have nothing to do with your Bean code (we'll cover them later). And, in case you're wondering, the only difference between a visual user interface Bean and a non-visual Bean (such as a database connection widget) is that a visual Bean inherits from the Component class.

As we mentioned, JavaBeans follow a few programming rules, defined in the JavaBeans 1.0 specifications. The rest of the JavaBeans API uses these patterns to do the real work. To give you a good background on the architecture of JavaBeans, we'll ignore JBuilder's JavaBean support in this article and focus on what happens behind the scenes with JavaBeans.

Core reflection and serialization are the keys
Why are the Bean component requirements so undemanding? The first reason is that most of the component's API information is found through the Core Reflection API. Core Reflection is a standard Java library of classes used to read compiled Java class structures. It can read a class's inheritance, implementations, and methods. In effect, Core Reflection allows a Bean-aware tool to read a component's API. You determine the Bean's API by simply discovering the class's inheritance, implementations, and methods.

You use the Introspector class, in the java.beans package, to perform classification and pattern matching to create an API for the component. The product from the Introspector is a BeanInfo object, which has all the component API information used to manipulate the Bean. The Bean designer conforms to the programming patterns and method signatures the Introspector recognizes.

Serialization is the second reason JavaBeans are simple. You can manipulate JavaBeans as live objects in a Bean-aware IDE. This is far different from a standard IDE, which incrementally changes source code that will eventually be compiled to form the final product. Using a JavaBeans editor, you create and manipulate the components, then save their states. Within the final application, instead of code that sets up a component step by step, the state of configured components and their interconnections is restored. Serialization also means that you need not create a secondary definitions file for each component that describes to the IDE how to generate code for end-user programs.

All you have to do is ensure that you're consistently following the rules in our list. Let's talk about the rules. Default class constructor
Beans have to function in the context of a Bean design tool. JavaBeans tools must have a null constructor so a programmer can initially create a Bean component in a component viewer. Bean tools simply aren't smart enough to determine what to put into a parameterized constructor, so the null constructor is a worry-free construction method.

A default constructor is one of the primary things we find missing from most custom components. Designers often create class constructors that accept most of the configuration data as parameters. There's nothing wrong with this one-stop, one-line construction when coding by hand, but you also need to account for the Bean tool. A Bean will often be manipulated in the context of a design tool, as well as being hand-coded.

Implement java.io.Serialiazable
Serializing an object lets you save the current state of the object. For visual components, such as buttons, the serializable state includes size, position, color, and other information. Since JavaBeans are manipulated as live objects, the easiest way to save a component or a group of components in a GUI design tool is to save the objects' states. When Bean-built applications are executed, the serialized state of each Bean is restored identically to its state at the time the Bean tool saved it. This result differs from that of other component technologies that generate dozens of lines of code into your programs to initialize and manipulate components.

The Serializable interface has no methods to implement, so there's nothing more to do for simple components. The Serializable interface is a marker interface that acts as a security pass—it simply allows the implementing class to be serialized.

You'll want to watch out for a couple of things when designing serialized components. Static member variables aren't serialized, so don't use statics to store persistent data in a Bean. In addition, when converting a class to be serializable, you must identify any object member variables that don't need to be serialized.

Non-serialized members require an initial state each time the object is reloaded. These members are called transient because they don't keep their states between executions. Transient members' definitions are prefixed with the keyword transient, which prevents serialization. For example, you'd use the transient modifier in a password dialog box, which should always start with blank entries and force the user to enter a complete password.

JavaBeans design signatures
As we've mentioned, you must follow certain programming patterns so the Introspector class can classify methods. Not following JavaBeans programming practices can prevent the Introspector from understanding a component. For instance, a programmer might have a method like the following to set a component's background color:

public voidbackground(Color newColor

The background method could set a color parameter called background or perform an action on anobject with a color property.

To alleviate such ambiguities, JavaBeans components should follow simple naming rules and programming patterns that we collectively call JavaBeans design signatures. Design signatures fall into two categories: property access and events. All other class methods are considered actions on the component. See Table A for the current list of signatures. Using a simple property access design signature, the background color property access methods would look like this:

public Color getBackground(){
  return background;
}
public void setBackground(Color newColor){
  background = newColor;
}

Table A:
JavaBeans design signatures

Design Signature Description Example
Simple Property Accesses the state of an object. public void set<PropertyName>
(<PropertyType> <propertyName>);
public <PropertyType>
get<PropertyName>();
Boolean Property A special case; accesses the state
of boolean property.
public boolean is<PropertyName>
(boolean <propertyName>);
public void set<PropertyName>
(boolean <propertyName>);
Indexed Property Accesses array-like properties. public void set<PropertyName>
(<IndexType> <indexName>,
<PropertyType> <propertyName>);
public <PropertyType> get<PropertyName>
( <IndexType> <indexName>);
Bound Property Component properties that notify
registered listeners that a change
has occurred in a Bean. The add
and remove property change listener methods are required before the property can be recognized as
vetoable.
public void set<PropertyName>
(<PropertyType> <propertyName>){
// Change property; notify listeners
//(use the PropertyChangeSupport class)
}
public <PropertyType>
get<PropertyName>();
public void addPropertyChangeListener
(PropertyChangeListener listener) {
// (use the PropertyChangeSupport class)
}
public void removePropertyChangeListener (PropertyChangeListener listener) {
// (use the PropertyChangeSupport class)
}
Vetoable Property Component properties that notify
registered listeners that a change
is about to occur in a Bean. If
the listener throws a
PropertyVetoException when
notified, the change is aborted.
The add and remove
vetoable
property listener methods are
required before the property can
be recognized as vetoable.
public void set<propertyName>
(<PropertyType> <propertyName>)
throws PropertyVetoException{
// Notify listeners; Change property
// (use the VetoableChangeSupport class)
}
public <PropertyType>
get<propertyName>();
public void addPropertyChangeListener
(PropertyChangeListener listener) {
// (use the VetoableChangeSupport class)
}
public void removePropertyChangeListener (PropertyChangeListener listener){
// (use the VetoableChangeSupport) class
}
Event Source Classes that implement the following methods for a particular event. public void add<EventType>
Listener(<EventListener> listener);
public void remove<EventType>
Listener(<EventListener> listener);
Event Listener Implements a specific listener interface. Class implements <EventType>Listener

With the design signatures in Table A, you can use the Core Reflection classes to locate matching types and names of the set/get pair. In the previous code snippet, the get and set methods would be matched by their method names and the common type. The result is a background property of type Color, which can be represented in an IDE's property sheet. The actual code to perform the design-signature parsing and classification tasks is in the Introspector class, which is part of the Java reference platform. As a result, you can apply the rules consistently for all Java implementations.

Using method signatures is incredibly simple—no interface classes to implement, no specialized classes to inherit, and no special calls to register methods as property accessors. Best of all, method signatures are completely human-readable! Java 1.1 components also follow the design rules, so new JavaBeans are also consistent with Java.

As you can see in Table A, there are several signature types, including simple, Boolean, and array. Two very useful types—bound and vetoable—use the 1.1 event model to advertise changes to properties. The event used in both cases is a PropertyChangeEvent. Since PropertyChangeEvent is part of the java.beans package, few people without an interest in JavaBeans have any idea of its existence. We believe this is one of the most important additions to the Java 1.1 API.

PropertyChangeEvent can significantly reduce the coupling between components. As you know, coupling between classes is something to be avoided. With a property change event, classes (not just Beans) don't need application-specific code to notify a class that depends upon changes.

You use vetoable events like bound events, except that event listeners can veto a property change. The primary use of a vetoable property is to add change-control management to a generic component without inheritance. Instead of creating a subclass of a component that limits input to a specific range of values, you create a component that fires a property change event to registered vetoable listeners. If a listener decides the new property value is out of range, it throws a PropertyChangeVetoException, which propagates back to the class that's attempting the change.

There are also events and general methods. For events, we have a few more signatures that the Introspector class uses to recognize both event sources and event listeners. Methods not classified as a property access or an event are classified as special methods for the component.

JavaBeans and the delegation event model
The new delegation event model for Java 1.1 was developed primarily to let you use components without overriding classes to change their behavior. Instead of overriding components to interpret and process events, events are propagated to registered event listeners where the event is processed. So, you can connect events between classes.

The delegation model is powerful because of its ability to reduce class coupling. Since listeners are interfaces, the listener class is an abstract part of another class. An even more powerful abstraction using the delegation model is to entirely avoid implementing the listener interface within application-specific classes. Instead, a class implementing the listener—called an external event adapter—acts as the glue to map events to specific objects and methods. For example, a target class's start() method can be called in an external event adapter from a class that implements a MouseListener. This is better than adding the Listener interface to the target class.

Bean tools use external event adapters as the primary method to connect components, because the adapter can be generated on the fly and added to a running application. When the application is serialized, the adapters are serialized too, preserving the object relationship of event connections to be restored in the final running application. The alternative is to implement the listener in a new version of an existing class. Because you'd prefer that the design tool stay out of your custom code, external event adapters are superior.

When processing events from a component or a group of components in a panel or window, a good rule of thumb is to never implement a listener in any class derived from AWT components. Instead, use inner classes to implement listeners or override the processEvent() or process<EventType>() methods of the base component. Implementing a listener at the customized component (the level visible to a Bean tool) falsely advertises that the component listens to external events. Also, any component that generates a new event type or generates property change events must have add and remove methods for the supported event. The Introspector class uses these method signatures to determine what events the component generates.

Complex Beans
JavaBeans are essentially just a class, so complexity in a Bean component is primarily associated with the Bean's representation in a Bean-aware tool. Everything you do from this point adds classes to the set to be delivered with a Bean. Beyond the requirements so far, nothing else needs to be modified in the Bean component class. You're concerned only with editing the component, or the way in which the Bean and its properties are viewed in a Bean tool, such as IBM's Visual Age or Borland's JBuilder.

Three classes are associated with changing how a Bean is viewed and edited in a Bean tool: the PropertyEditor class, the Bean Customizer class, and the BeanInfo interface. If the Bean has properties that are custom types or require special handling, you must add custom property editors by extending the PropertyEditor class. A property type that isn't a primitive or one of the supported class types, like Color or Font, needs a way of editing the property.

Some Beans have special editing requirements that can't be accomplished through a property sheet—for example, changing the state of the Bean by manipulating several properties in a sequence. In such a case, you'll use a class extending the Customizer class to present a dialog box that performs special configuration tasks by manipulating a component directly.

You create the BeanInfo object by implementing the BeanInfo interface in the type of object returned by the Introspector class. The BeanInfo describes the Bean's API. You can add further information to customize the display of properties and to add the icon that represents the Bean in a Bean-aware application. The methods in the BeanInfo all return a value. If an implemented method in a custom BeanInfo returns null, the value found by the Introspector class is used instead. Writing a BeanInfo is easy if only a couple of methods require overrides.


Daniel Brookshier is the author of JavaBeans Developer's Reference and is a contributing author to Industrial Strength Java (Chapters 3 and 14). He's a speaker, Java consultant, and Java mentor for Weston Brothers Software Inc. Dan is also the creator of the Talk-Java and Drink-Java user groups. You can reach him at turbogek@cluck.com.
Back to Top
Back to Index of Articles

Copyright © 1997, ZD Journals, a division of Ziff-Davis Inc. ZD Journals and ZD Jounals logo are trademarks of Ziff-Davis Inc. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of Ziff-Davis is prohibited.
Trademarks & Copyright © 1998 INPRISE Corporation.