001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
003: */
004: package com.tc.object.cache;
005:
006: import com.tc.logging.TCLogger;
007: import com.tc.logging.TCLogging;
008: import com.tc.text.PrettyPrinter;
009:
010: import gnu.trove.TLinkedList;
011:
012: import java.util.Collection;
013: import java.util.Collections;
014: import java.util.HashSet;
015:
016: /**
017: * Whimpy implementation of an LRU cache
018: */
019: public class LRUEvictionPolicy implements EvictionPolicy {
020: private static final TCLogger logger = TCLogging
021: .getLogger(LRUEvictionPolicy.class);
022: private final TLinkedList cache = new TLinkedList();
023: private final int capacity;
024: private final int evictionSize;
025:
026: public LRUEvictionPolicy(int capacity) {
027: this (capacity, capacity / 10);
028: }
029:
030: public LRUEvictionPolicy(int capacity, int evictionSize) {
031: if (logger.isDebugEnabled()) {
032: logger.debug("new " + getClass().getName() + "(" + capacity
033: + ")");
034: }
035: this .capacity = capacity;
036: this .evictionSize = (evictionSize <= 0 ? 1 : evictionSize);
037: }
038:
039: public synchronized boolean add(Cacheable obj) {
040: // Assert.eval(!contains(obj));
041: if (logger.isDebugEnabled()) {
042: logger.debug("Adding: " + obj);
043: }
044: cache.addLast(obj);
045:
046: return isCacheFull();
047: }
048:
049: private boolean isCacheFull() {
050: return (capacity > 0 && cache.size() > capacity);
051: }
052:
053: public synchronized Collection getRemovalCandidates(int maxCount) {
054: if (capacity > 0) {
055: if (!isCacheFull()) {
056: return Collections.EMPTY_LIST;
057: }
058: if (maxCount <= 0 || maxCount > evictionSize) {
059: maxCount = evictionSize;
060: }
061: } else if (maxCount <= 0) {
062: // disallow negetative maxCount when capacity is negative
063: throw new AssertionError(
064: "Please specify maxcount > 0 as capacity is set to : "
065: + capacity + " Max Count = " + maxCount);
066: }
067:
068: Collection rv = new HashSet();
069: int count = Math.min(cache.size(), maxCount);
070: Cacheable c = (Cacheable) cache.getFirst();
071: Object save = c;
072: while (cache.size() - rv.size() > capacity && count > 0) {
073: moveToTail(c);
074: if (c.canEvict()) {
075: rv.add(c);
076: count--;
077: }
078: c = (Cacheable) cache.getFirst();
079: if (save == c)
080: break;
081: }
082: return rv;
083: }
084:
085: public synchronized void remove(Cacheable obj) {
086: if (logger.isDebugEnabled()) {
087: logger.debug("Removing: " + obj);
088: }
089: if (contains(obj))
090: cache.remove(obj);
091: }
092:
093: private boolean contains(Cacheable obj) {
094: // XXX: This is here to get around bogus implementation of TLinkedList.contains(Object)
095: return obj != null
096: && (obj.getNext() != null || obj.getPrevious() != null);
097: }
098:
099: public synchronized void markReferenced(Cacheable obj) {
100: moveToTail(obj);
101: }
102:
103: private synchronized void moveToTail(Cacheable obj) {
104: if (contains(obj)) {
105: cache.remove(obj);
106: cache.addLast(obj);
107: }
108: }
109:
110: public synchronized PrettyPrinter prettyPrint(PrettyPrinter out) {
111: PrettyPrinter rv = out;
112: out.println(getClass().getName());
113: out = out.duplicateAndIndent();
114: out.indent().println("max size: " + capacity).indent().print(
115: "cache: ").visit(cache).println();
116: return rv;
117: }
118:
119: public int getCacheCapacity() {
120: return capacity;
121: }
122:
123: }
|