/**
* 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);
}
}
}
}
|