SDSU CS 696 Emerging Technologies: Distributed Objects
Spring Semester, 1998
JavaBean Events & Properties

To Lecture Notes Index
© 1998, All Rights Reserved, SDSU & Roger Whitney
San Diego State University -- This page last updated 30-Apr-98

Contents of Doc 32, JavaBean Events & Properties

  1. References
  2. Events
    1. Unicast - Multicast Listener Registration
    2. Event Delivery Semantics
    3. Passing Data Between Beans
    4. Event Adapters
  3. Properties
    1. Indexed Properties
    2. Bounded Properties
    3. Constrained Properties
CS 696 Doc 32 JavaBean Events & Properties

References


JavaBeans(TM) API Specification Version 1.01, July 24, 1997, Graham Hamilton, editor, Sun Microsystems

Developing Java Beans, Englander, O'Reilly, 1997, chapters 2-4, pp 13-84


Doc 32, JavaBean Events & Properties Slide # 2

Events

Unicast - Multicast Listener Registration


A class that registers listeners normally accepts an number of listeners

that is we have a multicast registration
   Vector listeners = new Vector();

   public synchronized void addTimeAdvancedListener(
          TimeAdvancedListener listener )
      {
      listeners.addElement( listener );
      }

A class indicates a unicast registration by throwing the java.util.TooManyListenersException

   TimeAdvancedListener myListener;

   public synchronized void addTimeAdvancedListener(
          TimeAdvancedListener listener ) throws 
             java.util.TooManyListenersException
      {
      if ( myListener != null )
         throw new TooManyListenersException();

      myListener = listener;
      }


Doc 32, JavaBean Events & Properties Slide # 3

Event Delivery Semantics


Event delivery is done on event source thread

If the event source has performance constrains and listeners could be slow, an event queue may be needed
Concurrency Control

Deadlock and race conditions can be a problem

Events delivery is prone to deadlock

A may deliver an event to B at the same moment as B is calling
a method on A

JaveBean Spec recommends:
event sources should use synchronized methods and synchronized blocks to synchronize access to the data structures that describe event listeners
event sources should avoid holding their own internal locks when they call event listener methods
event sources should use a synchronized block to locate the target listeners and then call the event listeners from unsynchronized code

See next slide for example recommended code

Doc 32, JavaBean Events & Properties Slide # 4
Recommended Concurrency Control
public interface ModelChangedListener extends 
      java.util.EventListener 
   {
   void modelChanged(EventObject e);
   }

public abstract class Model 
   {
   private Vector listeners = new Vector(); 

   public synchronized void addModelChangedListener( 
         ModelChangedListener mcl) 
      {
      listeners.addElement(mcl);
      }

   public synchronized void removeModelChangedListener(
         ModelChangedListener mcl) 
      {
      listeners.removeElement(mcl);
      }

   protected void notifyModelChanged() 
      {
      Vector l;
      EventObject e = new EventObject(this);
      
      synchronized(this) { l = (Vector)listeners.clone(); }

      for (int i = 0; i < l.size(); i++) 
         ((ModelChangedListener)l.elementAt(i)).modelChanged(e);
      }
   }

Doc 32, JavaBean Events & Properties Slide # 5

Passing Data Between Beans


Events can be used to pass data between beans as events contain state
ExampleDatagramListener
package beanExamples;

public interface  DatagramListener extends java.util.EventListener
   {
   public void newData( DatagramEvent data );
   }

DatagramEvent
package beanExamples;

public class DatagramEvent extends java.util.EventObject
   {
   int datagram;
   
   public DatagramEvent( SDSUDataSource source, int data)
      {
      super( source );
      datagram = data;
      }
   
   public int getDatagram()
      {
      return datagram;
      }
   }


Doc 32, JavaBean Events & Properties Slide # 6
SDSUDataConsumer
package beanExamples;

public class SDSUDataConsumer implements  DatagramListener
   {
   public void newData( DatagramEvent datagram )
      {
      SDSUDataSource mySource = 
         (SDSUDataSource) datagram.getSource();

      int data = datagram.getDatagram();

      System.out.println( "Source: " + mySource.getName() + 
                        " data: " + data );
      }
   }

