Online Training Index
101, Part II:
Writing a Simple JavaBean
by Beth Stearns
November 2000
Introduction | Page 2 | Page 3 | Page 4
Adding Properties
The previous lesson illustrated how to add simple properties to a Bean, plus compile the Bean and test it in the BeanBox. In this part, we'll see how to implement other types of properties in a Bean, including:
- Bound properties
- Constrained properties
These more complex types of properties require that Beans communicate with each other. Recall that
Beans use events to communicate with other Beans. A source Bean sends or fires events and a listener
Bean, after it registers its interest in the event with the source Bean, receives or handles events.
Bound Properties
A bound property gives a JavaBean the capability of notifying another JavaBean when there is a change
to the value of that property. The JavaBean that is notified can then react to the change. For example, you might have a button in one JavaBean that, when pressed, causes another Bean to display some information.
Because other Beans are notified when there is a change to a Bean's bound property, there has to be a means for Beans to communicate. Beans communicate using events. A change to a bound property causes an event to be fired. These events are referred to as property change events.
When you set up a bound property, you also set up other Beans as interested listeners to changes in the bound property. Then, whenever the value of the bound property of the first JavaBean changes, notification is sent to those Beans that have registered as interested listeners.
Let's look at the Bean MyButton.java first, which uses bound properties. MyButton includes code to notify other interested Beans when its properties change. Once we're done with MyButton, we'll set up a listener Bean that responds to changes in MyButton's properties.
Setting Up Bound Properties
These are the basic steps for setting up bound properties in a Bean:
-
Import the
java.beans package to gain access to certain convenience classes defined by the package. MyButton imports the package as follows:
import java.beans.*;
-
Instantiate the
java.beans.PropertyChangeSupport
class.
private PropertyChangeSupport changes = new
PropertyChangeSupport(this);
MyButton creates a new object, called changes, which instantiates the
PropertyChangeSupport
class. The
changes
variable holds a collection of listener objects that will be notified about property changes
to the
bound property. It defines two supporting methods, addPropertyChangeListener and
removePropertyChangeListener, which provide a public interface that allows
interested
listeners to register themselves with MyButton.
-
Implement the methods defined by the
PropertyChangeSupport
class.
The
PropertyChangeSupport
class includes methods to add and remove listener objects, specifically
PropertyChangeListener
objects. The
addPropertyChangeListener
method adds a new listener to the list, while the
removePropertyChangeListener
method removes a listener from the list. The
PropertyChangeSupport
class also includes a third method&--
firePropertyChange
--to fire
PropertyChangeEvent
objects to these interested listeners. MyButton includes the following code to implement the
add and
remove listener methods.
Note that the parameter
l
refers to the property change listener Bean that registers or removes its interest as a
property
change listener.
public void addPropertyChangeListener(
PropertyChangeListener l) {
changes.addPropertyChangeListener(l);
}public void removePropertyChangeListener(
PropertyChangeListener l) {
changes.removePropertyChangeListener(l);
}
|
-
Modify the Bean's setter methods for the bound properties.
For those properties that you want to be bound properties, modify the Bean's setter methods to
include
code to fire a property change event when the property value changes. MyButton calls the
firePropertyChange
method within each method that sets a new property value. For example, when an application or
user
changes the button's font, this action executes the
MyButton.setFont
method. Because the
firePropertyChange
method requires both the old and new value of the changed property, the
setFont
method first captures the old value by calling the
getFont
method. It sets the new, changed value, then calls
changes.firePropertyChange
to notify interested listeners. It passes three parameters to the method: the name of the
changed
property, the old value of the property, and the property's new value.
public void setFont(Font f) {
Font old = getFont();
super.setFont(f);
sizeToFit();
changes.firePropertyChange(
"font", old, f);
} |
What happens under the covers with the
firePropertyChange
method? The method bundles its three parameters into a
PropertyChangeEvent
object. It then calls the method
propertyChange
, passing it the
PropertyChangeEvent
object, on each registered listener. Keep in mind that
propertyChange
treats the old and new values of the property as
Object
values. This is important if your property values are primitive types. If they are, you must
use the
object wrapper version for the specific type, such as
java.lang.Integer
for an
int
primitive type, before calling
firePropertyChange.
Keep in mind that with bound properties, the property value changes first, then the property
change
event is fired.
Setting Up a Property Listener
Now we set up a listener Bean that receives and perhaps responds to property change events in
MyButton.
-
Implement the
PropertyChangeListener
interface. There is one method in this interface, the
propertyChange
method:
public abstract void propertyChange(
PropertyChangeEvent evt)
Beans that fire property change events call the
propertyChange
method to notify property change listeners of changed properties.
-
In the listener Bean's implementation of the
propertyChange
method define the actions you want to take as a result of the property change.
You make the connection between the source Beanthe Bean that fires the property change
eventand the listener Bean that reacts to the changed property in the BeanBox (or in
another
builder tool). You can also manually make this connection by writing a special adapter
class.
For example, the listener Bean MyChangeReporter includes a method
reportChange
whose parameter is a
PropertyChangeEvent
object. The method extracts the property name and new value from the passed object, formats
its text
window, and displays the information.
public void reportChange(PropertyChangeEvent evt) {
String text = evt.getPropertyName()
+ " := " + evt.getNewValue();
int width = getSize().width - 10;
Font f = getFont();
if (f != null) {
// Trim the text to fit.
FontMetrics fm = getFontMetrics(f);
while (fm.stringWidth(text) > width) {
text = text.substring(
0, text.length()-1);
}
}
setText(text);
} |
You can also make the connection between the source and listener Beans in the code itself. To
do this,
write an adapter class to catch the property change event.
You set up the adapter class to call the correct method within the listener object.
-
Set up the listener Bean to call the listener registration method on the source Bean. For
example, our
listener invokes the following method on MyButton:
MyButton.addPropertyChangeListener(
aPropertyChangeListener);
Connecting the Beans in the BeanBox
The BeanBox recognizes when a Bean correctly defines a bound propertythat it can
broadcast
property change eventsand the BeanBox includes a
propertyChange
interface item for that Bean. The
propertyChange
interface item appears in the Events menu for the selected Bean that defines a bound
property.
-
Select Events in the Bean Edit menu.
Once you select propertyChange, connect the source Bean to the listener Bean.
-
Extending the rubber band line from the source Bean to the listener Bean.
An EventTargetDialog appears.
-
Select the appropriate listener method (in our example, this is the
reportChange
method).
Beneath the surface, the BeanBox generates an event hookup adapter when you connect the
propertyChange
event from the source Bean containing the bound property to the listener Bean. The event
hookup
adapter implements the
PropertyChangeListener
interface and generates a
propertyChange
method implementation that calls the listener Bean's
reportChange
method. Because the BeanBox has generated the event hookup adapter class, and that class does
the
work of hooking up the source Bean to the listener Bean, your listener Bean does not have to
implement
the
PropertyChangeListener
interface itself.
Constrained Properties
Constrained properties are bound properties that go one step further. With a constrained
property, an
outside object--either a listener Bean or the source Bean itself--can veto a property change.
The JavaBeans API provides an event mechanism for handling constrained properties that is
similar to
the one used for bound properties.
To implement a constrained property, you must have:
- A source Bean that defines a constrained property.
-
Listener objects that implement the
VetoableChangeListener
interface.
-
A
PropertyChangeEvent
object that contains the property name, its old value, and its new value. (Note that this is
the same
object used by bound properties.)
Setting Up Constrained Properties
A source Bean sets up constrained properties as follows:
-
Implements a mechanism to allow listener objects who have implemented the
VetoableChangeListener
interface to register and unregister their interest in receiving property change
notification. Rather
than receive notification that a property has already changed, these listeners receive
notification
that a change to a property has been proposed.
The Bean accomplishes this using the
VetoableChangeSupport
utility class, which it either inherits or instantiates. The utility class includes methods
to add
and remove
VetoableChangeListener
objects to a listener list, plus a method that fires property change events and also catches
and
responds appropriately to veto responses.
fireVetoableChange(<property name>,
<old value>, <new value>);
addVetoableChangeListener(listener);
removeVetoableChangeListener(listener);
Fires proposed property change events to interested listeners. Because these changes may be
vetoed,
the source Bean fires the event before the change takes place. Source Beans fire the
PropertyChangeEvent
by calling the listener's
vetoableChange
method.
Provides a means for listener objects to keep the original property value should any one of
the
listeners veto the change. To do this, the source Bean issues the
vetoableChange
call to all listeners a second time, and passes the listeners the original property
value.
Walking Through an Example
The JellyBean JavaBean implements a constrained property, which it calls priceInCents. The
Bean begins
by instantiating two new objects: a
VetoableChangeSupport
object
vetos
to maintain the list of vetoable listeners and a
PropertyChangeSupport
object
changes
to hold the list of property change listeners. It does this as follows:
private VetoableChangeSupport vetos =
new VetoableChangeSupport(this);
private PropertyChangeSupport changes =
new PropertyChangeSupport(this);
Then, JellyBean implements the
VetoableChangeSupport
interface methods to register and unregister constrained property change listeners:
public void addVetoableChangeListener(
VetoableChangeListener l) {
vetos.addVetoableChangeListener(l);
}
public void removeVetoableChangeListener(
VetoableChangeListener l) {
vetos.removeVetoableChangeListener(l);
}
|
The above add and remove listener methods apply to all constrained properties of the Bean.
It's
possible to specify that the listener be attuned only to a specific constrained property
within the
Bean. If so, these two methods can include an additional
String
parameter containing the property name. The property name appears first in the parameter
list,
followed by the listener object. For example, to add a listener Bean for a specific
property:
public void addVetoableChangeListener(
String propertyName,
VetoableChangeListener
listener);
You can also register and unregister vetoable change listeners on a per property basis by
specifying
the particular property name, as follows:
void add<PropertyName>Listener(
VetoableChangeListener p);
void remove<PropertyName>Listener(
VetoableChangeListener p);
JellyBean also defines a method to get the current price and another method to set a new
price. In the
method to set the new price, JellyBean invokes the method
fireVetoableChange
to notify registered listeners of the property change.
public void setPriceInCents(int newPriceInCents)
throws PropertyVetoException {
vetos.fireVetoableChange("priceInCents",
new Integer(oldPriceInCents),
new Integer(newPriceInCents));
//if vetoed (PropertyVetoException thrown)
// don't catch
// exception here so routine exits.
//if not vetoed, make the property
// change
ourPriceInCents = newPriceInCents;
changes.firePropertyChange(
"priceInCents",
new Integer(oldPriceInCents),
new Integer(newPriceInCents));
} |
When the Bean executes the
fireVetoableChange
method, it notifies interested listeners of the proposed change to the
priceInCents
property, and informs them of the current price and proposed new price. If the method
executes
successfully--that is, no exception is thrown--it means that no listener has vetoed the
change. The
setPriceInCents
method then executes the
firePropertyChange
method to fire a property change event to notify listeners that the property has now changed.
However, if the
fireVetoableChange
method causes a
PropertyVetoException
to be thrown, that indicates that a listener has vetoed the proposed change. The property
change does
not go into effect. The
setPriceInCents
method does not catch the exception, thus exiting the method and allowing the exception to be
handled
at a higher level.
Setting Up Constrained Property Listeners
Constrained property listener objects implement the
VetoableChangeListener
interface, which includes the
vetoableChange
method. When the source Bean fires a constrained property change event, it calls
vetoableChange
method on each registered listener. The listener uses this method to receive the change event
and to
accept or reject proposed changes. If they reject a proposed change, they throw a
PropertyVetoException.
Similar to bound properties, your listener Bean can implement the
VetoableChangeListener
interface and
vetoableChange
method itself, or you can use the BeanBox to generate an event hookup adapter class that
provides
this implementation for your Bean. If you have the BeanBox generate the adapter class, then
the
listener Bean does not need to implement the
VetoableChangeListener
interface. You only need to implement the
vetoableChange
method, which the Voter Java Bean does as follows:
public void vetoableChange(
PropertyChangeEvent x)
hrows PropertyVetoException {
if (vetoAll) {
throw new PropertyVetoException(
"NO!", x);
}
}
|
Constrained Properties in the BeanBox
The BeanBox handles constrained properties very much like bound properties.
-
Drop the Bean that defines the constrained property into the BeanBox.
Our example uses the JellyBean.
-
Drop in a Bean that listens for changes to that property and that can potentially veto such a
change.
In our example, we use the Voter Bean, which vetoes changes to that property.
-
Choose the
vetoableChange
event for the JellyBean. Select the JellyBean and, from the Edit menu, select the Events >
vetoableChange > vetoableChange menu item.

-
Connect the event to the listener Bean and select its
vetoableChange
method.
- Connect the rubber band to the Voter Bean instance. When the EventTargetDialog panel
appears,
select the
vetoableChange
method.
The BeanBox generates the event hookup adapter.
-
Test the constrained property.
Select the JellyBean and, in the Property sheet, try changing its
priceInCents
property. In the terminal window from which you started the BeanBox, you see a message
indicating
that an exception was thrown and the change is not allowed.
For more practice setting up properties, see the next section, Manipulating Properties.

Introduction | Page 2 | Page 3 | Page 4
|
|