/**
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2005 Sun Microsystems Inc. All Rights Reserved
*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the License at
* See the License for the specific language governing
* permission and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at opensso/legal/CDDLv1.0.txt.
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* $Id: Cache.java,v 1.4 2008/06/27 20:56:21 arviranga Exp $
*
* Portions Copyrighted 2011-2015 ForgeRock AS.
*/
// IMPORTANT NOTE: The cache has be implemented by modifing the existing
// java.util.Hashtable code. Its has added functionality of a built in
// replacement strategy which in this case is based on Least Recently Used
// concept. The synchronization functionality that exists in a Hashtable has
// been removed for better performance
/**
* IMPORTANT NOTE: The cache has be implemented by modifing the existing
* java.util.Hashtable code. Its has added functionality of a built in
* replacement strategy which in this case is based on Least Recently Used
* concept. The synchronization functionality that exists in a Hashtable has
* been removed to improve performance.
* <p>
*
* The class <code>Cache</code> provides the functionality to cache objects
* based on their usage. The maximum size of the cache can be set using the
* constructor. If the maximum size is not set the default cache size for
* <code>Cache</code> will be obtained from the config file <code>???</code>
* file, defined using the key <code>???</code>. The
* object that needs to be cached can be supplied to the instance of this class
* using the put method. The object can be obtained by invoking the get method
* on the instance. Each object that is cached is tracked based on its usage.
* If a new object needs to added to the cache and the maximum size limit of
* the cache is reached, then the least recently used object is replaced.
*
* This class implements a Cache, which maps keys to values. Any
* non-<code>null</code> object can be used as a key or as a value. <p>
*
* To successfully store and retrieve objects from a Cache, the
* objects used as keys must implement the <code>hashCode</code>
* method and the <code>equals</code> method. <p>
*
* An instance of <code>Cache</code> has two parameters that affect its
* performance: <i>capacity</i> and <i>load factor</i>. The
* <i>capacity</i> is the number of <i>buckets</i> in the hash table, and the
* <i>capacity</i> is simply the capacity at the time the hash table
* is created. Note that the hash table is <i>open</i>: in the case a "hash
* collision", a single bucket stores multiple entries, which must be searched
* sequentially. The <i>load factor</i> is a measure of how full the hash
* table is allowed to get before its capacity is automatically increased.
* When the number of entries in the Cache exceeds the product of the load
* factor and the current capacity, the capacity is increased by calling the
* <code>rehash</code> method.<p>
*
* Generally, the default load factor (.75) offers a good tradeoff between
* time and space costs. Higher values decrease the space overhead but
* increase the time cost to look up an entry (which is reflected in most
* <tt>Cache</tt> operations, including <tt>get</tt> and <tt>put</tt>).<p>
*
* The capacity controls a tradeoff between wasted space and the
* need for <code>rehash</code> operations, which are time-consuming.
* No <code>rehash</code> operations will <i>ever</i> occur if the
* capacity is greater than the maximum number of entries the
* <tt>Cache</tt> will contain divided by its load factor. However,
* setting the capacity too high can waste space.<p>
*
* If many entries are to be made into a <code>Cache</code>,
* creating it with a sufficiently large capacity may allow the
* entries to be inserted more efficiently than letting it perform
* automatic rehashing as needed to grow the table. <p>
*
* This class has been retrofitted to implement Map, so that it becomes a
* part of Java's collection framework.
*
* The Iterators returned by the iterator and listIterator methods
* of the Collections returned by all of Cache's "collection view methods"
* are <em>fail-fast</em>: if the Cache is structurally modified
* at any time after the Iterator is created, in any way except through the
* Iterator's own remove or add methods, the Iterator will throw a
* ConcurrentModificationException. Thus, in the face of concurrent
* modification, the Iterator fails quickly and cleanly, rather than risking
* arbitrary, non-deterministic behavior at an undetermined time in the future.
* The Enumerations returned by Cache's keys and values methods are
* <em>not</em> fail-fast.
*
* @see Object#equals(java.lang.Object)
* @see Object#hashCode()
* @see Collection
* @see Map
* @since JDK1.0
*/
// Default Cache size.
/**
* The hash table data.
*/
private transient int maxSize;
/**
* A circular doubly linked list which maintains the entry list in the cache
* (table) based on their usage. The listed is updated everytime an entry is
* accessed the table such that the most recent entry accessed will be
* placed at the end of the list. This way, the least recently used entry
* will be always at the front of the list.
*/
/**
* The total number of entries in the hash table.
*/
private transient int count;
/**
* The table is rehashed when its size exceeds this threshold. (The value of
* this field is (int)(capacity * loadFactor).)
*
* @serial
*/
private int threshold;
/**
* The load factor for the Cache.
*
* @serial
*/
private float loadFactor;
/**
* The number of times this Cache has been structurally modified Structural
* modifications are those that change the number of entries in the Cache or
* otherwise modify its internal structure (e.g., rehash). This field is
* used to make iterators on Collection-views of the Cache fail-fast. (See
* ConcurrentModificationException).
*/
/**
* Constructs a new, empty Cache with the specified capacity and the
* specified load factor.
*
* @param capacity
* the capacity of the Cache.
* @param loadFactor
* the load factor of the Cache.
* @exception IllegalArgumentException
* if the capacity is less than zero, or if the load factor
* is nonpositive.
*/
if (capacity < 0)
if (loadFactor <= 0)
if (capacity == 0)
capacity = 1;
this.loadFactor = loadFactor;
lruTracker = new LRUList();
}
/**
* Constructs a new, empty Cache with the specified capacity and default
* load factor, which is <tt>0.75</tt>.
*
* @param capacity
* the capacity of the Cache.
* @exception IllegalArgumentException
* if the capacity is less than zero.
*/
this(capacity, 0.75f);
lruTracker = new LRUList();
}
/**
* Constructs a new, empty Cache with a default capacity and load factor,
* which is <tt>0.75</tt>.
*/
public Cache() {
// Obtain the cache size
this(DEFAULT_CACHE_SIZE, 0.75f);
lruTracker = new LRUList();
}
/**
* Returns the number of keys in this Cache.
*
* @return the number of keys in this Cache.
*/
public int size() {
return count;
}
/**
* Tests if this Cache maps no keys to values.
*
* @return <code>true</code> if this Cache maps no keys to values;
* <code>false</code> otherwise.
*/
public boolean isEmpty() {
return count == 0;
}
/**
* Returns an enumeration of the keys in this Cache.
*
* @return an enumeration of the keys in this Cache.
* @see Enumeration
* @see #elements()
* @see #keySet()
* @see Map
*/
return new Enumerator(KEYS, false);
}
/**
* Returns an enumeration of the values in this Cache. Use the Enumeration
* methods on the returned object to fetch the elements sequentially.
*
* @return an enumeration of the values in this Cache.
* @see java.util.Enumeration
* @see #keys()
* @see #values()
* @see Map
*/
return new Enumerator(VALUES, false);
}
/**
* Tests if some key maps into the specified value in this Cache. This
* operation is more expensive than the <code>containsKey</code> method.
* <p>
*
* Note that this method is identical in functionality to containsValue,
* (which is part of the Map interface in the PolicyCollections framework).
*
* @param value
* a value to search for.
* @return <code>true</code> if and only if some key maps to the
* <code>value</code> argument in this Cache as determined by the
* <tt>equals</tt> method; <code>false</code> otherwise.
* @exception NullPointerException
* if the value is <code>null</code>.
* @see #containsKey(Object)
* @see #containsValue(Object)
* @see Map
*/
throw new NullPointerException();
}
return true;
}
}
}
return false;
}
/**
* Returns true if this Cache maps one or more keys to this value.
* <p>
*
* Note that this method is identical in functionality to contains (which
* predates the Map interface).
*
* @param value
* value whose presence in this Cache is to be tested.
* @see Map
* @since JDK1.2
*/
}
/**
* Tests if the specified object is a key in this Cache.
*
* @param key
* possible key.
* @return <code>true</code> if and only if the specified object is a key
* in this Cache, as determined by the <tt>equals</tt> method;
* <code>false</code> otherwise.
* @see #contains(Object)
*/
return true;
}
}
return false;
}
/**
* Returns the value to which the specified key is mapped in this Cache.
*
* @param key
* a key in the Cache.
* @return the value to which the key is mapped in this Cache;
* <code>null</code> if the key is not mapped to any value in this
* Cache.
* @see #put(Object, Object)
*/
// Since the entry is accessed, move it to the end of the list
return e.value;
}
}
return null;
}
/**
* Increases the capacity of and internally reorganizes this Cache, in order
* to accommodate and access its entries more efficiently. This method is
* called automatically when the number of keys in the Cache exceeds this
* Cache's capacity and load factor.
*/
protected void rehash() {
modCount++;
for (int i = oldCapacity; i-- > 0;) {
}
}
}
/**
* Maps the specified <code>key</code> to the specified <code>value</code>
* in this Cache. Neither the key nor the value can be <code>null</code>.
* If the cache is full to its capacity, then the least recently used entry
* in the cache will be replaced.
* <p>
*
*
* The value can be retrieved by calling the <code>get</code> method with
* a key that is equal to the original key.
*
* @param key
* the Cache key.
* @param value
* the value.
* @return the previous value of the specified key in this Cache, or
* <code>null</code> if it did not have one.
* @exception NullPointerException
* if the key or value is <code>null</code>.
* @see Object#equals(Object)
* @see #get(Object)
*/
// Make sure the value is not null
throw new NullPointerException();
// Makes sure the key is not already in the Cache.
// Since the entry is already present move it to the end of the
// list
return old;
}
}
// new one
// Get the least recently used entry
e = lruTracker.getFirst();
// Remove the entry from the table to accomidate new entry
adjustEntry(e.key);
// Modify the values of this entry to reflect new entry
// (Avoiding the creation of a new entry object)
} else {
modCount++;
count++;
// Creates the new entry.
lruTracker.addLast(e);
}
return null;
}
/**
* This method adjusts the table by removing the entry corresponding to key
* from the table.
*/
// NOTE: The remove() method cannot be used for this functionality as the
// counter and modCount value should not be changed in this context
{
} else {
}
}
}
}
/**
* Removes the key (and its corresponding value) from this Cache. This
* method does nothing if the key is not in the Cache.
*
* @param key
* the key that needs to be removed.
* @return the value to which the key had been mapped in this Cache, or
* <code>null</code> if the key did not have a mapping.
*/
{
modCount++;
} else {
}
count--;
lruTracker.remove(e);
return oldValue;
}
}
return null;
}
/**
* Copies all of the mappings from the specified Map to this Hashtable These
* mappings will replace any mappings that this Hashtable had for any of the
* keys currently in the specified Map.
*
* @since JDK1.2
*/
while (i.hasNext()) {
}
}
/**
* Clears this Cache so that it contains no keys.
*/
public synchronized void clear() {
modCount++;
// Clear the LRU Tracker
lruTracker.clear();
count = 0;
}
/**
* Returns a string representation of this <tt>Cache</tt> object in the
* form of a set of entries, enclosed in braces and separated by the ASCII
* characters "<tt>, </tt>" (comma and space). Each entry is
* rendered as the key, an equals sign <tt>=</tt>, and the associated
* element, where the <tt>toString</tt> method is used to convert the key
* and element to strings.
* <p>
* Overrides to <tt>toString</tt> method of <tt>Object</tt>.
*
* @return a string representation of this Cache.
*/
for (int i = 0; i <= max; i++) {
if (i < max)
}
}
// ensure LRU list length is the same as the number of elements in the
// cache
}
return retStr;
}
// Views
/**
* Returns a Set view of the keys contained in this Cache. The Set is backed
* by the Cache, so changes to the Cache are reflected in the Set, and
* vice-versa. The Set supports element removal (which removes the
* corresponding entry from the Cache), but not element addition.
*
* @since JDK1.2
*/
return keySet;
}
return new Enumerator(KEYS, true);
}
public int size() {
return count;
}
return containsKey(o);
}
}
public void clear() {
}
}
/**
* Returns a Set view of the entries contained in this Cache. Each element
* in this collection is a Map.Entry. The Set is backed by the Cache, so
* changes to the Cache are reflected in the Set, and vice-versa. The Set
* supports element removal (which removes the corresponding entry from the
* Cache), but not element addition.
*
* @see java.util.Map.Entry
* @since JDK1.2
*/
return entrySet;
}
return new Enumerator(ENTRIES, true);
}
return false;
return true;
return false;
}
return false;
{
modCount++;
else
count--;
return true;
}
}
return false;
}
public int size() {
return count;
}
public void clear() {
}
}
/**
* Returns a Collection view of the values contained in this Cache. The
* Collection is backed by the Cache, so changes to the Cache are reflected
* in the Collection, and vice-versa. The Collection supports element
* removal (which removes the corresponding entry from the Cache), but not
* element addition.
*
* @since JDK1.2
*/
return values;
}
return new Enumerator(VALUES, true);
}
public int size() {
return count;
}
return containsValue(o);
}
public void clear() {
}
}
// Comparison and hashing
/**
* Compares the specified Object with this Map for equality, as per the
* definition in the Map interface.
*
* @return true if the specified Object is equal to this Map.
* @see Map#equals(Object)
* @since JDK1.2
*/
if (o == this)
return true;
if (!(o instanceof Map))
return false;
return false;
while (i.hasNext()) {
return false;
} else {
return false;
}
}
return true;
}
/**
* Returns the hash code value for this Map as per the definition in the Map
* interface.
*
* @see Map#hashCode()
* @since JDK1.2
*/
public synchronized int hashCode() {
int h = 0;
while (i.hasNext())
return h;
}
/**
* Class which is used to create and maintain a circular doubly linked with
* functionality to add and delete Entry objects.
*/
private class LRUList {
int size;
protected LRUList() {
}
header = h;
}
// Method to add an entry to the end (last) of the list
} else {
}
size++;
}
// Method to remove an entry from the list
if (e == null)
return;
size--;
}
// Method to get the first entry in the list
}
// Method to get the last entry in the list
}
// Method to remove the first entry from the list
return first;
}
// Method to move an entry to the end of the list
remove(e);
addLast(e);
}
protected int length() {
return (size);
}
protected void clear() {
}
}
/**
* Cache collision list.
*/
int hash;
// Fields used to maintain a sorted list
}
}
// Do not change the lru pointers. b'coz they can be changed
// accordingly
}
// Map.Entry Ops
return key;
}
return value;
}
throw new NullPointerException();
return oldValue;
}
return false;
.getValue()));
}
public int hashCode() {
}
}
}
// Types of Enumerations/Iterations
/**
* A Cache enumerator class. This class implements both the Enumeration and
* Iterator interfaces, but individual instances can be created with the
* Iterator methods disabled. This is necessary to avoid unintentionally
* increasing the capabilities granted a user by passing an Enumeration.
*/
int type;
/**
* Indicates whether this Enumerator is serving as an Iterator or an
* Enumeration. (true -> Iterator).
*/
boolean iterator;
/**
* The modCount value that the iterator believes that the backing List
* should have. If this expectation is violated, the iterator has
* detected concurrent modification.
*/
}
public boolean hasMoreElements() {
}
}
throw new NoSuchElementException("Cache Enumerator");
}
// Iterator methods
public boolean hasNext() {
return hasMoreElements();
}
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
return nextElement();
}
public void remove() {
if (!iterator)
throw new UnsupportedOperationException();
if (lastReturned == null)
throw new IllegalStateException("Cache Enumerator");
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
synchronized (Cache.this) {
{
if (e == lastReturned) {
modCount++;
else
count--;
lastReturned = null;
return;
}
}
throw new ConcurrentModificationException();
}
}
}
Collection c; // Backing Collection
if (c == null)
throw new NullPointerException();
this.c = c;
mutex = this;
}
this.c = c;
}
public int size() {
synchronized (mutex) {
return c.size();
}
}
public boolean isEmpty() {
synchronized (mutex) {
return c.isEmpty();
}
}
synchronized (mutex) {
return c.contains(o);
}
}
synchronized (mutex) {
return c.toArray();
}
}
synchronized (mutex) {
return c.toArray(a);
}
}
return c.iterator(); // Must be manually synched by user!
}
synchronized (mutex) {
return c.add(o);
}
}
synchronized (mutex) {
return c.remove(o);
}
}
synchronized (mutex) {
return c.containsAll(coll);
}
}
synchronized (mutex) {
}
}
synchronized (mutex) {
}
}
synchronized (mutex) {
}
}
public void clear() {
synchronized (mutex) {
c.clear();
}
}
synchronized (mutex) {
return c.toString();
}
}
}
super(s);
}
super(s, mutex);
}
synchronized (mutex) {
return c.equals(o);
}
}
public int hashCode() {
synchronized (mutex) {
return c.hashCode();
}
}
}
}