COLLECTIONS-241 added PassiveExpiringMap and tests
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/trunk@1376827 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
2f87b3c187
commit
33fa40b72d
|
@ -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} ≥ 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 ≥ 0
|
||||
* and <code>expirationTimeObject</code> < <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();
|
||||
}
|
||||
}
|
|
@ -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"));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue