Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2014 Guidewire Software, Inc.
*/
package gw.util.concurrent;
import java.io.Serializable;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.math.BigDecimal;
import gw.util.concurrent.Cache.ConcurrentLinkedHashMap.Node.State;
import gw.util.ILogger;
/**
* static var MY_CACHE = new Cache( 1000, \ foo -> getBar( foo ) )
*/
public class Cache {
private ConcurrentLinkedHashMap _cacheImpl;
private final MissHandler _missHandler;
private final String _name;
private final int _size;
//statistics
private final AtomicInteger _requests = new AtomicInteger();
private final AtomicInteger _misses = new AtomicInteger();
private final AtomicInteger _hits = new AtomicInteger();
private ScheduledFuture> _loggingTask;
/** This will create a new cache
*
* @param name the name of the cache for logging
* @param size the maximum size of the log
* @param missHandler how to handle misses, this is required not to be null
*/
public Cache( String name, int size, MissHandler missHandler) {
_name = name;
_size = size;
clearCacheImpl();
_missHandler = missHandler;
}
private void clearCacheImpl() {
_cacheImpl = new ConcurrentLinkedHashMap(ConcurrentLinkedHashMap.EvictionPolicy.SECOND_CHANCE, _size);
}
/** This will evict a specific key from the cache.
*
* @param key the key to evict
* @return the current value for that key
*/
public V evict(K key) {
return _cacheImpl.remove(key);
}
/** This will put a specific entry in the cache
*
* @param key this is the key
* @param value this is the value
* @return the old value for this key
*/
public V put(K key, V value) {
return _cacheImpl.put(key, value);
}
/** This will get a specific entry, it will call the missHandler if it is not found.
*
* @param key the object to find
* @return the found object (may be null)
*/
public V get(K key) {
V value = _cacheImpl.get(key);
_requests.incrementAndGet();
if (value == null) {
value = _missHandler.load(key);
_cacheImpl.put(key, value);
_misses.incrementAndGet();
} else {
_hits.incrementAndGet();
}
return value;
}
public int getConfiguredSize() {
return _size;
}
public int getUtilizedSize() {
return _cacheImpl.size();
}
public int getRequests() {
return _requests.get();
}
public int getMisses() {
return _misses.get();
}
public int getHits() {
return _hits.get();
}
public double getHitRate() {
int requests = getRequests();
int hits = getHits();
if (requests == 0) {
return 0.0;
} else {
return ((double) hits) / requests;
}
}
/**
* Sets up a recurring task every n seconds to report on the status of this cache. This can be useful
* if you are doing exploratory caching and wish to monitor the performance of this cache with minimal fuss.
* Consider
* @param seconds how often to log the entry
* @param logger the logger to use
* @return this
*/
public synchronized Cache logEveryNSeconds(int seconds, final ILogger logger) {
if (_loggingTask == null) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
_loggingTask = service.scheduleAtFixedRate(new Runnable() {
public void run() {
logger.info(Cache.this);
}
}, seconds, seconds, TimeUnit.SECONDS);
} else {
throw new IllegalStateException("Logging for " + this + " is already enabled");
}
return this;
}
public synchronized void stopLogging() {
if (_loggingTask != null) {
_loggingTask.cancel(false);
}
}
public interface MissHandler {
public W load(L key);
}
public void clear() {
clearCacheImpl();
_hits.set(0);
_misses.set(0);
_requests.set(0);
}
@Override
public String toString() {
return "Cache \"" + _name + "\"( Hits:" + getHits() + ", Misses:" + getMisses() + ", Requests:" + getRequests() + ", Hit rate:" + BigDecimal.valueOf(getHitRate() * 100.0).setScale(2, BigDecimal.ROUND_DOWN) + "% )";
}
public static Cache make(String name, int size, MissHandler handler) {
return new Cache(name, size, handler);
}
/**
* A {@link ConcurrentMap} with a doubly-linked list running through its entries.
*
* This class provides the same semantics as a {@link ConcurrentHashMap} in terms of
* iterators, acceptable keys, and concurrency characteristics, but perform slightly
* worse due to the added expense of maintaining the linked list. It differs from
* {@link java.util.LinkedHashMap} in that it does not provide predictable iteration
* order.
*
* This map is intended to be used for caches and provides the following eviction policies:
*
*
First-in, First-out: Also known as insertion order. This policy has excellent
* concurrency characteristics and an adequate hit rate.
*
Second-chance: An enhanced FIFO policy that marks entries that have been retrieved
* and saves them from being evicted until the next pass. This enhances the FIFO policy
* by making it aware of "hot" entries, which increases its hit rate to be equal to an
* LRU's under normal workloads. In the worst case, where all entries have been saved,
* this policy degrades to a FIFO.
*
Least Recently Used: An eviction policy based on the observation that entries that
* have been used recently will likely be used again soon. This policy provides a good
* approximation of an optimal algorithm, but suffers by being expensive to maintain.
* The cost of reordering entries on the list during every access operation reduces
* the concurrency and performance characteristics of this policy.
*
*
* The Second Chance eviction policy is recommended for common use cases as it provides
* the best mix of performance and efficiency of the supported replacement policies.
*
* If the Least Recently Used policy is chosen then the sizing should compensate for the
* proliferation of dead nodes on the linked list. While the values are removed immediately, the
* nodes are evicted only when they reach the head of the list. Under FIFO-based policies, dead
* nodes occur when explicit removals are requested and does not normally produce a noticeable
* impact on the map's hit rate. The LRU policy creates a dead node on every successful retrieval
* and a new node is placed at the tail of the list. For this reason, the LRU's efficiency cannot
* be compared directly to a {@link java.util.LinkedHashMap} evicting in access order.
*
* Ben Manes
*/
static class ConcurrentLinkedHashMap extends AbstractMap implements ConcurrentMap, Serializable {
private static final long serialVersionUID = 8350170357874293408L;
final List> listeners;
final ConcurrentMap> data;
final AtomicInteger capacity;
final EvictionPolicy policy;
final AtomicInteger length;
final Node head;
final Node tail;
/**
* Creates a new, empty, unbounded map with the specified maximum capacity and the default
* concurrencyLevel.
*
* @param policy The eviction policy to apply when the size exceeds the maximum capacity.
* @param maximumCapacity The maximum capacity to coerces to. The size may exceed it temporarily.
* @param listeners The listeners registered for notification when an entry is evicted.
*/
public ConcurrentLinkedHashMap(EvictionPolicy policy, int maximumCapacity, EvictionListener... listeners) {
this(policy, maximumCapacity, 16, listeners);
}
/**
* Creates a new, empty, unbounded map with the specified maximum capacity and concurrency level.
*
* @param policy The eviction policy to apply when the size exceeds the maximum capacity.
* @param maximumCapacity The maximum capacity to coerces to. The size may exceed it temporarily.
* @param concurrencyLevel The estimated number of concurrently updating threads. The implementation
* performs internal sizing to try to accommodate this many threads.
* @param listeners The listeners registered for notification when an entry is evicted.
*/
public ConcurrentLinkedHashMap(EvictionPolicy policy, int maximumCapacity, int concurrencyLevel, EvictionListener... listeners) {
if ((policy == null) || (maximumCapacity < 0) || (concurrencyLevel <= 0)) {
throw new IllegalArgumentException();
}
this.listeners = (listeners == null) ? Collections.>emptyList() : Arrays.asList(listeners);
this.data = new ConcurrentHashMap>(maximumCapacity, 0.75f, concurrencyLevel);
this.capacity = new AtomicInteger(maximumCapacity);
this.length = new AtomicInteger();
this.head = new Node();
this.tail = new Node();
this.policy = policy;
head.setPrev(head);
head.setNext(tail);
tail.setPrev(head);
tail.setNext(tail);
}
/**
* Determines whether the map has exceeded its capacity.
*
* @return Whether the map has overflowed and an entry should be evicted.
*/
private boolean isOverflow() {
return length.get() > capacity();
}
/**
* Sets the maximum capacity of the map and eagerly evicts entries until the
* it shrinks to the appropriate size.
*
* @param capacity The maximum capacity of the map.
*/
public void setCapacity(int capacity) {
if (capacity < 0) {
throw new IllegalArgumentException();
}
this.capacity.set(capacity);
while (isOverflow()) {
evict();
}
}
/**
* Retrieves the maximum capacity of the map.
*
* @return The maximum capacity.
*/
public int capacity() {
return capacity.get();
}
/**
* {@inheritDoc}
*/
@Override
public int size() {
return data.size();
}
/**
* {@inheritDoc}
*/
@Override
public void clear() {
for (K key : keySet()) {
remove(key);
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean containsKey(Object key) {
return data.containsKey(key);
}
/**
* {@inheritDoc}
*/
@Override
public boolean containsValue(Object value) {
return data.containsValue(new Node