The JavaTM Tutorial

| Start of Tutorial > Start of Trail > Start of Lesson | Search Feedback Form

Trail: JavaBeans(TM)
Lesson: Properties

## Constrained Properties

> > To get the most out of this section, first read the following:
> > - Chapter 7 of the JavaBeans API Specification
> > - `VetoableChangeListener` interface
> > - `VetoableChangeSupport` class
> > - `PropertyChangeEvent` class
> > - `PropertyVetoException` 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 JavaBeans 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 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 listeners `vetoableChange` 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 a `PropertyChangeEvent` containing the old value.

> The `VetoableChangeSupport` utility class is provided to implement these capabilities. This class implements methods to add and remove `VetoableChangeListener` 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 from `VetoableChangeSupport`, 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 via `PropertyChangeListener.propertyChange` to signal all `VetoableChangeListener` Beans that the change has taken effect. This lets all the vetoable change listeners know that the change was not vetoed by any listener.

> 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:

> 1. Import the `java.beans` package. This gives you access to the `VetoableChangeSupport` class.

> 2. Instantiate a `VetoableChangeSupport` object within your Bean:

>    >     private VetoableChangeSupport vetos =
>    >     	new VetoableChangeSupport(this);

>    `VetoableChangeSupport` manages a list of `VetoableChangeListener` objects, and fires property change events at each object in the list when a change occurs to a constrained property.

> 3. 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);
>    >     }

> 4. 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's `setPriceInCents` 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 old `price`, because both the old and new prices must be passed to `fireVetoableChange`. Also note that the primitive `int` prices are converted to `Integer` objects.

>    >     public void fireVetoableChange(String propertyName,
>    >     			       Object oldValue,
>    >     			       Object newValue)
>    >                                    throws PropertyVetoException

>    These values are then bundled into a `PropertyChangeEvent` object sent to each listener. The old and new values are treated as `Object` values, so if they are primitive types such as `int`, you must use the object version such as `java.lang.Integer`.

> Now you are ready 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:

> 1. Implement the `VetoableChangeListener` interface.

> 2. 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 the `VetoableChangeSupport` object). This is also the method that exercises veto power. A property change is vetoed by throwing the `PropertyVetoException`.

> Note that the `VetoableChangeListener` object is often an adapter class. The adapter class implements the `VetoableChangeListener` interface and the `vetoableChange` method. This adapter is added to the constrained Bean's listener list, intercepts the `vetoableChange` 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:

> 1. Drop `Voter` and `JellyBean` instances into the BeanBox.
> 2. Select the `JellyBean` instance, and choose the Edit|Events|vetoableChange|vetoableChange menu item.
> 3. Connect the rubber band line to the `Voter` Bean. This brings up the `EventTargetDialog` panel.
> 4. Choose the `Voter` Bean's `vetoableChange` method, and push the OK button. This generates an event adapter. You can view this adapter in the `beans/beanbox/tmp/sunw/beanbox` directory.
> 5. Test the constrained property. Select the `JellyBean` and edit its `priceInCents` property in the Properties sheet. A `PropertyVetoException` 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 a `vetoableChange` method implementation that calls the `Voter.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 the `VetoableChangeListener` interface; instead, the generated adapter class implements `VetoableChangeListener`. The adapter's `vetoableChange` method calls the appropriate method in the target object (`Voter`).

### Per Property Constraint

> As with bound property support, JavaBeans has design pattern support for adding and removing `VetoableChangeListener` objects that are tied to a specific named property:

> >     void addVetoableChangeListener(String propertyName,
> >                                    VetoableChangeListener listener);
> >     void removeVetoableChangeListener(String propertyName,
> >                                       VetoableChangeListener listener);

> 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 add<PropertyName>Listener(VetoableChangeListener p);
> >     void remove<PropertyName>Listener(VetoableChangeListener p);

| Start of Tutorial > Start of Trail > Start of Lesson | Search Feedback Form