Note that there are two ways to get data from the SDSUDataSource

The source is passed as part of the event, can interact with the source object directly

The event object can contain information that the listener can access

Doc 32, JavaBean Events & Properties Slide # 7
SDSUDataSource
package beanExamples;

import java.util.Random;
import java.util.Vector;

public class SDSUDataSource implements TimeAdvancedListener
   {
   Vector listeners = new Vector();
   Random dataGenerator = new Random();
   String name;
   
   public SDSUDataSource() {  this("SDSU");  }
      
   public SDSUDataSource(String name)
      {
      setName( name );
      SDSUClock myClock = new SDSUClock( 10000 );
      myClock.addTimeAdvancedListener( this );
      }
   
   public String getName() { return name; }

   public void  setName( String name) {this.name = name; }
      
   public void timeAdvanced( TimeAdvancedEvent timeEvent )
      {
      notifyListeners();
      }


Doc 32, JavaBean Events & Properties Slide # 8

   public synchronized void addDatagramListener(
          DatagramListener listener )
      {
      listeners.addElement( listener );
      }
      
   public synchronized void removeDatagramListener( 
         DatagramListener listener )
      {
      listeners.removeElement( listener );
      }
   
   protected void notifyListeners()
      {
      int data = dataGenerator.nextInt();
      DatagramEvent time = new DatagramEvent(this, data);
      
      for ( int k = 0; k < listeners.size(); k++)
         {
         DatagramListener aListener = 
            (DatagramListener) listeners.elementAt( k );
         aListener.newData( time );
         }
      }
   }


Doc 32, JavaBean Events & Properties Slide # 9
Example Notes

In the BeanBox one can connect a SDSUDataSource to a SDSUDataConsumer

When this is done the SDSUDataConsumer gets DatagramEvent which allows it to get data from the SDSUDataSource


Doc 32, JavaBean Events & Properties Slide # 10

Event Adapters

Intent of Adapter Pattern

Convert the interface of a class into another interface clients expect. Adapters lets classes work together that couldn't otherwise because of incompatible interfaces



JavaBean Spec states:
Event adapters are an extremely important part of the Java event model
Uses the spelling adaptor, but JDK uses adapter

Some uses of adapters listed in the spec
Filtering events
Event queue between sources and listeners
Demultiplexing multiple event sources onto a single listener
Acting as a generic "wiring manager" between sources and listeners


Doc 32, JavaBean Events & Properties Slide # 11
Adapter Example

On Doc 31, slide 3 the SDSUReporter has the method:
   public void report()
      {
      System.out.println( "Report number: " + (count++)  + 
         " from " + name );
      }

On Doc 31, slide 5 the SDSUClock adds TimeAdvancedListeners

The TimeAdvancedListener interface has one method
public interface  TimeAdvancedListener extends 
      java.util.EventListener
   {
   void timeAdvanced( TimeAdvancedEvent timeEvent );
   }


Doc 32, JavaBean Events & Properties Slide # 12

The following adapter will allow the SDSUClock to trigger the report method in an SDSUReporter object
package beanExamples;

public class TimeAdvancedListenerAdapter
   {
   SDSUReporter adaptee;
   
   public TimeAdvancedListenerAdapter( SDSUReporter adaptee )
      {
      this.adaptee = adaptee;
      }
      
   public void timeAdvanced( TimeAdvancedEvent timeEvent )
      {
      adaptee.report();
      }
   }

The BeanBox generated such a adapter in the demo on Doc 31, slide 13-14


Doc 32, JavaBean Events & Properties Slide # 13
Generic Adapters
package beanExamples;
import java.lang.reflect.*;

public class TimeAdvancedListenerAdapter {
   Object adaptee;
   Method adapteeMethod;
   
