1 23 24 package com.sun.ejb.containers.util.cache; 25 26 import com.sun.appserv.util.cache.BaseCache; 27 import com.sun.appserv.util.cache.Cache; 28 import com.sun.appserv.util.cache.CacheListener; 29 30 import com.sun.ejb.base.io.IOUtils; 31 32 import com.sun.ejb.containers.util.cache.LruCache; 33 34 import com.sun.ejb.spi.container.ContainerService; 35 import com.sun.ejb.spi.container.SFSBContainerCallback; 36 import com.sun.ejb.spi.container.StatefulEJBContext; 37 38 import com.sun.ejb.base.stats.StatefulSessionStoreMonitor; 39 40 import com.sun.ejb.spi.sfsb.store.SFSBBeanState; 41 import com.sun.ejb.spi.sfsb.store.SFSBStoreManager; 42 import com.sun.ejb.spi.sfsb.store.SFSBStoreManagerException; 43 44 import com.sun.logging.*; 45 46 import java.io.ObjectOutputStream ; 47 import java.io.ObjectInputStream ; 48 49 import java.util.*; 50 import java.util.logging.*; 51 52 53 public class LruSessionCache 54 extends LruCache 55 implements com.sun.ejb.spi.stats.EJBCacheStatsProvider 56 { 57 58 protected int numActivated; 59 60 protected int cacheIdleTimeoutInSeconds; 61 protected int removalTimeoutInSeconds; 62 63 protected Object loadCountLock = new Object (); 64 protected int loadFromBackupCount; 65 66 protected boolean removeIfIdle = false; 67 68 public int passivationCount = 0; 69 protected Object passivationCountLock = new Object (); 70 71 private Object numPassLock = new Object (); 72 private int numVictimsAccessed = 0; 73 74 protected SFSBContainerCallback container; 75 protected SFSBStoreManager storeManager; 76 78 private static final byte CACHE_ITEM_VALID = 0; 79 private static final byte CACHE_ITEM_LOADING = 1; 80 private static final byte CACHE_ITEM_REMOVED = 2; 81 82 protected String configData; 83 84 private static final int STATE_RUNNING = 0; 85 private static final int STATE_SHUTTING_DOWN = 1; 86 private static final int STATE_UNDEPLOYING = 2; 87 private static final int STATE_DESTROYED = 3; 88 89 private int currentCacheState = STATE_RUNNING; 90 91 protected int confMaxCacheSize = Integer.MAX_VALUE; 92 93 private StatefulSessionStoreMonitor sfsbStoreMonitor; 94 95 101 public void destroy() { 102 this.currentCacheState = STATE_DESTROYED; 103 this.container = null; 104 105 super.destroy(); 106 } 107 108 109 public LruSessionCache(String cacheName, 110 SFSBContainerCallback container, 111 int cacheIdleTime, int removalTime) { 112 super(); 113 super.setCacheName(cacheName); 114 115 this.container = container; 116 117 this.cacheIdleTimeoutInSeconds = 118 (cacheIdleTime <= 0) ? 0 : cacheIdleTime; 119 this.removalTimeoutInSeconds = 120 (removalTime <= 0) ? 0 : removalTime; 121 122 if (cacheIdleTimeoutInSeconds > 0) { 123 super.timeout = cacheIdleTimeoutInSeconds*1000; 124 } 125 126 removeIfIdle = (removalTimeoutInSeconds > 0) 127 && (removalTimeoutInSeconds <= cacheIdleTimeoutInSeconds); 128 } 129 130 public void setStatefulSessionStoreMonitor( 131 StatefulSessionStoreMonitor storeMonitor) 132 { 133 this.sfsbStoreMonitor = storeMonitor; 134 } 135 136 public void setSessionStore(SFSBStoreManager storeManager) { 137 this.storeManager = storeManager; 138 } 140 141 145 protected void trimItem(CacheItem item) { 146 LruCacheItem removed = (LruCacheItem) item; 147 148 if (removeIfIdle) { 149 StatefulEJBContext ctx = (StatefulEJBContext) item.value; 150 151 long idleThreshold = 152 System.currentTimeMillis() - removalTimeoutInSeconds*1000; 153 if (ctx.getLastAccessTime() <= idleThreshold) { 154 container.passivateEJB(ctx); 155 return; 156 } 157 } 158 159 for (int i = 0; i < listeners.size(); i++) { 160 CacheListener listener = (CacheListener) listeners.get(i); 161 listener.trimEvent(removed.key, removed.value); 162 } 163 } 164 165 protected void itemAccessed(CacheItem item) { 166 LruCacheItem lc = (LruCacheItem) item; 167 synchronized (this) { 168 if (lc.isTrimmed) { 169 lc.isTrimmed = false; 170 numVictimsAccessed += 1; 171 CacheItem overflow = super.itemAdded(item); 172 if (overflow != null) { 173 trimItem(overflow); 174 } 175 } else { 176 super.itemAccessed(item); 177 } 178 } 179 } 180 181 public int getLoadFromBackupCount() { 182 return loadFromBackupCount; 183 } 184 185 protected void incrementLoadFromBackupCount() { 186 synchronized (loadCountLock) { 187 loadFromBackupCount++; 188 } 189 } 190 191 public StatefulEJBContext lookupEJB(Object sessionKey, 194 SFSBContainerCallback container, Object cookie) 195 { 196 int hashCode = hash(sessionKey); 197 int index = getIndex(hashCode); 198 CacheItem item = null; 199 LruSessionCacheItem newItem = null; 200 Object value = null; 201 202 synchronized (bucketLocks[index]) { 203 item = buckets[index]; 204 for (; item != null; item = item.next) { 205 if ( (hashCode == item.hashCode) && 206 (item.key.equals(sessionKey)) ) 207 { 208 value = item.value; 209 break; 210 } 211 } 212 213 if (value != null) { 215 itemAccessed(item); 216 } else if (item == null) { 217 newItem = new LruSessionCacheItem(hashCode, sessionKey, 218 null, -1, CACHE_ITEM_LOADING); 219 newItem.next = buckets[index]; 220 buckets[index] = newItem; 221 } 222 } 223 224 225 if (value != null) { 226 incrementHitCount(); 227 return (StatefulEJBContext) value; 228 } 229 230 incrementMissCount(); 231 if (item != null) { 232 synchronized (item) { 233 LruSessionCacheItem lruItem = (LruSessionCacheItem) item; 234 if ((lruItem.value == null) && (lruItem.cacheItemState == CACHE_ITEM_LOADING)) { 235 lruItem.waitCount++; 236 try { item.wait(); } catch (InterruptedException inEx) {} 237 } 238 return (StatefulEJBContext) item.value; 239 } 240 } 241 242 long activationStartTime = -1; 244 if (sfsbStoreMonitor.isMonitoringOn()) { 245 activationStartTime = System.currentTimeMillis(); 246 } 247 try { 248 newItem.value = value = getStateFromStore(sessionKey, container); 249 synchronized (buckets[index]) { 250 if (value == null) { 251 CacheItem prev = null; 253 for (CacheItem current = buckets[index]; current != null; 254 current = current.next) 255 { 256 if (current == newItem) { 257 if (prev == null) { 258 buckets[index] = current.next; 259 } else { 260 prev.next = current.next; 261 } 262 current.next = null; 263 break; 264 } 265 prev = current; 266 } 267 } else { 268 container.activateEJB(sessionKey, 269 (StatefulEJBContext) value, cookie); 270 sfsbStoreMonitor.incrementActivationCount(true); 271 272 CacheItem overflow = itemAdded(newItem); 273 incrementEntryCount(); 274 if (overflow != null) { 276 trimItem(overflow); 277 } 278 } 279 } } catch (javax.ejb.EJBException ejbEx) { 281 sfsbStoreMonitor.incrementActivationCount(false); 282 remove(sessionKey); 283 value = null; 284 } finally { 285 synchronized (newItem) { 286 newItem.cacheItemState = CACHE_ITEM_VALID; 287 if (newItem.waitCount > 0) { 288 newItem.notifyAll(); 289 } 290 } 291 if (activationStartTime != -1) { 292 long timeSpent = System.currentTimeMillis() 293 - activationStartTime; 294 sfsbStoreMonitor.setActivationTime(timeSpent); 295 } 296 } 297 298 return (StatefulEJBContext) value; 299 } 300 301 public Object remove(Object sessionKey) { 304 return remove(sessionKey, true); 305 } 306 307 public Object remove(Object sessionKey, boolean removeFromStore) { 308 int hashCode = hash(sessionKey); 309 int index = getIndex(hashCode); 310 311 CacheItem prev = null, item = null; 312 313 synchronized (bucketLocks[index]) { 314 for (item = buckets[index]; item != null; item = item.next) { 315 if ((hashCode == item.hashCode) && sessionKey.equals(item.key)) { 316 if (prev == null) { 317 buckets[index] = item.next; 318 } else { 319 prev.next = item.next; 320 } 321 item.next = null; 322 323 itemRemoved(item); 324 ((LruSessionCacheItem) item).cacheItemState = CACHE_ITEM_REMOVED; 325 break; 326 } 327 prev = item; 328 } 329 330 333 if (removeFromStore) { 335 try { 336 storeManager.remove(sessionKey); 337 } catch (SFSBStoreManagerException sfsbEx) { 338 _logger.log(Level.WARNING, "[" + cacheName + "]: Exception in " 339 + "storeManager.remove(" + sessionKey + ")", sfsbEx); 340 } 341 } 342 } 343 344 if (item != null) { 345 decrementEntryCount(); 346 incrementRemovalCount(); 347 348 incrementHitCount(); 349 } else { 350 incrementMissCount(); 351 } 352 353 return null; 354 } 355 356 public boolean passivateEJB(StatefulEJBContext ctx, Object sessionKey) 359 throws java.io.NotSerializableException 360 { 361 362 try { 363 int hashCode = hash(sessionKey); 364 int index = getIndex(hashCode); 365 366 boolean itemRemoved = false; 367 CacheItem prev = null, item = null; 368 synchronized (bucketLocks[index]) { 369 for (item = buckets[index]; item != null; item = item.next) { 370 if (item.value == ctx) { 371 LruCacheItem lruSCItem = (LruCacheItem) item; 372 if (lruSCItem.isTrimmed == false) { 373 if(_logger.isLoggable(Level.FINE)) { 375 _logger.log(Level.FINE, cacheName + ": session accessed after marked for passivation: " + sessionKey); 376 } 377 return false; 378 } 379 break; 380 } 381 prev = item; 382 } 383 384 if (item == null) { 385 return true; } 389 390 if (removeIfIdle) { 391 long idleThreshold = System.currentTimeMillis() - 392 removalTimeoutInSeconds*1000; 393 if (ctx.getLastAccessTime() <= idleThreshold) { 395 if(_logger.isLoggable(Level.FINE)) { 396 _logger.log(Level.FINE, cacheName + 397 ": Removing session " 398 + " instead of passivating for key: " + sessionKey); 399 } 400 401 if (prev == null) { 402 buckets[index] = item.next; 403 } else { 404 prev.next = item.next; 405 } 406 item.next = null; 407 itemRemoved = true; 408 } 410 } 411 412 } 413 414 if (itemRemoved) { 415 decrementEntryCount(); 416 incrementRemovalCount(); 417 return true; 418 } 419 420 if (saveStateToStore(sessionKey, ctx) == false) { 421 return false; 422 } 423 424 synchronized (bucketLocks[index]) { 425 prev = null; 426 for (item = buckets[index]; item != null; item = item.next) { 427 if (item.value == ctx) { 428 LruCacheItem lruSCItem = (LruCacheItem) item; 429 if (lruSCItem.isTrimmed == false) { 430 return false; 432 } 433 434 if (prev == null) { 435 buckets[index] = item.next; 436 } else { 437 prev.next = item.next; 438 } 439 item.next = null; 440 break; 441 } 442 prev = item; 443 } 444 } 445 446 if (item != null) { 447 decrementEntryCount(); 448 incrementRemovalCount(); 449 } 450 451 return true; 452 } catch (java.io.NotSerializableException notSerEx) { 453 throw notSerEx; 454 } catch (Exception ex) { 455 _logger.log(Level.WARNING, 456 "[" + cacheName + "]: passivateEJB(), Exception caught -> ",ex); 457 } 458 return false; 459 } 461 private Object getStateFromStore(Object sessionKey, SFSBContainerCallback container) { 462 463 Object object = null; 464 465 try { 466 SFSBBeanState beanState = storeManager.getState(sessionKey); 467 byte[] data = (beanState != null) 468 ? beanState.getState() 469 : null; 470 if ( data == null ) { 471 if(_logger.isLoggable(Level.SEVERE)) { 472 _logger.log(Level.SEVERE, cacheName + ": Cannot load from " 473 + " BACKUPSTORE FOR Key: <" + sessionKey + ">"); 474 } 475 } else { 476 sfsbStoreMonitor.setActivationSize(data.length); 477 incrementLoadFromBackupCount(); 478 object = IOUtils.deserializeObject(data, true, 479 container.getClassLoader()); 480 } 481 } catch ( Exception ex ) { 482 _logger.log(Level.SEVERE, cacheName + ": Exception while " 483 + " loading from backup session: <" + sessionKey + ">", ex); 484 } catch ( Error ex ) { 485 _logger.log(Level.SEVERE, cacheName + ": Error while " 486 + " loading from backup session: <" + sessionKey + ">", ex); 487 } 488 489 return object; 490 } 491 492 private boolean saveStateToStore(Object sessionKey, StatefulEJBContext ctx) 493 throws java.io.NotSerializableException 494 { 495 496 byte[] data = IOUtils.serializeObject(ctx.getSessionContext(), true); 497 498 boolean status = false; 500 501 if (data != null) { 502 SFSBBeanState beanState = storeManager.createSFSBBeanState( 503 sessionKey, ctx.getLastAccessTime(), 504 !ctx.existsInStore(), data); 505 try { 506 storeManager.passivateSave(beanState); 507 sfsbStoreMonitor.setPassivationSize(data.length); 508 status = true; 509 } catch (SFSBStoreManagerException sfsbEx) { 510 _logger.log(Level.WARNING, "[" + cacheName + "]: Exception during " 511 + "storeManager.passivateSave(" + sessionKey + ")", sfsbEx); 512 } 513 } 514 515 return status; 516 } 517 518 private void trimSelectedVictims(ArrayList victims) { 519 int sz = victims.size(); 520 521 synchronized (this) { 522 trimCount += sz; 523 } 524 CacheItem item = null; 525 for (int i=0; i<sz; i++) { 526 item = (CacheItem) victims.get(i); 527 trimItem(item); 528 } 529 } 530 531 public void setShutdownState() { 532 currentCacheState = STATE_SHUTTING_DOWN; 533 } 534 535 public void setUndeployedState() { 536 currentCacheState = STATE_UNDEPLOYING; 537 } 538 539 543 public Iterator values() { 544 ArrayList valueList = new ArrayList(); 545 546 synchronized (this) { 547 LruCacheItem item = tail; 548 while (item != null) { 549 StatefulEJBContext ctx = (StatefulEJBContext) item.value; 550 if (ctx != null) { 551 valueList.add(ctx); 552 } 553 554 if( (item == head) && (item.lPrev != null) ) { 556 _logger.log(Level.WARNING, 557 "[" + cacheName + "]: Iterator(), resetting head.lPrev"); 558 item.lPrev = null; 559 } 560 item = item.lPrev; 562 } 563 } 564 565 return valueList.iterator(); 566 } 567 568 574 public void trimTimedoutItems(int maxTrimCount) { 575 576 int count = 0; 577 LruCacheItem item; 578 long currentTime = System.currentTimeMillis(); 579 long idleThresholdTime = currentTime - cacheIdleTimeoutInSeconds*1000; 580 ArrayList victimList = new ArrayList(); 581 582 synchronized (this) { 583 if(_logger.isLoggable(Level.FINE)) { 584 _logger.log(Level.FINE, 585 "[" + cacheName + "]: TrimTimedoutBeans started..."); 586 } 587 588 if (tail == null) { if(_logger.isLoggable(Level.FINE)) { 590 _logger.log(Level.FINE, 591 "[" + cacheName + "]: TrimTimedoutBeans " 592 + " finished after removing 0 idle beans"); 593 } 594 head = null; 595 return; 596 } 597 item = tail; 599 while (true) { 600 if (currentCacheState != STATE_RUNNING) { 601 _logger.log(Level.WARNING, 602 "[" + cacheName + "]: Exiting TrimTimedoutBeans() because " 603 + "current cache state: " + currentCacheState); 604 break; 605 } 606 607 StatefulEJBContext ctx = (StatefulEJBContext) item.value; 608 if (ctx != null) { 609 if ((ctx.getLastAccessTime() <= idleThresholdTime) && 611 ctx.canBePassivated()) { 612 item.isTrimmed = true; 613 victimList.add(item); 614 } else { 615 break; 616 } 617 } 618 if( (item == head) && (item.lPrev != null) ) { 620 _logger.log(Level.WARNING, 621 "[" + cacheName + "]: TrimTimedoutBeans(), resetting head.lPrev"); 622 item.lPrev = null; 623 } 624 item = item.lPrev; 626 if (item == null) { 627 break; 628 } 629 item.lNext.lPrev = null; 632 item.lNext.lNext = null; 633 634 item.lNext = null; 635 } 636 if (item == tail) { 637 if(_logger.isLoggable(Level.FINE)) { 639 _logger.log(Level.FINE, 640 "[" + cacheName + "]: TrimTimedoutBeans " 641 + " finished after removing 0 idle beans"); 642 } 643 return; 644 } 645 646 if (item == null) 648 head = null; 649 650 tail = item; 651 count = victimList.size(); 652 listSize -= count; 653 trimCount += count; 654 } 655 656 for (int idx = 0;idx < count; idx++) { 658 trimItem((LruCacheItem) victimList.get(idx)); 659 } 660 661 if(_logger.isLoggable(Level.FINE)) { 662 _logger.log(Level.FINE, 663 "[" + cacheName + "]: TrimTimedoutBeans " 664 + " finished after removing " + count + " idle beans"); 665 } 666 } 667 668 674 public void trimUnSortedTimedoutItems(int maxCount) { 675 int maxIndex = buckets.length; 676 long idleThreshold = System.currentTimeMillis() - timeout; 677 ArrayList victims = new ArrayList(); 678 int sz = 0; 679 int totalSize = 0; 680 681 if(_logger.isLoggable(Level.FINE)) { 682 _logger.log(Level.FINE, 683 "[" + cacheName + "]: TrimUnsortedTimedoutBeans started..."); 684 } 685 for (int index = 0; index < maxIndex ; index++) { 688 689 if (buckets[index] != null) { 690 synchronized (bucketLocks[index]) { 691 for (CacheItem item = buckets[index]; item != null; 692 item = item.next) { 693 StatefulEJBContext ctx = 694 (StatefulEJBContext) item.value; 695 if ((ctx != null) && 697 (ctx.getLastAccessTime() <= idleThreshold) && 698 ctx.canBePassivated()) { 699 LruCacheItem litem = (LruCacheItem)item; 700 synchronized (this) { 701 if (currentCacheState != STATE_RUNNING) { 702 _logger.log(Level.WARNING, 703 "[" + cacheName + "]: Exiting TrimUnSortedTimedoutBeans() " 704 + "because current cache state: " + currentCacheState); 705 break; 706 } 707 if (litem.isTrimmed == false) { 708 itemRemoved(litem); 709 litem.isTrimmed = true; 710 victims.add(litem); 711 } 712 } 713 } 714 } 715 } 716 sz = victims.size(); 719 if (sz >= container.getPassivationBatchCount()) { 720 trimSelectedVictims(victims); 721 totalSize += sz; 722 victims.clear(); 723 } 724 } 725 } 726 727 sz = victims.size(); 728 if (sz > 0) { 729 trimSelectedVictims(victims); 730 totalSize += sz; 731 } 732 if(_logger.isLoggable(Level.FINE)) { 733 _logger.log(Level.FINE, "[" + cacheName + "]: TrimUnsortedTimedoutBeans " 734 + " finished after removing " + totalSize + " idle beans"); 735 } 736 } 737 738 public int getNumVictimsAccessed() { 739 return numVictimsAccessed; 740 } 741 742 protected CacheItem createItem(int hashCode, Object sessionKey, 743 Object value, int size) 744 { 745 return new LruSessionCacheItem(hashCode, sessionKey, value, size); 746 } 747 748 protected static class LruSessionCacheItem 750 extends LruCacheItem 751 { 752 753 protected byte waitCount; 754 protected byte cacheItemState = CACHE_ITEM_VALID; 755 756 protected LruSessionCacheItem(int hashCode, Object key, Object value, 757 int size) 758 { 759 super(hashCode, key, value, size); 760 this.cacheItemState = CACHE_ITEM_VALID; 761 } 762 763 protected LruSessionCacheItem(int hashCode, Object key, Object value, 764 int size, byte state) 765 { 766 super(hashCode, key, value, size); 767 this.cacheItemState = state; 768 } 769 } 770 771 public void setConfigData(String configData) { 772 this.configData = configData; 773 } 774 775 777 public void appendStats(StringBuffer sbuf) { 778 sbuf.append("[Cache: ") 779 .append("Size=").append(entryCount).append("; ") 780 .append("HitCount=").append(hitCount).append("; ") 781 .append("MissCount=").append(missCount).append("; ") 782 .append("Passivations=").append(getNumPassivations()).append("; "); 783 if (configData != null) { 784 sbuf.append(configData); 785 } 786 sbuf.append("]"); 787 } 788 789 public int getCacheHits() { 790 return hitCount; 791 } 792 793 public int getCacheMisses() { 794 return missCount; 795 } 796 797 public int getNumBeansInCache() { 798 return entryCount; 799 } 800 801 public int getNumExpiredSessionsRemoved() { 802 return (sfsbStoreMonitor == null) 803 ? 0 : sfsbStoreMonitor.getNumExpiredSessionsRemoved(); 804 } 805 806 public int getNumPassivationErrors() { 807 return (sfsbStoreMonitor == null) 808 ? 0 : sfsbStoreMonitor.getNumPassivationErrors(); 809 } 810 811 public int getNumPassivations() { 812 return (sfsbStoreMonitor == null) 813 ? 0 : sfsbStoreMonitor.getNumPassivations(); 814 } 815 816 public int getNumPassivationSuccess() { 817 return (sfsbStoreMonitor == null) 818 ? 0 : sfsbStoreMonitor.getNumPassivationSuccess(); 819 } 820 821 public void setMaxCacheSize(int val) { 822 this.confMaxCacheSize = val; 823 } 824 825 public int getMaxCacheSize() { 826 return confMaxCacheSize; 827 } 828 829 } 830 831 | Popular Tags |