| /* |
| * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0, which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * This Source Code may also be made available under the following Secondary |
| * Licenses when the conditions for such availability set forth in the |
| * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, |
| * version 2 with the GNU Classpath Exception, which is available at |
| * https://www.gnu.org/software/classpath/license.html. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 |
| */ |
| |
| package com.sun.appserv.util.cache; |
| |
| import com.sun.enterprise.util.CULoggerInfo; |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.Vector; |
| |
| /** |
| * BaseCache |
| * Generic in-memory, abstract cache |
| */ |
| public class BaseCache implements Cache { |
| |
| /** |
| * The resource bundle containing the localized message strings. |
| */ |
| static final int MAX_ENTRIES = 1 << 30; |
| static final float DEFAULT_LOAD_FACTOR = 0.75f; |
| |
| // maximum number of entries this cache may ever hold |
| protected int maxEntries; |
| |
| // the number of cache entries in this cache |
| protected int entryCount; |
| private Object entryCountLk = new Object(); |
| |
| /** threshold for the cache; once the threshold is reached |
| * entries are removed to accomodate newer inserts |
| */ |
| protected int threshold = 0; |
| |
| // the number of cache hits |
| protected int hitCount; |
| private Object hitCountLk = new Object(); |
| |
| // the number of cache misses |
| protected int missCount; |
| private Object missCountLk = new Object(); |
| |
| // the number of cache item removals |
| protected int removalCount; |
| private Object removalCountLk = new Object(); |
| |
| // the number of cache item refreshes |
| private int refreshCount; |
| private Object refreshCountLk = new Object(); |
| |
| // the number of times an item was added to cache |
| private int addCount; |
| private Object addCountLk = new Object(); |
| |
| // the number of times the cache overflowed |
| private int overflowCount; |
| private Object overflowCountLk = new Object(); |
| |
| // table size |
| protected int maxBuckets; |
| |
| // cache entries hash table |
| protected CacheItem[] buckets; |
| // bucket-wide locks |
| protected Object[] bucketLocks; |
| |
| // boolean status and locks for item thread-safe refreshes |
| protected boolean[] refreshFlags; |
| |
| protected ArrayList listeners = new ArrayList(); |
| |
| /** |
| * default constructor for the basic cache |
| */ |
| public BaseCache() { } |
| |
| /** |
| * initialize the cache |
| * @param maxEntries maximum number of entries expected in the cache |
| * @param props opaque list of properties for a given cache implementation |
| * @throws a generic Exception if the initialization failed |
| */ |
| public void init(int maxEntries, Properties props) throws Exception { |
| init(maxEntries, DEFAULT_LOAD_FACTOR, props); |
| } |
| |
| /** |
| * initialize the cache |
| * @param maxEntries maximum number of entries expected in the cache |
| * @param loadFactor the load factor |
| * @param props opaque list of properties for a given cache implementation |
| * @throws a generic Exception if the initialization failed |
| */ |
| public void init(int maxEntries, float loadFactor, Properties props) { |
| |
| if (maxEntries <= 0) { |
| String msg = CULoggerInfo.getString(CULoggerInfo.illegalMaxEntries); |
| |
| Integer obj = Integer.valueOf(maxEntries); |
| Object[] params = { obj }; |
| msg = MessageFormat.format(msg, params); |
| |
| throw new IllegalArgumentException(msg); |
| } |
| |
| if (maxEntries > MAX_ENTRIES) |
| maxEntries = MAX_ENTRIES; |
| |
| this.maxEntries = maxEntries; |
| |
| // find a power of 2 >= maxEntries |
| maxBuckets = 1; |
| while (maxBuckets < maxEntries) |
| maxBuckets <<= 1; |
| |
| //Cannot have the loadfactor as a negative value |
| if( loadFactor < 0 ) |
| loadFactor = 0; |
| |
| /** initialize the threshold; a zero value for maxEntries |
| * implies no caching. |
| */ |
| if (maxEntries != 0) { |
| threshold = (int)(maxEntries * loadFactor) + 1; |
| } |
| |
| // create the cache and the bucket locks |
| entryCount = 0; |
| buckets = new CacheItem[maxBuckets]; |
| bucketLocks = new Object[maxBuckets]; |
| refreshFlags = new boolean[maxBuckets]; |
| |
| for (int i=0; i<maxBuckets; i++) { |
| buckets[i] = null; |
| bucketLocks[i] = new Object(); |
| refreshFlags[i] = false; |
| } |
| } |
| |
| /** |
| * add the cache module listener |
| * @param listener <code>CacheListener</code> implementation |
| */ |
| public void addCacheListener(CacheListener listener) { |
| listeners.add(listener); |
| } |
| |
| /** |
| * Returns a hash code for non-null Object x. |
| * @See also <code>HashMap</code> |
| */ |
| protected int hash(Object x) { |
| int h = x.hashCode(); |
| return h - (h << 7); // i.e., -127 * h |
| } |
| |
| /** |
| * Check for equality of non-null reference x and possibly-null y. |
| */ |
| protected boolean eq(Object x, Object y) { |
| return x == y || x.equals(y); |
| } |
| |
| /** |
| * increase the threshold |
| */ |
| protected void handleOverflow() { |
| // just double the threshold; this may degenerate the cache. |
| threshold = (threshold * 2); |
| incrementOverflowCount(); |
| } |
| |
| /** |
| * this item is just added to the cache |
| * @param item <code>CacheItem</code> that was created |
| * @return a overflow item; may be null |
| * Cache bucket is already synchronized by the caller |
| * |
| * Here, if cache is overflowing (i.e. reached threshold); this class |
| * simply makes the cache unbounded by raising the threshold. Subclasses |
| * are expected to provide a robust cache replacement algorithm. |
| * |
| * Subclasses should enhance this implemntation. |
| */ |
| protected CacheItem itemAdded(CacheItem item) { |
| if (isThresholdReached()) { |
| handleOverflow(); |
| } |
| return null; |
| } |
| |
| /** |
| * this item is accessed |
| * @param item <code>CacheItem</code> accessed |
| * |
| * Cache bucket is already synchronized by the caller |
| */ |
| protected void itemAccessed(CacheItem item) { } |
| |
| /** |
| * item value has been refreshed |
| * @param item <code>CacheItem</code> that was refreshed |
| * @param oldSize size of the previous value that was refreshed |
| * Cache bucket is already synchronized by the caller |
| */ |
| protected void itemRefreshed(CacheItem item, int oldSize) { } |
| |
| /** |
| * item value has been removed from the cache |
| * @param item <code>CacheItem</code> that was just removed |
| * |
| * Cache bucket is already synchronized by the caller |
| */ |
| protected void itemRemoved(CacheItem item) { } |
| |
| /** |
| * Cannot find an item with the given key and hashCode |
| * @param key <code>Object</code> that is not found |
| * @param hashCode <code>int</code> its hashCode |
| * |
| * @returns the Object value associated with the item |
| * Cache bucket is already synchronized by the caller |
| */ |
| protected Object loadValue(Object key, int hashCode) { |
| return null; |
| } |
| |
| /** |
| * create new item |
| * @param hashCode for the entry |
| * @param key <code>Object</code> key |
| * @param value <code>Object</code> value |
| * @param size size in bytes of the item |
| * subclasses may override to provide their own CacheItem extensions |
| * e.g. one that permits persistence. |
| */ |
| protected CacheItem createItem(int hashCode, Object key, |
| Object value, int size) { |
| return new CacheItem(hashCode, key, value, size); |
| } |
| |
| /** |
| * has cache reached its threshold |
| * @return true when the cache reached its threshold |
| */ |
| protected boolean isThresholdReached() { |
| return (entryCount > threshold); |
| } |
| |
| /** |
| * get the index of the item in the cache |
| * @param hashCode of the entry |
| * @return the index to be used in the cache |
| */ |
| protected final int getIndex(int hashCode) { |
| return (hashCode & (maxBuckets - 1)); |
| } |
| |
| /** |
| * get the index of the item given a key |
| * @param key of the entry |
| * @return the index to be used in the cache |
| */ |
| public final int getIndex(Object key) { |
| return getIndex(hash(key)); |
| } |
| |
| /** |
| * get the item stored at the key. |
| * @param key lookup key |
| * @returns the item stored at the key; null if not found. |
| */ |
| public Object get(Object key) { |
| int hashCode = hash(key); |
| |
| return get(hashCode, key); |
| } |
| |
| /** |
| * get the item stored at the given pre-computed hash code and the key. |
| * @param key lookup key |
| * @returns the item stored at the key; null if not found. |
| */ |
| public Object get(int hashCode, Object key) { |
| |
| int index = getIndex(hashCode); |
| Object value; |
| CacheItem item = null; |
| |
| synchronized (bucketLocks[index]) { |
| item = buckets[index]; |
| |
| for (; item != null; item = item.next) { |
| if ( (hashCode == item.hashCode) && eq(key, item.key) ) { |
| break; |
| } |
| } |
| |
| // update the stats in line |
| if (item != null) { |
| value = item.getValue(); |
| itemAccessed(item); |
| } |
| else |
| value = loadValue(key, hashCode); |
| } |
| |
| if (item != null) |
| incrementHitCount(); |
| else |
| incrementMissCount(); |
| |
| return value; |
| } |
| |
| /** |
| * check if the cache contains the item at the key |
| * @param key lookup key |
| * @returns true if there is an item stored at the key; false if not. |
| */ |
| public boolean contains(Object key) { |
| return (get(key) != null); |
| } |
| |
| /** |
| * get all the items stored at the key. |
| * @param key lookup key |
| * @returns an Iterator over the items with the given key. |
| */ |
| public Iterator getAll(Object key) { |
| int hashCode = hash(key); |
| int index = getIndex(hashCode); |
| |
| ArrayList valueList = new ArrayList(entryCount); |
| synchronized (bucketLocks[index]) { |
| CacheItem item = buckets[index]; |
| |
| for (; item != null; item = item.next) { |
| if ( (hashCode == item.hashCode) && eq(key, item.key) ) { |
| incrementHitCount(); |
| valueList.add(item.getValue()); |
| } |
| } |
| |
| } |
| |
| return valueList.iterator(); |
| } |
| |
| /** |
| * get an Iterator for the keys stored in the cache |
| * @returns an Iterator |
| */ |
| public Iterator keys() { |
| ArrayList keyList = new ArrayList(entryCount); |
| |
| for (int index=0; index < maxBuckets; index++) { |
| synchronized (bucketLocks[index]) { |
| for (CacheItem item = buckets[index]; item != null; |
| item = item.next) { |
| keyList.add(item.key); |
| } |
| } |
| } |
| |
| return keyList.iterator(); |
| } |
| |
| /** |
| * get an Enumeration for the keys stored in the cache |
| * @returns an Enumeration |
| * XXX: should use Iterator which is based on Collections |
| */ |
| public Enumeration elements() { |
| Vector keyList = new Vector(); |
| |
| for (int index=0; index < maxBuckets; index++) { |
| synchronized (bucketLocks[index]) { |
| for (CacheItem item = buckets[index]; item != null; |
| item = item.next) { |
| keyList.addElement(item.key); |
| } |
| } |
| } |
| |
| return keyList.elements(); |
| } |
| |
| /** |
| * get an Iterator for the values stored in the cache |
| * @returns an Iterator |
| */ |
| public Iterator values() { |
| ArrayList valueList = new ArrayList(entryCount); |
| |
| for (int index=0; index < maxBuckets; index++) { |
| synchronized (bucketLocks[index]) { |
| for (CacheItem item = buckets[index]; item != null; |
| item = item.next) { |
| valueList.add(item.value); |
| } |
| } |
| } |
| |
| return valueList.iterator(); |
| } |
| |
| /** |
| /** |
| * cache the given value at the specified key and return previous value |
| * @param key lookup key |
| * @param object item value to be stored |
| * @returns the previous item stored at the key; null if not found. |
| */ |
| public Object put(Object key, Object value) { |
| int hashCode = hash(key); |
| |
| return _put(hashCode, key, value, -1, false); |
| } |
| |
| /** |
| * cache the given value at the specified key and return previous value |
| * @param key lookup key |
| * @param object item value to be stored |
| * @param size in bytes of the value being cached |
| * @returns the previous item stored at the key; null if not found. |
| */ |
| public Object put(Object key, Object value, int size) { |
| int hashCode = hash(key); |
| |
| return _put(hashCode, key, value, size, false); |
| } |
| |
| /** |
| * add the given value to the cache at the specified key |
| * @param key lookup key |
| * @param object item value to be stored |
| */ |
| public void add(Object key, Object value) { |
| int hashCode = hash(key); |
| |
| _put(hashCode, key, value, -1, true); |
| } |
| |
| /** |
| * add the given value with specified size to the cache at specified key |
| * @param key lookup key |
| * @param object item value to be stored |
| * @param size in bytes of the value being added |
| * |
| * This function is suitable for multi-valued keys. |
| */ |
| public void add(Object key, Object value, int size) { |
| int hashCode = hash(key); |
| |
| _put(hashCode, key, value, size, true); |
| } |
| |
| /** |
| * cache the given value at the specified key and return previous value |
| * @param hashCode previously computed hashCode for the key |
| * @param key lookup key |
| * @param object item value to be stored |
| * @param size in bytes of the value being cached |
| * @param addValue treate this operation to add (default is to replace) |
| * @returns the previous item stored at the key; null if not found. |
| * |
| * Note: This can be used just to refresh the cached item as well, altho |
| * it may call trimCache() if the cache reached its threshold -- which is |
| * is probably not very intuitive. |
| */ |
| protected Object _put(int hashCode, Object key, |
| Object value, int size, boolean addValue) { |
| int index = getIndex(hashCode); |
| |
| CacheItem item, newItem = null, oldItem = null, overflow = null; |
| Object oldValue; |
| int oldSize = 0; |
| |
| // lookup the item |
| synchronized (bucketLocks[index]) { |
| for (item = buckets[index]; item != null; item = item.next) { |
| if ((hashCode == item.hashCode) && eq(key, item.key)) { |
| |
| oldItem = item; |
| break; |
| } |
| } |
| |
| // if there was no item in the cache, insert the given item |
| if (addValue || oldItem == null) { |
| newItem = createItem(hashCode, key, value, size); |
| |
| // add the item at the head of the bucket list |
| newItem.next = buckets[index]; |
| buckets[index] = newItem; |
| |
| oldValue = null; |
| overflow = itemAdded(newItem); |
| } |
| else { |
| oldSize = oldItem.getSize(); |
| oldValue = oldItem.refreshValue(value, size); |
| itemRefreshed(oldItem, oldSize); |
| } |
| } |
| |
| if (newItem != null) { |
| incrementEntryCount(); |
| incrementAddCount(); |
| |
| // make sure we are are not crossing the threshold |
| if (overflow != null) |
| trimItem(overflow); |
| } |
| else |
| incrementRefreshCount(); |
| |
| return oldValue; |
| } |
| |
| /** |
| * remove the item stored at the key. |
| * @param key lookup key |
| * @returns the item stored at the key; null if not found. |
| */ |
| public Object remove(Object key) { |
| int hashCode = hash(key); |
| |
| Object retVal = null; |
| CacheItem removed = _remove( hashCode, key, null); |
| |
| if (removed != null) |
| retVal = removed.getValue(); |
| return retVal; |
| } |
| |
| /** |
| * remove the item stored at the key. |
| * @param hashCode a precomputed hashCode |
| * @param key lookup key |
| * @returns the item stored at the key; null if not found. |
| */ |
| public Object remove(int hashCode, Object key) { |
| Object retVal = null; |
| CacheItem removed = _remove( hashCode, key, null); |
| |
| if (removed != null) |
| retVal = removed.getValue(); |
| return retVal; |
| } |
| |
| /** |
| * remove the given value stored at the key; value-specific removals. |
| * @param key lookup key |
| * @param value to match (for a multi-valued keys) |
| * @returns the item stored at the key; null if not found. |
| */ |
| public Object remove(Object key, Object value) { |
| int hashCode = hash(key); |
| |
| Object retVal = null; |
| CacheItem removed = _remove( hashCode, key, value); |
| |
| if (removed != null) |
| retVal = removed.getValue(); |
| return retVal; |
| } |
| |
| /** |
| * remove the item stored at the key. |
| * @param hashCode a precomputed hashCode |
| * @param key lookup key |
| * @param value of the item to be matched |
| * @returns the item stored at the key; null if not found. |
| */ |
| protected CacheItem _remove(int hashCode, Object key, Object value) { |
| int index = getIndex(hashCode); |
| |
| CacheItem prev = null, item = null; |
| |
| synchronized (bucketLocks[index]) { |
| for (item = buckets[index]; item != null; item = item.next) { |
| if (hashCode == item.hashCode && key.equals(item.key)) { |
| |
| if (value == null || value == item.value) { |
| |
| if (prev == null) { |
| buckets[index] = item.next; |
| } else { |
| prev.next = item.next; |
| } |
| item.next = null; |
| |
| itemRemoved(item); |
| break; |
| } |
| } |
| prev = item; |
| } |
| } |
| |
| if (item != null) { |
| decrementEntryCount(); |
| incrementRemovalCount(); |
| |
| incrementHitCount(); |
| } else |
| incrementMissCount(); |
| |
| return item; |
| } |
| |
| /** |
| * remove the item stored at the key. |
| * @param item CacheItem to be removed |
| * @return the item stored at the key; null if not found. |
| */ |
| protected CacheItem _removeItem(CacheItem ritem) { |
| |
| int index = getIndex(ritem.hashCode); |
| |
| CacheItem prev = null, item = null; |
| |
| synchronized (bucketLocks[index]) { |
| for (item = buckets[index]; item != null; item = item.next) { |
| if (item == ritem) { |
| if (prev == null) { |
| buckets[index] = item.next; |
| } else { |
| prev.next = item.next; |
| } |
| item.next = null; |
| break; |
| } |
| prev = item; |
| } |
| } |
| |
| if (item != null) { |
| decrementEntryCount(); |
| } |
| |
| return item; |
| } |
| |
| /** |
| * remove all the item with the given key. |
| * @param key lookup key |
| */ |
| public void removeAll(Object key) { |
| int hashCode = hash(key); |
| int index = getIndex(hashCode); |
| |
| CacheItem prev = null, item = null; |
| ArrayList items = new ArrayList(entryCount); |
| |
| synchronized (bucketLocks[index]) { |
| for (item = buckets[index]; item != null; |
| item = item.next) { |
| if (hashCode == item.hashCode && key.equals(item.key)) { |
| if (prev == null) { |
| buckets[index] = item.next; |
| } else { |
| prev.next = item.next; |
| } |
| item.next = null; |
| |
| decrementEntryCount(); |
| incrementRemovalCount(); |
| |
| items.add(item); |
| } |
| prev = item; |
| } |
| } |
| |
| // notify subclasses |
| for (int i = 0; i < items.size(); i++) { |
| itemRemoved((CacheItem)items.get(i)); |
| } |
| } |
| |
| /** |
| * trim the item from the cache and notify listeners |
| * @param item to be trimmed |
| */ |
| protected void trimItem(CacheItem item) { |
| CacheItem removed = _removeItem(item); |
| |
| if (removed != null) { |
| for (int i = 0; i < listeners.size(); i++) { |
| CacheListener listener = (CacheListener) listeners.get(i); |
| listener.trimEvent(removed.key, removed.value); |
| } |
| } |
| } |
| |
| /** |
| * wait for a refresh on the object associated with the key |
| * @param key lookup key |
| * @returns true on successful notification, or false if there is |
| * no thread refreshing this entry. |
| */ |
| public boolean waitRefresh(int index) { |
| synchronized (bucketLocks[index]) { |
| if (refreshFlags[index] == false) { |
| refreshFlags[index] = true; |
| return false; |
| } |
| |
| // wait till refresh is finished |
| try { |
| while(refreshFlags[index]) |
| bucketLocks[index].wait(); |
| } catch (InterruptedException ie) {} |
| } |
| return true; |
| } |
| |
| /** |
| * notify threads waiting for a refresh on the object associated with the key |
| * @param key lookup key |
| */ |
| public void notifyRefresh(int index) { |
| // notify other threads waiting for refresh |
| synchronized (bucketLocks[index]) { |
| refreshFlags[index] = false; |
| bucketLocks[index].notifyAll(); |
| } |
| } |
| |
| /** |
| * clear all the entries from the cache. |
| * @returns the number of entries cleared from the cache |
| */ |
| public int clear() { |
| |
| CacheItem item=null; |
| int count = 0; |
| |
| for (int index = 0; index < maxBuckets; index++) { |
| synchronized (bucketLocks[index]) { |
| for (item = buckets[index]; item != null; |
| item = item.next) { |
| item.next = null; |
| |
| count++; |
| decrementEntryCount(); |
| itemRemoved(item); |
| |
| if (entryCount == 0) |
| break; |
| } |
| buckets[index] = null; |
| } |
| } |
| |
| |
| return count; |
| } |
| |
| /** |
| * trim the expired entries from the cache. |
| * @param maxCount maximum number of invalid entries to trim |
| * specify Integer.MAX_VALUE to trim all timedout entries |
| * |
| * This call is to be scheduled by a thread managed by the container. |
| */ |
| public void trimExpiredEntries(int maxCount) {} |
| |
| /** |
| * get the number of entries in the cache |
| * @return the number of entries the cache currently holds |
| */ |
| public int getEntryCount() { |
| return entryCount; |
| } |
| |
| /*** methods for monitoring the cache ***/ |
| |
| /** |
| * is this cache empty? |
| * @returns true if the cache is empty; false otherwise. |
| */ |
| public boolean isEmpty() { |
| return (entryCount == 0); |
| } |
| |
| /** |
| * synchronized counter updates |
| */ |
| protected final void incrementEntryCount() { |
| synchronized(entryCountLk) { |
| entryCount++; |
| } |
| } |
| |
| protected final void decrementEntryCount() { |
| synchronized(entryCountLk) { |
| entryCount--; |
| } |
| } |
| |
| protected final void incrementHitCount() { |
| synchronized (hitCountLk) { |
| hitCount++; |
| } |
| } |
| |
| protected final void incrementMissCount() { |
| synchronized (missCountLk) { |
| missCount++; |
| } |
| } |
| |
| protected final void incrementRemovalCount() { |
| synchronized (removalCountLk) { |
| removalCount++; |
| } |
| } |
| |
| protected final void incrementRefreshCount() { |
| synchronized (refreshCountLk) { |
| refreshCount++; |
| } |
| } |
| |
| protected final void incrementAddCount() { |
| synchronized (addCountLk) { |
| addCount++; |
| } |
| } |
| |
| protected final void incrementOverflowCount() { |
| synchronized (overflowCountLk) { |
| overflowCount++; |
| } |
| } |
| |
| /** |
| * get generic stats from subclasses |
| */ |
| |
| /** |
| * get the desired statistic counter |
| * @param key to corresponding stat |
| * @return an Object corresponding to the stat |
| * See also: Constant.java for the key |
| */ |
| public Object getStatByName(String key) { |
| Object stat = null; |
| |
| if (key == null) |
| return null; |
| |
| if (key.equals(Constants.STAT_BASECACHE_MAX_ENTRIES)) |
| stat = Integer.valueOf(maxEntries); |
| else if (key.equals(Constants.STAT_BASECACHE_THRESHOLD)) |
| stat = Integer.valueOf(threshold); |
| else if (key.equals(Constants.STAT_BASECACHE_TABLE_SIZE)) |
| stat = Integer.valueOf(maxBuckets); |
| else if (key.equals(Constants.STAT_BASECACHE_ENTRY_COUNT)) |
| stat = Integer.valueOf(entryCount); |
| else if (key.equals(Constants.STAT_BASECACHE_HIT_COUNT)) |
| stat = Integer.valueOf(hitCount); |
| else if (key.equals(Constants.STAT_BASECACHE_MISS_COUNT)) |
| stat = Integer.valueOf(missCount); |
| else if (key.equals(Constants.STAT_BASECACHE_REMOVAL_COUNT)) |
| stat = Integer.valueOf(removalCount); |
| else if (key.equals(Constants.STAT_BASECACHE_REFRESH_COUNT)) |
| stat = Integer.valueOf(refreshCount); |
| else if (key.equals(Constants.STAT_BASECACHE_OVERFLOW_COUNT)) |
| stat = Integer.valueOf(overflowCount); |
| else if (key.equals(Constants.STAT_BASECACHE_ADD_COUNT)) |
| stat = Integer.valueOf(addCount); |
| |
| return stat; |
| } |
| |
| /** |
| * get the stats snapshot |
| * @return a Map of stats |
| * See also: Constant.java for the keys |
| */ |
| public Map getStats() { |
| HashMap stats = new HashMap(); |
| |
| stats.put(Constants.STAT_BASECACHE_MAX_ENTRIES, |
| Integer.valueOf(maxEntries)); |
| stats.put(Constants.STAT_BASECACHE_THRESHOLD, |
| Integer.valueOf(threshold)); |
| stats.put(Constants.STAT_BASECACHE_TABLE_SIZE, |
| Integer.valueOf(maxBuckets)); |
| stats.put(Constants.STAT_BASECACHE_ENTRY_COUNT, |
| Integer.valueOf(entryCount)); |
| stats.put(Constants.STAT_BASECACHE_HIT_COUNT, |
| Integer.valueOf(hitCount)); |
| stats.put(Constants.STAT_BASECACHE_MISS_COUNT, |
| Integer.valueOf(missCount)); |
| stats.put(Constants.STAT_BASECACHE_REMOVAL_COUNT, |
| Integer.valueOf(removalCount)); |
| stats.put(Constants.STAT_BASECACHE_REFRESH_COUNT, |
| Integer.valueOf(refreshCount)); |
| stats.put(Constants.STAT_BASECACHE_OVERFLOW_COUNT, |
| Integer.valueOf(overflowCount)); |
| stats.put(Constants.STAT_BASECACHE_ADD_COUNT, |
| Integer.valueOf(addCount)); |
| |
| return stats; |
| } |
| |
| /** |
| * Sets all references to null. This method should be called |
| * at the end of this object's life cycle. |
| */ |
| public void destroy() { |
| if ((listeners != null) && (buckets != null) && (bucketLocks != null)) { |
| clear(); |
| listeners.clear(); |
| } |
| |
| entryCountLk = null; |
| hitCountLk = null; |
| missCountLk = null; |
| removalCountLk = null; |
| refreshCountLk = null; |
| addCountLk = null; |
| overflowCountLk = null; |
| buckets = null; |
| bucketLocks = null; |
| refreshFlags = null; |
| listeners = null; |
| } |
| |
| /** |
| * clear the stats |
| */ |
| public void clearStats() { |
| hitCount = 0; |
| missCount = 0; |
| removalCount = 0; |
| refreshCount = 0; |
| overflowCount = 0; |
| addCount = 0; |
| } |
| |
| /** default CacheItem class implementation ***/ |
| public static class CacheItem { |
| int hashCode; |
| Object key; |
| Object value; |
| int size; |
| |
| CacheItem next; |
| |
| protected CacheItem(int hashCode, Object key, Object value, int size) { |
| this.hashCode = hashCode; |
| this.key = key; |
| this.value = value; |
| this.size = size; |
| } |
| |
| /** |
| * get the item's hashCode |
| */ |
| public int getHashCode() { |
| return hashCode; |
| } |
| |
| /** |
| * get the item's key |
| */ |
| public Object getKey() { |
| return key; |
| } |
| |
| /** |
| * get the item's next reference |
| */ |
| public CacheItem getNext() { |
| return next; |
| } |
| |
| /** |
| * set the item's next reference |
| */ |
| public void setNext(CacheItem next) { |
| this.next = next; |
| } |
| |
| /** |
| * get the item's value |
| */ |
| public Object getValue() { |
| return value; |
| } |
| |
| /** |
| * @return size of the entry in bytes |
| * a value of -1 indicates unknown size |
| */ |
| public int getSize() { |
| return size; |
| } |
| |
| /** |
| * refresh the item's value |
| * @param value value to be updated |
| * @param newSize of the field |
| */ |
| protected Object refreshValue(Object value, int newSize) { |
| Object oldValue = this.value; |
| this.value = value; |
| this.size = newSize; |
| |
| return oldValue; |
| } |
| |
| public String toString() { |
| return "key: " + key + "; value: " + value.toString(); |
| } |
| } |
| } |