001: /* Copyright 2002 The JA-SIG Collaborative. All rights reserved.
002: * See license distributed with this file and
003: * available online at http://www.uportal.org/license.html
004: */
005:
006: package org.jasig.portal.concurrency.locking;
007:
008: import java.util.ArrayList;
009: import java.util.Collection;
010: import java.util.Date;
011: import java.util.HashMap;
012: import java.util.Iterator;
013: import java.util.List;
014: import java.util.Map;
015:
016: import org.jasig.portal.concurrency.IEntityLock;
017: import org.jasig.portal.concurrency.LockingException;
018: import org.jasig.portal.utils.SmartCache;
019:
020: /**
021: * In-memory store for <code>IEntityLocks</code>.
022: * @author Dan Ellentuck
023: * @version $Revision: 35080 $
024: */
025: public class MemoryEntityLockStore implements IEntityLockStore {
026: private static IEntityLockStore singleton;
027:
028: // The lock store is a Map that contains a SmartCache for each entity type:
029: private Map lockCache;
030:
031: /**
032: * MemoryEntityLockStore constructor comment.
033: */
034: public MemoryEntityLockStore() {
035: super ();
036: initializeCache();
037: }
038:
039: /**
040: * Adds this IEntityLock to the store.
041: * @param lock
042: */
043: public void add(IEntityLock lock) throws LockingException {
044: primAdd(lock, lock.getExpirationTime());
045: }
046:
047: /**
048: * Deletes this IEntityLock from the store.
049: * @param lock
050: */
051: public void delete(IEntityLock lock) throws LockingException {
052: Map m = getLockCache(lock.getEntityType());
053: synchronized (m) {
054: m.remove(getCacheKey(lock));
055: }
056: }
057:
058: public void deleteAll() {
059: initializeCache();
060: }
061:
062: /**
063: * Deletes the expired IEntityLocks from the underlying store.
064: * @param expiration java.util.Date
065: */
066: public void deleteExpired(java.util.Date expiration)
067: throws LockingException {
068: // let SmartCache handle it.
069: }
070:
071: /**
072: * Returns an IEntityLock[] based on the params, any or all of which may be null. A
073: * null param means any value, so <code>find(myType,myKey,null,null,null)</code> will
074: * return all <code>IEntityLocks</code> for myType and myKey.
075: *
076: * @return org.jasig.portal.concurrency.locking.IEntityLock[]
077: * @param entityType Class
078: * @param entityKey String
079: * @param lockType Integer - so we can accept a null value.
080: * @param expiration Date
081: * @param lockOwner String
082: * @exception LockingException - wraps an Exception specific to the store.
083: */
084: public IEntityLock[] find(Class entityType, String entityKey,
085: Integer lockType, java.util.Date expiration,
086: String lockOwner) throws LockingException {
087: List locks = new ArrayList();
088: Map cache = null;
089: Collection caches = null;
090: Iterator cacheIterator = null;
091: Iterator cacheKeyIterator = null;
092: Iterator keyIterator = null;
093: IEntityLock lock = null;
094:
095: if (entityType == null) {
096: caches = getLockCache().values();
097: } else {
098: caches = new ArrayList(1);
099: caches.add(getLockCache(entityType));
100: }
101:
102: cacheIterator = caches.iterator();
103: while (cacheIterator.hasNext()) {
104: cache = (Map) cacheIterator.next();
105: cacheKeyIterator = cache.keySet().iterator();
106: List keys = new ArrayList();
107:
108: // Synchronize on the cache only while collecting its keys. There is some
109: // exposure here.
110: synchronized (cache) {
111: while (cacheKeyIterator.hasNext()) {
112: keys.add(cacheKeyIterator.next());
113: }
114: }
115:
116: keyIterator = keys.iterator();
117: while (keyIterator.hasNext()) {
118: lock = getLockFromCache(keyIterator.next(), cache);
119: if ((lock != null)
120: && ((entityKey == null) || (entityKey
121: .equals(lock.getEntityKey())))
122: && ((lockType == null) || (lockType.intValue() == lock
123: .getLockType()))
124: && ((lockOwner == null) || (lockOwner
125: .equals(lock.getLockOwner())))
126: && ((expiration == null) || (expiration
127: .equals(lock.getExpirationTime())))) {
128: locks.add(lock);
129: }
130: }
131: }
132: return ((IEntityLock[]) locks.toArray(new IEntityLock[locks
133: .size()]));
134: }
135:
136: /**
137: * Returns this lock if it exists in the store.
138: * @param lock
139: * @return IEntityLock
140: */
141: public IEntityLock find(IEntityLock lock) throws LockingException {
142: IEntityLock foundLock = null;
143: Map m = getLockCache(lock.getEntityType());
144: foundLock = getLockFromCache(getCacheKey(lock), m);
145:
146: if (foundLock != null) {
147: if (lock.getLockType() != foundLock.getLockType()
148: || !lock.getExpirationTime().equals(
149: foundLock.getExpirationTime())) {
150: foundLock = null;
151: }
152: }
153:
154: return foundLock;
155: }
156:
157: /**
158: * Returns an IEntityLock[] containing unexpired locks, based on the params,
159: * any or all of which may be null EXCEPT FOR <code>expiration</code>. A null
160: * param means any value, so <code> find(expir,myType,myKey,null,null)</code>
161: * will return all <code>IEntityLocks</code> for myType and myKey unexpired
162: * as of <code>expir</code>.
163: *
164: * @param expiration Date
165: * @param entityType Class
166: * @param entityKey String
167: * @param lockType Integer - so we can accept a null value.
168: * @param lockOwner String
169: * @exception LockingException - wraps an Exception specific to the store.
170: */
171: public IEntityLock[] findUnexpired(java.util.Date expiration,
172: Class entityType, String entityKey, Integer lockType,
173: String lockOwner) throws LockingException {
174: IEntityLock[] locks = find(entityType, entityKey, lockType,
175: null, lockOwner);
176: List lockAL = new ArrayList(locks.length);
177: for (int i = 0; i < locks.length; i++) {
178: if (locks[i].getExpirationTime().after(expiration)) {
179: lockAL.add(locks[i]);
180: }
181: }
182: return ((IEntityLock[]) lockAL.toArray(new IEntityLock[lockAL
183: .size()]));
184: }
185:
186: /**
187: * @param lock org.jasig.portal.concurrency.locking.IEntityLock
188: */
189: private String getCacheKey(IEntityLock lock) {
190: return lock.getEntityKey() + lock.getLockOwner();
191: }
192:
193: /**
194: * @return java.util.Map
195: */
196: private java.util.Map getLockCache() {
197: return lockCache;
198: }
199:
200: /**
201: * @return java.util.Map
202: */
203: private synchronized Map getLockCache(Class type) {
204: Map m = (Map) getLockCache().get(type);
205: if (m == null) {
206: m = new SmartCache();
207: getLockCache().put(type, m);
208: }
209: return m;
210: }
211:
212: private IEntityLock getLockFromCache(Object cacheKey, Map cache) {
213: synchronized (cache) {
214: return (IEntityLock) cache.get(cacheKey);
215: }
216: }
217:
218: /**
219: *
220: */
221: private void initializeCache() {
222: lockCache = new HashMap(10);
223: }
224:
225: /**
226: * @param newLockCache java.util.Map
227: */
228: private void setLockCache(java.util.Map newLockCache) {
229: lockCache = newLockCache;
230: }
231:
232: /**
233: * @return org.jasig.portal.concurrency.locking.IEntityLockStore
234: */
235: public static synchronized IEntityLockStore singleton() {
236: if (singleton == null) {
237: singleton = new MemoryEntityLockStore();
238: }
239: return singleton;
240: }
241:
242: /**
243: * @param lock
244: * @param newExpiration
245: */
246: public void update(IEntityLock lock, java.util.Date newExpiration)
247: throws LockingException {
248: update(lock, newExpiration, null);
249: }
250:
251: /**
252: * Make sure the store has a reference to the lock, and then add the lock
253: * to refresh the SmartCache wrapper.
254: * @param lock org.jasig.portal.concurrency.locking.IEntityLock
255: * @param newExpiration java.util.Date
256: * @param newLockType Integer
257: */
258: public void update(IEntityLock lock, java.util.Date newExpiration,
259: Integer newLockType) throws LockingException {
260: if (find(lock) == null) {
261: throw new LockingException("Problem updating " + lock
262: + " : not found in store.");
263: }
264: primAdd(lock, newExpiration);
265: }
266:
267: /**
268: * Adds this IEntityLock to the store.
269: * @param lock
270: * @param expiration
271: */
272: private void primAdd(IEntityLock lock, Date expiration)
273: throws LockingException {
274: long now = System.currentTimeMillis();
275: long willExpire = expiration.getTime();
276: long cacheIntervalSecs = (willExpire - now) / 1000;
277:
278: if (cacheIntervalSecs > 0) {
279: SmartCache sc = (SmartCache) getLockCache(lock
280: .getEntityType());
281:
282: synchronized (sc) {
283: sc.put(getCacheKey(lock), lock, (cacheIntervalSecs));
284: }
285: }
286: // Else the lock has already expired.
287: }
288: }
|