mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-05 04:25:29 -04:00
#600 TimeoutMap now longer gets Long.MAX_VALUE as next expiry time if map is empty when removeExpiredEntries() is invoked.
This commit is contained in:
parent
46ce99e10f
commit
913a03608c
@ -330,7 +330,7 @@ abstract class AbstractDecoratedMap<K, V> extends AbstractMap<K, V> implements M
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple Map.Entry implementaton.
|
* A simple Map.Entry implementation.
|
||||||
*/
|
*/
|
||||||
static class BasicEntry<K, V> implements Entry<K, V>, Serializable {
|
static class BasicEntry<K, V> implements Entry<K, V>, Serializable {
|
||||||
K mKey;
|
K mKey;
|
||||||
|
@ -34,7 +34,7 @@ import java.io.Serializable;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@code Map} implementation that removes (exipres) its elements after
|
* A {@code Map} implementation that removes (expires) its elements after
|
||||||
* a given period. The map is by default backed by a {@link java.util.HashMap},
|
* a given period. The map is by default backed by a {@link java.util.HashMap},
|
||||||
* or can be instantiated with any given {@code Map} as backing.
|
* or can be instantiated with any given {@code Map} as backing.
|
||||||
* <p>
|
* <p>
|
||||||
@ -67,7 +67,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
|||||||
protected long expiryTime = 60000L; // 1 minute
|
protected long expiryTime = 60000L; // 1 minute
|
||||||
|
|
||||||
//////////////////////
|
//////////////////////
|
||||||
private volatile long nextExpiryTime;
|
private volatile long nextExpiryTime = Long.MAX_VALUE;
|
||||||
//////////////////////
|
//////////////////////
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -178,7 +178,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
|||||||
* @return {@code true} if this map contains no key-value mappings.
|
* @return {@code true} if this map contains no key-value mappings.
|
||||||
*/
|
*/
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return (size() <= 0);
|
return size() <= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -208,7 +208,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
|||||||
* @see #containsKey(java.lang.Object)
|
* @see #containsKey(java.lang.Object)
|
||||||
*/
|
*/
|
||||||
public V get(Object pKey) {
|
public V get(Object pKey) {
|
||||||
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey);
|
TimedEntry entry = (TimedEntry) entries.get(pKey);
|
||||||
|
|
||||||
if (entry == null) {
|
if (entry == null) {
|
||||||
return null;
|
return null;
|
||||||
@ -236,7 +236,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
|||||||
* {@code null} values.
|
* {@code null} values.
|
||||||
*/
|
*/
|
||||||
public V put(K pKey, V pValue) {
|
public V put(K pKey, V pValue) {
|
||||||
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey);
|
TimedEntry entry = (TimedEntry) entries.get(pKey);
|
||||||
V oldValue;
|
V oldValue;
|
||||||
|
|
||||||
if (entry == null) {
|
if (entry == null) {
|
||||||
@ -272,7 +272,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
|||||||
* {@code null} values.
|
* {@code null} values.
|
||||||
*/
|
*/
|
||||||
public V remove(Object pKey) {
|
public V remove(Object pKey) {
|
||||||
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.remove(pKey);
|
TimedEntry entry = (TimedEntry) entries.remove(pKey);
|
||||||
return (entry != null) ? entry.getValue() : null;
|
return (entry != null) ? entry.getValue() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,13 +284,12 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
|||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*protected*/ TimedEntry<K, V> createEntry(K pKey, V pValue) {
|
/*protected*/ TimedEntry createEntry(K pKey, V pValue) {
|
||||||
return new TimedEntry<K, V>(pKey, pValue);
|
return new TimedEntry(pKey, pValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes any expired mappings.
|
* Removes any expired mappings.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
protected void removeExpiredEntries() {
|
protected void removeExpiredEntries() {
|
||||||
// Remove any expired elements
|
// Remove any expired elements
|
||||||
@ -312,7 +311,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
|||||||
long next = Long.MAX_VALUE;
|
long next = Long.MAX_VALUE;
|
||||||
nextExpiryTime = next; // Avoid multiple runs...
|
nextExpiryTime = next; // Avoid multiple runs...
|
||||||
for (Iterator<Entry<K, V>> iterator = new EntryIterator(); iterator.hasNext();) {
|
for (Iterator<Entry<K, V>> iterator = new EntryIterator(); iterator.hasNext();) {
|
||||||
TimedEntry<K, V> entry = (TimedEntry<K, V>) iterator.next();
|
TimedEntry entry = (TimedEntry) iterator.next();
|
||||||
////
|
////
|
||||||
long expires = entry.expires();
|
long expires = entry.expires();
|
||||||
if (expires < next) {
|
if (expires < next) {
|
||||||
@ -376,7 +375,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
|||||||
|
|
||||||
while (mNext == null && mIterator.hasNext()) {
|
while (mNext == null && mIterator.hasNext()) {
|
||||||
Entry<K, Entry<K, V>> entry = mIterator.next();
|
Entry<K, Entry<K, V>> entry = mIterator.next();
|
||||||
TimedEntry<K, V> timed = (TimedEntry<K, V>) entry.getValue();
|
TimedEntry timed = (TimedEntry) entry.getValue();
|
||||||
|
|
||||||
if (timed.isExpiredBy(mNow)) {
|
if (timed.isExpiredBy(mNow)) {
|
||||||
// Remove from map, and continue
|
// Remove from map, and continue
|
||||||
@ -425,19 +424,28 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
|||||||
/**
|
/**
|
||||||
* Keeps track of timed objects
|
* Keeps track of timed objects
|
||||||
*/
|
*/
|
||||||
private class TimedEntry<K, V> extends BasicEntry<K, V> {
|
private class TimedEntry extends BasicEntry<K, V> {
|
||||||
private long mTimestamp;
|
private long mTimestamp;
|
||||||
|
|
||||||
TimedEntry(K pKey, V pValue) {
|
TimedEntry(K pKey, V pValue) {
|
||||||
super(pKey, pValue);
|
super(pKey, pValue);
|
||||||
mTimestamp = System.currentTimeMillis();
|
updateTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
public V setValue(V pValue) {
|
public V setValue(V pValue) {
|
||||||
mTimestamp = System.currentTimeMillis();
|
updateTimestamp();
|
||||||
return super.setValue(pValue);
|
return super.setValue(pValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateTimestamp() {
|
||||||
|
mTimestamp = System.currentTimeMillis();
|
||||||
|
|
||||||
|
long expires = expires();
|
||||||
|
if (expires < nextExpiryTime) {
|
||||||
|
nextExpiryTime = expires;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final boolean isExpired() {
|
final boolean isExpired() {
|
||||||
return isExpiredBy(System.currentTimeMillis());
|
return isExpiredBy(System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
|
@ -557,7 +557,7 @@ public class TimeoutMapTest extends MapAbstractTest {
|
|||||||
// NOTE: Only wait fist time, to avoid slooow tests
|
// NOTE: Only wait fist time, to avoid slooow tests
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
try {
|
try {
|
||||||
wait(60l);
|
wait(60L);
|
||||||
}
|
}
|
||||||
catch (InterruptedException e) {
|
catch (InterruptedException e) {
|
||||||
}
|
}
|
||||||
@ -591,7 +591,7 @@ public class TimeoutMapTest extends MapAbstractTest {
|
|||||||
try {
|
try {
|
||||||
wait(60l);
|
wait(60l);
|
||||||
}
|
}
|
||||||
catch (InterruptedException e) {
|
catch (InterruptedException ignore) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -651,5 +651,24 @@ public class TimeoutMapTest extends MapAbstractTest {
|
|||||||
assertTrue("Wrong entry removed, keySet().iterator() is broken.", !map.containsKey(removedKey));
|
assertTrue("Wrong entry removed, keySet().iterator() is broken.", !map.containsKey(removedKey));
|
||||||
assertTrue("Wrong entry removed, keySet().iterator() is broken.", map.containsKey(otherKey));
|
assertTrue("Wrong entry removed, keySet().iterator() is broken.", map.containsKey(otherKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testContainsKeyOnEmptyMap() {
|
||||||
|
// See #600
|
||||||
|
Map<String, String> timeoutMap = new TimeoutMap<>(30);
|
||||||
|
assertFalse(timeoutMap.containsKey("xyz"));
|
||||||
|
timeoutMap.put("xyz", "xyz");
|
||||||
|
assertTrue(timeoutMap.containsKey("xyz"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(50); // Let the item expire
|
||||||
|
}
|
||||||
|
catch (InterruptedException ignore) {
|
||||||
|
}
|
||||||
|
|
||||||
|
assertFalse(timeoutMap.containsKey("xyz"));
|
||||||
|
assertNull(timeoutMap.get("xyz"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user