Previous | Next | Trail Map | JavaBeans Tutorial | Table of Contents


Writing a Simple Bean

In the following sections you will learn how to implement and use the basic Bean attributes: properties, events, and persistence. You will also learn how to use the BeanBox's applet generating capability.

Now is a good time to read or review the JavaBeans API Specification. Chapter 7 describes properties, Chapter 6 describes events, and Chapter 5 describes persistence.


Previous | Next | Trail Map | JavaBeans Tutorial | Table of Contents
Previous | Next | Trail Map | JavaBeans Tutorial | Writing a Simple Bean


Implementing Properties


To get the most out of this section, first read Chapters 7, Properties, and Chapter 8, Introspection, of the JavaBeans API Specification.

If you create a Bean class, give it an instance variable named color, and access color through a getter method named getColor and a setter method named setColor, then you have created a property.

Properties are aspects of a Bean's appearance and behavior that are changeable at design time.

JavaBean properties follow specific rules, called design patterns, when naming getter and setter method names. This lets Beans-aware builder tools (and the BeanBox) discover, display (usually in a property sheet), and alter those properties at design time.

For example, a builder tool, in introspecting your Bean, discovers two methods, getColor() and setColor(), infers that a property named color exists, and displays that property in a property sheet where it can be edited.

Adding a Color Property to SimpleBean

Make the following changes to SimpleBean.java to add a color property:

  1. Create and initialize a private instance variable.
       private Color color = Color.green; 
      
  2. Write a getter method.
       public Color getColor(){
         return color;
       } 
      
  3. Write a setter method.
        public void setColor(Color newColor){
         color = newColor;
         repaint();
        }
       
  4. Override the inherited paint() method. This is a requirement for all subclasses of Canvas.
        public void paint(Graphics g) {
         g.setColor(color);
         g.fillRect(20, 5, 20, 30);
        }
       
  5. Compile the Bean, load it in the ToolBox, and create an instance in the BeanBox.

The results are:

Here is a BeanBox illustration showing the revised SimpleBean instance within the BeanBox, SimpleBean's new color property within the Properties sheet, and the Color property editor shipped with the BeanBox. Remember, clicking on the color property entry in the Properties sheet displays this editor.

You can change the color property by menu, or by RGB value. Try changing colors.

Here is the complete SimpleBean source code, revised to add a color property.

package sunw.demo.simple;
 
import java.awt.*;
import java.io.Serializable;
 
public class SimpleBean extends Canvas
                     implements Serializable{
 
  private Color color = Color.green;
 
  //property getter method
  public Color getColor(){
     return color;
  }
 
  //property setter method. Sets new SimpleBean
  //color and repaints.
  public void setColor(Color newColor){
     color = newColor;
     repaint();
  }
 
  public void paint(Graphics g) {
   g.setColor(color);
   g.fillRect(20, 5, 20, 30);
  }
 
  //Constructor sets inherited properties
  public SimpleBean(){
   setSize(60,40);
   setBackground(Color.red);
  }
}

You can learn how to implement bound, constrained, and indexed properties in the advanced Beans section of this document.

In the next lesson, you'll learn about events, and how to manipulate them in the BeanBox.


Previous | Next | Trail Map | JavaBeans Tutorial | Writing a Simple Bean
Previous | Next | Trail Map | JavaBeans Tutorial | Writing a Simple Bean


Manipulating Events in the BeanBox


You'll need a good understanding of the JDK 1.1 event mechanism before reading this section. Here's the list of recommended readings:

Beans use the new event mechanism implemented in JDK 1.1, so implementing Bean events is the same as implementing events in any JDK 1.1 component. This section describes how this event mechanism is used by Beans and the BeanBox.

How the BeanBox Discovers a Beans Event Capabilities

The BeanBox uses either design pattern introspection or a BeanInfo class to discover what events a Bean can fire.

Using Introspection to Discover the Events A Bean Fires

JavaBeans provides event-oriented design patterns to give introspecting tools the ability to discover what events a Bean can fire. For a Bean to be the source of an event, it must implement methods that add and remove listener objects for that type of event. The design patterns for these methods are

