FileDocCategorySizeDatePackage
LruCache.javaAPI DocGlassfish v2 API12425Fri May 04 22:35:28 BST 2007com.sun.appserv.util.cache

LruCache

public class LruCache extends BaseCache
LRUCache in-memory bounded cache with an LRU list

Fields Summary
public static final long
NO_TIMEOUT
protected LruCacheItem
head
protected LruCacheItem
tail
protected int
trimCount
protected int
listSize
protected long
timeout
protected int
defaultMaxEntries
protected boolean
isUnbounded
Constructors Summary
public LruCache()
default constructor


           
       
public LruCache(int defaultMaxEntries)
constructor with specified max entries.

param
defaultMaxEntries specifies the default max entries to use when the maxEntries is <= 0.

        this.defaultMaxEntries = defaultMaxEntries;
    
Methods Summary
protected CacheItemcreateItem(int hashCode, java.lang.Object key, java.lang.Object value, int size)
create new item

param
hashCode for the entry
param
key Object key
param
value Object value
param
size size in bytes of the item subclasses may override to provide their own CacheItem extensions e.g. one that permits persistence.

        return new LruCacheItem(hashCode, key, value, size);
    
public java.lang.ObjectgetStatByName(java.lang.String key)
get the desired statistic counter

param
key to corresponding stat
return
an Object corresponding to the stat See also: Constant.java for the key

        Object stat = super.getStatByName(key);

        if (stat == null && key != null) {
            if (key.equals(Constants.STAT_LRUCACHE_LIST_LENGTH))
                stat = Integer.valueOf(listSize);
            else if (key.equals(Constants.STAT_LRUCACHE_TRIM_COUNT))
                stat = Integer.valueOf(trimCount);
        }
        return stat;
    
public java.util.MapgetStats()

        Map stats = super.getStats();
        stats.put(Constants.STAT_LRUCACHE_LIST_LENGTH,
                  Integer.valueOf(listSize));
        stats.put(Constants.STAT_LRUCACHE_TRIM_COUNT,
                  Integer.valueOf(trimCount));

        return stats;
    
public voidinit(int maxEntries, long timeout, float loadFactor, java.util.Properties props)
initialize the cache

param
maxEntries maximum number of entries expected in the cache
param
loadFactor the load factor
param
timeout to be used to trim the expired entries
param
props opaque list of properties for a given cache implementation
throws
a generic Exception if the initialization failed


        // if the max entries is <= 0 then set the default max entries
        if (maxEntries <= 0) {
            maxEntries = defaultMaxEntries;

            // mark this cache unbounded
            isUnbounded = true;
        }
        setTimeout(timeout);

        super.init(maxEntries, loadFactor, props);
    
protected voiditemAccessed(CacheItem item)
this item is accessed

param
item CacheItem accessed Cache bucket is already synchronized by the caller

        LruCacheItem lc = (LruCacheItem) item;

        synchronized (this) {

            // if the item is already trimmed from the LRU list, nothing to do.
            if (lc.isTrimmed)
                return;

            // update the timestamp
            lc.lastAccessed = System.currentTimeMillis();

            LruCacheItem prev = lc.lPrev;
            LruCacheItem next = lc.lNext;
            // update the LRU list
            if (prev != null) {
                // put the item at the head of LRU list
                lc.lPrev = null;
                lc.lNext = head;
                head.lPrev = lc;
                head = lc;
    
                // patch up the previous neighbors
                prev.lNext = next;
                if (next != null)
                    next.lPrev = prev;
                else
                    tail = prev;
           }
        }
    
protected CacheItemitemAdded(CacheItem item)
/** this item is just added to the cache

param
item CacheItem that was created
return
a overflow item; may be null this function checks if adding the new item results in a overflow; if so, it returns the item to be removed. Cache bucket is already synchronized by the caller

        boolean updateThreshold = false;
        CacheItem overflow = null;
        LruCacheItem lc = (LruCacheItem) item;

        // set the timestamp
        lc.lastAccessed = System.currentTimeMillis();

        // update the LRU
        synchronized (this) {
            if (head != null) {
                head.lPrev = lc;
                lc.lNext = head;
                lc.lPrev = null;
                head = lc;
            }
            else {
                head = tail = lc;
                lc.lPrev = lc.lNext = null;
            }

            listSize++;

            if (isThresholdReached()) {
                if (!isUnbounded) 
                    overflow = trimLru(lc.lastAccessed);
                else
                    updateThreshold = true;
            }
        }

        // update the base cache threshold if needed
        if (updateThreshold)
            super.handleOverflow(); 

        return overflow;
    
protected voiditemRefreshed(CacheItem item, int oldSize)
item value has been refreshed

param
item CacheItem that was refreshed
param
oldSize size of the previous value that was refreshed Cache bucket is already synchronized by the caller

        itemAccessed(item);   
    
protected voiditemRemoved(CacheItem item)
item value has been removed from the cache

param
item CacheItem that was just removed Cache bucket is already synchronized by the caller

        LruCacheItem l = (LruCacheItem) item;

        // remove the item from the LRU list
        synchronized (this) {
            LruCacheItem prev = l.lPrev;
            LruCacheItem next = l.lNext;

            // if the item is already trimmed from the LRU list, nothing to do.
            if (l.isTrimmed)
                return;

            // patch up the neighbors and make sure head/tail are correct
            if (prev != null)
                prev.lNext = next;
            else
                head = next;

            if (next != null)
                next.lPrev = prev;
            else
                tail = prev;

            l.lPrev = l.lNext = null;
            listSize--;
        }
    
public voidsetTimeout(long timeout)
sets the timeout value

param
timeout to be used to trim the expired entries

        // accept a positive timeout
        if (timeout > 0)
            this.timeout = timeout;
    
public voidtrimExpiredEntries(int maxCount)
trim the expired entries from the cache.

param
maxCount maximum number of invalid entries to trim specify Integer.MAX_VALUE to trim all invalid entries This call is to be scheduled by a thread managed by the container. NOTE: this algorithm assumes that all the entries in the cache have identical timeout (otherwise traversing from tail won't be right).

        
        int count = 0;
        LruCacheItem item;
        long currentTime = System.currentTimeMillis();
        ArrayList list = new ArrayList();

        synchronized (this) {
            // traverse LRU list till we reach a valid item; remove them at once
            for (item = tail; item != null && count < maxCount;
                                                item = item.lPrev) {

                if (timeout != NO_TIMEOUT && 
                    (item.lastAccessed + timeout) <= currentTime) {
                    item.isTrimmed = true;
		    list.add(item);

                    count++;
                } else
                    break;
            }

            // if there was at least one invalid item then item != tail.
            if (item != tail) {
                if (item != null)
                    item.lNext = null;
                else
                    head = null;

                tail = item;
            }
            listSize -= count;
            trimCount += count;
        }
        
        // trim the items from the BaseCache from the old tail backwards
        for (int index=list.size()-1; index >= 0; index--) {
            trimItem((LruCacheItem) list.get(index));
        }
    
protected CacheItemtrimLru(long currentTime)
trim one item from the LRU list

param
currentTime of this operation
return
the item trimmed from cache list synchronization is handled by the caller


        LruCacheItem trimItem = tail;

        if (tail != head) {
            tail = trimItem.lPrev;
            if(tail == null) {
                //TODO:: print out a warning message here
                tail = head = null;
            } else {
                tail.lNext = null;
                trimItem.lPrev = null;
            }
        } else {
            tail = head = null;
        }

        if (trimItem != null) {
            trimItem.isTrimmed = true;
            trimItem.lPrev = null;
            trimCount++;
            listSize--;
        }

        return trimItem;