"Double Shot, Half Decaf, Skinny Latte" -- Customize your Java

How to tailor JavaBeans to fit your application

Consider the following exchange:

Sally: I'd like the chef salad please with oil and vinegar on the side, and the apple pie a la mode.

Waitress: Chef and apple a la mode.

Sally: But I'd like the pie heated, and I don't want the ice cream on top. I want it on the side, and I'd like strawberry instead of vanilla if you have it. If not, then no ice cream, just whipped cream, but only if it's real. If it's out of the can, then nothing.

Waitress: Not even the pie?

Sally: No, just the pie, but then not heated.

This was Meg Ryan in the role of Sally Albright in the movie When Harry Met Sally.

Sally knows exactly what she wants. As an application designer, so do you. Fortunately, no matter how complex your customization requirements may be, JavaBeans is up to the task. Customization allows an application developer to control the behavior and appearance of a software component. Making a component customizable greatly extends where it can be used. For example, consider a Spreadsheet bean. If such a bean insisted on taking up the whole screen, or clashed with the color scheme of your application, or only displayed its data in hexadecimal, you might not be able to use it -- even if it provided all of the other functionality you needed. A customizable bean has properties (like number of cells, color, and so on) that a developer can view and modify, custom-fitting the bean to the application.

The JavaBeans Specification (hereafter known as "the JavaBeans Spec") includes features that make identifying and modifying component properties extremely easy in simple cases, and provides extensions for more complex situations.

