User: user57
Date: 02/02/16 04:34:27
Added: src/main/org/jboss/util CachePolicy.java LRUCachePolicy.java
TimedCachePolicy.java
Log:
o Moved CachePolicy & friends to common
o Changed CachePolicy to not implement Service, nothing thinks it is a
service. If it implemented service there would be a cyclical
dependency between common and server.
Revision Changes Path
1.1 jboss-common/src/main/org/jboss/util/CachePolicy.java
Index: CachePolicy.java
===================================================================
/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.util;
/**
* Interface that specifies a policy for caches. <p>
* Implementation classes can implement a LRU policy, a random one,
* a MRU one, or any other suitable policy.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Simone Bordet</a>
* @version $Revision: 1.1 $
*/
public interface CachePolicy
{
/**
* Returns the object paired with the specified key if it's
* present in the cache, otherwise must return null. <br>
* Implementations of this method must have complexity of order O(1).
* Differently from {@link #peek} this method not only return whether
* the object is present in the cache or not, but also
* applies the implemented policy that will "refresh" the cached
* object in the cache, because this cached object
* was really requested.
*
* @param key the key paired with the object
* @see #peek
*/
Object get(Object key);
/**
* Returns the object paired with the specified key if it's
* present in the cache, otherwise must return null. <br>
* Implementations of this method must have complexity of order O(1).
* This method should not apply the implemented caching policy to the
* object paired with the given key, so that a client can
* query if an object is cached without "refresh" its cache status. Real
* requests for the object must be done using {@link #get}.
*
* @param key the key paired with the object
* @see #get
*/
Object peek(Object key);
/**
* Inserts the specified object into the cache following the
* implemented policy. <br>
* Implementations of this method must have complexity of order O(1).
*
* @param key the key paired with the object
* @param object the object to cache
* @see #remove
*/
void insert(Object key, Object object);
/**
* Remove the cached object paired with the specified key. <br>
* Implementations of this method must have complexity of order O(1).
*
* @param key the key paired with the object
* @see #insert
*/
void remove(Object key);
/**
* Flushes the cached objects from the cache.
*/
void flush();
/**
* Get the size of the cache.
*/
int size();
/**
* create the service, do expensive operations etc
*/
void create() throws Exception;
/**
* start the service, create is already called
*/
void start() throws Exception;
/**
* stop the service
*/
void stop();
/**
* destroy the service, tear down
*/
void destroy();
}
1.1 jboss-common/src/main/org/jboss/util/LRUCachePolicy.java
Index: LRUCachePolicy.java
===================================================================
/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.util;
import java.util.HashMap;
/**
* Implementation of a Least Recently Used cache policy.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Simone Bordet</a>
* @version $Revision: 1.1 $
*/
public class LRUCachePolicy
implements CachePolicy
{
// Constants -----------------------------------------------------
// Attributes ----------------------------------------------------
/**
* The map holding the cached objects
*/
protected HashMap m_map;
/**
* The linked list used to implement the LRU algorithm
*/
protected LRUList m_list;
/**
* The maximum capacity of this cache
*/
protected int m_maxCapacity;
/**
* The minimum capacity of this cache
*/
protected int m_minCapacity;
// Static --------------------------------------------------------
// Constructors --------------------------------------------------
/**
* Creates a LRU cache policy object with zero cache capacity.
* @see #init
*/
public LRUCachePolicy()
{
}
/**
* Creates a LRU cache policy object with the specified minimum
* and maximum capacity.
* @see #init
*/
public LRUCachePolicy(int min, int max)
{
if (min < 2 || min > max) {throw new IllegalArgumentException("Illegal cache
capacities");}
m_minCapacity = min;
m_maxCapacity = max;
}
// Public --------------------------------------------------------
// Service implementation ----------------------------------------------
/**
* Initializes the cache, creating all required objects and initializing their
* values.
* @see #start
* @see #destroy
*/
public void create()
{
m_map = new HashMap();
m_list = createList();
m_list.m_maxCapacity = m_maxCapacity;
m_list.m_minCapacity = m_minCapacity;
m_list.m_capacity = m_maxCapacity;
}
/**
* Starts this cache that is now ready to be used.
* @see #init
* @see #stop
*/
public void start()
{
}
/**
* Stops this cache thus {@link #flush}ing all cached objects. <br>
* After this method is called, a call to {@link #start} will restart the cache.
* @see #start
* @see #destroy
*/
public void stop()
{
if (m_list != null)
{
flush();
}
}
/**
* Destroys the cache that is now unusable. <br>
* To have it working again it must be re-{@link #init}ialized and
* re-{@link #start}ed.
* @see #init
*/
public void destroy()
{
}
public Object get(Object key)
{
if (key == null)
{
throw new IllegalArgumentException("Requesting an object using a null key");
}
LRUCacheEntry value = (LRUCacheEntry)m_map.get(key);
if (value != null)
{
m_list.promote(value);
return value.m_object;
}
else
{
cacheMiss();
return null;
}
}
public Object peek(Object key)
{
if (key == null)
{
throw new IllegalArgumentException("Requesting an object using a null key");
}
LRUCacheEntry value = (LRUCacheEntry)m_map.get(key);
if (value == null)
{
return null;
}
else
{
return value.m_object;
}
}
public void insert(Object key, Object o)
{
if (o == null) {throw new IllegalArgumentException("Cannot insert a null
object in the cache");}
if (key == null) {throw new IllegalArgumentException("Cannot insert an object
in the cache with null key");}
Object obj = m_map.get(key);
if (obj == null)
{
m_list.demote();
LRUCacheEntry entry = createCacheEntry(key, o);
m_list.promote(entry);
m_map.put(key, entry);
}
else
{
throw new IllegalStateException("Attempt to put in the cache an object that
is already there");
}
}
public void remove(Object key)
{
if (key == null) {throw new IllegalArgumentException("Removing an object using
a null key");}
Object value = m_map.get(key);
if (value != null)
{
m_list.remove((LRUCacheEntry)value);
}
else {} // Do nothing, the object isn't in the cache list
m_map.remove(key);
}
public void flush()
{
LRUCacheEntry entry = null;
while ((entry = m_list.m_tail) != null)
{
ageOut(entry);
}
}
public int size() {
return m_list.m_count;
}
// Y overrides ---------------------------------------------------
// Package protected ---------------------------------------------
// Protected -----------------------------------------------------
/**
* Factory method for the linked list used by this cache implementation.
*/
protected LRUList createList() {return new LRUList();}
/**
* Callback method called when the cache algorithm ages out of the cache
* the given entry. <br>
* The implementation here is removing the given entry from the cache.
*/
protected void ageOut(LRUCacheEntry entry)
{
remove(entry.m_key);
}
/**
* Callback method called when a cache miss happens.
*/
protected void cacheMiss()
{
}
/**
* Factory method for cache entries
*/
protected LRUCacheEntry createCacheEntry(Object key, Object value)
{
return new LRUCacheEntry(key, value);
}
// Private -------------------------------------------------------
// Inner classes -------------------------------------------------
/**
* Double queued list used to store cache entries.
*/
public class LRUList
{
/** The maximum capacity of the cache list */
public int m_maxCapacity;
/** The minimum capacity of the cache list */
public int m_minCapacity;
/** The current capacity of the cache list */
public int m_capacity;
/** The number of cached objects */
public int m_count;
/** The head of the double linked list */
public LRUCacheEntry m_head;
/** The tail of the double linked list */
public LRUCacheEntry m_tail;
/** The cache misses happened */
public int m_cacheMiss;
/**
* Creates a new double queued list.
*/
protected LRUList()
{
m_head = null;
m_tail = null;
m_count = 0;
}
/**
* Promotes the cache entry <code>entry</code> to the last used position
* of the list. <br>
* If the object is already there, does nothing.
* @param entry the object to be promoted, cannot be null
* @see #demote
* @throws IllegalStateException if this method is called with a full cache
*/
protected void promote(LRUCacheEntry entry)
{
if (entry == null) {throw new IllegalArgumentException("Trying to promote a
null object");}
if (m_capacity < 1) {throw new IllegalStateException("Can't work with
capacity < 1");}
entry.m_time = System.currentTimeMillis();
if (entry.m_prev == null)
{
if (entry.m_next == null)
{
// entry is new or there is only the head
if (m_count == 0) // cache is empty
{
m_head = entry;
m_tail = entry;
++m_count;
entryAdded(entry);
}
else if (m_count == 1 && m_head == entry) {} // there is only the
head and I want to promote it, do nothing
else if (m_count < m_capacity)
{
entry.m_prev = null;
entry.m_next = m_head;
m_head.m_prev = entry;
m_head = entry;
++m_count;
entryAdded(entry);
}
else if (m_count < m_maxCapacity)
{
entry.m_prev = null;
entry.m_next = m_head;
m_head.m_prev = entry;
m_head = entry;
++m_count;
int oldCapacity = m_capacity;
++m_capacity;
entryAdded(entry);
capacityChanged(oldCapacity);
}
else {throw new IllegalStateException("Attempt to put a new cache
entry on a full cache");}
}
else {} // entry is the head, do nothing
}
else
{
if (entry.m_next == null) // entry is the tail
{
LRUCacheEntry beforeLast = entry.m_prev;
beforeLast.m_next = null;
entry.m_prev = null;
entry.m_next = m_head;
m_head.m_prev = entry;
m_head = entry;
m_tail = beforeLast;
}
else // entry is in the middle of the list
{
LRUCacheEntry previous = entry.m_prev;
previous.m_next = entry.m_next;
entry.m_next.m_prev = previous;
entry.m_prev = null;
entry.m_next = m_head;
m_head.m_prev = entry;
m_head = entry;
}
}
}
/**
* Demotes from the cache the least used entry. <br>
* If the cache is not full, does nothing.
* @see #promote
*/
protected void demote()
{
if (m_capacity < 1) {throw new IllegalStateException("Can't work with
capacity < 1");}
if (m_count > m_maxCapacity) {throw new IllegalStateException("Cache list
entries number (" + m_count + ") > than the maximum allowed (" + m_maxCapacity + ")");}
if (m_count == m_maxCapacity)
{
LRUCacheEntry entry = m_tail;
// the entry will be removed by ageOut
ageOut(entry);
}
else {} // cache is not full, do nothing
}
/**
* Removes from the cache list the specified entry.
*/
protected void remove(LRUCacheEntry entry)
{
if (entry == null) {throw new IllegalArgumentException("Cannot remove a
null entry from the cache");}
if (m_count < 1) {throw new IllegalStateException("Trying to remove an
entry from an empty cache");}
entry.m_key = entry.m_object = null;
if (m_count == 1)
{
m_head = m_tail = null;
}
else
{
if (entry.m_prev == null) // the head
{
m_head = entry.m_next;
m_head.m_prev = null;
entry.m_next = null;
}
else if (entry.m_next == null) // the tail
{
m_tail = entry.m_prev;
m_tail.m_next = null;
entry.m_prev = null;
}
else // in the middle
{
entry.m_next.m_prev = entry.m_prev;
entry.m_prev.m_next = entry.m_next;
entry.m_prev = null;
entry.m_next = null;
}
}
--m_count;
entryRemoved(entry);
}
/**
* Callback that signals that the given entry has been added to the cache.
*/
protected void entryAdded(LRUCacheEntry entry) {}
/**
* Callback that signals that the given entry has been removed from the cache.
*/
protected void entryRemoved(LRUCacheEntry entry) {}
/**
* Callback that signals that the capacity of the cache is changed.
* @param oldCapacity the capacity before the change happened
*/
protected void capacityChanged(int oldCapacity) {}
public String toString()
{
String s = Integer.toHexString(super.hashCode());
s += " size: " + m_count;
for (LRUCacheEntry entry = m_head; entry != null; entry = entry.m_next)
{
s += "\n" + entry;
}
return s;
}
}
/**
* Double linked cell used as entry in the cache list.
*/
public class LRUCacheEntry
{
/** Reference to the next cell in the list */
public LRUCacheEntry m_next;
/** Reference to the previous cell in the list */
public LRUCacheEntry m_prev;
/** The key used to retrieve the cached object */
public Object m_key;
/** The cached object */
public Object m_object;
/** The timestamp of the creation */
public long m_time;
/**
* Creates a new double linked cell, storing the object we
* want to cache and the key that is used to retrieve it.
*/
protected LRUCacheEntry(Object key, Object object)
{
m_key = key;
m_object = object;
m_next = null;
m_prev = null;
m_time = 0; // Set when inserted in the list.
}
public String toString()
{
return "key: " + m_key + ", object: " +
Integer.toHexString(m_object.hashCode()) + ", entry: " +
Integer.toHexString(super.hashCode());
}
}
}
1.1 jboss-common/src/main/org/jboss/util/TimedCachePolicy.java
Index: TimedCachePolicy.java
===================================================================
/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.util;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
/** An implementation of a timed cache. This is a cache whose entries have a
limited lifetime with the ability to refresh their lifetime. The entries
managed by the cache implement the TimedCachePolicy.TimedEntry interface. If
an object inserted into the cache does not implement this interface, it will
be wrapped in a DefaultTimedEntry and will expire without the possibility of
refresh after getDefaultLifetime() seconds.
This is a lazy cache policy in that objects are not checked for expiration
until they are accessed.
@author <a href="mailto:[EMAIL PROTECTED]">Scott Stark</a>.
@version $Revision: 1.1 $
*/
public class TimedCachePolicy
extends TimerTask
implements CachePolicy
{
/** The interface that cache entries support.
*/
public static interface TimedEntry
{
/** Initializes an entry with the current cache time. This is called when
the entry is first inserted into the cache so that entries do not
have to know the absolute system time.
*/
void init(long now);
/** Is the entry still valid basis the current time
@return true if the entry is within its lifetime, false if it is expired.
*/
boolean isCurrent(long now);
/** Attempt to extend the entry lifetime by refreshing it.
@return true if the entry was refreshed successfully, false otherwise.
*/
boolean refresh();
/** Get the value component of the TimedEntry. This may or may not
be the TimedEntry implementation.
*/
Object getValue();
}
protected static Timer resolutionTimer = new Timer(true);
/** The map of cached TimedEntry objects. */
protected Map entryMap;
/** The lifetime in seconds to use for objects inserted
that do not implement the TimedEntry interface. */
protected int defaultLifetime;
/** A flag indicating if entryMap should be synchronized */
protected boolean threadSafe;
/** The caches notion of the current time */
protected long now;
/** The resolution in seconds of the cach current time */
protected int resolution;
/** Creates a new TimedCachePolicy with a default entry lifetime of 30 mins
that does not synchronized access to its policy store and uses a 60
second resolution.
*/
public TimedCachePolicy()
{
this(30*60, false, 0);
}
/** Creates a new TimedCachePolicy with the given default entry lifetime
that does not synchronized access to its policy store and uses a 60
second resolution.
*/
public TimedCachePolicy(int defaultLifetime)
{
this(defaultLifetime, false, 0);
}
/** Creates a new TimedCachePolicy with the given default entry lifetime
that does/does not synchronized access to its policy store depending
on the value of threadSafe.
@param defaultLifetime, the lifetime in seconds to use for objects inserted
that do not implement the TimedEntry interface.
@param threadSafe, a flag indicating if the cach store should be synchronized
to allow correct operation under multi-threaded access. If true, the
cache store is synchronized. If false the cache store is unsynchronized and
the cache is not thread safe.
@param resolution, the resolution in seconds of the cache timer. A cache does
not query the system time on every get() invocation. Rather the cache
updates its notion of the current time every 'resolution' seconds.
*/
public TimedCachePolicy(int defaultLifetime, boolean threadSafe, int resolution)
{
this.defaultLifetime = defaultLifetime;
this.threadSafe = threadSafe;
if( resolution <= 0 )
resolution = 60;
this.resolution = resolution;
}
// Service implementation ----------------------------------------------
/** Initializes the cache for use. Prior to this the cache has no store.
*/
public void create()
{
if( threadSafe )
entryMap = new HashMap();
else
entryMap = Collections.synchronizedMap(new HashMap());
now = System.currentTimeMillis();
}
/** Schedules this with the class resolutionTimer Timer object for
execution every resolution seconds.
*/
public void start()
{
resolutionTimer.scheduleAtFixedRate(this, 0, 1000*resolution);
}
/** Stop cancels the resolution timer and flush()es the cache.
*/
public void stop()
{
super.cancel();
flush();
}
/** Clears the cache of all entries.
*/
public void destroy()
{
entryMap.clear();
}
// --- Begin CachePolicy interface methods
/** Get the cache value for key if it has not expired.
@returns the TimedEntry value or the original value if it was not an
instancee of TimedEntry if key is in the cache, null otherwise.
*/
public Object get(Object key)
{
TimedEntry entry = (TimedEntry) entryMap.get(key);
if( entry == null )
return null;
if( entry.isCurrent(now) == false )
{ // Try to refresh the entry
if( entry.refresh() == false )
{ // Failed, remove the entry and return null
entryMap.remove(key);
return null;
}
}
Object value = entry.getValue();
return value;
}
/** Get the cache value for key. This method does not check to see if
the entry has expired.
@returns the TimedEntry value or the original value if it was not an
instancee of TimedEntry if key is in the cache, null otherwise.
*/
public Object peek(Object key)
{
TimedEntry entry = (TimedEntry) entryMap.get(key);
Object value = null;
if( entry != null )
value = entry.getValue();
return value;
}
/** Insert a value into the cache. In order to have the cache entry
reshresh itself value would have to implement TimedEntry and
implement the required refresh() method logic.
@param key, the key for the cache entry
@param value, Either an instance of TimedEntry that will be inserted without
change, or an abitrary value that will be wrapped in a non-refreshing
TimedEntry.
*/
public void insert(Object key, Object value)
{
if( entryMap.containsKey(key) )
throw new IllegalStateException("Attempt to insert duplicate entry");
TimedEntry entry = null;
if( (value instanceof TimedEntry) == false )
{ // Wrap the value in a DefaultTimedEntry
entry = new DefaultTimedEntry(defaultLifetime, value);
}
else
{
entry = (TimedEntry) value;
}
entry.init(now);
entryMap.put(key, entry);
}
/** Remove the entry associated with key.
*/
public void remove(Object key)
{
entryMap.remove(key);
}
/** Remove all entries from the cache.
*/
public void flush()
{
entryMap.clear();
}
public int size() {
return entryMap.size();
}
// --- End CachePolicy interface methods
/** The TimerTask run method. It updates the cache time to the
current system time.
*/
public void run()
{
now = System.currentTimeMillis();
}
/** Get the cache time.
@return the cache time last obtained from System.currentTimeMillis()
*/
public long currentTimeMillis()
{
return now;
}
/** Get the raw TimedEntry for key without performing any expiration check.
@return the TimedEntry value associated with key if one exists, null
otherwise.
*/
public TimedEntry peekEntry(Object key)
{
TimedEntry entry = (TimedEntry) entryMap.get(key);
return entry;
}
/** The default implementation of TimedEntry used to wrap non-TimedEntry
objects inserted into the cache.
*/
static class DefaultTimedEntry implements TimedEntry
{
long expirationTime;
Object value;
DefaultTimedEntry(long lifetime, Object value)
{
this.expirationTime = 1000 * lifetime;
this.value = value;
}
public void init(long now)
{
expirationTime += now;
}
public boolean isCurrent(long now)
{
return expirationTime > now;
}
public boolean refresh()
{
return false;
}
public Object getValue()
{
return value;
}
}
}
_______________________________________________
Jboss-development mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/jboss-development