Author: brentworden Date: Sun Aug 26 19:35:45 2012 New Revision: 1377492 URL: http://svn.apache.org/viewvc?rev=1377492&view=rev Log: fix indentation
Modified: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/map/PassiveExpiringMap.java commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/map/TestPassiveExpiringMap.java Modified: 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=1377492&r1=1377491&r2=1377492&view=diff ============================================================================== --- commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/map/PassiveExpiringMap.java (original) +++ commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/map/PassiveExpiringMap.java Sun Aug 26 19:35:45 2012 @@ -55,523 +55,484 @@ import java.util.concurrent.TimeUnit; * synchronization. * </p> * - * @param <K> - * the type of the keys in the map - * - * @param <V> - * the type of the values in the map - * + * @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(); - } +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(); + } } Modified: 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=1377492&r1=1377491&r2=1377492&view=diff ============================================================================== --- commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/map/TestPassiveExpiringMap.java (original) +++ commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/map/TestPassiveExpiringMap.java Sun Aug 26 19:35:45 2012 @@ -9,199 +9,201 @@ 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> { +public class TestPassiveExpiringMap<K, V> + extends AbstractTestMap<K, V> { - private static class TestExpirationPolicy implements - ExpirationPolicy<Integer, String> { + private static class TestExpirationPolicy + implements ExpirationPolicy<Integer, String> { - private static final long serialVersionUID = 1L; + 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")); - } + 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")); + } }