View Javadoc

1   package org.sourceforge.jemm.client;
2   
3   import java.util.concurrent.ConcurrentHashMap;
4   import java.util.concurrent.ConcurrentMap;
5   import java.util.concurrent.CountDownLatch;
6   
7   import org.sourceforge.jemm.database.ClientId;
8   import org.sourceforge.jemm.database.ClientThreadId;
9   import org.sourceforge.jemm.database.LockAcquiredListener;
10  import org.sourceforge.jemm.types.ID;
11  
12  /**
13   * An ObjectDatabase implementation that converts acquireLock calls into
14   * synchronous calls that wait for the lock to be acquired.
15   * 
16   * Implementation notes -------------------- This implementation works by each
17   * thread registering itself in a Map with its clientThreadId pointing to a lock
18   * object on which the thread waits. Once the async listener calls for that
19   * particular ID then the lock is notified and the original thread is released.
20   * 
21   * Since a clientThreadId is unique for each thread there is only ever one
22   * listener per lock. Note that if the clientThreadId is not unique by thread
23   * then acquireLock will return without acquiring the lock and threads could be
24   * locked indefinitely.
25   * 
26   * @author Paul Keeble
27   * 
28   */
29  public class SynchronousLockDecorator extends DelegatingObjectDatabase
30  		implements LockAcquiredListener {
31  	ClientId clientId;
32  	ConcurrentMap<ClientThreadId, CountDownLatch> waitingThreads;
33  
34  	public SynchronousLockDecorator(ClientId clientId, ObjectDatabase delegate) {
35  		super(delegate);
36  		this.clientId = clientId;
37  		delegate.setClientLockAcquiredListener(clientId, this);
38  
39  		waitingThreads = new ConcurrentHashMap<ClientThreadId, CountDownLatch>();
40  	}
41  
42  	/**
43  	 * Acquires a lock by calling onto the delegate database and waiting for an
44  	 * asynchronous response back. Blocks until a response of some description
45  	 * is returned.
46  	 * 
47  	 * @param threadId
48  	 *            a unique identifier for a Thread (and client). MUST BE UNIQUE
49  	 *            PER THREAD.
50  	 * @param jemmId
51  	 *            The ID to lock on
52  	 */
53  	public void acquireLock(ClientThreadId threadId, ID jemmId) {
54  		CountDownLatch lock = new CountDownLatch(1);
55  		waitingThreads.put(threadId, lock);
56  		super.acquireLock(threadId, jemmId);
57  
58  		awaitLock(lock);
59  		waitingThreads.remove(threadId);
60  	}
61  
62      private void awaitLock(CountDownLatch lock) {
63          try {
64  			lock.await();
65  		} catch (InterruptedException e) {
66  		}
67      }
68  
69  	/**
70  	 * Attempts to change the LockAcquiredListener are ignored.
71  	 */
72  	public void removeLockAcquiredListener(ClientId clientId) {
73  	}
74  
75  	/**
76  	 * Attempts to change the LockAcquiredListener are ignored.
77  	 */
78  	public void setClientLockAcquiredListener(ClientId clientId,
79  			LockAcquiredListener listener) {
80  	}
81  
82  	/**
83  	 * A listener method implementation that gets the lock and notifies the
84  	 * waiting thread.
85  	 */
86  	@Override
87  	public void lockAcquired(ClientThreadId threadId, ID object) {
88  		CountDownLatch lock = waitingThreads.get(threadId);
89  
90  		lock.countDown();
91  
92  	}
93  }