public void add<EventListenerType>(<EventListenerType> a)
public void remove<EventListenerType>(<EventListenerType> a)
These methods let a source Bean know where to fire events. The source Bean then fires events at those listener Beans using the methods for those particular interfaces. For example, if a source Bean registers ActionListener objects, it will fire events at those objects by calling the actionPerformed() method on those listeners.

To see events discovered using design patterns, drop an instance of OurButton into the BeanBox and pull down the Edit|Events menu. This displays a list of event interfaces to which OurButton can fire events. Note that OurButton itself only adds and removes two of these interfaces; the rest are inherited from the base class.

Using BeanInfo to Define the Events a Bean Fires

You can explicitly "advertise" the events a Bean fires by using a class that implements the BeanInfo interface. The ExplicitButton demo Bean subclasses OurButton, and provides an associated ExplicitButtonBeanInfo class. ExplicitButtonBeanInfo implements the following method to explicitly define interfaces to which ExplicitButton fires events.

public EventSetDescriptor[] getEventSetDescriptors() {
    try {
        EventSetDescriptor push = new EventSetDescriptor(beanClass,
                    "actionPerformed",
                    java.awt.event.ActionListener.class,
                    "actionPerformed");
 
        EventSetDescriptor changed = new EventSetDescriptor(beanClass,
                    "propertyChange",
                    java.beans.PropertyChangeListener.class,
                    "propertyChange");
 
        push.setDisplayName("button push");
        changed.setDisplayName("bound property change");
     
        EventSetDescriptor[] rv = { push, changed};
        return rv;
    } catch (IntrospectionException e) {
        throw new Error(e.toString());
    }
}
Drop an ExplicitButton instance in the BeanBox, and pull down the Edit|Events menu. Notice that only those interfaces explicitly exposed in the ExplicitButtonBeanInfo class are listed. No inherited capabilities are exposed. See the Using BeanInfo section for more information on the BeanInfo interface.

Viewing a Bean's Events in the BeanBox

If you select an OurButton Bean in the BeanBox, then pull down the Edit|Events menu, you will see a list of interfaces that OurButton can fire events at. Each interface item will, when selected, display the methods that fire different events at those interfaces. These correspond to all the events that OurButton can fire.

Hooking Up Events in the BeanBox

In this example you will use two OurButton bean instances to stop and start an instance of the animated Juggler bean. You will label the buttons "start" and "stop"; make the start button, when pressed, invoke the Juggler bean's start method; and make the stop button, when pressed, invoke the Juggler bean's stop method.

  1. Start the BeanBox.
  2. Drop a Juggler bean and two OurButton bean instances into the BeanBox.
  3. Select an OurButton instance. In the Properties sheet, change the label property to "start". Select the second OurButton instance and change its label to "stop".
  4. Select the start button. Choose the Edit|Events|action|actionPerformed menu item. This causes a rubber band line to track between the start button and the cursor. Click on the Juggler instance. This brings up the EventTargetDialog:

    This list contains Juggler methods that take no arguments, or arguments of type actionPerformed.

  5. Select the start method and press OK. You will see a message that the BeanBox is generating adapter classes.
  6. Do the above two steps on the stop button, except choose the stop method in the EventTargetDialog.

Clicking on the start and stop buttons will now start and stop the Juggler. Here is a general description of what happened:

You will use this juggler example later when you learn how to generate applets, so use the File|Save menu item to save this example to a file of your choice.

Event Adapter Classes

The BeanBox generates an adapter class the interposes between the source and the target. The adapter class implements the appropriate event listener interface (and so is the actual listener, not the target Juggler Bean), catches the event fired by the button, and then calls the selected target method. Here is the BeanBox-generated adapter class that interposes between the start button and the JugglerBean:

// Automatically generated event hookup file.

package tmp.sunw.beanbox;
import sunw.demo.juggler.Juggler;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class ___Hookup_1474c0159e implements
           java.awt.event.ActionListener, java.io.Serializable {

    public void setTarget(sunw.demo.juggler.Juggler t) {
        target = t;
    }

    public void actionPerformed(java.awt.event.ActionEvent arg0) {
        target.startJuggling(arg0);
    }

    private sunw.demo.juggler.Juggler target;
}

