1 17 18 package org.sape.carbon.services.cache.mru; 19 20 import java.util.Collection ; 21 import java.util.Collections ; 22 import java.util.HashMap ; 23 import java.util.Iterator ; 24 import java.util.Map ; 25 import java.util.Set ; 26 import java.util.TreeSet ; 27 28 import org.sape.carbon.core.component.Component; 29 import org.sape.carbon.core.component.ComponentConfiguration; 30 import org.sape.carbon.core.component.lifecycle.Configurable; 31 import org.sape.carbon.core.component.lifecycle.Initializable; 32 import org.sape.carbon.core.config.InvalidConfigurationException; 33 34 import org.apache.commons.logging.Log; 35 import org.apache.commons.logging.LogFactory; 36 37 38 99 public abstract class AbstractMRUCache 100 implements MRUCache, Configurable, Initializable { 101 102 103 private Log log = LogFactory.getLog(this.getClass()); 104 105 106 protected int capacity = 0; 107 108 109 protected long expirationInterval = 0; 110 111 112 protected Map map; 113 114 118 protected TreeSet ordering; 119 120 121 protected Map keyMap; 122 123 124 protected String name; 125 126 127 protected long cacheHits = 0; 128 129 130 protected long cacheMisses = 0; 131 132 133 public static final long DEFAULT_ELEMENT_EXPIRATION_INTERVAL = -1; 134 135 137 public static final long MAX_EXPIRATION_TIME = Long.MAX_VALUE; 138 139 143 146 public void configure(ComponentConfiguration configuration) { 147 148 MRUCacheConfiguration myConfig = (MRUCacheConfiguration) configuration; 149 150 if (myConfig.getCapacity() == null) { 151 throw new InvalidConfigurationException( 152 this.getClass(), myConfig.getConfigurationName(), 153 "Capacity", 154 "Must be specified, not optional"); 155 } 156 157 setCapacity(myConfig.getCapacity()); 158 setExpirationInterval(myConfig.getExpirationInterval()); 159 160 this.name = myConfig.getConfigurationName(); 161 162 if (log.isInfoEnabled()) { 163 log.info("configuring cache with capacity " + capacity); 164 } 165 } 166 167 170 public void initialize(Component thisComponent) { 171 this.ordering = new TreeSet (); 172 this.map = new HashMap (); 173 this.keyMap = new HashMap (); 174 } 175 176 177 183 public synchronized void refreshAll() { 184 this.clear(); 185 } 186 187 188 193 public synchronized void clear() { 194 double totalFetches = this.cacheHits + this.cacheMisses; 195 double hitRate = (double) this.cacheHits * 100 / totalFetches; 196 197 if (log.isInfoEnabled()) { 198 log.info("Current cache size is " 199 + this.ordering.size() 200 + ". Total fetches from cache: " 201 + totalFetches 202 + ". Cache hit rate: " 203 + hitRate 204 + "%."); 205 } 206 this.map.clear(); 207 this.keyMap.clear(); 208 this.ordering.clear(); 209 this.cacheHits = 0; 210 this.cacheMisses = 0; 211 } 212 213 214 225 public synchronized boolean containsKey(Object key) { 226 return this.map.containsKey(key); 227 } 228 229 230 246 public synchronized boolean containsValue(Object value) { 247 return this.map.containsValue(value); 248 } 249 250 251 258 public synchronized Set entrySet() { 259 return Collections.unmodifiableSet(this.map.entrySet()); 260 } 261 262 263 269 public synchronized boolean isEmpty() { 270 return this.map.isEmpty(); 271 } 272 273 274 283 public synchronized Set keySet() { 284 return Collections.unmodifiableSet(this.map.keySet()); 285 } 286 287 288 299 public synchronized Object put(Object key, Object value) { 300 Object previousEntry = null; 301 Object previousKey = null; 302 KeyInfo keyInfo = new KeyInfo(key, this.expirationInterval); 303 304 previousEntry = this.map.put(key, value); 307 previousKey = this.keyMap.put(key, keyInfo); 308 if (previousKey != null) { 309 this.ordering.remove(previousKey); 311 } 312 313 this.ordering.add(keyInfo); 314 315 if (this.ordering.size() > this.capacity) { 316 removeLast(); 317 } 318 319 return previousEntry; 322 } 323 324 336 public synchronized void putAll(Map t) { 337 Map.Entry entry = null; 338 Iterator entryIterator = t.entrySet().iterator(); 339 340 while (entryIterator.hasNext()) { 343 entry = (Map.Entry ) entryIterator.next(); 344 this.put(entry.getKey(), entry.getValue()); 345 } 346 } 347 348 349 356 public synchronized Object remove(Object key) { 357 Object value = null; 358 359 value = this.map.remove(key); 360 KeyInfo keyInfo = (KeyInfo) this.keyMap.remove(key); 361 this.ordering.remove(keyInfo); 362 363 return value; 364 } 365 366 376 public synchronized int size() { 377 return this.map.size(); 378 } 379 380 381 387 public synchronized Collection values() { 388 return Collections.unmodifiableCollection(this.map.values()); 389 } 390 391 392 397 public Long getHits() { 398 return new Long (this.cacheHits); 399 } 400 401 406 public Long getMisses() { 407 return new Long (this.cacheMisses); 408 } 409 410 415 public Float getHitPercentage() { 416 return new Float ((1.0 * cacheHits) / (cacheHits + cacheMisses)); 417 } 418 419 424 public Integer getCapacity() { 425 return new Integer (this.capacity); 426 } 427 428 433 public synchronized void setCapacity(Integer capacity) { 434 this.capacity = capacity.intValue(); 435 while (size() > this.capacity) { 436 removeLast(); 437 } 438 } 439 440 445 public Long getExpirationInterval() { 446 return new Long (this.expirationInterval); 447 } 448 449 454 public void setExpirationInterval(Long newInterval) { 455 if (newInterval == null) { 456 this.expirationInterval = DEFAULT_ELEMENT_EXPIRATION_INTERVAL; 457 } else { 458 this.expirationInterval = newInterval.longValue(); 459 } 460 } 461 462 467 public Long getSize() { 468 return new Long (this.map.size()); 469 } 470 471 478 public String toString() { 479 double totalFetches = this.cacheHits + this.cacheMisses; 480 double hitRate = (double) this.cacheHits * 100 / totalFetches; 481 482 StringBuffer stringBuffer = new StringBuffer (256); 483 484 stringBuffer.append("MRUCache - name: ["); 485 stringBuffer.append(this.name); 486 stringBuffer.append("] with capacity ["); 487 stringBuffer.append(capacity); 488 stringBuffer.append("] size: ["); 489 stringBuffer.append(this.ordering.size()); 490 stringBuffer.append("] requests: ["); 491 stringBuffer.append(totalFetches); 492 stringBuffer.append("] hit rate: ["); 493 stringBuffer.append(hitRate); 494 stringBuffer.append("]%"); 495 496 return stringBuffer.toString(); 497 } 498 499 504 public void runScheduledTask() { 505 refreshAll(); 506 } 507 508 509 519 protected synchronized Object getObject(Object key) { 520 KeyInfo keyInfo = (KeyInfo) this.keyMap.get(key); 522 523 Object value = null; 524 if (keyInfo != null) { 526 if (keyInfo.hasExpired()) { 528 remove(keyInfo.getKey()); 529 } else { 530 value = this.map.get(key); 532 this.ordering.remove(keyInfo); 533 keyInfo.updateAccessTime(); 534 this.ordering.add(keyInfo); 535 this.cacheHits++; 536 } 537 } 538 return value; 539 } 540 541 544 protected synchronized void removeLast() { 545 KeyInfo keyInfo = (KeyInfo) this.ordering.first(); 546 remove(keyInfo.getKey()); 547 } 548 549 550 551 555 protected long getCacheHits() { 556 return this.cacheHits; 557 } 558 559 560 564 protected long getCacheMisses() { 565 return this.cacheMisses; 566 } 567 568 569 } 570 571 572 589 class KeyInfo implements Comparable { 590 591 private Object key = null; 592 593 594 private long lastAccessTime = 0; 595 596 600 private long expirationTime = 0; 601 602 610 KeyInfo(Object key, long expirationInterval) { 611 this.key = key; 612 this.lastAccessTime = System.currentTimeMillis(); 613 614 if (expirationInterval 617 == AbstractMRUCache.DEFAULT_ELEMENT_EXPIRATION_INTERVAL) { 618 619 this.expirationTime = AbstractMRUCache.MAX_EXPIRATION_TIME; 620 } else { 621 this.expirationTime = this.lastAccessTime + expirationInterval; 622 } 623 624 } 625 626 631 Object getKey() { 632 return this.key; 633 } 634 635 642 long getExpirationTime() { 643 return this.expirationTime; 644 } 645 646 652 boolean hasExpired() { 653 return (System.currentTimeMillis() >= this.expirationTime); 654 } 655 656 657 661 void updateAccessTime() { 662 this.lastAccessTime = System.currentTimeMillis(); 663 } 664 665 666 683 public final int compareTo(final Object other) { 684 KeyInfo otherKeyInfo = (KeyInfo) other; 686 int returnValue = 0; 687 688 if (this.lastAccessTime == otherKeyInfo.lastAccessTime) { 691 if (this.expirationTime == otherKeyInfo.expirationTime) { 692 returnValue = 693 ((Comparable ) this.key).compareTo(otherKeyInfo.key); 694 695 } else if (this.expirationTime > otherKeyInfo.expirationTime) { 696 returnValue = 1; 697 } else { 698 returnValue = -1; 699 } 700 } else if (this.lastAccessTime > otherKeyInfo.lastAccessTime) { 701 returnValue = 1; 705 } else { 706 returnValue = -1; 707 } 708 709 return returnValue; 711 } 712 713 714 721 public final boolean equals(final Object other) { 722 return this.compareTo(other) == 0; 723 } 724 725 730 public int hashCode() { 731 return this.key.hashCode(); 732 } 733 734 735 741 public String toString() { 742 return "Key: '" + this.key.toString() 743 + "', Last Access Time: " + this.lastAccessTime 744 + "', Expiration Time: " + this.expirationTime; 745 } 746 } 747 748 749 | Popular Tags |