KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > rift > coad > util > lock > ObjectLockFactory


1 /*
2  * CoadunationUtil: The coaduntion utility library.
3  * Copyright (C) 2006 Rift IT Contracting
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  *
19  * ObjectLockFactory.java
20  */

21
22 // package path
23
package com.rift.coad.util.lock;
24
25 // java imports
26
import java.util.Map JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.concurrent.ConcurrentHashMap JavaDoc;
29
30 // logging import
31
import org.apache.log4j.Logger;
32
33
34 /**
35  * The object lock factory is responsible for assigning locks based on the
36  * object passed in. Object locks are designed to work in conjunction with
37  * transaction locks. They are designed to cater for named locks that are not
38  * thread id dependant. This means it is possible to lock an object until a
39  * transaction is complete, even though the transaction is being controlled
40  * from another and hense different threads.
41  *
42  * There are four different types of object locks; read lock, read named lock,
43  * write lock and a named write lock.
44  *
45  * All locks are re-entrant. Meaning they can be locked a number of times and
46  * must be unlocked the same number of times. If a read lock needs to change to
47  * a write lock, that lock must be released before this can be done otherwise a
48  * dead lock will occur.
49  *
50  * A write lock can change from a write thread id lock to a write thread named
51  * lock. A read lock cannot change from a thread id lock to a named id lock.
52  *
53  * @author Brett Chaldecott
54  */