The adapter implements the ActionListener interface that you selected in the BeanBox's Edit|Events menu. ActionListener declares one method, actionPerformed(), which is implemented by the adapter to call the target Bean method that you selected. The setTarget() method is called by the BeanBox to set the target Bean.

The EventMonitor Demo Bean

The EventMonitor Bean (beans/demo/sunw/demo/encapsulatedEvents) prints out source Bean event reports, as they occur, in a scrolling listbox. To see how this works, take the following steps:

  1. Drop OurButton and EventMonitor instances in the BeanBox. You might want to resize the EventMonitor (and the BeanBox) to accommodate viewing the event reports.
  2. Select the OurButton instance, and choose any event method in the Edit|Events menu.
  3. Connect the rubber band line to the EventMonitor, and choose its initiateEventSourcMonitoring in the EventTargetDialog.
  4. Select the OurButton Bean. You will begin seeing event reports in the EventMonitor
When the first event is delivered, EventMonitor analyzes the source Bean to discover all the events it fires, creates and registers an event listener for each event type, and then reports whenever any event is fired. This is useful for debugging. Try connecting other demo Beans to EventMonitor to observer their events.

Events for Bound and Constrained Properties

The bound and constrained properties sections describe two specific event listener interfaces.


Previous | Next | Trail Map | JavaBeans Tutorial | Writing a Simple Bean
Previous | Next | Trail Map | JavaBeans Tutorial | Writing a Simple Bean


Implementing Persistence


For complete information on persistence and serialization, see the Object Serialization web site.

A Bean persists by having its properties, fields, and state information saved and restored to and from storage. The mechanism that makes persistence possible is called serialization. When a Bean instance is serialized, it is converted into a data stream and written to storage. Any applet, application, or tool that uses that Bean can then "reconstitute" it by deserialization. JavaBeans uses the JDK's Object Serialization API for its serialization needs.

As long as one class in a class's inheritance hierarchy implements Serializable or Externalizable, that class is serializable.

All Beans must persist. To persist, your Beans must support serialization by implementing either the java.io.Serializable interface, or the java.io.Externalizable interface. These interfaces offer you the choice between automatic serialization, and "roll your own".

Controlling Serialization

You can control the level of serialization that your Beans undergo:

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 with Serializable means you are telling the JVM that you have made sure your class will work with default serialization. Here are some important points about working with the Serializable interface:

The BeanBox writes serialized Beans to a file with a .ser extension.

The OurButton demo Bean uses default serialization to make its properties persist. OurButton only added Serializable to its class definition to make use of default serialization:

   public class OurButton extends Component implements Serializable,...

If you drop an OurButton instance into the BeanBox, the properties sheet displays OurButton's properties. To ascertain that serialization is working

  1. Change some OurButton properties. For example change the font size and colors.
  2. Serialize the changed OurButton instance by selecting the File|SerializeComponent... BeanBox menu item. A file browser will pop up.
  3. Put the .ser file in a JAR file with a suitable manifest (need to explain how to do this).
  4. Clear the BeanBox form by selecting the File|Clear menu item.
  5. Reload the serialized instance by selecting the File|LoadJar menu item.
The OurButton instance will appear in the BeanBox with your property changes intact. By implementing Serializable in your class, simple, primitive properties and fields can be serialized. For more complex class members, different techniques must be used.

Selective Serialization Using the transient Keyword

To exclude fields from serialization in a Serializable object from serialization, 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.

private void writeObject(java.io.ObjectOutputStream out)
    throws IOException;
private void readObject(java.io.ObjectInputStream in)
    throws IOException, ClassNotFoundException;

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().

Example: The Molecule Demo Bean

The Molecule demo keeps a version number in a static field. Since static fields are not serialized by default, writeObject() and readObject() are implemented to serialize this field. Here is the writeObject() and readObject() implementations in Molecule.java:

