PassiveExpiringMap not expiring objects

Viewed 2665

I was expecting org.apache.commons.collections4.map.PassiveExpiringMap to work out of the box. Unfortunately it doesn't work for me in the most obvious use case of expiring objects on time. The code below creates an expiration policy of 5 seconds. The test waits for 10 seconds which is a time by which all map object should be expired and removed. But they're not.

import org.apache.commons.collections4.map.PassiveExpiringMap;
import org.junit.Assert;
import org.junit.Test;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
    
public class PassiveExpiringMapTest {
    @Test
    public void givenDataMap_whenWrappingMapWithPassiveExpiringMap_thenObjectsAreRemovedWhenExpired() {
        
        final Map<String, Integer> m = new HashMap<>();
        m.put("one", Integer.valueOf(1));
        m.put("two", Integer.valueOf(2));
        m.put("three", Integer.valueOf(3));

        PassiveExpiringMap.ConstantTimeToLiveExpirationPolicy<String, Integer>
            expirationPolicy = new PassiveExpiringMap.ConstantTimeToLiveExpirationPolicy<>(
            5, TimeUnit.SECONDS);

        PassiveExpiringMap<String, Integer> expiringMap = new PassiveExpiringMap<>(expirationPolicy, m);

        int initialCapacity = expiringMap.size();
        System.out.println("initialCapacity = " + initialCapacity);
        Assert.assertEquals(3, initialCapacity);

        System.out.println("Sleeping...");
        try { Thread.sleep(10000L); } catch (InterruptedException e) { }

        int updatedCapacity = expiringMap.size();
        System.out.println("updatedCapacity = " + updatedCapacity);
        Integer one = expiringMap.get("one");
        Assert.assertNull(one);
        Assert.assertEquals(0, updatedCapacity);
    }
}

The test fails with the following output:

initialCapacity = 3
Sleeping...
updatedCapacity = 3

java.lang.AssertionError: expected null, but was:<1>

    at org.junit.Assert.fail(Assert.java:88)
    at org.junit.Assert.failNotNull(Assert.java:755)
    at org.junit.Assert.assertNull(Assert.java:737)
    at org.junit.Assert.assertNull(Assert.java:747)

Any idea what I am missing?

2 Answers

In your code add map 'm' to 'expiringMap'

    PassiveExpiringMap<String, Integer> expiringMap = new PassiveExpiringMap<>(expirationPolicy,m);
    expiringMap.putAll(m);

Looking at the source code of PassiveExpiringMap helped me figure out why my original version did not work. Initializing using a map causes an inner expirationMap field to be empty. expirationMap must contain an entry for every object in order for expired objects to be evicted. In order for this to happen the put method needs to be used. Modified code below works. Note that objects are added to expiringMap directly, rather than to the HashMap as before:

import org.apache.commons.collections4.map.PassiveExpiringMap;
import org.junit.Assert;
import org.junit.Test;

import java.util.HashMap;
import java.util.concurrent.TimeUnit;

public class PassiveExpiringMapTest {

    @Test
    public void givenDataMap_whenWrappingMapWithPassiveExpiringMap_thenObjectsAreRemovedWhenExpired() {
        
        PassiveExpiringMap.ConstantTimeToLiveExpirationPolicy<String, Integer>
            expirationPolicy = new PassiveExpiringMap.ConstantTimeToLiveExpirationPolicy<>(
            5, TimeUnit.SECONDS);

        PassiveExpiringMap<String, Integer> expiringMap = new PassiveExpiringMap<>(expirationPolicy, new HashMap<>());
        expiringMap.put("one", Integer.valueOf(1));
        expiringMap.put("two", Integer.valueOf(2));
        expiringMap.put("three", Integer.valueOf(3));
        int initialCapacity = expiringMap.size();
        System.out.println("initialCapacity = " + initialCapacity);
        Assert.assertEquals(3, initialCapacity);

        System.out.println("Sleeping...");
        try { Thread.sleep(10000L); } catch (InterruptedException e) { }

        int updatedCapacity = expiringMap.size();
        System.out.println("updatedCapacity = " + updatedCapacity);
        Integer one = expiringMap.get("one");
        Assert.assertNull(one);
        Assert.assertEquals(0, updatedCapacity);
    }
}
Related