/hg/icedtea-web: TimedHashMap implements Map, new tests
aazores at icedtea.classpath.org
aazores at icedtea.classpath.org
Fri May 9 21:31:07 UTC 2014
changeset 69dd2eb02dbf in /hg/icedtea-web
details: http://icedtea.classpath.org/hg/icedtea-web?cmd=changeset;node=69dd2eb02dbf
author: Andrew Azores <aazores at redhat.com>
date: Fri May 09 17:30:54 2014 -0400
TimedHashMap implements Map, new tests
* netx/net/sourceforge/jnlp/util/TimedHashMap.java: implements Map
interface, added all missing methods. (timeStamps) removed, refactored to
only be composed of one backing map rather than two.
* tests/netx/unit/net/sourceforge/jnlp/util/TimedHashMapTest.java: new
test methods added
diffstat:
ChangeLog | 8 +
netx/net/sourceforge/jnlp/util/TimedHashMap.java | 167 ++++++++-
tests/netx/unit/net/sourceforge/jnlp/util/TimedHashMapTest.java | 176 ++++++++-
3 files changed, 311 insertions(+), 40 deletions(-)
diffs (427 lines):
diff -r 84fb0215c0bc -r 69dd2eb02dbf ChangeLog
--- a/ChangeLog Fri May 09 16:19:42 2014 -0400
+++ b/ChangeLog Fri May 09 17:30:54 2014 -0400
@@ -1,3 +1,11 @@
+2014-05-09 Andrew Azores <aazores at redhat.com>
+
+ * netx/net/sourceforge/jnlp/util/TimedHashMap.java: implements Map
+ interface, added all missing methods. (timeStamps) removed, refactored to
+ only be composed of one backing map rather than two.
+ * tests/netx/unit/net/sourceforge/jnlp/util/TimedHashMapTest.java: new
+ test methods added
+
2014-05-09 Andrew Azores <aazores at redhat.com>
* netx/net/sourceforge/jnlp/cache/ResourceTracker.java: (selectByFlag)
diff -r 84fb0215c0bc -r 69dd2eb02dbf netx/net/sourceforge/jnlp/util/TimedHashMap.java
--- a/netx/net/sourceforge/jnlp/util/TimedHashMap.java Fri May 09 16:19:42 2014 -0400
+++ b/netx/net/sourceforge/jnlp/util/TimedHashMap.java Fri May 09 17:30:54 2014 -0400
@@ -37,38 +37,90 @@
package net.sourceforge.jnlp.util;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import static java.util.Objects.requireNonNull;
import net.sourceforge.jnlp.util.logging.OutputController;
-import java.util.HashMap;
-
-import net.sourceforge.jnlp.runtime.JNLPRuntime;
/**
* Simple utility class that extends HashMap by adding an expiry to the entries.
*
- * This map stores entries, and returns them only if the entries were last accessed within time t=10 seconds
+ * This map stores entries, and returns them only if the entries were last accessed within a specified timeout period.
+ * Otherwise, null is returned.
+ *
+ * This map does not allow null keys but does allow null values.
*
* @param K The key type
* @param V The Object type
*/
-public class TimedHashMap<K, V> {
+public class TimedHashMap<K, V> implements Map<K, V> {
- HashMap<K,V> actualMap = new HashMap<K,V>();
- HashMap<K, Long> timeStamps = new HashMap<K, Long>();
- Long expiry = 10000000000L;
+ private static class TimedEntry<T> {
+ private final T value;
+ private long timestamp;
- public void setExpiry(long expiry) {
- this.expiry = expiry;
+ public TimedEntry(final T value) {
+ this.value = value;
+ updateTimestamp();
+ }
+
+ public void updateTimestamp() {
+ timestamp = System.nanoTime();
+ }
+ }
+
+ private static final long DEFAULT_TIMEOUT = TimeUnit.SECONDS.toNanos(10);
+
+ private final HashMap<K, TimedEntry<V>> actualMap = new HashMap<>();
+ private long timeout = DEFAULT_TIMEOUT;
+
+ public TimedHashMap() {
+ this(DEFAULT_TIMEOUT, TimeUnit.NANOSECONDS);
}
/**
- * Store the item in the map and associate a timestamp with it
+ * Create a new map with a non-default entry timeout period
+ * @param unit the units of the timeout
+ * @param timeout the length of the timeout
+ */
+ public TimedHashMap(final long timeout, final TimeUnit unit) {
+ setTimeout(timeout, unit);
+ }
+
+ /**
+ * Specify how long (in nanoseconds) entries are valid for
+ * @param unit the units of the timeout
+ * @param timeout the length of the timeout
+ */
+ public void setTimeout(final long timeout, final TimeUnit unit) {
+ this.timeout = unit.toNanos(timeout);
+ }
+
+ /**
+ * Store the item in the map and associate a timestamp with it. null is not accepted as a key.
*
* @param key The key
* @param value The value to store
*/
- public V put(K key, V value) {
- timeStamps.put(key, System.nanoTime());
- return actualMap.put(key, value);
+ @Override
+ public V put(final K key, final V value) {
+ requireNonNull(key);
+ final TimedEntry<V> oldEntry = actualMap.get(key);
+ final V oldValue;
+ if (oldEntry != null) {
+ oldValue = oldEntry.value;
+ } else {
+ oldValue = null;
+ }
+ actualMap.put(key, new TimedEntry<>(value));
+ return oldValue;
}
/**
@@ -79,23 +131,94 @@
*
* @param key The key
*/
- public V get(K key) {
- Long now = System.nanoTime();
+ @Override
+ public V get(final Object key) {
+ final long now = System.nanoTime();
if (actualMap.containsKey(key)) {
- Long age = now - timeStamps.get(key);
+ final TimedEntry<V> timedEntry = actualMap.get(key);
+ final long age = now - timedEntry.timestamp;
// Item exists. If it has not expired, renew its access time and return it
- if (age <= expiry) {
- OutputController.getLogger().log(OutputController.Level.ERROR_DEBUG, "Returning proxy " + actualMap.get(key) + " from cache for " + key);
- timeStamps.put(key, System.nanoTime());
- return actualMap.get(key);
+ if (age <= timeout) {
+ OutputController.getLogger().log(OutputController.Level.ERROR_DEBUG, "Returning entry " + actualMap.get(key) + " from cache for " + key);
+ timedEntry.updateTimestamp();
+ return timedEntry.value;
} else {
- OutputController.getLogger().log(OutputController.Level.ERROR_DEBUG, "Proxy cache for " + key + " has expired (age=" + (age * 1e-9) + " seconds)");
+ OutputController.getLogger().log(OutputController.Level.ERROR_DEBUG, "Cached entry for " + key + " has expired (age=" + (age * 1e-9) + " seconds)");
}
}
return null;
}
+
+ @Override
+ public boolean containsKey(final Object key) {
+ return actualMap.containsKey(key);
+ }
+
+ @Override
+ public int size() {
+ return actualMap.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return actualMap.isEmpty();
+ }
+
+ @Override
+ public boolean containsValue(final Object value) {
+ for (final TimedEntry<V> entry : actualMap.values()) {
+ if (Objects.equals(entry.value, value)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public V remove(final Object key) {
+ if (actualMap.containsKey(key)) {
+ return actualMap.remove(key).value;
+ }
+ return null;
+ }
+
+ @Override
+ public void putAll(Map<? extends K, ? extends V> m) {
+ for (final Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
+ actualMap.put(entry.getKey(), new TimedEntry<V>(entry.getValue()));
+ }
+ }
+
+ @Override
+ public void clear() {
+ actualMap.clear();
+ }
+
+ @Override
+ public Set<K> keySet() {
+ return new HashSet<>(actualMap.keySet());
+ }
+
+ @Override
+ public Collection<V> values() {
+ final Collection<V> values = new ArrayList<>(actualMap.size());
+ for (final TimedEntry<V> value : actualMap.values()) {
+ values.add(value.value);
+ }
+ return values;
+ }
+
+ @Override
+ public Set<Map.Entry<K, V>> entrySet() {
+ final Map<K, V> strippedMap = new HashMap<>(actualMap.size());
+ for (final Map.Entry<K, TimedEntry<V>> entry : actualMap.entrySet()) {
+ strippedMap.put(entry.getKey(), entry.getValue().value);
+ }
+ return strippedMap.entrySet();
+ }
+
}
diff -r 84fb0215c0bc -r 69dd2eb02dbf tests/netx/unit/net/sourceforge/jnlp/util/TimedHashMapTest.java
--- a/tests/netx/unit/net/sourceforge/jnlp/util/TimedHashMapTest.java Fri May 09 16:19:42 2014 -0400
+++ b/tests/netx/unit/net/sourceforge/jnlp/util/TimedHashMapTest.java Fri May 09 17:30:54 2014 -0400
@@ -1,4 +1,4 @@
-/*
+/* TimedHashMapTest.java
Copyright (C) 2014 Red Hat, Inc.
This file is part of IcedTea.
@@ -37,34 +37,174 @@
package net.sourceforge.jnlp.util;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Before;
import org.junit.Test;
-import static org.junit.Assert.*;
-
public class TimedHashMapTest {
+ private TimedHashMap<Object, Object> testMap;
+ private Object o1, o2, o3, o4;
+
+ @Before
+ public void resetTestMap() {
+ testMap = new TimedHashMap<>();
+ o1 = new Object();
+ o2 = new Object();
+ o3 = new Object();
+ o4 = new Object();
+ }
+
@Test
public void testPutAndGet() {
- final TimedHashMap<Object, Object> map = new TimedHashMap<>();
- final Object o1 = new Object(), o2 = new Object(), o3 = new Object(), o4 = new Object();
- map.put(o1, o2);
- map.put(o2, o4);
- map.put(o3, o4);
- assertEquals(o2, map.get(o1));
- assertEquals(o4, map.get(o2));
- assertEquals(o4, map.get(o3));
- map.put(o1, o3);
- assertEquals(o3, map.get(o1));
+ testMap.put(o1, o2);
+ testMap.put(o2, o4);
+ testMap.put(o3, o4);
+ assertEquals("map[o1] != o2", o2, testMap.get(o1));
+ assertEquals("map[o2] != o4", o4, testMap.get(o2));
+ assertEquals("map[o3] != o4", o4, testMap.get(o3));
+ testMap.put(o1, o3);
+ assertEquals("map[o1] != o3", o3, testMap.get(o1));
}
@Test
public void testEntryExpiry() throws Exception {
- final TimedHashMap<Object, Object> map = new TimedHashMap<>();
- final Object o1 = new Object(), o2 = new Object();
- map.setExpiry(0l); // immediate expiry
- map.put(o1, o2);
+ testMap.setTimeout(0, TimeUnit.NANOSECONDS); // immediate expiry
+ testMap.put(o1, o2);
Thread.sleep(5); // so we don't manage to put and get in the same nanosecond
- assertNull(map.get(o1));
+ assertNull("map[o1] should have expired", testMap.get(o1));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testPutNullKey() {
+ testMap.put(null, o1);
+ }
+
+ @Test
+ public void testPutNullValue() {
+ testMap.put(o1, null);
+ assertNull("map[o1] != null", testMap.get(o1));
+ assertTrue("testMap should contain the key o1", testMap.containsKey(o1));
+ }
+
+ @Test
+ public void testContainsKey() {
+ testMap.put(o1, o2);
+ assertTrue("testMap should contain the key o1", testMap.containsKey(o1));
+ }
+
+ @Test
+ public void testSize() {
+ assertEquals(0, testMap.size());
+ testMap.put(o1, o2);
+ assertEquals(1, testMap.size());
+ }
+
+ @Test
+ public void testIsEmpty() {
+ assertTrue("map should be empty", testMap.isEmpty());
+ testMap.put(o1, o2);
+ assertFalse("map should not be empty", testMap.isEmpty());
+ }
+
+ @Test
+ public void testContainsValue() {
+ assertFalse("map should not contain o2", testMap.containsValue(o2));
+ testMap.put(o1, o2);
+ assertTrue("map does not contain o2", testMap.containsValue(o2));
+ }
+
+ @Test
+ public void testContainsValueNull() {
+ assertFalse("map should not contain null value", testMap.containsValue(null));
+ testMap.put(o1, null);
+ assertTrue("map does not contain null value", testMap.containsValue(null));
+ }
+
+ @Test
+ public void testRemove() {
+ testMap.put(o1, o2);
+ o3 = testMap.remove(o1);
+ assertEquals("o2 != o3", o2, o3);
+ assertFalse("map should not contain o1", testMap.containsKey(o1));
+ }
+
+ @Test
+ public void testRemoveFromEmpty() {
+ o2 = testMap.remove(o1);
+ assertNull("o2 should be null", o2);
+ }
+
+ @Test
+ public void testPutAll() {
+ final Map<Object, Object> newMap = new HashMap<>();
+ newMap.put(o1, o2);
+ newMap.put(o3, o4);
+ testMap.putAll(newMap);
+ assertTrue("map should contain key o1", testMap.containsKey(o1));
+ assertTrue("map should contain value o2", testMap.containsValue(o2));
+ assertTrue("map should contain key o3", testMap.containsKey(o3));
+ assertTrue("map should contain value o4", testMap.containsValue(o4));
+ assertEquals("map[o1] != o2", o2, testMap.get(o1));
+ assertEquals("map[o3] != o4", o4, testMap.get(o3));
+ assertEquals(2, testMap.size());
+ }
+
+ @Test
+ public void testClear() {
+ testMap.put(o1, o2);
+ testMap.clear();
+ assertEquals(0, testMap.size());
+ assertFalse("map should not contain key o1", testMap.containsKey(o1));
+ assertFalse("map should not contain value o2", testMap.containsValue(o2));
+ }
+
+ @Test
+ public void testKeySet() {
+ testMap.put(o1, o2);
+ Set<Object> keys = testMap.keySet();
+ assertNotNull("keyset should not be null", keys);
+ assertTrue("keyset should contain o1", keys.contains(o1));
+ assertEquals(1, keys.size());
+ }
+
+ @Test
+ public void testValues() {
+ testMap.put(o1, o2);
+ Collection<Object> values = testMap.values();
+ assertNotNull("values collection should not be null", values);
+ assertTrue("values collection should contain o2", values.contains(o2));
+ assertEquals(1, values.size());
+ }
+
+ @Test
+ public void testEntrySet() {
+ testMap.put(o1, o2);
+ testMap.put(o3, o4);
+ Set<Map.Entry<Object, Object>> entrySet = testMap.entrySet();
+ assertNotNull("entryset should not be null", entrySet);
+ assertEquals(2, entrySet.size());
+ for (final Map.Entry<Object, Object> entry : entrySet) {
+ final Object key = entry.getKey();
+ final Object value = entry.getValue();
+ if (key.equals(o1)) {
+ assertEquals("entry with key o1 should have value o2", o2, value);
+ }
+ if (key.equals(o3)) {
+ assertEquals("entry with key o3 should have value o4", o4, value);
+ }
+ }
}
}
More information about the distro-pkg-dev
mailing list