Writing Advanced Beans |
To get the most out of this section, first read the following:
- Chapter 7 of the JavaBeans API Specification
VetoableChangeListener
interface
VetoableChangeSupport
class
PropertyChangeEvent
classPropertyVetoException
class
A Bean property is constrained when any change to that property can be vetoed. Usually an outside object exercises the right to veto, but the Bean itself can also veto a property change.
The Beans API provides an event mechanism, very similar to the bound property mechanism, that allows objects to veto a Bean's property changes.
There are three parts to constrained property implementations:
- A source Bean containing one or more constrained properties.
- Listener objects that implement the
VetoableChangeListener
interface. This object accepts or rejects proposed changes to a constrained property in the source Bean.- A
PropertyChangeEvent
object containing the property name, and its old and new values. This is the same class used for bound properties.Implementing Constrained Property Support Within a Source Bean
A Bean containing constrained properties must
- Allow
VetoableChangeListener
objects to register and unregister their interest in receiving notification that a property change is proposed.- Fire property change events at those interested listeners when a property change is proposed. The event should be fired before the actual property change takes place. This gives each listener a chance to veto the proposed change. The
PropertyChangeEvent
is fired by a call to each listenersvetoableChange()
method.- If a listener vetoes, then make sure that any other listeners can revert to the old value. This means reissuing the
vetoableChange()
call to all the listeners, with aPropertyChangeEvent
containing the old value.The
VetoableChangeSupport
utility class is provided to implement these capabilities. This class implements methods to add and removeVetoableChangeListener
objects to a listener list, and a method that fires property change events at each listener in that list when a property change is proposed. This method will also catch any vetoes, and resend the property change event with the original property value. Your Bean can either inherit fromVetoableChangeSupport
, or use an instance of it.Note that, in general, constrained properties should also be bound properties. When a constrained property change does occur, a
PropertyChangeEvent
can be sent viaPropertyChangeListener.propertyChange()
to signal allVetoableChangeListener
Beans that the change has taken effect.The
JellyBean
demo Bean has a constrained property. We will its code to illustrate the steps in implementing constrained properties. Here's the steps to implement constrained properties in your Beans:
- Import the
java.beans
package. This gives you access to theVetoableChangeSupport
class.
- Instantiate a
VetoableChangeSupport
object within your Bean:private VetoableChangeSupport vetos = new VetoableChangeSupport(this);VetoableChangeSupport
manages a list ofVetoableChangeListener
objects, and fires property change events at each object in the list when a change occurs to a constrained property.
- Implement methods to maintain the property-change listener list. These merely wrap calls to the
VetoableChangeSupport
object's methods:public void addVetoableChangeListener(VetoableChangeListener l) { vetos.addVetoableChangeListener(l); } public void removeVetoableChangeListener(VetoableChangeListener l) { vetos.removeVetoableChangeListener(l); }- Write a property's setter method to fire a property change event when the property is changed. This includes adding a
throws
clause to the setter method's signature. JellyBean'ssetPriceInCents()
method looks like this:public void setPriceInCents(int newPriceInCents) throws PropertyVetoException { int oldPriceInCents = ourPriceInCents; // First tell the vetoers about the change. If anyone objects, we // don't catch the exception but just let if pass on to our caller. vetos.fireVetoableChange("priceInCents", new Integer(oldPriceInCents), new Integer(newPriceInCents)); // No-one vetoed, so go ahead and make the change. ourPriceInCents = newPriceInCents; changes.firePropertyChange("priceInCents", new Integer(oldPriceInCents), new Integer(newPriceInCents)); }Note that
setPriceInCents()
stores the oldprice
, because both the old and new prices must be passed tofireVetoableChange()
. Also note that the primitiveint
prices are converted toInteger
objects.public void fireVetoableChange(String propertyName, Object oldValue, Object newValue) throws PropertyVetoExceptionThese values are then bundled into a
PropertyChangeEvent
object sent to each listener. The old and new values are treated asObject
values, so if they are primitive types such asint
, you must use the object version such asjava.lang.Integer
.Now you need to implement a Bean that listens for constrained property changes.
Implementing Constrained Property Listeners
To listen for property change events, your listener Bean must implement the
VetoableChangeListener
interface. This interface contains one method:void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException;So to make your class able to listen and respond to property change events, your listener class must
- Implement the
VetoableChangeListener
interface.
- Implement the
vetoableChange()
method. This is the method that will be called by the source Bean on each object in the listener list (maintained by theVetoableChangeSupport
object). This is also the method that exercises veto power. A property change is vetoed by throwing thePropertyVetoException
.Note that the
VetoableChangeListener
object is often an adapter class. The adapter class implements theVetoableChangeListener
interface and thevetoableChange()
method. This adapter is added to the constrained Bean's listener list, intercepts thevetoableChange()
call, and calls the target Bean method that exercises veto power. You'll see an example of this in the next section.Constrained Properties in the BeanBox
When the BeanBox recognizes the design patterns for constrained properties within your Bean, you will see a
vetoableChange
interface item when you pull down the Edit|Events menu.As with any event hookup, the BeanBox generates an adapter class when you hook up a Bean with a constrained property to another Bean. To see how this works, take the following steps:
- Drop
Voter
andJellyBean
instances into the BeanBox.- Select the
JellyBean
instance, and choose the Edit|Events|vetoableChange|vetoableChange menu item.- Connect the rubber band line to the
Voter
Bean. This brings up theEventTargetDialog
panel.- Choose the
Voter
Bean'svetoableChange
method, and push the OK button. This generates an event adapter. You can view this adapter in thebeans/beanbox/tmp/sunw/beanbox
directory.- Test the constrained property. Select the
JellyBean
and edit itspriceInCents
property in the Properties sheet. APropertyVetoException
is thrown, and an error dialog pops up.Behind the scenes the BeanBox generated the event hookup adapter. This adapter implements the
VetoableChangeListener
interface, and also generates avetoableChange()
method implementation that calls theVoter.vetoableChange()
method. Here's the generated adapter source code:// Automatically generated event hookup file. package tmp.sunw.beanbox; import sunw.demo.misc.Voter; import java.beans.VetoableChangeListener; import java.beans.PropertyChangeEvent; public class ___Hookup_1475dd3cb5 implements java.beans.VetoableChangeListener, java.io.Serializable { public void setTarget(sunw.demo.misc.Voter t) { target = t; } public void vetoableChange(java.beans.PropertyChangeEvent arg0) throws java.beans.PropertyVeto Exception { target.vetoableChange(arg0); } private sunw.demo.misc.Voter target; }The
Voter
Bean need not implement theVetoableChangeListener
interface; instead, the generated adapter class implementsVetoableChangeListener
. The adapter'svetoableChange()
method calls the appropriate method in the target object (Voter
).Per Property Constraint
Like bound property support, there is design pattern support for adding and removing
VetoableChangeListener
objects that are tied to a specific named property:As an alternative, for each constrained property a Bean can provide methods with the following signature to register and unregister vetoable change listeners on a per property basis:void addVetoableChangeListener(String propertyName, VetoableChangeListener listener); void removeVetoableChangeListener(String propertyName, VetoableChangeListener listener);void add<PropertyName>Listener(VetoableChangeListener p); void remove<PropertyName>Listener(VetoableChangeListener p);
Writing Advanced Beans |