Propiedades Restringidas

Una propiedad de un Bean está Restringida cuando cualquier cambio en esa propiedad puede ser vetado, Normalmente es un objeto exterior el que ejerce su derecho a veto, pero el propio Bean puede vetar un cambio en una propiedad.

El API de JavaBeans proporciona un mecanismo de eventos similar al mecanismo de las propiedades compartidas, que permite a los objetos vetar los cambios de una propiedad de un Bean.

Existen tres partes en la implementación de una propiedad Restringida:

Implementar Propiedades Restringidas dentro de un Bean.

Un Bean que contenga propiedades restringidas debe:

La clase VetoableChangeSupport se proporciona para implementar estas capacidades. Esta clase implementa métodos para añadir y eliminar objetos VetoableChangeListener a una lista de oyentes, y un método que dispara eventos de cambio de propiedad a todos los oyentes de la lista cuando se propone un cambio de propiedad. Este método también capturará cualquier veto, y re-enviará el evento de cambio de propiedad con el valor original de la propiedad. Nuestro Bean puede descencer de la clase VetoableChangeSupport, o utilizar un ejemplar de ella.

Observa que, en general, las propiedades restringidas también deberían ser propiedades compartidas. Cuando ocurre un cambio en una propiedad restringida, puede ser enviado un PropertyChangeEvent mediante PropertyChangeListener.propertyChange() para indicar a todos los Beans VetoableChangeListener que el cambio a tenido efecto.

El Bean JellyBean tiene una propiedad restringida. Veremos su código para ilustrar los pasos e implementar propiedades restringidas:

  1. Importar el paquete java.beans, esto nos da acceso a la clase VetoableChangeSupport.

  2. Ejemplarizar un objeto VetoableChangeSupport dentro de nuestro Bean:
          private VetoableChangeSupport vetos = 
                    new VetoableChangeSupport(this); 
    
    VetoableChangeSupport maneja una lista de objetos VetoableChangeListener, y dispara eventos de cambio de propiedad a cada objeto de la lista cuando ocurre un cambio en una propiedad restringida.

  3. Implementar métodos para mantener la lista de oyentes de cambio de propiedad. Esto sólo envuelve la llamada a los métodos del objeto VetoableChangeSupport:
           public void addVetoableChangeListener(VetoableChangeListener l) {
            vetos.addVetoableChangeListener(l);
           }
           public void removeVetoableChangeListener(VetoableChangeListener l) {
            vetos.removeVetoableChangeListener(l);
           }
    
  4. Escribir un método seleccionador de propiedades que dispare un evento de cambio de propiedad cuando la propiedad ha cambiado. Esto incluye añadir una clausula throws a la firma del método. El método setPriceInCents() de JellyBean se parece a esto:
          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));
          }
    
    Observa que setPriceInCents() almacena el valor antiguo de price, porque los dos valores, el nuevo y el antiguo, deben ser pasados a fireVetoableChange(). También observa que los precios primitivos int se han convertido a objetos Integer.
          public void fireVetoableChange(String propertyName, 
                                        Object oldValue,
                                        Object newValue)
                                 throws PropertyVetoException
    
    Estos valores se han empaquetado en un objeto PropertyChangeEvent enviado a cada oyente. Los valores nuevo y antiguo son tratados como valores Object, por eso si son tipos primitivos como int, deben utilizarse sus versiones objetos como java.lang.Integer.

Ahora necesitamos implementar un Bean que escuche los cambios en las propiedades restringidas.

Implementar Oyentes de Propiedades Restringidas

Para escuchar los eventos de cambio de propiedad, nuestro Bean oyente debe implementar el interface VetoableChangeListener. El interface contiene un método:
void vetoableChange(PropertyChangeEvent evt)
                        throws PropertyVetoException;
Por eso para hacer que nuestra clase pueda escuchar y responder a los eventos de cambio de propiedad debe:
  1. Implementar el interface VetoableChangeListener.

  2. Implementar el método vetoableChange(). Este es el método al que llamará el Bean fuente en cada objeto de la lista de oyentes (mantenida por el objeto VetoableChangeSupport). Este también es el método que ejerce el poder del veto. Un cambio de propiedad es vetado lanzando una PropertyVetoException.

Observa que el objeto VetoableChangeListener frecuentemente es una clase adaptador. La clase adaptador implementa el interface VetoableChangeListener y el método vetoableChange(). Este adaptador es añadido a la lista de oyentes del Bean restringido, intercepta la llamada a vetoableChange(), y llama al método del Bean fuente que ejerce el poder del veto.

Propiedades Restringidas en el BeanBox

Cuando el BeanBox reconoce el patrón de diseño de una propiedad restringida dentro de un Bean, se verá un ítem de un interface vetoableChange al desplegar el menú Edit|Events.

El BeanBox genera una clase adaptador cuando se conecta un Bean que tiene una propiedad restringida con otro Bean. Para ver como funciona esto, sigue estos pasos:

  1. Arrastra ejemplares de Voter y de JellyBean al BeanBox.
  2. Selecciona el ejemplar de JellyBean y elige el menú Edit|Events|vetoableChange|vetoableChange.
  3. Conecta la línea que aparece con el Bean Voter. Esto mostrará el panel EventTargetDialog.
  4. Elige el método vetoableChange del Bean Voter, y pulsa sobre el botón OK. Esto genera un adaptador de eventos que puedes ver en el directorio beans/beanbox/tmp/sunw/beanbox.
  5. Prueba la propiedad restringida. Selecciona el JellyBean y edita sus propiedades priceInCents en la hoja de propiedades. Se lanzará una PropertyVetoException, y se mostrará un dialogo de error.

Detrás de la escena el BeanBox genera el adaptador de evento. Este adaptador implementa el interface, VetoableChangeListener, y también genera un método vetoableChange() que llama al método Voter.vetoableChange(). Aquí tienes el código generado para el adaptador:

// 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;
}
El Bean Voter no necesita implementar el interface VetoableChangeListener; en su lugar, la clase adaptador generada implementa VetoableChangeListener. El método vetoableChange() del adaptador llama al método apropiado en el objeto fuente (Voter).

Para Propiedades Restringidas

Al igual que las propiedades compartidas, existe un patrón de diseño soportado para añadir y eliminar objetos VetoableChangeListener que se han unido a un nombre de propiedad específico:
 void addVetoableChangeListener(String propertyName,
                         VetoableChangeListener listener);
 void removeVetoableChangeListener(String propertyName,
                         VetoableChangeListener listener);
Como alternativa, por cada propiedad restingida de un Bean se pueden proporcionar métodos con la siguiente firma para registrar y eliminar oyentes de una propiedad básica:
 void add<PropertyName>Listener(VetoableChangeListener p);
 void remove<PropertyName>Listener(VetoableChangeListener p);

Ozito