55 public class ObjectLockFactory {
56     
57     /**
58      * This class contains the lock information.
59      */

60     public class Lock {
61         // lock types
62
public final static int READ_LOCK = 0;
63         public final static int THREAD_BOUND = 1;
64         public final static int NAME_BOUND = 2;
65         
66         // private member variables
67
private long threadId = 0;
68         private long lockCount = 0;
69         private Object JavaDoc name = null;
70         private Map JavaDoc readLock = new HashMap JavaDoc();
71         private int waiting = 0;
72         
73         /**
74          * The constructor of the lock object.
75          */

76         public Lock() {
77             
78         }
79         
80         
81         /**
82          * This method returns the id of the thread that hold the lock.
83          *
84          * @return The id of the thread id.
85          */

86         public long getThreadId() {
87             return threadId;
88         }
89         
90         
91         /**
92          * This method sets the thread id.
93          *
94          * @param threadId The id of the thread.
95          */

96         public void setThreadId(long threadId) {
97             this.threadId = threadId;
98         }
99         
100         
101         /**
102          * This method returns the name of the thread id.
103          *
104          * @return The name of the object lock.
105          */

106         public Object JavaDoc getName() {
107             return name;
108         }
109         
110         
111         /**
112          * This method sets the name of the object lock.
113          *
114          * @param name The name of the lock.
115          */

116         public void setName(Object JavaDoc name) {
117             this.name = name;
118         }
119         
120         
121         /**
122          * This method returns the lock type.
123          *
124          * @return The object containing the lock type.
125          */

126         public synchronized int lockType() {
127             if (readLock.size() > 0) {
128                 return READ_LOCK;
129             } else if (name == null) {
130                 return THREAD_BOUND;
131             }
132             return NAME_BOUND;
133         }
134         
135         
136         /**
137          * This method returns true if this lock is owned by the caller.
138          *
139          * @return TRUE if lock owned by CALLER
140          * @param name The name associated with the lock.
141          */

142         public boolean ownLock() {
143             if (threadId == Thread.currentThread().getId()) {
144                 return true;
145             }
146             return false;
147         }
148         
149         
150         /**
151          * This method returns true if this lock is owned by the caller.
152          *
153          * @return TRUE if lock owned by CALLER
154          * @param name The name associated with the lock.
155          */

156         public boolean ownLock(Object JavaDoc name) {
157             if (threadId == Thread.currentThread().getId()) {
158                 return true;
159             }
160             if (((name == null) && (this.name == null)) ||
161                     ((this.name == null) && (name != null))) {
162                 return false;
163             }
164             return this.name.equals(name);
165         }
166         
167         
168         /**
169          * This method returns true if the object is locked.
170          *
171          * @return TRUE if locked, FALSE if not.
172          */

173         public boolean isLocked() {
174             if ((threadId == 0) && (readLock.size() == 0)) {
175                 return false;
176             }
177             return true;
178         }
179         
180         
181         /**
182          * This method will aquire a read lock.
183          *
184          * @exception LockException
185          */

186         public synchronized void getReadLock() throws LockException {
187             waiting++;
188             try {
189                 Long JavaDoc threadId = new Long JavaDoc(Thread.currentThread().getId());
190                 if (readLock.containsKey(threadId)) {
191                     Integer JavaDoc lockCount = (Integer JavaDoc)readLock.get(threadId);
192                     readLock.put(threadId,new Integer JavaDoc(lockCount.intValue() + 1));
193                     return;
194                 }
195                 while(this.threadId != 0) {
196                     wait();
197                 }
198                 
199                 // setup lock environment
200
readLock.put(threadId,new Integer JavaDoc(1));
201                 this.threadId = 0;
202                 this.name = null;
203             } catch (Exception JavaDoc ex) {
204                 throw new LockException("Failed to aquire a lock : " +
205                         ex.getMessage(),ex);
206             } finally {
207                 waiting--;
208             }
209         }
210         
211         
212         /**
213          * This method will aquire a read lock for the given name
214          *
215          * @return LockException
216          */

217         public synchronized void getReadLock(Object JavaDoc name) throws LockException {
218             waiting++;
219             try {
220                 if (readLock.containsKey(name)) {
221                     Integer JavaDoc lockCount = (Integer JavaDoc)readLock.get(name);
222                     readLock.put(name,new Integer JavaDoc(lockCount.intValue() + 1));
223                     return;
224                 }
225                 while(this.threadId != 0) {
226                     wait();
227                 }
228                 
229                 // set up environment
230
readLock.put(name,new Integer JavaDoc(1));
231                 this.threadId = 0;
232                 this.name = null;
233                 
234             } catch (Exception JavaDoc ex) {
235                 throw new LockException("Failed to aquire a lock : " +
236                         ex.getMessage(),ex);
237             } finally {
238                 waiting--;
239             }
240         }
241         
242         
243         /**
244          * This method will aquire a read lock.
245          */

246         public synchronized void getWriteLock() throws LockException {
247             waiting++;
248             try {
249                 long threadId = Thread.currentThread().getId();
250                 if (this.threadId == threadId) {
251                     lockCount++;
252                     return;
253                 }
254                 while(this.threadId != 0) {
255                     wait();
256                 }
257                 // acquire the write lock
258
this.threadId = threadId;
259                 lockCount = 1;
260                 this.name = null;
261                 
262                 // wait for all read locks to end
263
while (readLock.size() > 0) {
264                     wait();
265                 }
266                 
267             } catch (Exception JavaDoc ex) {
268                 throw new LockException("Failed to aquire a write lock : " +
269                         ex.getMessage(),ex);
270             } finally {
271                 waiting--;
272             }
273         }
274         
275         
276         /**
277          * This method will aquire a read lock.
278          */

279         public synchronized void getWriteLock(Object JavaDoc name) throws
280                 LockException {
281             waiting++;
282             try {
283                 long threadId = Thread.currentThread().getId();
284                 if (this.threadId == threadId) {
285                     lockCount++;
286                     this.name = name;
287                     return;
288                 } else if ((this.name != null) && (name != null) &&
289                         this.name.equals(name)) {
290                     this.threadId = threadId;
291                     lockCount++;
292                     return;
293                 }
294                 while(this.threadId != 0) {
295                     wait();
296                 }
297                 // aquire the write named lock
298
this.threadId = threadId;
299                 lockCount = 1;
300                 this.name = name;
301                 
302                 // wait for all read locks to end
303
while (readLock.size() > 0) {
304                     wait();
305                 }
306                 
307             } catch (Exception JavaDoc ex) {
308                 throw new LockException("Failed to aquire a write lock : " +
309                         ex.getMessage(),ex);
310             } finally {
311                 waiting--;
312             }
313         }
314         
315         
316         /**
317          * This method will release the lock and return true if there are no
318          * more waiting threads.
319          *
320          * @return TRUE if there are no more waiting threads.
321          */

322         public synchronized boolean releaseLock() {
323             return releaseLock(new Long JavaDoc(Thread.currentThread().getId()));
324         }
325         
326         
327         /**
328          * This method will release the lock and return true if there are no
329          * more waiting threads.
330          *
331          * @return TRUE if there are no more waiting threads.
332          */

333         public synchronized boolean releaseLock(Object JavaDoc name) {
334             long threadId = Thread.currentThread().getId();
335             if (readLock.size() > 0) {
336                 readLock.get(threadId);
337                 int intValue = ((Integer JavaDoc)readLock.get(name)).intValue() - 1;
338                 if (intValue == 0) {
339                     readLock.remove(name);
340                 } else {
341                     readLock.put(name,new Integer JavaDoc(intValue));
342                 }
343             } else if ((this.threadId == threadId) ||
344                     ((this.name != null) && this.name.equals(name))) {
345                 lockCount--;
346                 if (lockCount == 0) {
347                     this.threadId = 0;
348                     lockCount = 0;
349                     name = null;
350                 }
351             }
352             notify();
353             if (waiting == 0) {
354                 return true;
355             }
356             return false;
357         }
358     }
359     
360     
361     /**
362      * The object that implements a reference to the object lock.
363      */

364     public class ObjectLockRef implements LockRef {
365         // private member variables
366
private Object JavaDoc obj = null;
367         private Lock lock = null;
368         private Object JavaDoc name = null;
369         
370         /**
371          * The constructor of object lock ref.
372          *
373          * @param obj The object reference.
374          * @param lock The reference to the lock information.
375          */

376         public ObjectLockRef(Object JavaDoc obj, Lock lock) {
377             this.obj = obj;
378             this.lock = lock;
379         }
380         
381         
382         /**
383          * The constructor of object lock ref.
384          *
385          * @param obj The object reference.
386          * @param lock The reference to the lock information.
387          */

388         public ObjectLockRef(Object JavaDoc obj, Lock lock, Object JavaDoc name) {
389             this.obj = obj;
390             this.lock = lock;
391             this.name = name;
392         }
393         
394         
395         /**
396          * This method returns the object lock key.
397          *
398          * @return The object that this lock is held for.
399          */

400         public Object JavaDoc getKey() {
401             return obj;
402         }
403         
404         
405         /**
406          * This method returns the id of the thread holding the lock.
407          *
408          * @return This mehod returns the thread id.
409          */

410         public long getThreadId() throws LockException {
411             if (lock.lockType() == Lock.READ_LOCK) {
412                 throw new LockException("This is a read lock no unique thread " +
413                         "id is present.");
414             }
415             return lock.getThreadId();
416         }
417         
418         
419         /**
420          * This method sets the thread id for for the object lock.
421          *
422          * @param id The id of the thread controlling the lock.
423          */

424         public void setThreadId(long id) throws LockException {
425             if (lock.lockType() == Lock.READ_LOCK) {
426                 throw new LockException("This is a read lock cannot set the " +
427                         "thread id on it");
428             }
429             lock.setThreadId(id);
430         }
431         
432         
433         /**
434          * This method returns the name of the object lock.
435          *
436          * @return The name of the object lock.
437          */

438         public Object JavaDoc getLockName() throws LockException {
439             if (lock.lockType() == Lock.READ_LOCK) {
440                 throw new LockException("This is a read lock no name associated " +
441                         "with it");
442             }
443             return lock.getName();
444         }
445         
446         
447         /**
448          * This method sets the name of the object lock.
449          *
450          * @param name The new name for the object lock.
451          */

452         public void setLockName(Object JavaDoc name) throws LockException {
453             if (lock.lockType() == Lock.READ_LOCK) {
454                 throw new LockException("This is a read lock cannot associate a" +
455                         "name with it");
456             }
457             lock.setName(name);
458         }
459         
460         
461         /**
462          * This method returns the lock type for this object.
463          *
464          * @return The lock type for this object.
465          * @exception LockException
466          */

467         public int getLockType() throws LockException {
468             if (lock.lockType() == Lock.READ_LOCK) {
469                 return LockRef.READ;
470             }
471             return LockRef.WRITE;
472         }
473         
474         
475         /**
476          * This method is called to release the lock on the object.
477          */

478         public void release() {
479             synchronized(obj) {
480                 boolean remove = false;
481                 if (name == null) {
482                     remove = lock.releaseLock();
483                 } else {
484                     remove = lock.releaseLock(name);
485                 }
486                 
487                 if (remove) {
488                     locks.remove(obj);
489                 }
490             }
491         }
492         
493     }
494     
495     // class constants
496
public final static int WAIT = 1;
497     public final static int WAIT_ON_NAMED = 2;
498     public final static int WAIT_ON_THREAD = 3;
499     public final static int DO_NOT_WAIT = 4;
500     
501     // private member variables
502
protected static Logger log =
503             Logger.getLogger(ObjectLockFactory.class.getName());
504     
505     // singleton methods
506
private static Map JavaDoc singletonMap = new HashMap JavaDoc();
507     
508     // private member variables
509
private Map JavaDoc locks = new ConcurrentHashMap JavaDoc();
510     
511     /**
512      * Creates a new instance of ObjectLockFactory
513      */

514     private ObjectLockFactory() {
515     }
516     
517     
518     /**
519      * This method creates a new object lock factory singleton for a class
520      * loader.
521      *
522      * @exception LockException
523      */

524     public synchronized static void init() throws LockException {
525         ClassLoader JavaDoc loader = Thread.currentThread().getContextClassLoader();
526         if (!singletonMap.containsKey(loader)) {
527             singletonMap.put(loader,new ObjectLockFactory());
528         }
529     }
530     
531     
532     /**
533      * This method returns the object lock factory singleton reference.
534      *
535      * @return The object lock factory singleton reference.
536      * @exception LockException.
537      */

538     public synchronized static ObjectLockFactory getInstance() throws
539             LockException {
540         ClassLoader JavaDoc loader = Thread.currentThread().getContextClassLoader();
541         ObjectLockFactory singleton = null;
542         if (singletonMap.containsKey(loader)) {
543             singleton = (ObjectLockFactory)singletonMap.get(loader);
544         } else {
545             throw new LockException("There is no object lock factory for " +
546                     "this class loader");
547         }
548         return singleton;
549     }
550     
551     
552     /**
553      * This method removes the object lock factory associated with a class
554      * loader.
555      *
556      * @exception LockException
557      */

558     public synchronized static void fin() throws LockException {
559         ClassLoader JavaDoc loader = Thread.currentThread().getContextClassLoader();
560         if (singletonMap.containsKey(loader)) {
561             singletonMap.remove(loader);
562         }
563     }
564     
565     
566     /**
567      * This method creates a new lock for the specified key.
568      *
569      * @return A reference to the lock object.
570      * @param key The key that identifies the unique lock.
571      * @exception LockException
572      */

573     public LockRef acquireWriteLock(Object JavaDoc key) throws LockException {
574         try {
575             Lock lock = getLock(key);
576             lock.getWriteLock();
577             return new ObjectLockRef(key,lock);
578         } catch (Exception JavaDoc ex) {
579             log.error("Failed to aquire the object lock : " +
580                     ex.getMessage(),ex);
581             throw new LockException("Failed to aquire the object lock : " +
582                     ex.getMessage());
583         }
584     }
585     
586     
587     /**
588      * This method creates a new lock for the specified key. It will wait
589      * depending on the specified flag.
590      *
591      * @return The object lock reference.
592      * @param key The key to lock this object with.
593      * @param waitFlags The flags to wait on.
594      * @exception LockException
595      * @exception LockConflict
596      */

597     public LockRef acquireWriteLock(Object JavaDoc key, int waitFlags)
598     throws LockException, LockConflict {
599         try {
600             Lock lock = null;
601             synchronized(key) {
602                 lock = getLock(key);
603                 // This is not a guaranteed check, due to raise conditions
604
// and what might be waiting to aquire a lock on this object
605
// this test might fail.
606
if (lock.isLocked()) {
607                     if (!lock.ownLock() && (WAIT_ON_NAMED == waitFlags) &&
608                             (lock.lockType() != Lock.NAME_BOUND)) {
609                         log.debug("The object is currently locked" +
610                                 " by a thread lock.");
611                         throw new LockConflict("The object is currently locked" +
612                                 " by a thread lock.");
613                     } else if (!lock.ownLock() && (WAIT_ON_THREAD == waitFlags) &&
614                             (lock.lockType() != Lock.THREAD_BOUND)) {
615                         log.debug("The object is currently locked" +
616                                 " by a named object.");
617                         throw new LockConflict("The object is currently locked" +
618                                 " by a named lock object.");
619                     } else if (!lock.ownLock() && (WAIT_ON_NAMED == waitFlags) &&
620                             (lock.lockType() != Lock.NAME_BOUND)) {
621                         log.debug("The object is currently locked" +
622                                 " by a named object.");
623                         throw new LockConflict("The object is currently locked" +
624                                 " by a named lock object.");
625                     } else if (!lock.ownLock() && (DO_NOT_WAIT == waitFlags)) {
626                         throw new LockConflict("The object is currently locked by " +
627                                 "another.");
628                     }
629                 }
630             }
631             lock.getWriteLock();
632             return new ObjectLockRef(key,lock);
633         } catch (LockConflict ex) {
634             throw ex;
635         } catch (Exception JavaDoc ex) {
636             log.error("Failed to aquire the object lock : " +
637                     ex.getMessage(),ex);
638             throw new LockException("Failed to aquire the object lock : " +
639                     ex.getMessage());
640         }
641     }
642     
643     
644     /**
645      * This method creates a new lock for the specified key.
646      *
647      * @return The reference to the lock object.
648      * @param key The key that the lock will be aquired for.
649      * @param name The name of the lock.
650      * @exception LockException
651      */

652     public LockRef acquireWriteLock(Object JavaDoc key, Object JavaDoc name) throws LockException {
653         try {
654             Lock lock = getLock(key);
655             lock.getWriteLock(name);
656             return new ObjectLockRef(key,lock);
657         } catch (Exception JavaDoc ex) {
658             log.error("Failed to aquire the object lock : " +
659                     ex.getMessage(),ex);
660             throw new LockException("Failed to aquire the object lock : " +
661                     ex.getMessage());
662         }
663     }
664     
665     
666     /**
667      * This method creates a new lock for the specified key. It will wait
668      * depending on the specified flag.
669      *
670      * @return The object lock reference.
671      * @param key The key to lock this object with.
672      * @param name The name ofthe lock.
673      * @param waitFlags The flags to wait on.
674      * @exception LockException
675      * @exception LockConflict
676      */

677     public LockRef acquireWriteLock(Object JavaDoc key, Object JavaDoc name, int waitFlags)
678     throws LockException, LockConflict {
679         try {
680             Lock lock = null;
681             synchronized(key) {
682                 lock = getLock(key);
683                 // This is not a guaranteed check, due to raise conditions
684
// and what might be waiting to aquire a lock on this object
685
// this test might fail.
686
if (lock.isLocked()) {
687                     if (!lock.ownLock(name) && (WAIT_ON_NAMED == waitFlags) &&
688                             (lock.lockType() != Lock.NAME_BOUND)) {
689                         log.debug("The object is currently locked" +
690                                 " by a thread lock.");
691                         throw new LockConflict("The object is currently locked" +
692                                 " by a thread lock.");
693                     } else if (!lock.ownLock(name) && (WAIT_ON_THREAD == waitFlags) &&
694                             (lock.lockType() != Lock.THREAD_BOUND)) {
695                         log.debug("The object is currently locked" +
696                                 " by a named object.");
697                         throw new LockConflict("The object is currently locked" +
698                                 " by a named lock object.");
699                     } else if (!lock.ownLock(name) && (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("The object is currently locked" +
704                                 " by a named lock object.");
705                     } else if (!lock.ownLock(name) && (DO_NOT_WAIT == waitFlags)) {
706                         throw new LockConflict("The object is currently locked by " +
707                                 "another.");
708                     }
709                 }
710             }
711             lock.getWriteLock(name);
712             return new ObjectLockRef(key,lock);
713         } catch (LockConflict ex) {
714             throw ex;
715         } catch (Exception JavaDoc ex) {
716             log.error("Failed to aquire the object lock : " +
717                     ex.getMessage(),ex);
718             throw new LockException("Failed to aquire the object lock : " +
719                     ex.getMessage());
720         }
721     }
722     
723     
724     /**
725      * This method creates a new lock for the specified key.
726      *
727      * @return The object lock reference.
728      * @param key The key to lock this object with.
729      * @exception LockException
730      */

731     public LockRef acquireReadLock(Object JavaDoc key)
732     throws LockException {
733         try {
734             Lock lock = getLock(key);
735             lock.getReadLock();
736             return new ObjectLockRef(key,lock);
737         } catch (Exception JavaDoc ex) {
738             log.error("Failed to aquire the object lock : " +
739                     ex.getMessage(),ex);
740             throw new LockException("Failed to aquire the object lock : " +
741                     ex.getMessage());
742         }
743     }
744     
745     /**
746      * This method creates a new read lock for the specified key.
747      *
748      * @return The object lock reference.
749      * @param key The key to lock this object with.
750      * @param name The name ofthe lock.
751      * @exception LockException
752      */

753     public LockRef acquireReadLock(Object JavaDoc key,Object JavaDoc name)
754     throws LockException {
755         try {
756             Lock lock = getLock(key);
757             lock.getReadLock(name);
758             return new ObjectLockRef(key,lock,name);
759         } catch (Exception JavaDoc ex) {
760             log.error("Failed to aquire the object lock : " +
761                     ex.getMessage(),ex);
762             throw new LockException("Failed to aquire the object lock : " +
763                     ex.getMessage());
764         }
765     }
766     
767     
768     /**
769      * This method releases the lock held on a given object.
770      *
771      * @param key The key identifying the lock.
772      * @exception LockException
773      */

774     public void releaseLock(Object JavaDoc key) throws LockException {
775         try {
776             synchronized(key) {
777                 Lock lock = (Lock)locks.get(key);
778                 if (lock.releaseLock()) {
779                     locks.remove(key);
780                 }
781             }
782         } catch (Exception JavaDoc ex) {
783             log.error("Failed to aquire the object lock : " +
784                     ex.getMessage(),ex);
785             throw new LockException("Failed to aquire the object lock : " +
786                     ex.getMessage());
787         }
788     }
789     
790     
791     /**
792      * This method releases the lock held on a given object.
793      *
794      * @param key The key identifying the lock.
795      * @exception LockException
796      */

797     public void releaseLock(Object JavaDoc key, Object JavaDoc name) throws LockException {
798         try {
799             synchronized(key) {
800                 Lock lock = (Lock)locks.get(key);
801                 if (lock.releaseLock(name)) {
802                     locks.remove(key);
803                 }
804             }
805         } catch (Exception JavaDoc ex) {
806             log.error("Failed to aquire the object lock : " +
807                     ex.getMessage(),ex);
808             throw new LockException("Failed to aquire the object lock : " +
809                     ex.getMessage());
810         }
811     }
812     
813     
814     /**
815      * This method retrievs a lock for an object
816      */

817     private Lock getLock(Object JavaDoc key) throws LockException {
818         synchronized (key) {
819             Lock lock = (Lock)locks.get(key);
820             if (lock == null) {
821                 lock = new Lock();
822                 locks.put(key,lock);
823             }
824             return lock;
825         }
826     }
827 }
828
Popular Tags