   public TimeAdvancedListenerAdapter( Object adaptee, 
            String methodName ) throws NoSuchMethodException {
      this.adaptee = adaptee;
      Class[] noParameters = new Class[0];  
      adapteeMethod = adaptee.getClass().
                           getMethod( methodName, noParameters );
   }
      
   public void timeAdvanced( TimeAdvancedEvent timeEvent ) 
         throws AdapterException  {
      Object[] arguements = new Object[0];
      try {
         adapteeMethod.invoke( adaptee, arguements);
      }
      catch ( Exception error ) {
         throw new AdapterException( error.toString() );
      }
   }
}
   
public class AdapterException extends RuntimeException {
   public AdapterException()  { super(); }

   public AdapterException(String s)  { super(s); }
}

Doc 32, JavaBean Events & Properties Slide # 14
import beanExamples.*;

public class TrivialApplication 
   {

   public static void main(String args[]) throws Exception
      {
      System.out.println( "Hello World!" );
      SDSUClock myClock = new SDSUClock();
      SDSUReporter reporter = new SDSUReporter();
      TimeAdvanceListenerAdapter adapt = 
         new TimeAdvanceListenerAdapter( reporter, "report");
      myClock.addTimeAdvancedListener( adapt );
      }
   }

Allows one adapter replace multiple adapter classes

JavaBean spec recommends only using the with builder tools


Doc 32, JavaBean Events & Properties Slide # 15
Demultiplexing Adapting

A object may receive the same message for different reasons
A dialog window may contain many buttons
It will receive an actionPerformed message when any button is pressed
The object will have to determine for which reason it received the event
Which button was pressed?
Adapter can be used to direct the event to a specific method in the object
An adapter can sit between the OK button and the dialog window
When the OK button is pressed the actionPerformed is sent to the adapter
The adapter calls the doOKAction() on dialog window object


Doc 32, JavaBean Events & Properties Slide # 16

Properties


Properties are discrete, named attributes of a Java Bean that can affect its appearance or its behavior

Properties are accessed with get<X>/set<X> methods

Properties can be:
read and write
(get/set)
read only
(get only)
write only
(set only)

Indexed Properties


Properties can be indexed

The index must be an int

Index properties can be accessed one at a time or as an array
void setX( int index, PropertyType value);
PropertyType getX( int index );

void setX( PropertyType[] values );
PropertyType[] getX(  );


Doc 32, JavaBean Events & Properties Slide # 17

Bounded Properties


Sometimes when a bean property changes then either the bean's container or some other bean may wish to be notified of the change

Notify other of a bounded property by sending a PropertyChangeEvent to all your propertyChangeListeners

So making a property bounded is just saying your bean will send a PropertyChangeEvent

Relevant items in java.bean package:
PropertyChangeEvent
PropertyChangeListener
PropertyChangeSupport
PropertyChangeListener
interface with the following method:
public abstract void propertyChange(PropertyChangeEvent evt)


Doc 32, JavaBean Events & Properties Slide # 18
PropertyChangeEvent
Methods
getNewValue() 
getOldValue() 
getPropagationId() 
   The "propagationId" field is reserved for future use. 
getPropertyName() 
setPropagationId(Object) 
Constructor
public PropertyChangeEvent(Object source,String propertyName,
   Object oldValue, Object newValue)
source - The bean that fired the event
propertyName - The programmatic name of the property that was changed
oldValue - The old value of the property
newValue - The new value of the property



Doc 32, JavaBean Events & Properties Slide # 19
PropertyChangeSupport

A class that will notify the PropertyChangeListeners for you
Methods
addPropertyChangeListener(PropertyChangeListener)
Add a PropertyChangeListener to the listener list
removePropertyChangeListener(PropertyChangeListener) 
Remove a PropertyChangeListener from the listener list
firePropertyChange(String propertyName,Object oldValue, Object newValue)
Report a bound property update to any registered listeners. No event is fired if old and new are equal and non-null.
propertyName - The programmatic name of the property that was changed
oldValue - The old value of the property
newValue - The new value of the property
Constructor

public PropertyChangeSupport(Object sourceBean)

