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/08/29 00:20:34 UTC
svn commit: r690027 - 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: Thu Aug 28 15:20:33 2008
New Revision: 690027
URL: http://svn.apache.org/viewvc?rev=690027&view=rev
Log:
Cache class that enforces a TTL for entries in it.
Added:
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
Added: 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=690027&view=auto
==============================================================================
--- incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/cache/TtlCache.java (added)
+++ incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/cache/TtlCache.java Thu Aug 28 15:20:33 2008
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shindig.common.cache;
+
+import org.apache.shindig.common.util.TimeSource;
+
+/**
+ * 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.
+ */
+public class TtlCache<K, V> {
+ private final Cache<K, TimeoutPair<V>> baseCache;
+ private final long minTtl;
+ private final long maxTtl;
+ private TimeSource timeSource;
+
+ /**
+ * Create a new TtlCache with the given capacity and TTL values.
+ * The cache provider provides an implementation of the actual storage.
+ * @param cacheProvider Creator of the actual cache.
+ * @param capacity Size of the underlying cache.
+ * @param minTtl Minimum amount of time a given entry must stay in the cache, in millis.
+ * @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();
+ }
+
+ /**
+ * Retrieve an element from the cache by key. If there is no such element
+ * for that key in the cache, or if the element has timed out, null is returned.
+ * @param key Key whose element to look up.
+ * @return Element in the cache, if present and not timed out.
+ */
+ public V getElement(K key) {
+ return getElementMaybeRemove(key, false);
+ }
+
+ /**
+ * Add an element to the cache, with the intended amount of time
+ * 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 lifetime Intended lifetime, in millis, of the element's entry.
+ */
+ public void addElement(K key, V val, long lifetime) {
+ 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);
+ }
+ }
+
+ /**
+ * Removes element for the given key from the cache. Returns it if
+ * it hasn't yet expired.
+ * @param key Element key.
+ * @return Element value.
+ */
+ public V removeElement(K key) {
+ return getElementMaybeRemove(key, true);
+ }
+
+ /**
+ * 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ /**
+ * Actual stored content in the cache. Saves the cached object along
+ * with its expiration date (in ms).
+ * @param <V> Type of stored object.
+ */
+ private static final class TimeoutPair<V> {
+ private V cachedObj;
+ private long expiration;
+
+ private TimeoutPair(V cachedObj, long expiration) {
+ this.cachedObj = cachedObj;
+ this.expiration = expiration;
+ }
+ }
+}
Added: 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=690027&view=auto
==============================================================================
--- incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/cache/TtlCacheTest.java (added)
+++ incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/common/cache/TtlCacheTest.java Thu Aug 28 15:20:33 2008
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shindig.common.cache;
+
+import org.apache.shindig.common.util.FakeTimeSource;
+
+import junit.framework.TestCase;
+
+public class TtlCacheTest extends TestCase {
+ private FakeTimeSource timeSource;
+ private CacheProvider cacheProvider;
+
+ @Override
+ public void setUp() throws Exception {
+ 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;
+ }
+
+ 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));
+ }
+
+ 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));
+ }
+
+ 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));
+ }
+
+ 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));
+ }
+}