/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
/**
* BaseCache
* Generic in-memory, abstract cache
*/
/**
* The resource bundle containing the localized message strings.
*/
// maximum number of entries this cache may ever hold
int maxEntries;
// the number of cache entries in this cache
protected int entryCount;
/** threshold for the cache; once the threshold is reached
* entries are removed to accomodate newer inserts
*/
// the number of cache hits
private int hitCount;
// the number of cache misses
private int missCount;
// the number of cache item removals
private int removalCount;
// the number of cache item refreshes
private int refreshCount;
// the number of times an item was added to cache
private int addCount;
// the number of times the cache overflowed
private int overflowCount;
// table size
protected int maxBuckets;
// cache entries hash table
// bucket-wide locks
// boolean status and locks for item thread-safe refreshes
protected boolean[] refreshFlags;
/**
* 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
*/
}
/**
* 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
*/
// web container logger
if (maxEntries <= 0) {
throw new IllegalArgumentException(msg);
}
if (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) {
}
// create the cache and the bucket locks
entryCount = 0;
refreshFlags = new boolean[maxBuckets];
for (int i=0; i<maxBuckets; i++) {
bucketLocks[i] = new Object();
refreshFlags[i] = false;
}
}
/**
* add the cache module listener
* @param listener <code>CacheListener</code> implementation
*/
}
/**
* Returns a hash code for non-null Object x.
* @See also <code>HashMap</code>
*/
int h = x.hashCode();
return h - (h << 7); // i.e., -127 * h
}
/**
* Check for equality of non-null reference x and possibly-null y.
*/
return x == y || x.equals(y);
}
/**
* increase the threshold
*/
protected void handleOverflow() {
// just double the threshold; this may degenerate the cache.
}
/**
* 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.
*/
if (isThresholdReached()) {
}
return null;
}
/**
* this item is accessed
* @param item <code>CacheItem</code> accessed
*
* Cache bucket is already synchronized by the caller
*/
/**
* 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
*/
/**
* 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
*/
/**
* 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
*/
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.
*/
}
/**
* 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
*/
}
/**
* get the index of the item given a key
* @param key of the entry
* @return the index to be used in the cache
*/
}
/**
* get the item stored at the key.
* @param key lookup key
* @returns the item stored at the key; null if not found.
*/
}
/**
* 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.
*/
synchronized (bucketLocks[index]) {
break;
}
}
// update the stats in line
}
else
}
else
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.
*/
}
/**
* get all the items stored at the key.
* @param key lookup key
* @returns an Iterator over the items with the given key.
*/
synchronized (bucketLocks[index]) {
}
}
}
}
/**
* get an Iterator for the keys stored in the cache
* @returns an Iterator
*/
synchronized (bucketLocks[index]) {
}
}
}
}
/**
* get an Enumeration for the keys stored in the cache
* @returns an Enumeration
* XXX: should use Iterator which is based on Collections
*/
synchronized (bucketLocks[index]) {
}
}
}
}
/**
* get an Iterator for the values stored in the cache
* @returns an Iterator
*/
synchronized (bucketLocks[index]) {
}
}
}
}
/**
/**
* 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.
*/
}
/**
* 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.
*/
}
/**
* add the given value to the cache at the specified key
* @param key lookup key
* @param object item value to be stored
*/
}
/**
* 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.
*/
}
/**
* 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.
*/
int oldSize = 0;
// lookup the item
synchronized (bucketLocks[index]) {
break;
}
}
// if there was no item in the cache, insert the given item
// add the item at the head of the bucket list
}
else {
}
}
// make sure we are are not crossing the threshold
}
else
return oldValue;
}
/**
* remove the item stored at the key.
* @param key lookup key
* @returns the item stored at the key; null if not found.
*/
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.
*/
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.
*/
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.
*/
synchronized (bucketLocks[index]) {
} else {
}
break;
}
}
}
}
} else
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.
*/
synchronized (bucketLocks[index]) {
} else {
}
break;
}
}
}
}
return item;
}
/**
* remove all the item with the given key.
* @param key lookup key
*/
synchronized (bucketLocks[index]) {
} else {
}
}
}
}
// notify subclasses
}
}
/**
* trim the item from the cache and notify listeners
* @param item to be trimmed
*/
}
}
}
/**
* 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.
*/
synchronized (bucketLocks[index]) {
if (refreshFlags[index] == false) {
refreshFlags[index] = true;
return false;
}
// wait till refresh is finished
try {
} catch (InterruptedException ie) {}
}
return true;
}
/**
* notify threads waiting for a refresh on the object associated with the key
* @param key lookup key
*/
// notify other threads waiting for refresh
synchronized (bucketLocks[index]) {
refreshFlags[index] = false;
}
}
/**
* clear all the entries from the cache.
* @returns the number of entries cleared from the cache
*/
public int clear() {
int count = 0;
synchronized (bucketLocks[index]) {
count++;
if (entryCount == 0)
break;
}
}
}
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.
*/
/**
* 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) {
}
}
/**
* 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
*/
return null;
return stat;
}
/**
* get the stats snapshot
* @return a Map of stats
* See also: Constant.java for the keys
*/
return stats;
}
/**
* Sets all references to null. This method should be called
* at the end of this object's life cycle.
*/
public void destroy() {
clear();
}
entryCountLk = null;
hitCountLk = null;
missCountLk = null;
addCountLk = null;
bucketLocks = null;
refreshFlags = null;
}
/**
* clear the stats
*/
public void clearStats() {
hitCount = 0;
missCount = 0;
removalCount = 0;
refreshCount = 0;
overflowCount = 0;
addCount = 0;
}
/** default CacheItem class implementation ***/
protected static class CacheItem {
int hashCode;
int size;
}
/**
* get the item's hashCode
*/
protected int getHashCode() {
return hashCode;
}
/**
* get the item's key
*/
return key;
}
/**
* get the item's value
*/
return value;
}
/**
* @return size of the entry in bytes
* a value of -1 indicates unknown size
*/
protected int getSize() {
return size;
}
/**
* refresh the item's value
* @param value value to be updated
* @param newSize of the field
*/
return oldValue;
}
}
}
}