KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ojb > broker > locking > LockManagerInMemoryImpl


1 package org.apache.ojb.broker.locking;
2
3 /* Copyright 2002-2005 The Apache Software Foundation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 import java.io.Serializable JavaDoc;
19 import java.util.Collection JavaDoc;
20 import java.util.HashMap JavaDoc;
21 import java.util.Hashtable JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.Map JavaDoc;
24
25 import org.apache.ojb.broker.util.logging.Logger;
26 import org.apache.ojb.broker.util.logging.LoggerFactory;
27 import org.apache.commons.lang.SystemUtils;
28
29 /**
30  * This implementation of the {@link LockManager} interface supports a simple, fast, non-blocking
31  * pessimistic locking for single JVM applications.
32  *
33  * @version $Id: LockManagerInMemoryImpl.java,v 1.1.2.3 2005/12/21 22:25:32 tomdz Exp $
34  */

35 public class LockManagerInMemoryImpl implements LockManager
36 {
37     private Logger log = LoggerFactory.getLogger(LockManagerInMemoryImpl.class);
38     private static long CLEANUP_FREQUENCY = 1000; // 1000 milliseconds.
39
private static int MAX_LOCKS_TO_CLEAN = 300;
40     /**
41      * MBAIRD: a LinkedHashMap returns objects in the order you put them in,
42      * while still maintaining an O(1) lookup like a normal hashmap. We can then
43      * use this to get the oldest entries very quickly, makes cleanup a breeze.
44      */

45     private HashMap JavaDoc locktable = new HashMap JavaDoc();
46     private LockIsolationManager lockStrategyManager = new LockIsolationManager();
47     private long m_lastCleanupAt = System.currentTimeMillis();
48     private long lockTimeout;
49     private long timeoutCounterRead;
50     private long timeoutCounterWrite;
51
52     public LockManagerInMemoryImpl()
53     {
54         this.lockTimeout = DEFAULT_LOCK_TIMEOUT;
55     }
56
57     public long getLockTimeout()
58     {
59         return lockTimeout;
60     }
61
62     public void setLockTimeout(long timeout)
63     {
64         this.lockTimeout = timeout;
65     }
66
67     /**
68      * NOOP
69      * @return Always '0'
70      */

71     public long getBlockTimeout()
72     {
73         return 0;
74     }
75
76     /**
77      * NOOP
78      */

79     public void setBlockTimeout(long timeout)
80     {
81     }
82
83     public String JavaDoc getLockInfo()
84     {
85         String JavaDoc eol = SystemUtils.LINE_SEPARATOR;
86         StringBuffer JavaDoc msg = new StringBuffer JavaDoc("Class: " + LockManagerInMemoryImpl.class.getName() + eol);
87         msg.append("lock timeout: " + getLockTimeout() + " [ms]" + eol);
88         msg.append("concurrent lock owners: " + locktable.size() + eol);
89         msg.append("timed out write locks: " + timeoutCounterWrite + eol);
90         msg.append("timed out read locks: " + timeoutCounterRead + eol);
91         return msg.toString();
92     }
93
94     public boolean readLock(Object JavaDoc key, Object JavaDoc resourceId, int isolationLevel)
95     {
96         if(log.isDebugEnabled()) log.debug("LM.readLock(tx-" + key + ", " + resourceId + ")");
97         checkTimedOutLocks();
98         LockEntry reader = new LockEntry(resourceId,
99                 key,
100                 System.currentTimeMillis(),
101                 isolationLevel,
102                 LockEntry.LOCK_READ);
103         LockIsolation ls = lockStrategyManager.getStrategyFor(isolationLevel);
104         return addReaderIfPossibleInternal(reader, ls.allowMultipleRead(), ls.allowReadWhenWrite());
105     }
106
107     private boolean addReaderIfPossibleInternal(LockEntry reader, boolean allowMultipleReader,
108                                                 boolean allowReaderWhenWriteLock)
109     {
110         boolean result = false;
111         ObjectLocks objectLocks = null;
112         Object JavaDoc oid = reader.getResourceId();
113         /**
114          * MBAIRD: We need to synchronize the get/put so we don't have two threads
115          * competing to check if something is locked and double-locking it.
116          */

117         synchronized(locktable)
118         {
119             objectLocks = (ObjectLocks) locktable.get(oid);
120             if(objectLocks == null)
121             {
122                 // no write or read lock, go on
123
objectLocks = new ObjectLocks();
124                 locktable.put(oid, objectLocks);
125                 objectLocks.addReader(reader);
126                 result = true;
127             }
128             else
129             {
130                 // ObjectLocks exist, first check for a write lock
131
LockEntry writer = objectLocks.getWriter();
132                 if(writer != null)
133                 {
134                     // if writer is owned by current entity, read lock is
135
// successful (we have an write lock)
136
if(writer.isOwnedBy(reader.getKey()))
137                     {
138                         result = true;
139                     }
140                     else
141                     {
142                         // if read lock is allowed when different entity hold write lock
143
// go on if multiple reader allowed, else do nothing
144
if(allowReaderWhenWriteLock && allowMultipleReader)
145                         {
146                             objectLocks.addReader(reader);
147                             result = true;
148                         }
149                         else
150                         {
151                             result = false;
152                         }
153                     }
154                 }
155                 else
156                 {
157                     // no write lock exist, check for existing read locks
158
if(objectLocks.getReaders().size() > 0)
159                     {
160                         // if we have already an read lock, do nothing
161
if(objectLocks.getReader(reader.getKey()) != null)
162                         {
163                             result = true;
164                         }
165                         else
166                         {
167                             // we have read locks of other entities, add read lock
168
// if allowed
169
if(allowMultipleReader)
170                             {
171                                 objectLocks.addReader(reader);
172                                 result = true;
173                             }
174                         }
175                     }
176                     else
177                     {
178                         // no read locks exist, so go on
179
objectLocks.addReader(reader);
180                         result = true;
181                     }
182                 }
183             }
184         }
185         return result;
186     }
187
188     /**
189      * Remove an read lock.
190      */

191     public boolean removeReader(Object JavaDoc key, Object JavaDoc resourceId)
192     {
193         boolean result = false;
194         ObjectLocks objectLocks = null;
195         synchronized(locktable)
196         {
197             objectLocks = (ObjectLocks) locktable.get(resourceId);
198             if(objectLocks != null)
199             {
200                 /**
201                  * MBAIRD, last one out, close the door and turn off the lights.
202                  * if no locks (readers or writers) exist for this object, let's remove
203                  * it from the locktable.
204                  */

205                 Map JavaDoc readers = objectLocks.getReaders();
206                 result = readers.remove(key) != null;
207                 if((objectLocks.getWriter() == null) && (readers.size() == 0))
208                 {
209                     locktable.remove(resourceId);
210                 }
211             }
212         }
213         return result;
214     }
215
216     /**
217      * Remove an write lock.
218      */

219     public boolean removeWriter(Object JavaDoc key, Object JavaDoc resourceId)
220     {
221         boolean result = false;
222         ObjectLocks objectLocks = null;
223         synchronized(locktable)
224         {
225             objectLocks = (ObjectLocks) locktable.get(resourceId);
226             if(objectLocks != null)
227             {
228                 /**
229                  * MBAIRD, last one out, close the door and turn off the lights.
230                  * if no locks (readers or writers) exist for this object, let's remove
231                  * it from the locktable.
232                  */

233                 LockEntry entry = objectLocks.getWriter();
234                 if(entry != null && entry.isOwnedBy(key))
235                 {
236                     objectLocks.setWriter(null);
237                     result = true;
238
239                     // no need to check if writer is null, we just set it.
240
if(objectLocks.getReaders().size() == 0)
241                     {
242                         locktable.remove(resourceId);
243                     }
244                 }
245             }
246         }
247         return result;
248     }
249
250     public boolean releaseLock(Object JavaDoc key, Object JavaDoc resourceId)
251     {
252         if(log.isDebugEnabled()) log.debug("LM.releaseLock(tx-" + key + ", " + resourceId + ")");
253         boolean result = removeReader(key, resourceId);
254         // if no read lock could be removed, try write lock
255
if(!result)
256         {
257             result = removeWriter(key, resourceId);
258         }
259         return result;
260     }
261
262     /**
263      * @see LockManager#releaseLocks(Object)
264      */

265     public void releaseLocks(Object JavaDoc key)
266     {
267         if(log.isDebugEnabled()) log.debug("LM.releaseLocks(tx-" + key + ")");
268         checkTimedOutLocks();
269         releaseLocksInternal(key);
270     }
271
272     private void releaseLocksInternal(Object JavaDoc key)
273     {
274         synchronized(locktable)
275         {
276             Collection JavaDoc values = locktable.values();
277             ObjectLocks entry;
278             for(Iterator JavaDoc iterator = values.iterator(); iterator.hasNext();)
279             {
280                 entry = (ObjectLocks) iterator.next();
281                 entry.removeReader(key);
282                 if(entry.getWriter() != null && entry.getWriter().isOwnedBy(key))
283                 {
284                     entry.setWriter(null);
285                 }
286             }
287         }
288     }
289
290     public boolean writeLock(Object JavaDoc key, Object JavaDoc resourceId, int isolationLevel)
291     {
292         if(log.isDebugEnabled()) log.debug("LM.writeLock(tx-" + key + ", " + resourceId + ")");
293         checkTimedOutLocks();
294         LockEntry writer = new LockEntry(resourceId,
295                 key,
296                 System.currentTimeMillis(),
297                 isolationLevel,
298                 LockEntry.LOCK_WRITE);
299         LockIsolation ls = lockStrategyManager.getStrategyFor(isolationLevel);
300         return setWriterIfPossibleInternal(writer, ls.allowWriteWhenRead());
301     }
302
303     private boolean setWriterIfPossibleInternal(LockEntry writer, boolean allowReaders)
304     {
305         boolean result = false;
306         ObjectLocks objectLocks = null;
307         /**
308          * MBAIRD: We need to synchronize the get/put so we don't have two threads
309          * competing to check if something is locked and double-locking it.
310          */

311         synchronized(locktable)
312         {
313             objectLocks = (ObjectLocks) locktable.get(writer.getResourceId());
314             // if we don't upgrade, go on
315
if(objectLocks == null)
316             {
317                 // no locks for current entity exist, so go on
318
objectLocks = new ObjectLocks();
319                 objectLocks.setWriter(writer);
320                 locktable.put(writer.getResourceId(), objectLocks);
321                 result = true;
322             }
323             else
324             {
325                 // the ObjectLock exist, check if there is already a write lock
326
LockEntry oldWriter = objectLocks.getWriter();
327                 if(oldWriter != null)
328                 {
329                     // if already a write lock exists, check owner
330
if(oldWriter.isOwnedBy(writer.getKey()))
331                     {
332                         // if current entity has already a write lock
333
// signal success
334
result = true;
335                     }
336                 }
337                 else
338                 {
339                     // current ObjectLock has no write lock, so check for readers
340
int readerSize = objectLocks.getReaders().size();
341                     if(readerSize > 0)
342                     {
343                         // does current entity have already an read lock
344
if(objectLocks.getReader(writer.getKey()) != null)
345                         {
346                             if(readerSize == 1)
347                             {
348                                 // only current entity has a read lock, so go on
349
objectLocks.readers.remove(writer.getKey());
350                                 objectLocks.setWriter(writer);
351                                 result = true;
352                             }
353                             else
354                             {
355                                 // current entity and others have already a read lock
356
// if aquire a write is allowed, go on
357
if(allowReaders)
358                                 {
359                                     objectLocks.readers.remove(writer.getKey());
360                                     objectLocks.setWriter(writer);
361                                     result = true;
362                                 }
363                             }
364                         }
365                         else
366                         {
367                             // current entity has no read lock, but others
368
// if aquire a write is allowed, go on
369
if(allowReaders)
370                             {
371                                 objectLocks.setWriter(writer);
372                                 result = true;
373                             }
374                         }
375                     }
376                     else
377                     {
378                         // no readers and writers, so go on if we don't upgrade
379
objectLocks.setWriter(writer);
380                         result = true;
381                     }
382                 }
383             }
384         }
385         return result;
386     }
387
388     public boolean upgradeLock(Object JavaDoc key, Object JavaDoc resourceId, int isolationLevel)
389     {
390         if(log.isDebugEnabled()) log.debug("LM.upgradeLock(tx-" + key + ", " + resourceId + ")");
391         return writeLock(key, resourceId, isolationLevel);
392     }
393
394     /**
395      * @see LockManager#hasWrite(Object, Object)
396      */

397     public boolean hasWrite(Object JavaDoc key, Object JavaDoc resourceId)
398     {
399         if(log.isDebugEnabled()) log.debug("LM.hasWrite(tx-" + key + ", " + resourceId + ")");
400         checkTimedOutLocks();
401         return hasWriteLockInternal(resourceId, key);
402     }
403
404     private boolean hasWriteLockInternal(Object JavaDoc resourceId, Object JavaDoc key)
405     {
406         boolean result = false;
407         ObjectLocks objectLocks = null;
408         synchronized(locktable)
409         {
410             objectLocks = (ObjectLocks) locktable.get(resourceId);
411             if(objectLocks != null)
412             {
413                 LockEntry writer = objectLocks.getWriter();
414                 if(writer != null)
415                 {
416                     result = writer.isOwnedBy(key);
417                 }
418             }
419         }
420         return result;
421     }
422
423     public boolean hasUpgrade(Object JavaDoc key, Object JavaDoc resourceId)
424     {
425         if(log.isDebugEnabled()) log.debug("LM.hasUpgrade(tx-" + key + ", " + resourceId + ")");
426         return hasWrite(key, resourceId);
427     }
428
429     /**
430      * @see LockManager#hasRead(Object, Object)
431      */

432     public boolean hasRead(Object JavaDoc key, Object JavaDoc resourceId)
433     {
434         if(log.isDebugEnabled()) log.debug("LM.hasRead(tx-" + key + ", " + resourceId + ')');
435         checkTimedOutLocks();
436         return hasReadLockInternal(resourceId, key);
437     }
438
439     private boolean hasReadLockInternal(Object JavaDoc resourceId, Object JavaDoc key)
440     {
441         boolean result = false;
442         ObjectLocks objectLocks = null;
443         synchronized(locktable)
444         {
445             objectLocks = (ObjectLocks) locktable.get(resourceId);
446             if(objectLocks != null)
447             {
448                 LockEntry reader = objectLocks.getReader(key);
449                 if(reader != null || (objectLocks.getWriter() != null && objectLocks.getWriter().isOwnedBy(key)))
450                 {
451                     result = true;
452                 }
453             }
454         }
455         return result;
456     }
457
458     /**
459      *
460      */

461     public int lockedObjects()
462     {
463         return locktable.size();
464     }
465
466     private void checkTimedOutLocks()
467     {
468         if(System.currentTimeMillis() - m_lastCleanupAt > CLEANUP_FREQUENCY)
469         {
470             removeTimedOutLocks(getLockTimeout());
471             m_lastCleanupAt = System.currentTimeMillis();
472         }
473     }
474
475     /**
476      * removes all timed out lock entries from the persistent storage.
477      * The timeout value can be set in the OJB properties file.
478      */

479     private void removeTimedOutLocks(long timeout)
480     {
481         int count = 0;
482         long maxAge = System.currentTimeMillis() - timeout;
483         boolean breakFromLoop = false;
484         ObjectLocks temp = null;
485         synchronized(locktable)
486         {
487             Iterator JavaDoc it = locktable.values().iterator();
488             /**
489              * run this loop while:
490              * - we have more in the iterator
491              * - the breakFromLoop flag hasn't been set
492              * - we haven't removed more than the limit for this cleaning iteration.
493              */

494             while(it.hasNext() && !breakFromLoop && (count <= MAX_LOCKS_TO_CLEAN))
495             {
496                 temp = (ObjectLocks) it.next();
497                 if(temp.getWriter() != null)
498                 {
499                     if(temp.getWriter().getTimestamp() < maxAge)
500                     {
501                         // writer has timed out, set it to null
502
temp.setWriter(null);
503                         ++timeoutCounterWrite;
504                     }
505                 }
506                 if(temp.getYoungestReader() < maxAge)
507                 {
508                     // all readers are older than timeout.
509
temp.getReaders().clear();
510                     ++timeoutCounterRead;
511                     if(temp.getWriter() == null)
512                     {
513                         // all readers and writer are older than timeout,
514
// remove the objectLock from the iterator (which
515
// is backed by the map, so it will be removed.
516
it.remove();
517                     }
518                 }
519                 else
520                 {
521                     // we need to walk each reader.
522
Iterator JavaDoc readerIt = temp.getReaders().values().iterator();
523                     LockEntry readerLock = null;
524                     while(readerIt.hasNext())
525                     {
526                         readerLock = (LockEntry) readerIt.next();
527                         if(readerLock.getTimestamp() < maxAge)
528                         {
529                             // this read lock is old, remove it.
530
readerIt.remove();
531                         }
532                     }
533                 }
534                 count++;
535             }
536         }
537     }
538
539
540     //===============================================================
541
// inner class
542
//===============================================================
543
static final class ObjectLocks
544     {
545         private LockEntry writer;
546         private Hashtable JavaDoc readers;
547         private long m_youngestReader = 0;
548
549         ObjectLocks()
550         {
551             this(null);
552         }
553
554         ObjectLocks(LockEntry writer)
555         {
556             this.writer = writer;
557             readers = new Hashtable JavaDoc();
558         }
559
560         LockEntry getWriter()
561         {
562             return writer;
563         }
564
565         void setWriter(LockEntry writer)
566         {
567             this.writer = writer;
568         }
569
570         Hashtable JavaDoc getReaders()
571         {
572             return readers;
573         }
574
575         void addReader(LockEntry reader)
576         {
577             /**
578              * MBAIRD:
579              * we want to track the youngest reader so we can remove all readers at timeout
580              * if the youngestreader is older than the timeoutperiod.
581              */

582             if((reader.getTimestamp() < m_youngestReader) || (m_youngestReader == 0))
583             {
584                 m_youngestReader = reader.getTimestamp();
585             }
586             this.readers.put(reader.getKey(), reader);
587         }
588
589         long getYoungestReader()
590         {
591             return m_youngestReader;
592         }
593
594         LockEntry getReader(Object JavaDoc key)
595         {
596             return (LockEntry) this.readers.get(key);
597         }
598
599         LockEntry removeReader(Object JavaDoc key)
600         {
601             return (LockEntry) this.readers.remove(key);
602         }
603     }
604
605
606     //===============================================================
607
// inner class
608
//===============================================================
609
/**
610      * A lock entry encapsulates locking information.
611      */

612     final class LockEntry implements Serializable JavaDoc
613     {
614         /**
615          * marks a Read Lock.
616          */

617         static final int LOCK_READ = 0;
618
619         /**
620          * marks a Write Lock.
621          */

622         static final int LOCK_WRITE = 1;
623
624         /**
625          * the object to be locked.
626          */

627         private Object JavaDoc resourceId;
628
629         /**
630          * key for locked object
631          */

632         private Object JavaDoc key;
633
634         /**
635          * the timestamp marking the time of acquisition of this lock
636          */

637         private long timestamp;
638
639         /**
640          * the isolationlevel for this lock.
641          */

642         private int isolationLevel;
643
644         /**
645          * marks if this is a read or a write lock.
646          * LOCK_READ = 0;
647          * LOCK_WRITE = 1;
648          */

649         private int lockType;
650
651         /**
652          * Multiargument constructor for fast loading of LockEntries by OJB.
653          */

654         public LockEntry(Object JavaDoc resourceId,
655                          Object JavaDoc key,
656                          long timestamp,
657                          int isolationLevel,
658                          int lockType)
659         {
660             this.resourceId = resourceId;
661             this.key = key;
662             this.timestamp = timestamp;
663             this.isolationLevel = isolationLevel;
664             this.lockType = lockType;
665
666         }
667
668         /**
669          * Returns the resource id of the locked object (or the locked object itself).
670          */

671         public Object JavaDoc getResourceId()
672         {
673             return resourceId;
674         }
675
676         /**
677          * Returns lock key.
678          */

679         public Object JavaDoc getKey()
680         {
681             return key;
682         }
683
684         /**
685          * returns the timestamp of the acqusition of the lock.
686          */

687         public long getTimestamp()
688         {
689             return timestamp;
690         }
691
692         /**
693          * returns the isolation level of this lock
694          */

695         public int getIsolationLevel()
696         {
697             return isolationLevel;
698         }
699
700         /**
701          * returns the locktype of this lock.
702          *
703          * @return LOCK_READ if lock is a readlock,
704          * LOCK_WRITE if lock is a Write lock.
705          */

706         public int getLockType()
707         {
708             return lockType;
709         }
710
711         /**
712          * sets the locktype of this lockentry.
713          *
714          * @param locktype LOCK_READ for read, LOCK_WRITE for write lock.
715          */

716         public void setLockType(int locktype)
717         {
718             this.lockType = locktype;
719         }
720
721         /**
722          * Returns true if this lock is owned by the specified key.
723          */

724         public boolean isOwnedBy(Object JavaDoc key)
725         {
726             return this.getKey().equals(key);
727         }
728
729
730         /**
731          * Sets the isolationLevel.
732          *
733          * @param isolationLevel The isolationLevel to set
734          */

735         public void setIsolationLevel(int isolationLevel)
736         {
737             this.isolationLevel = isolationLevel;
738         }
739
740         /**
741          * Sets the resourceId.
742          *
743          * @param resourceId The resourceId to set
744          */

745         public void setresourceId(String JavaDoc resourceId)
746         {
747             this.resourceId = resourceId;
748         }
749
750         /**
751          * Sets the timestamp.
752          *
753          * @param timestamp The timestamp to set
754          */

755         public void setTimestamp(long timestamp)
756         {
757             this.timestamp = timestamp;
758         }
759
760         /**
761          * Sets the key.
762          *
763          * @param key The key to set
764          */

765         public void setKey(Object JavaDoc key)
766         {
767             this.key = key;
768         }
769     }
770 }
771
Popular Tags