1 package org.apache.ojb.broker.locking;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 import java.io.Serializable;
19 import java.util.Collection;
20 import java.util.HashMap;
21 import java.util.Hashtable;
22 import java.util.Iterator;
23 import java.util.Map;
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
31
32
33
34
35 public class LockManagerInMemoryImpl implements LockManager
36 {
37 private Logger log = LoggerFactory.getLogger(LockManagerInMemoryImpl.class);
38 private static long CLEANUP_FREQUENCY = 1000;
39 private static int MAX_LOCKS_TO_CLEAN = 300;
40
41
42
43
44
45 private HashMap locktable = new HashMap();
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
69
70
71 public long getBlockTimeout()
72 {
73 return 0;
74 }
75
76
77
78
79 public void setBlockTimeout(long timeout)
80 {
81 }
82
83 public String getLockInfo()
84 {
85 String eol = SystemUtils.LINE_SEPARATOR;
86 StringBuffer msg = new StringBuffer("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 key, Object 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 oid = reader.getResourceId();
113
114
115
116
117 synchronized(locktable)
118 {
119 objectLocks = (ObjectLocks) locktable.get(oid);
120 if(objectLocks == null)
121 {
122
123 objectLocks = new ObjectLocks();
124 locktable.put(oid, objectLocks);
125 objectLocks.addReader(reader);
126 result = true;
127 }
128 else
129 {
130
131 LockEntry writer = objectLocks.getWriter();
132 if(writer != null)
133 {
134
135
136 if(writer.isOwnedBy(reader.getKey()))
137 {
138 result = true;
139 }
140 else
141 {
142
143
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
158 if(objectLocks.getReaders().size() > 0)
159 {
160
161 if(objectLocks.getReader(reader.getKey()) != null)
162 {
163 result = true;
164 }
165 else
166 {
167
168
169 if(allowMultipleReader)
170 {
171 objectLocks.addReader(reader);
172 result = true;
173 }
174 }
175 }
176 else
177 {
178
179 objectLocks.addReader(reader);
180 result = true;
181 }
182 }
183 }
184 }
185 return result;
186 }
187
188
189
190
191 public boolean removeReader(Object key, Object 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
202
203
204
205 Map 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
218
219 public boolean removeWriter(Object key, Object 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
230
231
232
233 LockEntry entry = objectLocks.getWriter();
234 if(entry != null && entry.isOwnedBy(key))
235 {
236 objectLocks.setWriter(null);
237 result = true;
238
239
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 key, Object resourceId)
251 {
252 if(log.isDebugEnabled()) log.debug("LM.releaseLock(tx-" + key + ", " + resourceId + ")");
253 boolean result = removeReader(key, resourceId);
254
255 if(!result)
256 {
257 result = removeWriter(key, resourceId);
258 }
259 return result;
260 }
261
262
263
264
265 public void releaseLocks(Object key)
266 {
267 if(log.isDebugEnabled()) log.debug("LM.releaseLocks(tx-" + key + ")");
268 checkTimedOutLocks();
269 releaseLocksInternal(key);
270 }
271
272 private void releaseLocksInternal(Object key)
273 {
274 synchronized(locktable)
275 {
276 Collection values = locktable.values();
277 ObjectLocks entry;
278 for(Iterator 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 key, Object 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
309
310
311 synchronized(locktable)
312 {
313 objectLocks = (ObjectLocks) locktable.get(writer.getResourceId());
314
315 if(objectLocks == null)
316 {
317
318 objectLocks = new ObjectLocks();
319 objectLocks.setWriter(writer);
320 locktable.put(writer.getResourceId(), objectLocks);
321 result = true;
322 }
323 else
324 {
325
326 LockEntry oldWriter = objectLocks.getWriter();
327 if(oldWriter != null)
328 {
329
330 if(oldWriter.isOwnedBy(writer.getKey()))
331 {
332
333
334 result = true;
335 }
336 }
337 else
338 {
339
340 int readerSize = objectLocks.getReaders().size();
341 if(readerSize > 0)
342 {
343
344 if(objectLocks.getReader(writer.getKey()) != null)
345 {
346 if(readerSize == 1)
347 {
348
349 objectLocks.readers.remove(writer.getKey());
350 objectLocks.setWriter(writer);
351 result = true;
352 }
353 else
354 {
355
356
357 if(allowReaders)
358 {
359 objectLocks.readers.remove(writer.getKey());
360 objectLocks.setWriter(writer);
361 result = true;
362 }
363 }
364 }
365 else
366 {
367
368
369 if(allowReaders)
370 {
371 objectLocks.setWriter(writer);
372 result = true;
373 }
374 }
375 }
376 else
377 {
378
379 objectLocks.setWriter(writer);
380 result = true;
381 }
382 }
383 }
384 }
385 return result;
386 }
387
388 public boolean upgradeLock(Object key, Object 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
396
397 public boolean hasWrite(Object key, Object 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 resourceId, Object 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 key, Object resourceId)
424 {
425 if(log.isDebugEnabled()) log.debug("LM.hasUpgrade(tx-" + key + ", " + resourceId + ")");
426 return hasWrite(key, resourceId);
427 }
428
429
430
431
432 public boolean hasRead(Object key, Object 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 resourceId, Object 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
477
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 it = locktable.values().iterator();
488
489
490
491
492
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
502 temp.setWriter(null);
503 ++timeoutCounterWrite;
504 }
505 }
506 if(temp.getYoungestReader() < maxAge)
507 {
508
509 temp.getReaders().clear();
510 ++timeoutCounterRead;
511 if(temp.getWriter() == null)
512 {
513
514
515
516 it.remove();
517 }
518 }
519 else
520 {
521
522 Iterator 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
530 readerIt.remove();
531 }
532 }
533 }
534 count++;
535 }
536 }
537 }
538
539
540
541
542
543 static final class ObjectLocks
544 {
545 private LockEntry writer;
546 private Hashtable 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();
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 getReaders()
571 {
572 return readers;
573 }
574
575 void addReader(LockEntry reader)
576 {
577
578
579
580
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 key)
595 {
596 return (LockEntry) this.readers.get(key);
597 }
598
599 LockEntry removeReader(Object key)
600 {
601 return (LockEntry) this.readers.remove(key);
602 }
603 }
604
605
606
607
608
609
610
611
612 final class LockEntry implements Serializable
613 {
614
615
616
617 static final int LOCK_READ = 0;
618
619
620
621
622 static final int LOCK_WRITE = 1;
623
624
625
626
627 private Object resourceId;
628
629
630
631
632 private Object key;
633
634
635
636
637 private long timestamp;
638
639
640
641
642 private int isolationLevel;
643
644
645
646
647
648
649 private int lockType;
650
651
652
653
654 public LockEntry(Object resourceId,
655 Object 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
670
671 public Object getResourceId()
672 {
673 return resourceId;
674 }
675
676
677
678
679 public Object getKey()
680 {
681 return key;
682 }
683
684
685
686
687 public long getTimestamp()
688 {
689 return timestamp;
690 }
691
692
693
694
695 public int getIsolationLevel()
696 {
697 return isolationLevel;
698 }
699
700
701
702
703
704
705
706 public int getLockType()
707 {
708 return lockType;
709 }
710
711
712
713
714
715
716 public void setLockType(int locktype)
717 {
718 this.lockType = locktype;
719 }
720
721
722
723
724 public boolean isOwnedBy(Object key)
725 {
726 return this.getKey().equals(key);
727 }
728
729
730
731
732
733
734
735 public void setIsolationLevel(int isolationLevel)
736 {
737 this.isolationLevel = isolationLevel;
738 }
739
740
741
742
743
744
745 public void setresourceId(String resourceId)
746 {
747 this.resourceId = resourceId;
748 }
749
750
751
752
753
754
755 public void setTimestamp(long timestamp)
756 {
757 this.timestamp = timestamp;
758 }
759
760
761
762
763
764
765 public void setKey(Object key)
766 {
767 this.key = key;
768 }
769 }
770 }