Previous | Next | Trail Map | JavaBeans: Components for the Java Platform | Properties

Constrained Properties


To get the most out of this section, first read the following:

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:

Implementing Constrained Property Support Within a Bean

A Bean containing constrained properties must

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);


Previous | Next | Trail Map | JavaBeans: Components for the Java Platform | Properties