commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From brentwor...@apache.org
Subject svn commit: r1376827 - in /commons/proper/collections/trunk/src: main/java/org/apache/commons/collections/map/PassiveExpiringMap.java test/java/org/apache/commons/collections/map/TestPassiveExpiringMap.java
Date Fri, 24 Aug 2012 05:08:36 GMT
Author: brentworden
Date: Fri Aug 24 05:08:36 2012
New Revision: 1376827

URL: http://svn.apache.org/viewvc?rev=1376827&view=rev
Log:
COLLECTIONS-241 added PassiveExpiringMap and tests

Added:
    commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/map/PassiveExpiringMap.java
  (with props)
    commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/map/TestPassiveExpiringMap.java

Added: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/map/PassiveExpiringMap.java
URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/map/PassiveExpiringMap.java?rev=1376827&view=auto
==============================================================================
--- commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/map/PassiveExpiringMap.java
(added)
+++ commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/map/PassiveExpiringMap.java
Fri Aug 24 05:08:36 2012
@@ -0,0 +1,577 @@
+/*
+ * 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.commons.collections.map;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Decorates a <code>Map</code> to evict expired entries once their expiration
+ * time has been reached.
+ * <p>
+ * When putting a key-value pair in the map this decorator uses a
+ * {@link ExpirationPolicy} to determine how long the entry should remain alive
+ * as defined by an expiration time value.
+ * </p>
+ * <p>
+ * When accessing the mapped value for a key, its expiration time is checked,
+ * and if it is a negative value or if it is greater than the current time, the
+ * mapped value is returned. Otherwise, the key is removed from the decorated
+ * map, and <code>null</code> is returned.
+ * </p>
+ * <p>
+ * When invoking methods that involve accessing the entire map contents (i.e
+ * {@link #containsKey(Object)}, {@link #entrySet()}, etc.) this decorator
+ * removes all expired entries prior to actually completing the invocation.
+ * </p>
+ * <p>
+ * <strong>Note that {@link PassiveExpiringMap} is not synchronized and is not
+ * thread-safe.</strong> If you wish to use this map from multiple threads
+ * concurrently, you must use appropriate synchronization. The simplest approach
+ * is to wrap this map using {@link java.util.Collections#synchronizedMap(Map)}.
+ * This class may throw exceptions when accessed by concurrent threads without
+ * synchronization.
+ * </p>
+ * 
+ * @param <K>
+ *            the type of the keys in the map
+ * 
+ * @param <V>
+ *            the type of the values in the map
+ * 
+ * @since 4.0
+ * @version $Id: $
+ */
+public class PassiveExpiringMap<K, V> extends AbstractMapDecorator<K, V>
+		implements Serializable {
+
+	/**
+	 * A {@link ExpirationPolicy} that returns a expiration time that is a
+	 * constant about of time in the future from the current time.
+	 * 
+	 * @param <K>
+	 *            the type of the keys in the map
+	 * @param <V>
+	 *            the type of the values in the map
+	 * 
+	 * @since 4.0
+	 * @version $Id: $
+	 */
+	public static class ConstantTimeToLiveExpirationPolicy<K, V> implements
+			ExpirationPolicy<K, V> {
+
+		/** Serialization version */
+		private static final long serialVersionUID = 1L;
+
+		/** the constant time-to-live value measured in milliseconds. */
+		private final long timeToLiveMillis;
+
+		/**
+		 * Default constructor. Constructs a policy using a negative
+		 * time-to-live value that results in entries never expiring.
+		 */
+		public ConstantTimeToLiveExpirationPolicy() {
+			this(-1L);
+		}
+
+		/**
+		 * Construct a policy with the given time-to-live constant measured in
+		 * milliseconds. A negative time-to-live value indicates entries never
+		 * expire. A zero time-to-live value indicates entries expire (nearly)
+		 * immediately.
+		 * 
+		 * @param timeToLiveMillis
+		 *            the constant amount of time (in milliseconds) an entry is
+		 *            available before it expires. A negative value results in
+		 *            entries that NEVER expire. A zero value results in entries
+		 *            that ALWAYS expire.
+		 */
+		public ConstantTimeToLiveExpirationPolicy(long timeToLiveMillis) {
+			super();
+			this.timeToLiveMillis = timeToLiveMillis;
+		}
+
+		/**
+		 * Construct a policy with the given time-to-live constant measured in
+		 * the given time unit of measure.
+		 * 
+		 * @param timeToLive
+		 *            the constant amount of time an entry is available before
+		 *            it expires. A negative value results in entries that NEVER
+		 *            expire. A zero value results in entries that ALWAYS
+		 *            expire.
+		 * @param timeUnit
+		 *            the unit of time for the <code>timeToLive</code>
+		 *            parameter, must not be null.
+		 * @throws IllegalArgumentException
+		 *             if the time unit is null.
+		 */
+		public ConstantTimeToLiveExpirationPolicy(long timeToLive,
+				TimeUnit timeUnit) {
+			this(validateAndConvertToMillis(timeToLive, TimeUnit.MILLISECONDS));
+		}
+
+		/**
+		 * Determine the expiration time for the given key-value entry.
+		 * 
+		 * @param key
+		 *            the key for the entry (ignored).
+		 * @param value
+		 *            the value for the entry (ignored).
+		 * @return if {@link #timeToLiveMillis} &ge; 0, an expiration time of
+		 *         {@link #timeToLiveMillis} +
+		 *         {@link System#currentTimeMillis()} is returned. Otherwise, -1
+		 *         is returned indicating the entry never expires.
+		 */
+		public long expirationTime(K key, V value) {
+			if (timeToLiveMillis >= 0L) {
+				// avoid numerical overflow
+				long now = System.currentTimeMillis();
+				if (now > Long.MAX_VALUE - timeToLiveMillis) {
+					// expiration would be greater than Long.MAX_VALUE
+					// never expire
+					return -1;
+				}
+
+				// timeToLiveMillis in the future
+				return now + timeToLiveMillis;
+			}
+			
+			// never expire
+			return -1L;
+		}
+	}
+
+	/**
+	 * A policy to determine the expiration time for key-value entries.
+	 * 
+	 * @param <K>
+	 *            the key object type.
+	 * @param <V>
+	 *            the value object type
+	 * 
+	 * @since 4.0
+	 * @version $Id: $
+	 */
+	public static interface ExpirationPolicy<K, V> extends Serializable {
+		/**
+		 * Determine the expiration time for the given key-value entry.
+		 * 
+		 * @param key
+		 *            the key for the entry.
+		 * @param value
+		 *            the value for the entry.
+		 * @return the expiration time value measured in milliseconds. A
+		 *         negative return value indicates the entry never expires.
+		 */
+		long expirationTime(K key, V value);
+	}
+
+	/** Serialization version */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * First validate the input parameters. If the parameters are valid, convert
+	 * the given time measured in the given units to the same time measured in
+	 * milliseconds. If the parameters are invalid, an
+	 * {@link IllegalArgumentException} is thrown.
+	 * 
+	 * @param timeToLive
+	 *            the constant amount of time an entry is available before it
+	 *            expires. A negative value results in entries that NEVER
+	 *            expire. A zero value results in entries that ALWAYS expire.
+	 * @param timeUnit
+	 *            the unit of time for the <code>timeToLive</code> parameter,
+	 *            must not be null.
+	 * @throws IllegalArgumentException
+	 *             if the time unit is null.
+	 */
+	private static long validateAndConvertToMillis(long timeToLive,
+			TimeUnit timeUnit) {
+		if (timeUnit == null) {
+			throw new IllegalArgumentException("Time unit must not be null");
+		}
+		return timeUnit.convert(timeToLive, TimeUnit.MILLISECONDS);
+	}
+
+	/** map used to manage expiration times for the actual map entries. */
+	private final Map<Object, Long> expirationMap = new HashMap<Object, Long>();
+
+	/** the policy used to determine time-to-live values for map entries. */
+	private final ExpirationPolicy<K, V> expiringPolicy;
+
+	/**
+	 * Default constructor. Constructs a map decorator that results in entries
+	 * NEVER expiring.
+	 */
+	public PassiveExpiringMap() {
+		this(-1L);
+	}
+
+	/**
+	 * Construct a map decorator using the given expiration policy to determine
+	 * expiration times.
+	 * 
+	 * @param expiringPolicy
+	 *            the policy used to determine expiration times of entries as
+	 *            they are added.
+	 */
+	public PassiveExpiringMap(ExpirationPolicy<K, V> expiringPolicy) {
+		this(expiringPolicy, new HashMap<K, V>());
+	}
+
+	/**
+	 * Construct a map decorator that decorates the given map and uses the given
+	 * expiration policy to determine expiration times. If there are any
+	 * elements already in the map being decorated, they will NEVER expire
+	 * unless they are replaced.
+	 * 
+	 * @param expiringPolicy
+	 *            the policy used to determine expiration times of entries as
+	 *            they are added.
+	 * @param map
+	 *            the map to decorate, must not be null.
+	 * @throws IllegalArgumentException
+	 *             if the map is null.
+	 */
+	public PassiveExpiringMap(ExpirationPolicy<K, V> expiringPolicy,
+			Map<K, V> map) {
+		super(map);
+		if (expiringPolicy == null) {
+			throw new IllegalArgumentException("Policy must not be null.");
+		}
+		this.expiringPolicy = expiringPolicy;
+	}
+
+	/**
+	 * Construct a map decorator that decorates the given map using the given
+	 * time-to-live value measured in milliseconds to create and use a
+	 * {@link ConstantTimeToLiveExpirationPolicy} expiration policy.
+	 * 
+	 * @param timeToLiveMillis
+	 *            the constant amount of time (in milliseconds) an entry is
+	 *            available before it expires. A negative value results in
+	 *            entries that NEVER expire. A zero value results in entries
+	 *            that ALWAYS expire.
+	 */
+	public PassiveExpiringMap(long timeToLiveMillis) {
+		this(new ConstantTimeToLiveExpirationPolicy<K, V>(timeToLiveMillis),
+				new HashMap<K, V>());
+	}
+
+	/**
+	 * Construct a map decorator using the given time-to-live value measured in
+	 * milliseconds to create and use a
+	 * {@link ConstantTimeToLiveExpirationPolicy} expiration policy. If there
+	 * are any elements already in the map being decorated, they will NEVER
+	 * expire unless they are replaced.
+	 * 
+	 * @param timeToLiveMillis
+	 *            the constant amount of time (in milliseconds) an entry is
+	 *            available before it expires. A negative value results in
+	 *            entries that NEVER expire. A zero value results in entries
+	 *            that ALWAYS expire.
+	 * @param map
+	 *            the map to decorate, must not be null.
+	 * @throws IllegalArgumentException
+	 *             if the map is null.
+	 */
+	public PassiveExpiringMap(long timeToLiveMillis, Map<K, V> map) {
+		this(new ConstantTimeToLiveExpirationPolicy<K, V>(timeToLiveMillis),
+				map);
+	}
+
+	/**
+	 * Construct a map decorator using the given time-to-live value measured in
+	 * the given time units of measure to create and use a
+	 * {@link ConstantTimeToLiveExpirationPolicy} expiration policy.
+	 * 
+	 * @param timeToLive
+	 *            the constant amount of time an entry is available before it
+	 *            expires. A negative value results in entries that NEVER
+	 *            expire. A zero value results in entries that ALWAYS expire.
+	 * @param timeUnit
+	 *            the unit of time for the <code>timeToLive</code> parameter,
+	 *            must not be null.
+	 * @throws IllegalArgumentException
+	 *             if the time unit is null.
+	 */
+	public PassiveExpiringMap(long timeToLive, TimeUnit timeUnit) {
+		this(validateAndConvertToMillis(timeToLive, timeUnit));
+	}
+
+	/**
+	 * Construct a map decorator that decorates the given map using the given
+	 * time-to-live value measured in the given time units of measure to create
+	 * {@link ConstantTimeToLiveExpirationPolicy} expiration policy. This policy
+	 * is used to determine expiration times. If there are any elements already
+	 * in the map being decorated, they will NEVER expire unless they are
+	 * replaced.
+	 * 
+	 * @param timeToLive
+	 *            the constant amount of time an entry is available before it
+	 *            expires. A negative value results in entries that NEVER
+	 *            expire. A zero value results in entries that ALWAYS expire.
+	 * @param timeUnit
+	 *            the unit of time for the <code>timeToLive</code> parameter,
+	 *            must not be null.
+	 * @throws IllegalArgumentException
+	 *             if the time unit is null.
+	 * @param map
+	 *            the map to decorate, must not be null.
+	 * @throws IllegalArgumentException
+	 *             if the map is null.
+	 */
+	public PassiveExpiringMap(long timeToLive, TimeUnit timeUnit, Map<K, V> map) {
+		this(validateAndConvertToMillis(timeToLive, timeUnit), map);
+	}
+
+	/**
+	 * Constructs a map decorator that decorates the given map and results in
+	 * entries NEVER expiring. If there are any elements already in the map
+	 * being decorated, they also will NEVER expire.
+	 * 
+	 * @param map
+	 *            the map to decorate, must not be null.
+	 * @throws IllegalArgumentException
+	 *             if the map is null.
+	 */
+	public PassiveExpiringMap(Map<K, V> map) {
+		this(-1L, map);
+	}
+
+	/**
+	 * Normal {@link Map#clear()} behavior with the addition of clearing all
+	 * expiration entries as well.
+	 */
+	@Override
+	public void clear() {
+		super.clear();
+		expirationMap.clear();
+	}
+
+	/**
+	 * All expired entries are removed from the map prior to determining the
+	 * contains result.
+	 */
+	@Override
+	public boolean containsKey(Object key) {
+		removeIfExpired(key, now());
+		return super.containsKey(key);
+	}
+
+	/**
+	 * All expired entries are removed from the map prior to determining the
+	 * contains result.
+	 */
+	@Override
+	public boolean containsValue(Object value) {
+		removeAllExpired(now());
+		return super.containsValue(value);
+	}
+
+	/**
+	 * All expired entries are removed from the map prior to returning the entry
+	 * set.
+	 */
+	@Override
+	public Set<Entry<K, V>> entrySet() {
+		removeAllExpired(now());
+		return super.entrySet();
+	}
+
+	/**
+	 * All expired entries are removed from the map prior to returning the entry
+	 * value.
+	 */
+	@Override
+	public V get(Object key) {
+		removeIfExpired(key, now());
+		return super.get(key);
+	}
+
+	/**
+	 * All expired entries are removed from the map prior to determining if it
+	 * is empty.
+	 */
+	@Override
+	public boolean isEmpty() {
+		removeAllExpired(now());
+		return super.isEmpty();
+	}
+
+	/**
+	 * Determines if the given expiration time is less than <code>now</code>
+	 * 
+	 * @param now
+	 *            the time in milliseconds used to compare against the
+	 *            expiration time.
+	 * @param expirationTimeObject
+	 *            the expiration time value retrieved from
+	 *            {@link #expirationMap}, can be null.
+	 * @return <code>true</code> if <code>expirationTimeObject</code>
is &ge; 0
+	 *         and <code>expirationTimeObject</code> &lt; <code>now</code>.
+	 *         <code>false</code> otherwise.
+	 */
+	private boolean isExpired(long now, Long expirationTimeObject) {
+		if (expirationTimeObject != null) {
+			long expirationTime = expirationTimeObject.longValue();
+			return (expirationTime >= 0 && now >= expirationTime);
+		}
+		return false;
+	}
+
+	/**
+	 * All expired entries are removed from the map prior to returning the key
+	 * set.
+	 */
+	@Override
+	public Set<K> keySet() {
+		removeAllExpired(now());
+		return super.keySet();
+	}
+
+	/**
+	 * The current time in milliseconds.
+	 */
+	private long now() {
+		return System.currentTimeMillis();
+	}
+
+	@Override
+	public V put(K key, V value) {
+		return put(key, value, now());
+	}
+
+	/**
+	 * Add the given key-value pair to this map as well as recording the entry's
+	 * expiration time based on the current time in milliseconds,
+	 * <code>now</code> and this map's {@link #expiringPolicy}.
+	 */
+	private V put(K key, V value, long now) {
+		// record expiration time of new entry
+		long expirationTime = expiringPolicy.expirationTime(key, value);
+		expirationMap.put(key, Long.valueOf(expirationTime));
+
+		return super.put(key, value);
+	}
+
+	@Override
+	public void putAll(Map<? extends K, ? extends V> mapToCopy) {
+		for (Map.Entry<? extends K, ? extends V> entry : mapToCopy.entrySet()) {
+			put(entry.getKey(), entry.getValue());
+		}
+	}
+
+	/**
+	 * Normal {@link Map#remove(Object)} behavior with the addition of removing
+	 * any expiration entry as well.
+	 */
+	@Override
+	public V remove(Object key) {
+		expirationMap.remove(key);
+		return super.remove(key);
+	}
+
+	/**
+	 * Removes all entries in the map whose expiration time is less than
+	 * <code>now</code>. The exceptions are entries with negative expiration
+	 * times; those entries are never removed.
+	 * 
+	 * @see #isExpired(long, Long)
+	 */
+	private void removeAllExpired(long now) {
+		Iterator<Map.Entry<Object, Long>> iter = expirationMap.entrySet()
+				.iterator();
+		while (iter.hasNext()) {
+			Map.Entry<Object, Long> expirationEntry = iter.next();
+			if (isExpired(now, expirationEntry.getValue())) {
+				// remove entry from collection
+				super.remove(expirationEntry.getKey());
+				// remove entry from expiration map
+				iter.remove();
+			}
+		}
+	}
+
+	/**
+	 * Removes the entry with the given key if the entry's expiration time is
+	 * less than <code>now</code>. If the entry has a negative expiration time,
+	 * the entry is never removed.
+	 */
+	private void removeIfExpired(Object key, long now) {
+		Long expirationTimeObject = expirationMap.get(key);
+		if (isExpired(now, expirationTimeObject)) {
+			remove(key);
+		}
+	}
+
+	/**
+	 * All expired entries are removed from the map prior to returning the size.
+	 */
+	@Override
+	public int size() {
+		removeAllExpired(now());
+		return super.size();
+	}
+
+	/**
+	 * Read the map in using a custom routine.
+	 * 
+	 * @param in
+	 *            the input stream
+	 * @throws IOException
+	 * @throws ClassNotFoundException
+	 */
+	@SuppressWarnings("unchecked")
+	// (1) should only fail if input stream is incorrect
+	private void readObject(ObjectInputStream in) throws IOException,
+			ClassNotFoundException {
+		in.defaultReadObject();
+		map = (Map<K, V>) in.readObject(); // (1)
+	}
+
+	/**
+	 * Write the map out using a custom routine.
+	 * 
+	 * @param out
+	 *            the output stream
+	 * @throws IOException
+	 */
+	private void writeObject(ObjectOutputStream out) throws IOException {
+		out.defaultWriteObject();
+		out.writeObject(map);
+	}
+
+	/**
+	 * All expired entries are removed from the map prior to returning the value
+	 * collection.
+	 */
+	@Override
+	public Collection<V> values() {
+		removeAllExpired(now());
+		return super.values();
+	}
+}