private void writeObject(java.io.ObjectOutputStream s)
                        throws java.io.IOException {
        s.writeInt(ourVersion);
        s.writeObject(moleculeName);
    }

private void readObject(java.io.ObjectInputStream s)
                        throws java.lang.ClassNotFoundException,
                               java.io.IOException {
        // Compensate for missing constructor.
        reset();
        if (s.readInt() != ourVersion) {
            throw new IOException("Molecule.readObject: version mismatch");
        }
        moleculeName = (String) s.readObject();
    }

These implementations limit the fields serialized to ourVersion and moleculeName. Any other data in the class will not be serialized.

It is best to use the ObjectInputStream's defaultWriteObject() and defaultReadObject before doing your own specific stream writing. For example:

private void writeObject(java.io.ObjectOutputStream s)
                        throws java.io.IOException {
        //First write out defaults
        s.defaultWriteObject();
        //...
    }

private void readObject(java.io.ObjectInputStream s)
                        throws java.lang.ClassNotFoundException,
                               java.io.IOException {
        //First read in defaults
        s.defaultReadObject();
        //...
    }

Roll Your Own Serialization: The Externalizable Interface

Use the Externalizable interface to serialize (write) your Beans in a specific file format. Externalizable gives you complete flexibility in specifying the serialized format. You need to implement two methods: readExternal() and writeExternal(). Externalizable classes must also have a no-argument constructor.

Example: The BlueButton and OrangeButton Demo Beans

When you run the BeanBox, you will see two Beans named BlueButton and OrangeButton in the ToolBox. These two Beans are actually serialized instances of the ExternalizableButton class.

ExternalizableButton implements the Externalizable interface. This means it does all its own serialization, by implementing Externalizable.readExternal() and Externalizable.writeExternal(). The BlueButtonWriter program is used by the buttons makefile to create an ExternalizableButton instance, change its background property to blue, and write the Bean out to the file BlueButton.ser. OrangeButton is created the same way, using OrangeButtonWriter. The button makefile then puts these .ser files in buttons.jar, where the ToolBox can find and reconstitute them.

Tips on what should not be serialized

Here is a short list of things you don't want to serialize:

A good strategy is to mark fields or objects that cannot be made persistent using the default offered by the Serialization interface. Then write your own implementations of the writeObject() and readObject() methods to serialize and deserialize those transient fields.


Previous | Next | Trail Map | JavaBeans Tutorial | Writing a Simple Bean
Previous | Next | Trail Map | JavaBeans Tutorial | Writing a Simple Bean


Using the BeanBox to Generate Applets

You can use the BeanBox's File|MakeApplet... menu item to generate an applet from the BeanBox contents. Making an applet from the BeanBox creates a JAR file containing

Take the following steps to generate an applet from the BeanBox:

  1. Use the Juggler example that you made in the events section. If you saved that example to a file, load it in the BeanBox using the File|Load menu item. If you didn't save it, follow the steps in the events section to build the example. The generated applet will have the same size as the BeanBox frame, so you might want to adjust the BeanBox size to the size of the applet you want.

  2. Choose File|Make Applet to bring up the MakeApplet dialog:


    Use the default JAR file and applet name for this example. Press the OK button.

That's it. The generated files were placed in the beanbox/tmp/myApplet directory. You can inspect your handiwork by bringing up appletviewer in the following way:

    appletviewer <BDKInstallation>/beanbox/tmp/myApplet.html.

Here is what you will see:

Don't forget to look at the generated myApplet_readme file, and the other files generated.

Generated applets can be used in any JDK 1.1-compliant browser. The appletviewer is a good test platform. Another fully compliant browser is the HotJava browser The preview2 Internet Explorer 4.0 release does not yet support JAR files, and you will have to expand the JAR and HTML files that are generated. Also, a deserialization bug causes components to not listen to mouse events. See the generated readme file for more information. The generated applet will not work in Netscape Communicator versions 4.0 and 4.01; versions with full JDK 1.1 support are expected later this year.


Previous | Next | Trail Map | JavaBeans Tutorial | Writing a Simple Bean