Advanced JavaBeans Topics

Getting started with JavaBeans development is easy, but beans have surprising depth. This lesson covers some more advanced topics, including how beans can be stored (persisted) and how you can supply custom editors for custom data types.

◈ Bean Persistence describes the mechanisms for saving and reconstituting a bean.
◈ Long Term Persistence covers representing a bean as XML.
◈ Bean Customization provides an overview of creating editor components for custom data types.

01. Bean Persistence

A bean has the property of persistence when its properties, fields, and state information are saved to and retrieved from storage. Component models provide a mechanism for persistence that enables the state of components to be stored in a non-volatile place for later retrieval.

The mechanism that makes persistence possible is called serialization. Object serialization means converting an object into a data stream and writing it to storage. Any applet, application, or tool that uses that bean can then "reconstitute" it by deserialization. The object is then restored to its original state.

For example, a Java application can serialize a Frame window on a Microsoft Windows machine, the serialized file can be sent with e-mail to a Solaris machine, and then a Java application can restore the Frame window to the exact state which existed on the Microsoft Windows machine.

Any applet, application, or tool that uses that bean can then "reconstitute" it by deserialization.

All beans must persist. To persist, your beans must support serialization by implementing either the java.io.Serializable (in the API reference documentation) interface, or the java.io.Externalizable (in the API reference documentation) interface. These interfaces offer you the choices of automatic serialization and customized serialization. If any class in a class's inheritance hierarchy implements Serializable or Externalizable, then that class is serializable.

Classes That Are Serializable

Any class is serializable as long as that class or a parent class implements the java.io.Serializable interface. Examples of serializable classes include Component, String, Date, Vector, and Hashtable. Thus, any subclass of the Component class, including Applet, can be serialized. Notable classes not supporting serialization include Image, Thread, Socket, and InputStream. Attempting to serialize objects of these types will result in an NotSerializableException.

The Java Object Serialization API automatically serializes most fields of a Serializable object to the storage stream. This includes primitive types, arrays,and strings. The API does not serialize or deserialize fields that are marked transient or static.

Controlling Serialization

You can control the level of serialization that your beans undergo. Three ways to control serilization are:

◈ Automatic serialization, implemented by the Serializable interface. The Java serialization software serializes the entire object, except transient and static fields.
◈ Customized serialization. Selectively exclude fields you do not want serialized by marking with the transient (or static) modifier.
◈ Customized file format, implemented by the Externalizable interface and its two methods. Beans are written in a specific file format.

Default Serialization: The Serializable Interface

The Serializable interface provides automatic serialization by using the Java Object Serialization tools. Serializable declares no methods; it acts as a marker, telling the Object Serialization tools that your bean class is serializable. Marking your class Serializable means you are telling the Java Virtual Machine (JVM) that you have made sure your class will work with default serialization. Here are some important points about working with the Serializable interface:

◈ Classes that implement Serializable must have an access to a no-argument constructor of supertype. This constructor will be called when an object is "reconstituted" from a .ser file.
◈ You don't need to implement Serializable in your class if it is already implemented in a superclass.
◈ All fields except static and transient fields are serialized. Use the transient modifier to specify fields you do not want serialized, and to specify classes that are not serializable.

Selective Serialization Using the transient Keyword

To exclude fields from serialization in a Serializable object mark the fields with the transient modifier.

transient int status;

Default serialization will not serialize transient and static fields.

Selective Serialization: writeObject and readObject

If your serializable class contains either of the following two methods (the signatures must be exact), then the default serialization will not take place.

You can control how more complex objects are serialized, by writing your own implementations of the writeObject and readObject methods. Implement writeObject when you need to exercise greater control over what gets serialized when you need to serialize objects that default serialization cannot handle, or when you need to add data to the serialization stream that is not an object data member. Implement readObject to reconstruct the data stream you wrote with writeObject.

The Externalizable Interface

Use the Externalizable interface when you need complete control over your bean's serialization (for example, when writing and reading a specific file format). To use the Externalizable interface you need to implement two methods: readExternal and writeExternal. Classes that implement Externalizable must have a no-argument constructor.

02. Long Term Persistence

Long-term persistence is a model that enables beans to be saved in XML format.

Information on the XML format and on how to implement long-term persistence for non-beans can be found in XML Schema and Using XMLEncoder.

Encoder and Decoder

The XMLEncoder class is assigned to write output files for textual representation of Serializable objects. The following code fragment is an example of writing a Java bean and its properties in XML format:

XMLEncoder encoder = new XMLEncoder(
new BufferedOutputStream(
new FileOutputStream("Beanarchive.xml")));

encoder.writeObject(object);
encoder.close();

The XMLDecoder class reads an XML document that was created with XMLEncoder:

XMLDecoder decoder = new XMLDecoder(
new BufferedInputStream(
new FileInputStream("Beanarchive.xml")));

Object object = decoder.readObject();
decoder.close();

What's in XML?

An XML bean archive has its own specific syntax, which includes the following tags to represent each bean element:

◈ an XML preamble to describe a version of XML and type of encoding
◈ a <java> tag to embody all object elements of the bean
◈ an <object> tag to represent a set of method calls needed to reconstruct an object from its serialized form

03. Bean Customization

Customization provides a means for modifying the appearance and behavior of a bean within an application builder so it meets your specific needs. There are several levels of customization available for a bean developer to allow other developers to get maximum benefit from a bean's potential functionality.

The following links are useful for learning about property editors and customizers:

A bean's appearance and behavior can be customized at design time within beans-compliant builder tools. There are two ways to customize a bean:

◈ By using a property editor. Each bean property has its own property editor. The NetBeans GUI Builder usually displays a bean's property editors in the Properties window. The property editor that is associated with a particular property type edits that property type.
◈ By using customizers. Customizers give you complete GUI control over bean customization. Customizers are used where property editors are not practical or applicable. Unlike a property editor, which is associated with a property, a customizer is associated with a bean.

Property Editors

A property editor is a tool for customizing a particular property type. Property editors are activated in the Properties window. This window determines a property's type, searches for a relevant property editor, and displays the property's current value in a relevant way.

Property editors must implement the PropertyEditor interface, which provides methods to specify how a property should be displayed in a property sheet. The following figure represents the Properties window containing myBean1 properties:

You begin the process of editing these properties by clicking the property entry. Clicking most of these entries will bring up separate panels. For example, to set up the foreground or background use selection boxes with choices of colors, or press the "..." button to work with a standard ColorEditor window. Clicking on the toolTipText property opens a StringEditor window.

The support class PropertyEditorSupport provides a default implementation of the PropertyEditor interface. By subclassing your property editor from PropertyEditorSupport, you can simply override the methods you need.

To display the current property value "sample" within the Properties window, you need to override isPaintable to return true. You then must override paintValue to paint the current property value in a rectangle in the property sheet. Here's how ColorEditor implements paintValue:

To support the custom property editor, override two more methods. Override supportsCustomEditor to return true, and then override getCustomEditor to return a custom editor instance. ColorEditor.getCustomEditor returns this.

In addition, the PropertyEditorSupport class maintains a PropertyChangeListener list, and fires property change event notifications to those listeners when a bound property is changed.

How Property Editors are Associated with Properties

Property editors are discovered and associated with a given property in the following ways:

◈ Explicit association by way of a BeanInfo object. The editor of the title's property is set with the following line of code:

pd.setPropertyEditorClass(TitleEditor.class);

◈ Explicit registration by way of the java.beans.PropertyEditorManager.registerEditor method. This method takes two arguments: the bean class type, and the editor class to be associated with that type.
◈ Name search. If a class has no explicitly associated property editor, then the PropertyEditorManager searchs for that class's property editor in the following ways:

◈ Appending "Editor" to the fully qualified class name. For example, for the my.package.ComplexNumber class, the property editor manager would search for the my.package.ComplexNumberEditor class.
◈ Appending "Editor" to the class name and searching a class path.

Customizers

You have learned that builder tools provide support for you to create your own property editors. What other needs should visual builders meet for complex, industrial-strength beans? Often it is undesirable to have all the properties of a bean revealed on a single (sometimes huge) property sheet. What if one single root choice about the type of the bean rendered half the properties irrelevant? The JavaBeans specification provides for user-defined customizers, through which you can define a higher level of customization for bean properties than is available with property editors.

When you use a bean Customizer, you have complete control over how to configure or edit a bean. A Customizer is an application that specifically targets a bean's customization. Sometimes properties are insufficient for representing a bean's configurable attributes. Customizers are used where sophisticated instructions would be needed to change a bean, and where property editors are too primitive to achieve bean customization.

All customizers must:

◈ Extend java.awt.Component or one of its subclasses.
◈ Implement the java.beans.Customizer interface This means implementing methods to register PropertyChangeListener objects, and firing property change events at those listeners when a change to the target bean has occurred.
◈ Implement a default constructor.
◈ Associate the customizer with its target class via BeanInfo.getBeanDescriptor.