public abstract class ExpiryCache<K,V> extends java.lang.Object implements Cache<K,V>
Entities interested in expiry events can register themselves as listeners
and they will receive an event notification
when such
events occur.
This implementation accepts an optional expiration test and capacity policy. If provided, the
expiration test is applied to each candidate for expiration when attempting to remove entries from
the cache in the process of ensuring the cache's capacity to accept a new entry. A failure to pass
the test results in that value NOT being removed from the cache. Depending on the chosen capacity
policy, this will result in an exception being thrown (strict policy
);
or in the cache's size growing beyond its set limit (lenient policy
)
temporarily. If the cache grows beyond its limit in lenient mode, attempts will be made during
subsequent ensure capacity processing to shrink the cache back down to its limit. If many expiration
candidates are vetoed by the expiration test, such that no candidates can be removed during a given
effort to ensure capacity, a message is logged informing that the set cache limit may be inadequate.
This implementation is not synchronized. Access to any of the public methods
of this class (including get(Object)
) modify the internal state of the cache, and must
be synchronized externally.
Cache.Entry<K,V>
Modifier and Type | Field and Description |
---|---|
protected java.util.Map<K,Cache.Entry<K,V>> |
cache
Map of keys to implementation-specific key-value entries
|
private CapacityPolicy |
capacityPolicy
Optional capacity policy to apply upon a failure to ensure capacity within the cache's set limit
|
private int |
cleanLimit
Number of elements to clear from cache when it is full and capacity is needed
|
private java.util.function.Function<V,java.lang.Boolean> |
expirationTest
Optional function to confirm removal of an element when ensuring capacity
|
protected int |
limit
Maximum number of elements allowed in the cache
|
private java.util.List<CacheExpiryListener<K,V>> |
listeners
List of cache expiry listeners
|
protected static java.util.logging.Logger |
log
Logger
|
private boolean |
loggedCapacityExceeded
Flag to ensure we only log once if the cache limit was exceeded (in
LENIENT mode) |
protected java.lang.String |
name
Cache name, for debug and logging purposes
|
Modifier | Constructor and Description |
---|---|
protected |
ExpiryCache(int limit)
Constructor.
|
protected |
ExpiryCache(java.lang.String name,
int limit)
Constructor.
|
protected |
ExpiryCache(java.lang.String name,
int limit,
java.util.function.Function<V,java.lang.Boolean> expirationTest,
CapacityPolicy capacityPolicy)
Constructor.
|
Modifier and Type | Method and Description |
---|---|
void |
addCacheExpiryListener(CacheExpiryListener<K,V> listener)
Add the given object to the list of listeners interested in receiving cache expiry events
from this cache.
|
private void |
applyCapacityPolicy(int attempts)
Respond to a failed attempt to ensure capacity within the cache's set limit, according to this cache's
capacity policy.
|
protected abstract Cache.Entry<K,V> |
createEntry(K key,
V value)
Create an entry of the appropriate type for the given key and value.
|
protected void |
ensureCapacity()
Ensure there is sufficient capacity to add a new record to the cache, by expiring the least
desirable element(s), according to the policy of the cache.
|
java.lang.Iterable<Cache.Entry<K,V>> |
entries()
Get an object which allows iteration of all of the entries in the cache, in a for-each loop.
|
protected abstract void |
entryAccessed(Cache.Entry<K,V> entry)
An entry in the cache was accessed by a call to
get(Object) . |
protected abstract void |
entryAdded(Cache.Entry<K,V> entry)
An entry in the cache was added by a call to
put(Object, Object) , and an old entry
with the same key may have been removed. |
protected abstract void |
entryRemoved(Cache.Entry<K,V> entry)
An entry in the cache was removed by a call to
remove(Object) , was replaced with a
call to put(Object, Object) , or due to its expiration. |
V |
get(K key)
Retrieve a value, given its key.
|
private java.lang.String |
getCapacityMessage()
Generate the message used to report/log a failed attempt to ensure capacity within the cache's set
limit.
|
protected abstract int |
getCleanLimit()
Get the number of elements which will be expired from the cache each time it becomes full,
and capacity is required for a new element.
|
java.lang.Iterable<K> |
keys()
Get an object which allows iteration of all of the keys in the cache, in a for-each loop.
|
protected abstract Cache.Entry<K,V> |
nextExpiredEntry()
Get the next expired entry to be reaped.
|
V |
put(K key,
V value)
Add an entry to the cache, replacing any existing value with the same key and pushing out the
least desirable element(s) if the cache is full (and this entry does not replace an existing entry
with the same key).
|
V |
putIfAbsent(K key,
V value)
Add an entry to the cache, pushing out the least desirable element(s) if the cache is full, unless
an entry with the same key already exists.
|
V |
remove(K key)
Remove the entry associated with the given key from the cache.
|
void |
removeCacheExpiryListener(CacheExpiryListener<K,V> listener)
Remove the given object from the list of listeners interested in receiving cache expiry
events from this cache.
|
int |
size()
Get the number of entries currently in the cache.
|
java.lang.Iterable<V> |
values()
Get an object which allows iteration of all of the values in the cache, in a for-each loop.
|
protected static final java.util.logging.Logger log
protected final java.lang.String name
protected final int limit
protected final java.util.Map<K,Cache.Entry<K,V>> cache
private final int cleanLimit
private final java.util.function.Function<V,java.lang.Boolean> expirationTest
private final CapacityPolicy capacityPolicy
private boolean loggedCapacityExceeded
LENIENT
mode)private java.util.List<CacheExpiryListener<K,V>> listeners
protected ExpiryCache(int limit)
limit
- Maximum number of elements allowed in the cache.java.lang.IllegalArgumentException
- if the specified limit is less than one.protected ExpiryCache(java.lang.String name, int limit)
name
- Cache name, for debug and logging purposes. May be null
.limit
- Maximum number of elements allowed in the cache.java.lang.IllegalArgumentException
- if the specified limit is less than one.protected ExpiryCache(java.lang.String name, int limit, java.util.function.Function<V,java.lang.Boolean> expirationTest, CapacityPolicy capacityPolicy)
name
- Cache name, for debug and logging purposes. May be null
.limit
- Maximum number of elements allowed in the cache.expirationTest
- A function which confirms whether an expiration candidate element may actually be expired.
If null
, no confirmation test is applied and every candidate offered by a concrete
subclass as the next element to be expired is removed when ensuring capacity for new elements.
If supplied, this function effectively provides a veto capability to disallow an element from
being expired.capacityPolicy
- The policy to follow when capacity cannot be ensured, due to expirationTest
vetoing
expiration candidates. If null
, the CapacityPolicy.STRICT
policy is assumed.java.lang.IllegalArgumentException
- if the specified limit is less than one.public V get(K key)
Note for multi-threaded access: this method makes changes to the state of the cache. A call to this method should therefore be treated as write access for concurrency purposes.
public V put(K key, V value)
CacheExpiryEvent
being sent to registered
listeners before this method returns.public V putIfAbsent(K key, V value)
CacheExpiryEvent
being sent
to registered listeners before this method returns.putIfAbsent
in interface Cache<K,V>
key
- A hashable key to associate with the value.value
- Value to store.null
if the given value was added
because there was no existing mapping for the given key.public int size()
public java.lang.Iterable<K> keys()
public java.lang.Iterable<V> values()
public java.lang.Iterable<Cache.Entry<K,V>> entries()
public void addCacheExpiryListener(CacheExpiryListener<K,V> listener)
Note that adding listeners (any number) to the cache will add a slight amount of overhead to cache expiry processing, in that the expired elements are collected in a linked hash map, to be sent with a cache expiry event. If no listeners are registered, this collection does not take place.
listener
- Listener to register.public void removeCacheExpiryListener(CacheExpiryListener<K,V> listener)
listener
- Listener to deregister.protected abstract int getCleanLimit()
protected abstract Cache.Entry<K,V> createEntry(K key, V value)
key
- Hashable key.value
- Cached value.protected abstract Cache.Entry<K,V> nextExpiredEntry()
protected abstract void entryAdded(Cache.Entry<K,V> entry)
put(Object, Object)
, and an old entry
with the same key may have been removed. Respond accordingly.entry
- Entry which was added.protected abstract void entryAccessed(Cache.Entry<K,V> entry)
get(Object)
. Respond accordingly,
based on the policy of the concrete implementation.entry
- Entry which was accessed.protected abstract void entryRemoved(Cache.Entry<K,V> entry)
remove(Object)
, was replaced with a
call to put(Object, Object)
, or due to its expiration. Respond accordingly.entry
- Entry which was removed.protected void ensureCapacity()
When elements are expired from the cache, a cache expiry event is sent to all registered listeners.
private void applyCapacityPolicy(int attempts)
attempts
- The number of expiration candidate entries which were tested.private java.lang.String getCapacityMessage()