/** * Cache class that can be used by any code. */ package org.myrobotlab.memory; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * Implementation of the Cache interface that stores information in local * memory. * * @author SwedaKonsult * */ public class LocalCache extends BaseCache { /** * Default concurrency level - grabbed from ConcurrentHashMap. */ public static final int DEFAULT_CONCURRENCY_LEVEL = 16; /** * Default load factor - grabbed from ConcurrentHashMap. */ public static final float DEFAULT_LOAD_FACTOR = 0.75f; /** * The cache of this instance. */ private final ConcurrentMap<String, Object> items; private final ConcurrentMap<String, Long> itemTimeouts; private final long timeout; private long nextTimeout; private final boolean useTimeout; /** * Constructor. Default load factor (0.75) and concurrencyLevel (16). Default * timeout: 0 (never). * * @param initialCapacity * the initial capacity. The implementation performs internal sizing * to accommodate this many elements. * @throws IllegalArgumentException * if the initial capacity of elements is negative. */ public LocalCache(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL, 0); } /** * Constructor. * * @param initialCapacity * the initial capacity. The implementation performs internal sizing * to accommodate this many elements. * @param loadFactor * the load factor threshold, used to control resizing. Resizing may * be performed when the average number of elements per bin exceeds * this threshold. * @param concurrencyLevel * the estimated number of concurrently updating threads. The * implementation performs internal sizing to try to accommodate this * many threads. * @param the * amount of time in ms after which an item should time out. * @throws IllegalArgumentException * if the initial capacity is negative or the load factor or * concurrencyLevel are non-positive. */ public LocalCache(int initialSize, float loadFactor, int concurrencyLevel, int timeout) { items = new ConcurrentHashMap<String, Object>(initialSize, loadFactor, concurrencyLevel); itemTimeouts = new ConcurrentHashMap<String, Long>(initialSize, loadFactor, concurrencyLevel); this.timeout = timeout; nextTimeout = 0l; useTimeout = this.timeout > 0l; } @Override protected void addToCache(String name, Object value) { items.put(name, value); // only set a timeout for the item if we want to timeout things in this // cache if (timeout > 0l) { itemTimeouts.put(name, System.currentTimeMillis() + timeout); } } @Override protected void clearCache() { items.clear(); // only need to clear things out if they've been used if (useTimeout) { itemTimeouts.clear(); nextTimeout = 0l; } } @Override protected boolean contains(String name) { return items.containsKey(name); } @Override protected void expireItem(String name) { if (name == null || name.isEmpty() || !itemTimeouts.containsKey(name) || !useTimeout) { return; } // time it out itemTimeouts.put(name, 0l); // make sure it's clear that something needs to be timed out nextTimeout = 0l; } @Override protected Object getFromCache(String name) { if (!items.containsKey(name)) { return null; } return items.get(name); } @Override protected void removeFromCache(String name) { if (!items.containsKey(name)) { return; } // TODO is this needed in order to make sure that the handle is removed? items.put(name, null); items.remove(name); // only remove it if we're using timeouts if (useTimeout) { itemTimeouts.remove(name); } } @Override protected void timeoutCache() { if (!useTimeout || (nextTimeout > 0l && nextTimeout > System.currentTimeMillis())) { // nothing to time out right now return; } // TODO loop through itemTimeouts in order to see if one of them needs // to be timed out } }