001: /*
002: * CoadunationUtil: The coaduntion utility library.
003: * Copyright (C) 2006 Rift IT Contracting
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
018: *
019: * ObjectLockFactory.java
020: */
021:
022: // package path
023: package com.rift.coad.util.lock;
024:
025: // java imports
026: import java.util.Map;
027: import java.util.HashMap;
028: import java.util.concurrent.ConcurrentHashMap;
029:
030: // logging import
031: import org.apache.log4j.Logger;
032:
033: /**
034: * The object lock factory is responsible for assigning locks based on the
035: * object passed in. Object locks are designed to work in conjunction with
036: * transaction locks. They are designed to cater for named locks that are not
037: * thread id dependant. This means it is possible to lock an object until a
038: * transaction is complete, even though the transaction is being controlled
039: * from another and hense different threads.
040: *
041: * There are four different types of object locks; read lock, read named lock,
042: * write lock and a named write lock.
043: *
044: * All locks are re-entrant. Meaning they can be locked a number of times and
045: * must be unlocked the same number of times. If a read lock needs to change to
046: * a write lock, that lock must be released before this can be done otherwise a
047: * dead lock will occur.
048: *
049: * A write lock can change from a write thread id lock to a write thread named
050: * lock. A read lock cannot change from a thread id lock to a named id lock.
051: *
052: * @author Brett Chaldecott
053: */
054: public class ObjectLockFactory {
055:
056: /**
057: * This class contains the lock information.
058: */
059: public class Lock {
060: // lock types
061: public final static int READ_LOCK = 0;
062: public final static int THREAD_BOUND = 1;
063: public final static int NAME_BOUND = 2;
064:
065: // private member variables
066: private long threadId = 0;
067: private long lockCount = 0;
068: private Object name = null;
069: private Map readLock = new HashMap();
070: private int waiting = 0;
071:
072: /**
073: * The constructor of the lock object.
074: */
075: public Lock() {
076:
077: }
078:
079: /**
080: * This method returns the id of the thread that hold the lock.
081: *
082: * @return The id of the thread id.
083: */
084: public long getThreadId() {
085: return threadId;
086: }
087:
088: /**
089: * This method sets the thread id.
090: *
091: * @param threadId The id of the thread.
092: */
093: public void setThreadId(long threadId) {
094: this .threadId = threadId;
095: }
096:
097: /**
098: * This method returns the name of the thread id.
099: *
100: * @return The name of the object lock.
101: */
102: public Object getName() {
103: return name;
104: }
105:
106: /**
107: * This method sets the name of the object lock.
108: *
109: * @param name The name of the lock.
110: */
111: public void setName(Object name) {
112: this .name = name;
113: }
114:
115: /**
116: * This method returns the lock type.
117: *
118: * @return The object containing the lock type.
119: */
120: public synchronized int lockType() {
121: if (readLock.size() > 0) {
122: return READ_LOCK;
123: } else if (name == null) {
124: return THREAD_BOUND;
125: }
126: return NAME_BOUND;
127: }
128:
129: /**
130: * This method returns true if this lock is owned by the caller.
131: *
132: * @return TRUE if lock owned by CALLER
133: * @param name The name associated with the lock.
134: */
135: public boolean ownLock() {
136: if (threadId == Thread.currentThread().getId()) {
137: return true;
138: }
139: return false;
140: }
141:
142: /**
143: * This method returns true if this lock is owned by the caller.
144: *
145: * @return TRUE if lock owned by CALLER
146: * @param name The name associated with the lock.
147: */
148: public boolean ownLock(Object name) {
149: if (threadId == Thread.currentThread().getId()) {
150: return true;
151: }
152: if (((name == null) && (this .name == null))
153: || ((this .name == null) && (name != null))) {
154: return false;
155: }
156: return this .name.equals(name);
157: }
158:
159: /**
160: * This method returns true if the object is locked.
161: *
162: * @return TRUE if locked, FALSE if not.
163: */
164: public boolean isLocked() {
165: if ((threadId == 0) && (readLock.size() == 0)) {
166: return false;
167: }
168: return true;
169: }
170:
171: /**
172: * This method will aquire a read lock.
173: *
174: * @exception LockException
175: */
176: public synchronized void getReadLock() throws LockException {
177: waiting++;
178: try {
179: Long threadId = new Long(Thread.currentThread().getId());
180: if (readLock.containsKey(threadId)) {
181: Integer lockCount = (Integer) readLock
182: .get(threadId);
183: readLock.put(threadId, new Integer(lockCount
184: .intValue() + 1));
185: return;
186: }
187: while (this .threadId != 0) {
188: wait();
189: }
190:
191: // setup lock environment
192: readLock.put(threadId, new Integer(1));
193: this .threadId = 0;
194: this .name = null;
195: } catch (Exception ex) {
196: throw new LockException("Failed to aquire a lock : "
197: + ex.getMessage(), ex);
198: } finally {
199: waiting--;
200: }
201: }
202:
203: /**
204: * This method will aquire a read lock for the given name
205: *
206: * @return LockException
207: */
208: public synchronized void getReadLock(Object name)
209: throws LockException {
210: waiting++;
211: try {
212: if (readLock.containsKey(name)) {
213: Integer lockCount = (Integer) readLock.get(name);
214: readLock.put(name, new Integer(
215: lockCount.intValue() + 1));
216: return;
217: }
218: while (this .threadId != 0) {
219: wait();
220: }
221:
222: // set up environment
223: readLock.put(name, new Integer(1));
224: this .threadId = 0;
225: this .name = null;
226:
227: } catch (Exception ex) {
228: throw new LockException("Failed to aquire a lock : "
229: + ex.getMessage(), ex);
230: } finally {
231: waiting--;
232: }
233: }
234:
235: /**
236: * This method will aquire a read lock.
237: */
238: public synchronized void getWriteLock() throws LockException {
239: waiting++;
240: try {
241: long threadId = Thread.currentThread().getId();
242: if (this .threadId == threadId) {
243: lockCount++;
244: return;
245: }
246: while (this .threadId != 0) {
247: wait();
248: }
249: // acquire the write lock
250: this .threadId = threadId;
251: lockCount = 1;
252: this .name = null;
253:
254: // wait for all read locks to end
255: while (readLock.size() > 0) {
256: wait();
257: }
258:
259: } catch (Exception ex) {
260: throw new LockException(
261: "Failed to aquire a write lock : "
262: + ex.getMessage(), ex);
263: } finally {
264: waiting--;
265: }
266: }
267:
268: /**
269: * This method will aquire a read lock.
270: */
271: public synchronized void getWriteLock(Object name)
272: throws LockException {
273: waiting++;
274: try {
275: long threadId = Thread.currentThread().getId();
276: if (this .threadId == threadId) {
277: lockCount++;
278: this .name = name;
279: return;
280: } else if ((this .name != null) && (name != null)
281: && this .name.equals(name)) {
282: this .threadId = threadId;
283: lockCount++;
284: return;
285: }
286: while (this .threadId != 0) {
287: wait();
288: }
289: // aquire the write named lock
290: this .threadId = threadId;
291: lockCount = 1;
292: this .name = name;
293:
294: // wait for all read locks to end
295: while (readLock.size() > 0) {
296: wait();
297: }
298:
299: } catch (Exception ex) {
300: throw new LockException(
301: "Failed to aquire a write lock : "
302: + ex.getMessage(), ex);
303: } finally {
304: waiting--;
305: }
306: }
307:
308: /**
309: * This method will release the lock and return true if there are no
310: * more waiting threads.
311: *
312: * @return TRUE if there are no more waiting threads.
313: */
314: public synchronized boolean releaseLock() {
315: return releaseLock(new Long(Thread.currentThread().getId()));
316: }
317:
318: /**
319: * This method will release the lock and return true if there are no
320: * more waiting threads.
321: *
322: * @return TRUE if there are no more waiting threads.
323: */
324: public synchronized boolean releaseLock(Object name) {
325: long threadId = Thread.currentThread().getId();
326: if (readLock.size() > 0) {
327: readLock.get(threadId);
328: int intValue = ((Integer) readLock.get(name))
329: .intValue() - 1;
330: if (intValue == 0) {
331: readLock.remove(name);
332: } else {
333: readLock.put(name, new Integer(intValue));
334: }
335: } else if ((this .threadId == threadId)
336: || ((this .name != null) && this .name.equals(name))) {
337: lockCount--;
338: if (lockCount == 0) {
339: this .threadId = 0;
340: lockCount = 0;
341: name = null;
342: }
343: }
344: notify();
345: if (waiting == 0) {
346: return true;
347: }
348: return false;
349: }
350: }
351:
352: /**
353: * The object that implements a reference to the object lock.
354: */
355: public class ObjectLockRef implements LockRef {
356: // private member variables
357: private Object obj = null;
358: private Lock lock = null;
359: private Object name = null;
360:
361: /**
362: * The constructor of object lock ref.
363: *
364: * @param obj The object reference.
365: * @param lock The reference to the lock information.
366: */
367: public ObjectLockRef(Object obj, Lock lock) {
368: this .obj = obj;
369: this .lock = lock;
370: }
371:
372: /**
373: * The constructor of object lock ref.
374: *
375: * @param obj The object reference.
376: * @param lock The reference to the lock information.
377: */
378: public ObjectLockRef(Object obj, Lock lock, Object name) {
379: this .obj = obj;
380: this .lock = lock;
381: this .name = name;
382: }
383:
384: /**
385: * This method returns the object lock key.
386: *
387: * @return The object that this lock is held for.
388: */
389: public Object getKey() {
390: return obj;
391: }
392:
393: /**
394: * This method returns the id of the thread holding the lock.
395: *
396: * @return This mehod returns the thread id.
397: */
398: public long getThreadId() throws LockException {
399: if (lock.lockType() == Lock.READ_LOCK) {
400: throw new LockException(
401: "This is a read lock no unique thread "
402: + "id is present.");
403: }
404: return lock.getThreadId();
405: }
406:
407: /**
408: * This method sets the thread id for for the object lock.
409: *
410: * @param id The id of the thread controlling the lock.
411: */
412: public void setThreadId(long id) throws LockException {
413: if (lock.lockType() == Lock.READ_LOCK) {
414: throw new LockException(
415: "This is a read lock cannot set the "
416: + "thread id on it");
417: }
418: lock.setThreadId(id);
419: }
420:
421: /**
422: * This method returns the name of the object lock.
423: *
424: * @return The name of the object lock.
425: */
426: public Object getLockName() throws LockException {
427: if (lock.lockType() == Lock.READ_LOCK) {
428: throw new LockException(
429: "This is a read lock no name associated "
430: + "with it");
431: }
432: return lock.getName();
433: }
434:
435: /**
436: * This method sets the name of the object lock.
437: *
438: * @param name The new name for the object lock.
439: */
440: public void setLockName(Object name) throws LockException {
441: if (lock.lockType() == Lock.READ_LOCK) {
442: throw new LockException(
443: "This is a read lock cannot associate a"
444: + "name with it");
445: }
446: lock.setName(name);
447: }
448:
449: /**
450: * This method returns the lock type for this object.
451: *
452: * @return The lock type for this object.
453: * @exception LockException
454: */
455: public int getLockType() throws LockException {
456: if (lock.lockType() == Lock.READ_LOCK) {
457: return LockRef.READ;
458: }
459: return LockRef.WRITE;
460: }
461:
462: /**
463: * This method is called to release the lock on the object.
464: */
465: public void release() {
466: synchronized (obj) {
467: boolean remove = false;
468: if (name == null) {
469: remove = lock.releaseLock();
470: } else {
471: remove = lock.releaseLock(name);
472: }
473:
474: if (remove) {
475: locks.remove(obj);
476: }
477: }
478: }
479:
480: }
481:
482: // class constants
483: public final static int WAIT = 1;
484: public final static int WAIT_ON_NAMED = 2;
485: public final static int WAIT_ON_THREAD = 3;
486: public final static int DO_NOT_WAIT = 4;
487:
488: // private member variables
489: protected static Logger log = Logger
490: .getLogger(ObjectLockFactory.class.getName());
491:
492: // singleton methods
493: private static Map singletonMap = new HashMap();
494:
495: // private member variables
496: private Map locks = new ConcurrentHashMap();
497:
498: /**
499: * Creates a new instance of ObjectLockFactory
500: */
501: private ObjectLockFactory() {
502: }
503:
504: /**
505: * This method creates a new object lock factory singleton for a class
506: * loader.
507: *
508: * @exception LockException
509: */
510: public synchronized static void init() throws LockException {
511: ClassLoader loader = Thread.currentThread()
512: .getContextClassLoader();
513: if (!singletonMap.containsKey(loader)) {
514: singletonMap.put(loader, new ObjectLockFactory());
515: }
516: }
517:
518: /**
519: * This method returns the object lock factory singleton reference.
520: *
521: * @return The object lock factory singleton reference.
522: * @exception LockException.
523: */
524: public synchronized static ObjectLockFactory getInstance()
525: throws LockException {
526: ClassLoader loader = Thread.currentThread()
527: .getContextClassLoader();
528: ObjectLockFactory singleton = null;
529: if (singletonMap.containsKey(loader)) {
530: singleton = (ObjectLockFactory) singletonMap.get(loader);
531: } else {
532: throw new LockException(
533: "There is no object lock factory for "
534: + "this class loader");
535: }
536: return singleton;
537: }
538:
539: /**
540: * This method removes the object lock factory associated with a class
541: * loader.
542: *
543: * @exception LockException
544: */
545: public synchronized static void fin() throws LockException {
546: ClassLoader loader = Thread.currentThread()
547: .getContextClassLoader();
548: if (singletonMap.containsKey(loader)) {
549: singletonMap.remove(loader);
550: }
551: }
552:
553: /**
554: * This method creates a new lock for the specified key.
555: *
556: * @return A reference to the lock object.
557: * @param key The key that identifies the unique lock.
558: * @exception LockException
559: */
560: public LockRef acquireWriteLock(Object key) throws LockException {
561: try {
562: Lock lock = getLock(key);
563: lock.getWriteLock();
564: return new ObjectLockRef(key, lock);
565: } catch (Exception ex) {
566: log.error("Failed to aquire the object lock : "
567: + ex.getMessage(), ex);
568: throw new LockException(
569: "Failed to aquire the object lock : "
570: + ex.getMessage());
571: }
572: }
573:
574: /**
575: * This method creates a new lock for the specified key. It will wait
576: * depending on the specified flag.
577: *
578: * @return The object lock reference.
579: * @param key The key to lock this object with.
580: * @param waitFlags The flags to wait on.
581: * @exception LockException
582: * @exception LockConflict
583: */
584: public LockRef acquireWriteLock(Object key, int waitFlags)
585: throws LockException, LockConflict {
586: try {
587: Lock lock = null;
588: synchronized (key) {
589: lock = getLock(key);
590: // This is not a guaranteed check, due to raise conditions
591: // and what might be waiting to aquire a lock on this object
592: // this test might fail.
593: if (lock.isLocked()) {
594: if (!lock.ownLock() && (WAIT_ON_NAMED == waitFlags)
595: && (lock.lockType() != Lock.NAME_BOUND)) {
596: log.debug("The object is currently locked"
597: + " by a thread lock.");
598: throw new LockConflict(
599: "The object is currently locked"
600: + " by a thread lock.");
601: } else if (!lock.ownLock()
602: && (WAIT_ON_THREAD == waitFlags)
603: && (lock.lockType() != Lock.THREAD_BOUND)) {
604: log.debug("The object is currently locked"
605: + " by a named object.");
606: throw new LockConflict(
607: "The object is currently locked"
608: + " by a named lock object.");
609: } else if (!lock.ownLock()
610: && (WAIT_ON_NAMED == waitFlags)
611: && (lock.lockType() != Lock.NAME_BOUND)) {
612: log.debug("The object is currently locked"
613: + " by a named object.");
614: throw new LockConflict(
615: "The object is currently locked"
616: + " by a named lock object.");
617: } else if (!lock.ownLock()
618: && (DO_NOT_WAIT == waitFlags)) {
619: throw new LockConflict(
620: "The object is currently locked by "
621: + "another.");
622: }
623: }
624: }
625: lock.getWriteLock();
626: return new ObjectLockRef(key, lock);
627: } catch (LockConflict ex) {
628: throw ex;
629: } catch (Exception ex) {
630: log.error("Failed to aquire the object lock : "
631: + ex.getMessage(), ex);
632: throw new LockException(
633: "Failed to aquire the object lock : "
634: + ex.getMessage());
635: }
636: }
637:
638: /**
639: * This method creates a new lock for the specified key.
640: *
641: * @return The reference to the lock object.
642: * @param key The key that the lock will be aquired for.
643: * @param name The name of the lock.
644: * @exception LockException
645: */
646: public LockRef acquireWriteLock(Object key, Object name)
647: throws LockException {
648: try {
649: Lock lock = getLock(key);
650: lock.getWriteLock(name);
651: return new ObjectLockRef(key, lock);
652: } catch (Exception ex) {
653: log.error("Failed to aquire the object lock : "
654: + ex.getMessage(), ex);
655: throw new LockException(
656: "Failed to aquire the object lock : "
657: + ex.getMessage());
658: }
659: }
660:
661: /**
662: * This method creates a new lock for the specified key. It will wait
663: * depending on the specified flag.
664: *
665: * @return The object lock reference.
666: * @param key The key to lock this object with.
667: * @param name The name ofthe lock.
668: * @param waitFlags The flags to wait on.
669: * @exception LockException
670: * @exception LockConflict
671: */
672: public LockRef acquireWriteLock(Object key, Object name,
673: int waitFlags) throws LockException, LockConflict {
674: try {
675: Lock lock = null;
676: synchronized (key) {
677: lock = getLock(key);
678: // This is not a guaranteed check, due to raise conditions
679: // and what might be waiting to aquire a lock on this object
680: // this test might fail.
681: if (lock.isLocked()) {
682: if (!lock.ownLock(name)
683: && (WAIT_ON_NAMED == waitFlags)
684: && (lock.lockType() != Lock.NAME_BOUND)) {
685: log.debug("The object is currently locked"
686: + " by a thread lock.");
687: throw new LockConflict(
688: "The object is currently locked"
689: + " by a thread lock.");
690: } else if (!lock.ownLock(name)
691: && (WAIT_ON_THREAD == waitFlags)
692: && (lock.lockType() != Lock.THREAD_BOUND)) {
693: log.debug("The object is currently locked"
694: + " by a named object.");
695: throw new LockConflict(
696: "The object is currently locked"
697: + " by a named lock object.");
698: } else if (!lock.ownLock(name)
699: && (WAIT_ON_NAMED == waitFlags)
700: && (lock.lockType() != Lock.NAME_BOUND)) {
701: log.debug("The object is currently locked"
702: + " by a named object.");
703: throw new LockConflict(
704: "The object is currently locked"
705: + " by a named lock object.");
706: } else if (!lock.ownLock(name)
707: && (DO_NOT_WAIT == waitFlags)) {
708: throw new LockConflict(
709: "The object is currently locked by "
710: + "another.");
711: }
712: }
713: }
714: lock.getWriteLock(name);
715: return new ObjectLockRef(key, lock);
716: } catch (LockConflict ex) {
717: throw ex;
718: } catch (Exception ex) {
719: log.error("Failed to aquire the object lock : "
720: + ex.getMessage(), ex);
721: throw new LockException(
722: "Failed to aquire the object lock : "
723: + ex.getMessage());
724: }
725: }
726:
727: /**
728: * This method creates a new lock for the specified key.
729: *
730: * @return The object lock reference.
731: * @param key The key to lock this object with.
732: * @exception LockException
733: */
734: public LockRef acquireReadLock(Object key) throws LockException {
735: try {
736: Lock lock = getLock(key);
737: lock.getReadLock();
738: return new ObjectLockRef(key, lock);
739: } catch (Exception ex) {
740: log.error("Failed to aquire the object lock : "
741: + ex.getMessage(), ex);
742: throw new LockException(
743: "Failed to aquire the object lock : "
744: + ex.getMessage());
745: }
746: }
747:
748: /**
749: * This method creates a new read lock for the specified key.
750: *
751: * @return The object lock reference.
752: * @param key The key to lock this object with.
753: * @param name The name ofthe lock.
754: * @exception LockException
755: */
756: public LockRef acquireReadLock(Object key, Object name)
757: throws LockException {
758: try {
759: Lock lock = getLock(key);
760: lock.getReadLock(name);
761: return new ObjectLockRef(key, lock, name);
762: } catch (Exception ex) {
763: log.error("Failed to aquire the object lock : "
764: + ex.getMessage(), ex);
765: throw new LockException(
766: "Failed to aquire the object lock : "
767: + ex.getMessage());
768: }
769: }
770:
771: /**
772: * This method releases the lock held on a given object.
773: *
774: * @param key The key identifying the lock.
775: * @exception LockException
776: */
777: public void releaseLock(Object key) throws LockException {
778: try {
779: synchronized (key) {
780: Lock lock = (Lock) locks.get(key);
781: if (lock.releaseLock()) {
782: locks.remove(key);
783: }
784: }
785: } catch (Exception ex) {
786: log.error("Failed to aquire the object lock : "
787: + ex.getMessage(), ex);
788: throw new LockException(
789: "Failed to aquire the object lock : "
790: + ex.getMessage());
791: }
792: }
793:
794: /**
795: * This method releases the lock held on a given object.
796: *
797: * @param key The key identifying the lock.
798: * @exception LockException
799: */
800: public void releaseLock(Object key, Object name)
801: throws LockException {
802: try {
803: synchronized (key) {
804: Lock lock = (Lock) locks.get(key);
805: if (lock.releaseLock(name)) {
806: locks.remove(key);
807: }
808: }
809: } catch (Exception ex) {
810: log.error("Failed to aquire the object lock : "
811: + ex.getMessage(), ex);
812: throw new LockException(
813: "Failed to aquire the object lock : "
814: + ex.getMessage());
815: }
816: }
817:
818: /**
819: * This method retrievs a lock for an object
820: */
821: private Lock getLock(Object key) throws LockException {
822: synchronized (key) {
823: Lock lock = (Lock) locks.get(key);
824: if (lock == null) {
825: lock = new Lock();
826: locks.put(key, lock);
827: }
828: return lock;
829: }
830: }
831: }
|