Propchange: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/map/PassiveExpiringMap.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/map/TestPassiveExpiringMap.java
URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/map/TestPassiveExpiringMap.java?rev=1376827&view=auto
==============================================================================
--- commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/map/TestPassiveExpiringMap.java
(added)
+++ commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/map/TestPassiveExpiringMap.java
Fri Aug 24 05:08:36 2012
@@ -0,0 +1,207 @@
+package org.apache.commons.collections.map;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.Test;
+
+import org.apache.commons.collections.BulkTest;
+import org.apache.commons.collections.map.PassiveExpiringMap.ExpirationPolicy;
+
+public class TestPassiveExpiringMap<K, V> extends AbstractTestMap<K, V> {
+
+	private static class TestExpirationPolicy implements
+			ExpirationPolicy<Integer, String> {
+
+		private static final long serialVersionUID = 1L;
+
+		public long expirationTime(Integer key, String value) {
+			// odd keys expire immediately, even keys never expire
+			if (key == null) {
+				return 0;
+			}
+
+			if (key.intValue() % 2 == 0) {
+				return -1;
+			}
+
+			return 0;
+		}
+	}
+
+	public static Test suite() {
+		return BulkTest.makeSuite(TestPassiveExpiringMap.class);
+	}
+
+	public TestPassiveExpiringMap(String testName) {
+		super(testName);
+	}
+
+	// public void testCreate() throws Exception {
+	// writeExternalFormToDisk((java.io.Serializable) makeObject(),
+	// "PassiveExpiringMap.emptyCollection.version4.obj");
+	//
+	// writeExternalFormToDisk((java.io.Serializable) makeFullMap(),
+	// "PassiveExpiringMap.fullCollection.version4.obj");
+	// }
+
+	@Override
+	public String getCompatibilityVersion() {
+		return "4";
+	}
+
+	private Map<Integer, String> makeDecoratedTestMap() {
+		Map<Integer, String> m = new HashMap<Integer, String>();
+		m.put(Integer.valueOf(1), "one");
+		m.put(Integer.valueOf(2), "two");
+		m.put(Integer.valueOf(3), "three");
+		m.put(Integer.valueOf(4), "four");
+		m.put(Integer.valueOf(5), "five");
+		m.put(Integer.valueOf(6), "six");
+		return new PassiveExpiringMap<Integer, String>(
+				new TestExpirationPolicy(), m);
+	}
+
+	@Override
+	public Map<K, V> makeObject() {
+		return new PassiveExpiringMap<K, V>();
+	}
+
+	private Map<Integer, String> makeTestMap() {
+		Map<Integer, String> m = new PassiveExpiringMap<Integer, String>(
+				new TestExpirationPolicy());
+		m.put(Integer.valueOf(1), "one");
+		m.put(Integer.valueOf(2), "two");
+		m.put(Integer.valueOf(3), "three");
+		m.put(Integer.valueOf(4), "four");
+		m.put(Integer.valueOf(5), "five");
+		m.put(Integer.valueOf(6), "six");
+		return m;
+	}
+
+	public void testConstructors() {
+		try {
+			Map<String, String> map = null;
+			new PassiveExpiringMap<String, String>(map);
+			fail("constructor - exception should have been thrown.");
+		} catch (IllegalArgumentException ex) {
+			// success
+		}
+
+		try {
+			ExpirationPolicy<String, String> policy = null;
+			new PassiveExpiringMap<String, String>(policy);
+			fail("constructor - exception should have been thrown.");
+		} catch (IllegalArgumentException ex) {
+			// success
+		}
+
+		try {
+			TimeUnit unit = null;
+			new PassiveExpiringMap<String, String>(10L, unit);
+			fail("constructor - exception should have been thrown.");
+		} catch (IllegalArgumentException ex) {
+			// success
+		}
+	}
+
+	public void testContainsKey() {
+		Map<Integer, String> m = makeTestMap();
+		assertFalse(m.containsKey(Integer.valueOf(1)));
+		assertFalse(m.containsKey(Integer.valueOf(3)));
+		assertFalse(m.containsKey(Integer.valueOf(5)));
+		assertTrue(m.containsKey(Integer.valueOf(2)));
+		assertTrue(m.containsKey(Integer.valueOf(4)));
+		assertTrue(m.containsKey(Integer.valueOf(6)));
+	}
+
+	public void testContainsValue() {
+		Map<Integer, String> m = makeTestMap();
+		assertFalse(m.containsValue("one"));
+		assertFalse(m.containsValue("three"));
+		assertFalse(m.containsValue("five"));
+		assertTrue(m.containsValue("two"));
+		assertTrue(m.containsValue("four"));
+		assertTrue(m.containsValue("six"));
+	}
+
+	public void testDecoratedMap() {
+		// entries shouldn't expire
+		Map<Integer, String> m = makeDecoratedTestMap();
+		assertEquals(6, m.size());
+		assertEquals("one", m.get(Integer.valueOf(1)));
+
+		// removing a single item shouldn't affect any other items
+		assertEquals("two", m.get(Integer.valueOf(2)));
+		m.remove(Integer.valueOf(2));
+		assertEquals(5, m.size());
+		assertEquals("one", m.get(Integer.valueOf(1)));
+		assertNull(m.get(Integer.valueOf(2)));
+
+		// adding a single, even item shouldn't affect any other items
+		assertNull(m.get(Integer.valueOf(2)));
+		m.put(Integer.valueOf(2), "two");
+		assertEquals(6, m.size());
+		assertEquals("one", m.get(Integer.valueOf(1)));
+		assertEquals("two", m.get(Integer.valueOf(2)));
+
+		// adding a single, odd item (one that expires) shouldn't affect any
+		// other items
+		// put the entry expires immediately
+		m.put(Integer.valueOf(1), "one-one");
+		assertEquals(5, m.size());
+		assertNull(m.get(Integer.valueOf(1)));
+		assertEquals("two", m.get(Integer.valueOf(2)));
+	}
+
+	public void testEntrySet() {
+		Map<Integer, String> m = makeTestMap();
+		assertEquals(3, m.entrySet().size());
+	}
+
+	public void testGet() {
+		Map<Integer, String> m = makeTestMap();
+		assertNull(m.get(Integer.valueOf(1)));
+		assertEquals("two", m.get(Integer.valueOf(2)));
+		assertNull(m.get(Integer.valueOf(3)));
+		assertEquals("four", m.get(Integer.valueOf(4)));
+		assertNull(m.get(Integer.valueOf(5)));
+		assertEquals("six", m.get(Integer.valueOf(6)));
+	}
+
+	public void testIsEmpty() {
+		Map<Integer, String> m = makeTestMap();
+		assertFalse(m.isEmpty());
+
+		// remove just evens
+		m = makeTestMap();
+		m.remove(Integer.valueOf(2));
+		m.remove(Integer.valueOf(4));
+		m.remove(Integer.valueOf(6));
+		assertTrue(m.isEmpty());
+	}
+
+	public void testKeySet() {
+		Map<Integer, String> m = makeTestMap();
+		assertEquals(3, m.keySet().size());
+	}
+
+	public void testSize() {
+		Map<Integer, String> m = makeTestMap();
+		assertEquals(3, m.size());
+	}
+
+	public void testValues() {
+		Map<Integer, String> m = makeTestMap();
+		assertEquals(3, m.values().size());
+	}
+
+	public void testZeroTimeToLive() {
+		// item should not be available
+		PassiveExpiringMap<String, String> m = new PassiveExpiringMap<String, String>(
+				0L);
+		m.put("a", "b");
+		assertNull(m.get("a"));
+	}
+}



Mime
View raw message