Doc 32, JavaBean Events & Properties Slide # 20
Bounded Property Example
package beanExamples;
import java.beans.*;

public class SDSUReporter implements TimeAdvancedListener {
   String name = "SDSU";
   int count = 0;
   PropertyChangeSupport propertyListeners =
      new PropertyChangeSupport( this );
      
   public void setName( String newName) {
      // change the property before notifying listeners
      String oldName = name;
      name = newName;
      propertyListeners.firePropertyChange( "name", 
         oldName, name );
   }
   
   public String getName() { return name; }
   
   public void addPropertyChangeListener (
         PropertyChangeListener listener) {
      propertyListeners.addPropertyChangeListener( listener );
   }

   public void removePropertyChangeListener ( 
         PropertyChangeListener listener) {
      propertyListeners.removePropertyChangeListener( listener );
   }
      
   public void timeAdvanced( TimeAdvancedEvent timeEvent )
      { report(); }
      
   public void report(){
      System.out.println( "Report number: " + (count++)  + 
         " from " + name );
   }
}


Doc 32, JavaBean Events & Properties Slide # 21

Constrained Properties


Sometimes when a property change occurs some other bean may wish to validate the change and reject it if it is inappropriate

We refer to properties that undergo this kind of checking as constrained properties

Constrained properties are indicated by throwing a PropertyVetoException in the set<X> method
PropertyType getFoo();
void setFoo(PropertyType value) throws PropertyVetoException;

Beans and containers that validate a property change must register them selves as VetoableChangeListeners with the property's bean

Thus a bean with constrained properties need to support the methods:
public void addVetoableChangeListener(VetoableChangeListener x);

public void removeVetoableChangeListener(VetoableChangeListener x);

When a constrained property is to be set the bean sends a PropertyChangeEvent to all its VetoableChangeListeners before changing

The change is vetoed by throwing a PropteryVetoException

Doc 32, JavaBean Events & Properties Slide # 22
Relevant java.beans.* items
java.beans.VetoableChangeListener

This the interface
public abstract void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException

java.beans.VetoableChangeSupport


Similar to PropertyChangeSupport

A class that will notify the VetoableChangeListeners for you


Doc 32, JavaBean Events & Properties Slide # 23
Constrained Properties Example
package beanExamples;
import java.beans.*;

public class SDSUReporter implements TimeAdvancedListener
   {
   String name = "SDSU";
   int count = 0;
   PropertyChangeSupport propertyListeners =
      new PropertyChangeSupport( this );

   VetoableChangeSupport  propertyVetors =
      new VetoableChangeSupport ( this );
      
   public void setName( String newName) throws 
         PropertyVetoException
      {
      String oldName = name;

      // See if we can make the change
      propertyVetors.fireVetoableChange( "name", 
            oldName, 
            newName );
      
      // No Vetos so make change
      name = newName;

      // Now let them know the change happened
      propertyListeners.firePropertyChange( "name", 
         oldName, 
         name );
      }


Doc 32, JavaBean Events & Properties Slide # 24
//Constrained Properties Example Continued

   public String getName()
      { return name; }

   public void addVetoableChangeListener (
         VetoableChangeListener listener)
      {
      propertyVetors.addVetoableChangeListener( listener );
      }

   public void removeVetoableChangeListener ( 
         VetoableChangeListener listener)
      {
      propertyVetors.removeVetoableChangeListener( listener );
      }
   
   public void addPropertyChangeListener (
         PropertyChangeListener listener)
      {
      propertyListeners.addPropertyChangeListener( listener );
      }

   public void removePropertyChangeListener ( 
         PropertyChangeListener listener)
      {
      propertyListeners.removePropertyChangeListener( listener );
      }
      
   public void timeAdvanced( TimeAdvancedEvent timeEvent )
      {
      report();
      }
      
   public void report()
      {
      System.out.println( "Report number: " + (count++)  + 
         " from " + name );
      }
   }


Copyright © 1998 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
All rights reserved.

visitors since 30-Apr-98