FileDocCategorySizeDatePackage
Lock.javaAPI DocExample7116Sat Feb 01 06:56:50 GMT 1997imaginary.persist

Lock.java

/**
 * This class represents a lock held by a lock holder over
 * one or more modified persistent objects.  Each time a new
 * object is locked by the Lock, it spawns a thread for monitoring
 * that lock.
 */
package imaginary.persist;

import java.util.Hashtable;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class Lock extends UnicastRemoteObject implements RemoteLock {
    /********************** Class attributes *********************/
    // Maps locks to lock holders
    static private Hashtable locks = new Hashtable();

    /************************ Class methods **********************/
    /**
     * Checks to see if the lock holder already has a lock associated
     * with it.  If so, return it.  If not, create a new one.
     * Either way, spawn a for this persistent.
     * @param holder the remote object holding the lock
     * @param p the persistent object being locked
     * @exception imaginary.persist.LockException
     */
    static public Lock createLock(RemoteLockHolder holder, Persistent p)
    throws LockException {
        Lock l;
        
        if( locks.containsKey(holder) ) {
            l = (Lock)locks.get(holder);
            l.createThread(p);
        }
        else {
            try {
                l = new Lock(holder);
            }
            catch( RemoteException e ) {
                throw new LockException(e.getMessage());
            }
            l.createThread(p);
            locks.put(holder, l);
        }
        return l;
    }

    /********************* Instance attributes ************************/
    private RemoteLockHolder lock_holder = null;
    private Hashtable        threads     = new Hashtable();
    private Transaction      transaction = null;

    /**
     * Constructs a new lock for the remote lock holder.
     * @param client the remote lock holder holding this lock
     * @exception imaginary.persist.LockException An error occurred finding
     * a Transaction for this lock.
     * @exception java.rmi.RemoteException An error occurred setting
     * the lock in the remote lock holder.
     */
    public Lock(RemoteLockHolder client)
    throws LockException, RemoteException {
        super();
        lock_holder = client;
        lock_holder.setLock(this);
        try {
            transaction = Transaction.getTransaction();
        }
        catch( PersistenceException e ) {
            throw new LockException(e);
        }
    }

    /**
     * @return the remote object holding this lock
     */
    public RemoteLockHolder getHolder() {
        return lock_holder;
    }

    /**
     * @return the Transaction associated with this lock
     */
    public Transaction getTransaction() {
        return transaction;
    }

    /**
     * Creates a new LockThread for monitoring a new persistent object
     * locked by this lock.
     * @param p the Persistent instance being locked
     * @exception imaginary.persist.LockException An error occurred
     * creating the LockThread.
     */
    public void createThread(Persistent p) throws LockException {
        if( threads.containsKey(p) ) {
            return;
        }
        else {
            LockThread thread = new LockThread(this, p);

            threads.put(p, thread);
            transaction.addPersistent(p);
        }
    }

    /**
     * If a problem occurs while a thread is monitoring a lock, it
     * calls this method.  This method then triggers loseLock() in both the
     * thread and persistent so they can clean up after the loss of the lock.
     * @param p the persistent whose lock was lost
     */
    public void loseLock(Persistent p) {
        if( !threads.containsKey(p) ) {
            return;
        }
        else {
            LockThread thread = (LockThread)threads.get(p);

            thread.loseLock();
            p.loseLock();
            threads.remove(p);
            transaction.removePersistent(p);
        }
    }

    /**
     * This method is triggered periodically by the LockThread
     * to monitor the lock and make sure the client/server connection
     * is still there.
     * @param p the perisistent object whose lock is being checked
     */
    public void monitorLock(Persistent p) throws Exception {
        p.monitorLock();
        if( !threads.containsKey(p) ) {
            return;
        }
        else {
            lock_holder.monitorLock(p);
        }
    }

    /**
     * This method signals a normal end to a lock, normally because
     * the client saved its objects.
     * @param p the persistent being released
     */
    public void releaseLock(Persistent p) {
        if( !threads.containsKey(p) ) {
            return;
        }
        else {
            LockThread thread = (LockThread)threads.get(p);
            
            thread.releaseLock();
            threads.remove(p);
        }
    }

    /**
     * Triggers the save process for all locked objects.
     * @exception imaginary.persist.PersistenceException An error occurred
     * during the save.
     */
    public synchronized void save() throws PersistenceException {
        transaction.save();
    }
}

class LockThread extends Thread {
    /**
     * Number of seconds between monitorLock() calls.
     */
    static public final int TIMEOUT = 1;

    // The lock has been released naturally
    private boolean          commit;
    // The lock that launched this thread
    private Lock             lock;
    // The persistent being locked
    private Persistent       target;

    /**
     * Constructs a new LockThread for the lock and persistent specified.
     * @param l the Lock owning this thread
     * @param t the Persistent being locked
     */
    public LockThread(Lock l, Persistent t) {
        lock = l;
        target = t;
        commit = false;
        setDaemon(true);
        start();
    }

    /**
     * The lock has been lost!
     */
    public synchronized void loseLock() {
        stop();
    }

    /**
     * Note that the lock has been released so the thread can
     * complete naturally.
     */
    public synchronized void releaseLock() {
        commit = true;
    }

    /**
     * Implementation of the Thread run() method.  It calls monitorLock()
     * in Lock every second to check up on the lock.
     */
    public void run() {
        try {
            int timeout = LockThread.TIMEOUT * 1000;

            while( true ) {
                try { Thread.sleep(timeout); }
                catch( InterruptedException e ) { }
                // throws an exception if the lock fails
                lock.monitorLock(target);
                if( commit ) {
                    stop();
                    break;
                }
            }
        }
        catch( Exception e ) {
            if( !commit ) {
                lock.loseLock(target);
            }
        }
    }
}