You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by jo...@apache.org on 2008/09/09 02:26:28 UTC
svn commit: r693325 - in /incubator/shindig/trunk/java/common/src:
main/java/org/apache/shindig/common/cache/TtlCache.java
test/java/org/apache/shindig/common/cache/TtlCacheTest.java
Author: johnh
Date: Mon Sep 8 17:26:28 2008
New Revision: 693325
URL: http://svn.apache.org/viewvc?rev=693325&view=rev
Log:
Improving TtlCache in preparation for using it throughout code where necessary to help clean up various APIs (eg. AbstractHttpCache, SHINDIG-579).
* TtlCache implements the Cache interface, with default behaviors for getElement and addElement. Because why not.
* New method getElementWithExpiration returns whether or not the returned element has expired along with the element itself. Callers can choose what to do with the results at that point.
Modified:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/cache/TtlCache.java
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/cache/TtlCacheTest.java
Modified: incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/cache/TtlCache.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/cache/TtlCache.java?rev=693325&r1=693324&r2=693325&view=diff
==============================================================================
--- incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/cache/TtlCache.java (original)
+++ incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/cache/TtlCache.java Mon Sep 8 17:26:28 2008
@@ -24,18 +24,32 @@
* Cache enforcing a Time-To-Live value atop whatever other base
* caching characteristics are provided. A minimum and maximum
* TTL is provided to ensure that TTL values are within reasonable
- * limits. This class contains a Cache but doesn't implement the
- * Cache interface due to the necessity of changing its addElement()
- * signature. If needed, however, it could implement addElement without
- * an explicitly provided object lifetime by using minTtl in that case.
- * @param <K> Key for the cache.
- * @param <V> Value the cache stores by Key.
+ * limits. Objects are not forced out of the cache after their TTL;
+ * they're simply treated as invalid, unless "stale" entries are
+ * explicitly requested.
+ *
+ * Two sets of APIs are supported:
+ * 1. The standard Cache interface. addElement() adds to the cache
+ * with a particular default lifetime, which may be set separately. Without
+ * being overridden, its default value is 0 (no TTL), which applies subject
+ * to minTtl and maxTtl configured for the cache. getElement() retrieves
+ * an element from the cache subject to configured expiration.
+ * 2. Extended TTL and expired-object caching interfaces. An addElement
+ * method is provided with a requested expiration for the object, which is
+ * in turn subject to minTtl and maxTtl restrictions as configured for the
+ * cache. Method getElementWithExpiration is provided which returns the cached object
+ * along with whether or not it is expired.
+ * @param <K> Type of key for the cache.
+ * @param <V> Type of value the cache stores by Key.
*/
-public class TtlCache<K, V> {
+public class TtlCache<K, V> implements Cache<K, V> {
private final Cache<K, TimeoutPair<V>> baseCache;
private final long minTtl;
private final long maxTtl;
private TimeSource timeSource;
+ private long defaultLifetime;
+
+ private static final long DEFAULT_LIFETIME_MILLIS = 0;
/**
* Create a new TtlCache with the given capacity and TTL values.
@@ -46,10 +60,20 @@
* @param maxTtl Maximum amount of time a given entry can stay in the cache, in millis.
*/
public TtlCache(CacheProvider cacheProvider, int capacity, long minTtl, long maxTtl) {
- this.baseCache = cacheProvider.createCache(capacity);
- this.minTtl = minTtl;
- this.maxTtl = maxTtl;
- this.timeSource = new TimeSource();
+ this.baseCache = cacheProvider.createCache(capacity);
+ this.minTtl = minTtl;
+ this.maxTtl = maxTtl;
+ this.timeSource = new TimeSource();
+ this.defaultLifetime = DEFAULT_LIFETIME_MILLIS;
+ }
+
+ /**
+ * Sets the default lifetime of a given element added to the cache (using the standard
+ * addElement method), in milliseconds.
+ * @param defaultLifetime
+ */
+ public void setDefaultLifetimeMillis(long defaultLifetime) {
+ this.defaultLifetime = defaultLifetime;
}
/**
@@ -59,7 +83,24 @@
* @return Element in the cache, if present and not timed out.
*/
public V getElement(K key) {
- return getElementMaybeRemove(key, false);
+ CachedObject<V> cached = getElementMaybeRemove(key, false);
+
+ if (!cached.isExpired) {
+ return cached.obj;
+ }
+
+ return null;
+ }
+
+ /**
+ * Retrieve an element from the cache along with whether or not it is
+ * expired. A "stale" element may be used by calling code if it so
+ * chooses, ie. if it's unable to pull a "fresh" version of content.
+ * @param key Key whose element to look up.
+ * @return Pair of cached element and whether or not it is expired.
+ */
+ public CachedObject<V> getElementWithExpiration(K key) {
+ return getElementMaybeRemove(key, false);
}
/**
@@ -67,17 +108,27 @@
* it should live in the cache provided in milliseconds. If below
* minTtl, minTtl is used. If above maxTtl, maxTtl is used.
* @param key Element key.
- * @param val Cached element value.
+ * @param val Element value to cache.
* @param lifetime Intended lifetime, in millis, of the element's entry.
*/
public void addElement(K key, V val, long lifetime) {
- long now = timeSource.currentTimeMillis();
+ long now = timeSource.currentTimeMillis();
long expiration = lifetime;
expiration = Math.max(now + minTtl, Math.min(now + maxTtl, expiration));
- TimeoutPair<V> entry = new TimeoutPair<V>(val, expiration);
- synchronized(baseCache) {
- baseCache.addElement(key, entry);
- }
+ TimeoutPair<V> entry = new TimeoutPair<V>(val, expiration);
+ synchronized(baseCache) {
+ baseCache.addElement(key, entry);
+ }
+ }
+
+ /**
+ * Add an element to the cache, with lifetime set to the default configured
+ * for this cache object.
+ * @param key Element key.
+ * @param val Element value to cache.
+ */
+ public void addElement(K key, V val) {
+ addElement(key, val, defaultLifetime);
}
/**
@@ -87,36 +138,38 @@
* @return Element value.
*/
public V removeElement(K key) {
- return getElementMaybeRemove(key, true);
- }
+ CachedObject<V> cached = getElementMaybeRemove(key, true);
+
+ if (!cached.isExpired) {
+ return cached.obj;
+ }
+
+ return null;
+ }
/**
* Set a new time source. Used for testing, so package-private.
* @param timeSource New time source to use.
*/
void setTimeSource(TimeSource timeSource) {
- this.timeSource = timeSource;
+ this.timeSource = timeSource;
}
- private V getElementMaybeRemove(K key, boolean remove) {
- TimeoutPair<V> entry = null;
- if (remove) {
- entry = baseCache.removeElement(key);
- } else {
- entry = baseCache.getElement(key);
- }
- if (entry == null) {
- return null;
- }
+ private CachedObject<V> getElementMaybeRemove(K key, boolean remove) {
+ TimeoutPair<V> entry = null;
+
+ if (remove) {
+ entry = baseCache.removeElement(key);
+ } else {
+ entry = baseCache.getElement(key);
+ }
+ if (entry == null) {
+ return new CachedObject<V>((V)null, true);
+ }
- long now = timeSource.currentTimeMillis();
- if (now < entry.expiration) {
- // Not yet timed out. Still valid, so return.
- return entry.cachedObj;
- }
-
- // No need to clean up the cache - that happens under the covers.
- return null;
+ long now = timeSource.currentTimeMillis();
+
+ return new CachedObject<V>(entry.cachedObj, now >= entry.expiration);
}
/**
@@ -125,12 +178,22 @@
* @param <V> Type of stored object.
*/
private static final class TimeoutPair<V> {
- private V cachedObj;
- private long expiration;
+ private V cachedObj;
+ private long expiration;
- private TimeoutPair(V cachedObj, long expiration) {
- this.cachedObj = cachedObj;
- this.expiration = expiration;
- }
+ private TimeoutPair(V cachedObj, long expiration) {
+ this.cachedObj = cachedObj;
+ this.expiration = expiration;
+ }
+ }
+
+ public static class CachedObject<V> {
+ public V obj;
+ public boolean isExpired;
+
+ private CachedObject(V obj, boolean isExpired) {
+ this.obj = obj;
+ this.isExpired = isExpired;
+ }
}
}
Modified: incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/cache/TtlCacheTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/cache/TtlCacheTest.java?rev=693325&r1=693324&r2=693325&view=diff
==============================================================================
--- incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/cache/TtlCacheTest.java (original)
+++ incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/cache/TtlCacheTest.java Mon Sep 8 17:26:28 2008
@@ -28,91 +28,99 @@
@Override
public void setUp() throws Exception {
- timeSource = new FakeTimeSource(0);
- cacheProvider = new DefaultCacheProvider();
+ timeSource = new FakeTimeSource(0);
+ cacheProvider = new DefaultCacheProvider();
}
// Capacity just needs to be big enough to retain elements.
private static final int CAPACITY = 100;
private TtlCache<String, String> makeTtlCache(long minTtl, long maxTtl) {
- TtlCache<String, String> ttlCache =
- new TtlCache<String, String>(cacheProvider, CAPACITY, minTtl, maxTtl);
- ttlCache.setTimeSource(timeSource);
- return ttlCache;
+ TtlCache<String, String> ttlCache =
+ new TtlCache<String, String>(cacheProvider, CAPACITY, minTtl, maxTtl);
+ ttlCache.setTimeSource(timeSource);
+ return ttlCache;
}
public void testGeneralCacheExpiration() {
- TtlCache<String, String> ttlCache = makeTtlCache(120 * 1000, 360 * 1000);
- String key = "key1", val = "val1";
- ttlCache.addElement(key, val, 240 * 1000);
-
- // Time is still 0: should be in the cache.
- assertEquals(val, ttlCache.getElement(key));
-
- // Time = 120 seconds: still in cache.
- timeSource.setCurrentTimeMillis(120 * 1000);
- assertEquals(val, ttlCache.getElement(key));
-
- // Time = 240 seconds - 1 ms: still in cache.
- timeSource.setCurrentTimeMillis(240 * 1000 - 1);
- assertEquals(val, ttlCache.getElement(key));
-
- // Time = 300 seconds: out of cache.
- timeSource.setCurrentTimeMillis(300 * 1000);
- assertNull(ttlCache.getElement(key));
+ TtlCache<String, String> ttlCache = makeTtlCache(120 * 1000, 360 * 1000);
+ String key = "key1", val = "val1";
+ ttlCache.addElement(key, val, 240 * 1000);
+
+ // Time is still 0: should be in the cache.
+ assertEquals(val, ttlCache.getElement(key));
+ assertEquals(val, ttlCache.getElementWithExpiration(key).obj);
+ assertFalse(ttlCache.getElementWithExpiration(key).isExpired);
+
+ // Time = 120 seconds: still in cache.
+ timeSource.setCurrentTimeMillis(120 * 1000);
+ assertEquals(val, ttlCache.getElement(key));
+ assertEquals(val, ttlCache.getElementWithExpiration(key).obj);
+ assertFalse(ttlCache.getElementWithExpiration(key).isExpired);
+
+ // Time = 240 seconds - 1 ms: still in cache.
+ timeSource.setCurrentTimeMillis(240 * 1000 - 1);
+ assertEquals(val, ttlCache.getElement(key));
+ assertEquals(val, ttlCache.getElementWithExpiration(key).obj);
+ assertFalse(ttlCache.getElementWithExpiration(key).isExpired);
+
+ // Time = 300 seconds: out of cache.
+ timeSource.setCurrentTimeMillis(300 * 1000);
+ assertNull(ttlCache.getElement(key));
+ assertEquals(val, ttlCache.getElementWithExpiration(key).obj);
+ assertTrue(ttlCache.getElementWithExpiration(key).isExpired);
}
public void testRemoveFromCache() {
- TtlCache<String, String> ttlCache = makeTtlCache(120 * 1000, 360 * 1000);
- String key = "key1", val = "val1";
- ttlCache.addElement(key, val, 240 * 1000);
-
- // Time at 0: still in cache
- assertEquals(val, ttlCache.getElement(key));
-
- // Time at 120: still in cache, but removing it.
- timeSource.setCurrentTimeMillis(120 * 1000);
- assertEquals(val, ttlCache.removeElement(key));
-
- // Still at 120: should be gone.
- assertNull(ttlCache.removeElement(key));
+ TtlCache<String, String> ttlCache = makeTtlCache(120 * 1000, 360 * 1000);
+ String key = "key1", val = "val1";
+ ttlCache.addElement(key, val, 240 * 1000);
+
+ // Time at 0: still in cache
+ assertEquals(val, ttlCache.getElement(key));
+
+ // Time at 120: still in cache, but removing it.
+ timeSource.setCurrentTimeMillis(120 * 1000);
+ assertEquals(val, ttlCache.removeElement(key));
+
+ // Still at 120: should be gone.
+ assertNull(ttlCache.removeElement(key));
}
public void testCacheMinTtl() {
- TtlCache<String, String> ttlCache = makeTtlCache(120 * 1000, 360 * 1000);
- String key = "key1", val = "val1";
-
- // Add with a value below minTtl
- ttlCache.addElement(key, val, 60 * 1000);
-
- // Time 0: still in cache.
- assertEquals(val, ttlCache.getElement(key));
-
- // Time 65: still in cache - not expired! minTtl takes precedence.
- timeSource.setCurrentTimeMillis(65 * 1000);
- assertEquals(val, ttlCache.getElement(key));
-
- // Time 121: out of cache.
- timeSource.setCurrentTimeMillis(121 * 1000);
- assertNull(ttlCache.getElement(key));
+ TtlCache<String, String> ttlCache = makeTtlCache(120 * 1000, 360 * 1000);
+ String key = "key1", val = "val1";
+
+ // Add with a value below minTtl
+ ttlCache.addElement(key, val, 60 * 1000);
+
+ // Time 0: still in cache.
+ assertEquals(val, ttlCache.getElement(key));
+
+ // Time 65: still in cache - not expired! minTtl takes precedence.
+ timeSource.setCurrentTimeMillis(65 * 1000);
+ assertEquals(val, ttlCache.getElement(key));
+
+ // Time 121: out of cache.
+ timeSource.setCurrentTimeMillis(121 * 1000);
+ assertNull(ttlCache.getElement(key));
}
public void testCacheMaxTtl() {
- TtlCache<String, String> ttlCache = makeTtlCache(120 * 1000, 360 * 1000);
- String key = "key1", val = "val1";
-
- // Add with a value above maxTtl
- ttlCache.addElement(key, val, 400 * 1000);
-
- // Time 0: still in cache.
- assertEquals(val, ttlCache.getElement(key));
-
- // Time 360 - 1ms: still in cache.
- timeSource.setCurrentTimeMillis(360 * 1000 - 1);
- assertEquals(val, ttlCache.getElement(key));
-
- // Time 361: out of cache. Expired despite "desired" ttl of 400 secs.
- timeSource.setCurrentTimeMillis(361 * 1000);
- assertNull(ttlCache.getElement(key));
+ TtlCache<String, String> ttlCache = makeTtlCache(120 * 1000, 360 * 1000);
+ String key = "key1", val = "val1";
+
+ // Add with a value above maxTtl
+ ttlCache.addElement(key, val, 400 * 1000);
+
+ // Time 0: still in cache.
+ assertEquals(val, ttlCache.getElement(key));
+
+ // Time 360 - 1ms: still in cache.
+ timeSource.setCurrentTimeMillis(360 * 1000 - 1);
+ assertEquals(val, ttlCache.getElement(key));
+
+ // Time 361: out of cache. Expired despite "desired" ttl of 400 secs.
+ timeSource.setCurrentTimeMillis(361 * 1000);
+ assertNull(ttlCache.getElement(key));
}
}