 |
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
- References
- Events
- Unicast - Multicast Listener Registration
- Event Delivery Semantics
- Passing Data Between Beans
- Event Adapters
- Properties
- Indexed Properties
- Bounded Properties
- Constrained Properties
CS 696 Doc 32 JavaBean Events & Properties
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
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;
}
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
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);
}
}
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;
}
}
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
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();
}
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 );
}
}
}
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
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
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 );
}
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
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); }
}
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
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
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)
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( );
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)
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
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)
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 );
}
}
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
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
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 );
}
//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