1 package org.apache.ojb.broker.locking; 2 3 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 35 public class LockManagerInMemoryImpl implements LockManager 36 { 37 private Logger log = LoggerFactory.getLogger(LockManagerInMemoryImpl.class); 38 private static long CLEANUP_FREQUENCY = 1000; private static int MAX_LOCKS_TO_CLEAN = 300; 40 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 71 public long getBlockTimeout() 72 { 73 return 0; 74 } 75 76 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 117 synchronized(locktable) 118 { 119 objectLocks = (ObjectLocks) locktable.get(oid); 120 if(objectLocks == null) 121 { 122 objectLocks = new ObjectLocks(); 124 locktable.put(oid, objectLocks); 125 objectLocks.addReader(reader); 126 result = true; 127 } 128 else 129 { 130 LockEntry writer = objectLocks.getWriter(); 132 if(writer != null) 133 { 134 if(writer.isOwnedBy(reader.getKey())) 137 { 138 result = true; 139 } 140 else 141 { 142 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 if(objectLocks.getReaders().size() > 0) 159 { 160 if(objectLocks.getReader(reader.getKey()) != null) 162 { 163 result = true; 164 } 165 else 166 { 167 if(allowMultipleReader) 170 { 171 objectLocks.addReader(reader); 172 result = true; 173 } 174 } 175 } 176 else 177 { 178 objectLocks.addReader(reader); 180 result = true; 181 } 182 } 183 } 184 } 185 return result; 186 } 187 188 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 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 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 233 LockEntry entry = objectLocks.getWriter(); 234 if(entry != null && entry.isOwnedBy(key)) 235 { 236 objectLocks.setWriter(null); 237 result = true; 238 239 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 if(!result) 256 { 257 result = removeWriter(key, resourceId); 258 } 259 return result; 260 } 261 262 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 311 synchronized(locktable) 312 { 313 objectLocks = (ObjectLocks) locktable.get(writer.getResourceId()); 314 if(objectLocks == null) 316 { 317 objectLocks = new ObjectLocks(); 319 objectLocks.setWriter(writer); 320 locktable.put(writer.getResourceId(), objectLocks); 321 result = true; 322 } 323 else 324 { 325 LockEntry oldWriter = objectLocks.getWriter(); 327 if(oldWriter != null) 328 { 329 if(oldWriter.isOwnedBy(writer.getKey())) 331 { 332 result = true; 335 } 336 } 337 else 338 { 339 int readerSize = objectLocks.getReaders().size(); 341 if(readerSize > 0) 342 { 343 if(objectLocks.getReader(writer.getKey()) != null) 345 { 346 if(readerSize == 1) 347 { 348 objectLocks.readers.remove(writer.getKey()); 350 objectLocks.setWriter(writer); 351 result = true; 352 } 353 else 354 { 355 if(allowReaders) 358 { 359 objectLocks.readers.remove(writer.getKey()); 360 objectLocks.setWriter(writer); 361 result = true; 362 } 363 } 364 } 365 else 366 { 367 if(allowReaders) 370 { 371 objectLocks.setWriter(writer); 372 result = true; 373 } 374 } 375 } 376 else 377 { 378 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 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 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 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 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 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 temp.setWriter(null); 503 ++timeoutCounterWrite; 504 } 505 } 506 if(temp.getYoungestReader() < maxAge) 507 { 508 temp.getReaders().clear(); 510 ++timeoutCounterRead; 511 if(temp.getWriter() == null) 512 { 513 it.remove(); 517 } 518 } 519 else 520 { 521 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 readerIt.remove(); 531 } 532 } 533 } 534 count++; 535 } 536 } 537 } 538 539 540 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 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 612 final class LockEntry implements Serializable 613 { 614 617 static final int LOCK_READ = 0; 618 619 622 static final int LOCK_WRITE = 1; 623 624 627 private Object resourceId; 628 629 632 private Object key; 633 634 637 private long timestamp; 638 639 642 private int isolationLevel; 643 644 649 private int lockType; 650 651 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 671 public Object getResourceId() 672 { 673 return resourceId; 674 } 675 676 679 public Object getKey() 680 { 681 return key; 682 } 683 684 687 public long getTimestamp() 688 { 689 return timestamp; 690 } 691 692 695 public int getIsolationLevel() 696 { 697 return isolationLevel; 698 } 699 700 706 public int getLockType() 707 { 708 return lockType; 709 } 710 711 716 public void setLockType(int locktype) 717 { 718 this.lockType = locktype; 719 } 720 721 724 public boolean isOwnedBy(Object key) 725 { 726 return this.getKey().equals(key); 727 } 728 729 730 735 public void setIsolationLevel(int isolationLevel) 736 { 737 this.isolationLevel = isolationLevel; 738 } 739 740 745 public void setresourceId(String resourceId) 746 { 747 this.resourceId = resourceId; 748 } 749 750 755 public void setTimestamp(long timestamp) 756 { 757 this.timestamp = timestamp; 758 } 759 760 765 public void setKey(Object key) 766 { 767 this.key = key; 768 } 769 } 770 } 771 | Popular Tags |