This month, we'll explore the JavaBeans customization interface. You'll learn about component properties, and how to write your beans so that integrated development environments (IDEs) can present those properties to applications developers. We'll discuss bound and constrained properties, which help beans communicate or maintain consistency. We'll also touch on special-case customizers for situations in which you want more control over customization. Finally, we'll start using the BeanBox, Sun's free testbed for JavaBeans. (For more on the BeanBox, see my companion article in this month's JavaWorld: "The BeanBox: SunSoft's JavaBeans test container.")

What is customization?

Software components typically are general-purpose "chunks" of functionality and data, written to be usable in a variety of situations. (For an introduction to JavaBeans, see last month's column in JavaWorld, "A walking tour of JavaBeans.") What's important about customization? A software component can be used in a wider range of applications when the application developer has control over its appearance and behavior. For example, a PushButton class wouldn't be very useful if its text label were always the word "Button," and its associated action were always, say, to reboot your machine (although with some operating systems, this might be one of your most useful tools).

Even a component as simple as a lowly PushButton may have many attributes that a developer might want to control, including:

  • whether or not it is enabled
  • action
  • background color
  • text color
  • size
  • position
  • shape
  • label text (or maybe an icon instead of a label)
  • the sound file to play when the button is pressed

Complex components have even more involved customization requirements. Customizing a remote database connection might entail selecting from a list of available servers (information available only at run time), choosing a protocol (ditto), specifying user name and password, and setting up access through a firewall.

Properties of a bean

In general, customization means configuring the internal state of a bean so that it appears and behaves properly in the situation in which it is being used. The individual elements of this internal state (color, size, password string, and so on) are called properties in the JavaBeans Spec. A bean's properties may be examined and modified by using methods called accessors. An accessor is either a getter method to read a property's value, or a setter method to change it. The PushButton bean, then, might have a method called String getLabel(), which returns the bean's current label, and void setLabel(String newLabel), which sets it. A property with only a getter method can be considered read-only.

Why not simply access the data members inside the bean class and modify or read them directly, instead of writing these accessors? Because there's no guarantee that the properties correspond directly to data members inside the bean. A bean's label property, for example, may be an AWT Label object; but then again, it may not be. Maybe the label text is read from a database, maybe it's the label of another bean, or maybe the bean makes up a label only if it's asked to do so! An accessor method is a uniform interface to a property of a bean that hides how that property is implemented. This is simply good object-oriented programming practice because it decreases the dependencies or "coupling" between objects.

An IDE that complies with the JavaBeans Spec knows how to analyze a bean to discover its properties. It also knows how to create a visual representation for each property type, called a property editor, to allow the application developer to modify the properties at design time. When the developer drops a bean into an application, the IDE draws the bean on the panel. It then presents a property sheet, which is simply a list of all of the bean's properties, with associated editors for each property. The IDE will call all of the getter methods so that the property sheet contains the bean's current property values. If the developer changes a property in the property sheet, the IDE calls the corresponding setter method to update the associated property of the selected bean.

For beans of low to moderate complexity, configuring a bean at design time by calling its setter methods is often enough. To support the higher degree of customization control required by more complex beans, though, the Spec defines a customizer class that developers may extend and include with a bean. This leaves the door wide open for customization of any sort: sophisticated color pickers, graphical network configuration wizards, even a VRML interface if you're up to writing one! Actually, the property sheet described above can be considered a customizer that is automatically generated by an IDE. You'll see this pattern often in the JavaBeans framework: Simple things (in this case, simple property types for which property editors already exist) usually entail little or no work, but access is provided "under the hood" for more complicated situations.

How to make beans customizable

The JavaBeans framework makes creating beans with customizable properties a snap; much of the time, there's no need to write any code at all! This is because the JavaBeans Spec defines a naming convention that IDEs use to infer which methods correspond to properties. The Spec calls these conventions design patterns (which is unfortunate, because the phrase "design pattern" means something else entirely in architecture and in object-oriented programming -- see Resources below). Semantic nit-picking aside, the principle is simple: The name, the return type, and the argument types (the signature, in OO parlance) determine the names and types of the properties. A getter method for a property called Property of type TYPE is defined as TYPE getProperty(), and the setter method for this property is void setProperty(TYPE newValue). When an IDE loads a bean, it uses the JDK 1.1 reflection mechanism to scan all of the bean's methods, looking for methods whose names start with get and set. The IDE adds the properties it finds to the property sheet so the developer can customize the bean.

Time to customize a bean

Let's look at a concrete example of bean customization by extending the nascent BarChart bean from last month. A quick peek at the source code reveals a lucky break: The BarChart bean already has getter and setter methods that conform to the Spec's design pattern -- void setPercent(int iPercent) and int getPercent(). In other words, this bean already has a property: percent. Let's load the BarChart bean into the BeanBox and see what happens.

BarChart in BeanBox

To the left of the figure is a panel containing the available beans for placement in the BeanBox. In the center is the BeanBox itself, with the BarChart selected, and to the right is the property sheet for the BarChart. The property sheet has five properties: foreground and background (of type Color), font (of type Font), percent (an integer), and name (a String). When the BeanBox loaded the BarChart, it found the methods int getPercent() and void setPercent(int iPercent) and recognized percent as a property of the bean. It then added that property to the property sheet, along with a property editor for an integer (the textbox to the right of the label percent). The getPercent() function was called to fill in the value in the property editor (in this case, the value was 0). This process occurred for all of the properties on the property sheet.

But wait! BarChart only defines percent as a property. Where did all those other properties come from? Since BarChart is defined as:

public class BarChart extends Canvas implements Serializable

BarChart inherits java.awt.Canvas, which inherits java.awt.Component, which defines (you guessed it) Color getForeground(), void setForeground(Color), and so on. This means that, handily, if you subclass a bean, what you get is a new bean with all the parent bean's properties.

If you change the property in the BeanBox's property sheet, the bean is updated on the fly. Input "62" in the property editor for percent and the BeanBox responds accordingly, as shown in this figure:

BarChart at 62%

The IDE (in this case, the BeanBox) detects that the value in the property editor for percent changed, and updates the BarChart's percent by calling setPercent().

All this is fine, as long as your application requires a BarChart of exactly 25 by 150 pixels, with a blue background, a two-pixel black border, and red flooding to the percentage chosen.

Fortunately, we've got the source code for this bean, so let's add some new properties: Colors fillColor, floodColor, and borderColor; and ints borderWidth, barHeight, and barWidth. As an example, let's add the floodColor() accessors. We previously defined the flood color as private Color fgColor_ = Color.red. Let's rename it floodColor to avoid confusion with java.awt.Component's foreground and background properties, and write accessors for it:

private Color floodColor_ = Color.red;
public void setFloodColor(Color floodColor)
{
    floodColor_ = floodColor;
}
public Color getFloodColor()
{
    return floodColor_;
}

There's no more to it than that. Adding similar accessors for the other properties results in a new class file containing a flexible new bean. (Check out the source code for the new bean.)

Now that our BarChart beans are customizable, we can make them look any way we like, as shown here:

Customized BarChartBeans

Here we see four BarChart beans, each customized with a different color scheme and filled to a different percentage. Adding just a few methods to the BarChartBean class gives the application developer enormous flexibility. (Judging from the hideousness of some of the color schemes, maybe we've provided a bit too much flexibility!)

This works well, as long as your properties are already of built-in types -- or you don't mind being limited to typing strings and picking colors. What if a property type were of a type you had defined yourself? It would be cool if the BarChart had a direction property indicating which direction the BarChart should grow. Its valid values might be UP, DOWN, LEFT, and RIGHT, but how would you customize that?

The nitwit answer is: "Make the user type in an integer 0-3 to indicate the direction." The smart answer appears in the JavaBeans Spec: Write your own property editor. The interface java.beans.PropertyEditor and the utility class java.beans.PropertyEditorSupport allow the beans developer to specify a property editor for a "custom" type like the direction type we've just described. Custom property editors can even be graphical: A NetworkConfiguration bean might have a HostConnections property that could be configured by drawing connections between servers with a mouse. How to write a property editor class and link it to a bean would be a good subject for a future column, if there's sufficient interest. Write and let me know! (See my bio below for contact information.)

One final note on basic properties. Properties may be defined as arrays of values instead of just scalars. Such properties are known as indexed properties, and differ from their scalar counterparts by having different signatures for their accessor methods. The getter and setter methods are "overloaded": You can access a property either by individual element:

void setProperty(int index, TYPE value)
TYPE getProperty(int index)

or by an entire array at once:

void setProperty(TYPE valueList[])
TYPE[] getProperty()

For example, if you had a Polygon3D bean that drew itself onto a canvas, you could access the Polygon3D's points individually:

Point3D newPoint = new Point3D(0, 25, 0);
myPolygon.setPoint(2, newPoint); // Throws exception if < 3 points

or set a whole array at once:

1 2 3 Page 1
Page 1 of 3