package ca.sqlpower.util;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* The AbstractCache class is a generic cache that never disposes of
* the items it contains. This class is declared abstract because
* without a disposal policy, there is no difference between a cache
* and a map.
*
* <p>This class is may not extend HashMap in the future, but it will
* always implement Cache, which will always implement Map.
* Therefore, it's not safe to assume an AbstractCache is a HashMap
* but it is safe to assume an AbstractCache is a Map (or a Collection).
*/
public abstract class AbstractCache extends HashMap implements Cache, java.io.Serializable {
public AbstractCache(int initialMaxMembers) {
super();
maxMembers = initialMaxMembers;
lastFlushDate = new Date();
stats = new CacheStats();
}
/**
* This field holds the maximum member count normally allowed in
* the cache. Subclasses may interpret this as a hard or soft
* limit, and should document which interpretation they have
* chosen.
*/
protected int maxMembers;
/**
* This is initialized to the current date when the cache is first
* created, and is updated to the current date whenever the {@link
* #flush()} method is called.
*/
protected Date lastFlushDate;
/**
* This object gets notified when things happen, so that
* statistics can be gathered to help administrators tune the
* cache parameters.
*/
protected CacheStats stats;
/**
* Whenever an item is inserted (using <code>put</code> or
* <code>putAll</code>), this method will be called. At that
* time, the subclass's implementation should ensure that the
* cache's item count does not exceed maxMembers by removing zero
* or more items as necessary. The removal process is permitted
* to remove more items than necessary to lower the count, and in
* some cases (such as "soft" limit policies) the item count may
* remain in excess of maxMembers. The actual policy used to
* decide which item to dispose of is left up to each individual
* subclass.
*/
protected abstract void itemsInserted(Object[] keys);
/**
* Whenever an item is requested (via <code>get</code>), this
* method will be called. Many cache eviction policies need to
* know when items are requsted to keep their stats accurate.
*
* @param key The key which was used in the request.
* @param wasPresent True if the requested key was present in the
* cache (a cache hit), false otherwise (a cache miss).
*/
protected abstract void itemRequested(Object key, boolean wasPresent);
/**
* Sets the maximum member count, which influences the behaviour
* of <code>itemsInserted</code>.
*
* @see #maxMembers
*/
public void setMaxMembers(int argMaxMembers) {
maxMembers=argMaxMembers;
}
/**
* Gets the maximum member count, which influences the behaviour
* of <code>itemsInserted</code>.
*
* @see #maxMembers
*/
public int getMaxMembers() {
return maxMembers;
}
/**
* Works like Put() as specified in the java.util.Map interface,
* but also is allowed to delete zero or more items from this
* collection after inserting the given item. The choice of which
* items to delete and when is controlled by the various
* <code>itemsInserted</code> implementations of concrete
* subclasses.
*/
public Object put(Object key, Object value) {
Object retval=super.put(key, value);
Object[] theKey=new Object[1];
theKey[0]=key;
internalItemsInserted(theKey);
return retval;
}
/**
* Works like PutAll() as specified in the java.util.Map
* interface, but also is allowed to delete zero or more items
* from this collection after inserting the given items. The
* choice of which items to delete and when is controlled by the
* various <code>itemsInserted</code> implementations of concrete
* subclasses.
*/
public void putAll(Map t) {
super.putAll(t);
internalItemsInserted(t.keySet().toArray());
}
/**
* Works like get() as specified in the java.util.Map interface,
* but also notifies the implementing subclass of the request via
* the itemRequested hook.
*/
public Object get(Object key) {
Object retval=super.get(key);
internalItemRequested(key, retval!=null);
return retval;
}
/**
* @see #lastFlushDate
*/
public Date getLastFlushDate() {
return lastFlushDate;
}
/**
* Releases all objects in the cache and updates the last flush
* date.
*/
public void flush() {
super.clear();
stats.cacheFlush();
lastFlushDate = new Date();
}
/**
* Returns the statistics-tracking instance for this cache.
*/
public CacheStats getStats() {
return stats;
}
/**
* Handles statistics gathering and calls the abstract
* itemsInserted hook.
*/
private void internalItemsInserted(Object[] keys) {
stats.totalInserted += keys.length;
itemsInserted(keys);
}
/**
* Handles statistics gathering and calls the abstract
* itemsInserted hook.
*/
private void internalItemRequested(Object key, boolean wasPresent) {
stats.totalRequested++;
if (wasPresent) {
stats.totalHits++;
} else {
stats.totalMisses++;
}
itemRequested(key, wasPresent);
}
}