FileDocCategorySizeDatePackage
Observable.javaAPI DocExample4413Wed Feb 05 01:48:20 GMT 1997imaginary.util

Observable.java

/**
 * This class is a distributed version of the standard Java
 * Observable class.  It uses the exact same API as the Java class.
 * @see java.util.Observable
 */
package imaginary.util;

import java.io.Serializable;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.Vector;

public class Observable extends UnicastRemoteObject
implements RemoteObservable {
    // the object is ready for observer notification
    private boolean changed   = false;
    // the list of RemoteObserver objects observing this object
    private Vector  observers = new Vector();

    /*********************** Constructors *********************/
    /**
     * Constructs a new observable
     */
    public Observable() throws RemoteException {
        super();
    }

    /*************** Attribute accessor methods **************/
    /**
     * Marks the observable as unchanged.
     */
    protected synchronized void clearChanged() {
        changed = false;
    }

    /**
     * @return true if the observable is flagged as changed
     */
    public synchronized boolean hasChanged() {
        return changed;
    }

    /**
     * Marks the observable as changed.
     */
    protected synchronized void setChanged() {
        changed = true;
    }

    /**
     * @return the number of observers observing this object
     */
    public synchronized int countObservers() {
        return observers.size();
    }

    /**
     * Adds a RemoteObserver to the list of objects observing this
     * object.
     * @param ob the new RemoteObserver
     */
    public synchronized void addObserver(RemoteObserver ob) {
        if( !observers.contains(ob) ) {
            observers.addElement(ob);
        }
    }

    /**
     * Removes the specified RemoteObserver from the list of
     * objects being observed.
     * @param ob the RemoteObserver to be removed
     */
    public synchronized void deleteObserver(RemoteObserver ob) {
        if( observers.contains(ob) ) {
            observers.removeElement(ob);
        }
    }

    /**
     * Clears out the entire observer list.
     */
    public synchronized void deleteObservers() {
        observers = new Vector();
    }

    /***************** Observer notification *****************/
    /**
     * Assuming the object has been changed called, this method
     * will notify its observers with null as an argument.
     */
    public void notifyObservers() {
        performNotify(null);
    }

    /**
     * Notifies observers of a change with the specified remote
     * reference as an argument.
     * @param r the remote object to pass to observers
     */
    public void notifyObservers(Remote r) {
        performNotify(r);
    }

    /**
     * Notifies observers of a change with the specified serializable
     * object as an argument.
     * @param s the serializable object to send to the observers
     */
    public void notifyObservers(Serializable s) {
        performNotify(s);
    }

    // This performs actual observer notification.
    // It first copies the observers Vector into an array.
    // This allows it to move out of the synchronized block
    // to avoid dead locks and avoid holding up other threads
    // while it notifies observers across potentially nasty Internet
    // links.  For example, if you synchronized the whole block,
    // processing could take several seconds while some calls time
    // out.  This means another thread coming in and adding to
    // the observer list would wait for this block to release its
    // monitor.  Yuck!
    public void performNotify(Object arg) {
        RemoteObserver[] obs;
        int count;
        
        synchronized (this) {
            if( !hasChanged() ) {
                return;
            }
            count = observers.size();
            obs = new RemoteObserver[count];
            observers.copyInto(obs);
            clearChanged();
        }
        for(int i=0; i<count; i++) {
            if( obs[i] != null ) {
                try {
                    obs[i].update(this, arg);
                }
                catch( RemoteException e ) {
                    e.printStackTrace();
                }
            }
        }
    }
}