package org.infinispan.container;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertNotNull;
import static org.testng.AssertJUnit.assertNull;
import static org.testng.AssertJUnit.assertTrue;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.infinispan.container.entries.ImmortalCacheEntry;
import org.infinispan.container.entries.InternalCacheEntry;
import org.infinispan.container.entries.MortalCacheEntry;
import org.infinispan.container.entries.TransientCacheEntry;
import org.infinispan.container.entries.TransientMortalCacheEntry;
import org.infinispan.eviction.ActivationManager;
import org.infinispan.expiration.ExpirationManager;
import org.infinispan.metadata.EmbeddedMetadata;
import org.infinispan.test.AbstractInfinispanTest;
import org.infinispan.util.ControlledTimeService;
import org.infinispan.util.CoreImmutables;
import org.mockito.Mockito;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@Test(groups = "unit", testName = "container.SimpleDataContainerTest")
public class SimpleDataContainerTest extends AbstractInfinispanTest {
private DataContainer<String, String> dc;
private ControlledTimeService timeService;
@BeforeMethod
public void setUp() {
dc = createContainer();
}
@AfterMethod
public void tearDown() {
dc = null;
}
protected DataContainer<String, String> createContainer() {
DefaultDataContainer<String, String> dc = new DefaultDataContainer<>(16);
InternalEntryFactoryImpl internalEntryFactory = new InternalEntryFactoryImpl();
timeService = new ControlledTimeService();
internalEntryFactory.injectTimeService(timeService);
ActivationManager activationManager = mock(ActivationManager.class);
doNothing().when(activationManager).onUpdate(Mockito.any(), Mockito.anyBoolean());
dc.initialize(null, null, internalEntryFactory, activationManager, null, timeService, null, mock(
ExpirationManager.class));
return dc;
}
public void testExpiredData() throws InterruptedException {
dc.put("k", "v", new EmbeddedMetadata.Builder().maxIdle(100, TimeUnit.MINUTES).build());
timeService.advance(100);
InternalCacheEntry entry = dc.get("k");
assertNotNull(entry);
assertEquals(transienttype(), entry.getClass());
assertEquals(timeService.wallClockTime(), entry.getLastUsed());
long entryLastUsed = entry.getLastUsed();
timeService.advance(100);
entry = dc.get("k");
assertEquals(entryLastUsed + 100, entry.getLastUsed());
dc.put("k", "v", new EmbeddedMetadata.Builder().maxIdle(1, TimeUnit.MILLISECONDS).build());
long oldTime = timeService.wallClockTime();
dc.put("k", "v", new EmbeddedMetadata.Builder().lifespan(100, TimeUnit.MINUTES).build());
timeService.advance(100);
assertEquals(1, dc.size());
entry = dc.get("k");
assertNotNull(entry);
assertEquals(mortaltype(), entry.getClass());
assertEquals(oldTime, entry.getCreated());
assertEquals(-1, entry.getMaxIdle());
dc.put("k", "v", new EmbeddedMetadata.Builder().lifespan(1, TimeUnit.MILLISECONDS).build());
timeService.advance(10);
assertNull(dc.get("k"));
assertEquals(0, dc.size());
dc.put("k", "v", new EmbeddedMetadata.Builder().lifespan(1, TimeUnit.MILLISECONDS).build());
timeService.advance(100);
assertEquals(0, dc.size());
}
public void testResetOfCreationTime() throws Exception {
long now = timeService.wallClockTime();
timeService.advance(1);
dc.put("k", "v", new EmbeddedMetadata.Builder().lifespan(1000, TimeUnit.SECONDS).build());
long created1 = dc.get("k").getCreated();
assertEquals(now + 1, created1);
timeService.advance(100);
dc.put("k", "v", new EmbeddedMetadata.Builder().lifespan(1000, TimeUnit.SECONDS).build());
long created2 = dc.get("k").getCreated();
assertEquals(now + 101, created2);
}
public void testUpdatingLastUsed() throws Exception {
long idle = 600000;
dc.put("k", "v", new EmbeddedMetadata.Builder().build());
InternalCacheEntry ice = dc.get("k");
assertEquals(immortaltype(), ice.getClass());
assertEquals(-1, ice.toInternalCacheValue().getExpiryTime());
assertEquals(-1, ice.getMaxIdle());
assertEquals(-1, ice.getLifespan());
dc.put("k", "v", new EmbeddedMetadata.Builder().maxIdle(idle, TimeUnit.MILLISECONDS).build());
timeService.advance(100); // for time calc granularity
ice = dc.get("k");
assertEquals(transienttype(), ice.getClass());
assertEquals(idle + timeService.wallClockTime(), ice.toInternalCacheValue().getExpiryTime());
assertEquals(timeService.wallClockTime(), ice.getLastUsed());
assertEquals(idle, ice.getMaxIdle());
assertEquals(-1, ice.getLifespan());
timeService.advance(100); // for time calc granularity
assertNotNull(dc.get("k"));
long oldTime = timeService.wallClockTime();
// check that the last used stamp has been updated on a get
assertEquals(oldTime, ice.getLastUsed());
timeService.advance(100); // for time calc granularity
assertEquals(oldTime, ice.getLastUsed());
}
protected Class<? extends InternalCacheEntry> mortaltype() {
return MortalCacheEntry.class;
}
protected Class<? extends InternalCacheEntry> immortaltype() {
return ImmortalCacheEntry.class;
}
protected Class<? extends InternalCacheEntry> transienttype() {
return TransientCacheEntry.class;
}
protected Class<? extends InternalCacheEntry> transientmortaltype() {
return TransientMortalCacheEntry.class;
}
public void testExpirableToImmortalAndBack() {
String value = "v";
dc.put("k", value, new EmbeddedMetadata.Builder().lifespan(100, TimeUnit.MINUTES).build());
assertContainerEntry(mortaltype(), value);
value = "v2";
dc.put("k", value, new EmbeddedMetadata.Builder().build());
assertContainerEntry(immortaltype(), value);
value = "v3";
dc.put("k", value, new EmbeddedMetadata.Builder().maxIdle(100, TimeUnit.MINUTES).build());
assertContainerEntry(transienttype(), value);
value = "v4";
dc.put("k", value, new EmbeddedMetadata.Builder()
.lifespan(100, TimeUnit.MINUTES).maxIdle(100, TimeUnit.MINUTES).build());
assertContainerEntry(transientmortaltype(), value);
value = "v41";
dc.put("k", value, new EmbeddedMetadata.Builder()
.lifespan(100, TimeUnit.MINUTES).maxIdle(100, TimeUnit.MINUTES).build());
assertContainerEntry(transientmortaltype(), value);
value = "v5";
dc.put("k", value, new EmbeddedMetadata.Builder().lifespan(100, TimeUnit.MINUTES).build());
assertContainerEntry(mortaltype(), value);
}
private void assertContainerEntry(Class<? extends InternalCacheEntry> type,
String expectedValue) {
assertTrue(dc.containsKey("k"));
InternalCacheEntry entry = dc.get("k");
assertEquals(type, entry.getClass());
assertEquals(expectedValue, entry.getValue());
}
public void testKeySet() {
dc.put("k1", "v", new EmbeddedMetadata.Builder().lifespan(100, TimeUnit.MINUTES).build());
dc.put("k2", "v", new EmbeddedMetadata.Builder().build());
dc.put("k3", "v", new EmbeddedMetadata.Builder().maxIdle(100, TimeUnit.MINUTES).build());
dc.put("k4", "v", new EmbeddedMetadata.Builder()
.maxIdle(100, TimeUnit.MINUTES).lifespan(100, TimeUnit.MINUTES).build());
Set<String> expected = new HashSet<>();
expected.add("k1");
expected.add("k2");
expected.add("k3");
expected.add("k4");
for (String o : dc.keySet()) assertTrue(expected.remove(o));
assertTrue("Did not see keys " + expected + " in iterator!", expected.isEmpty());
}
public void testContainerIteration() {
dc.put("k1", "v", new EmbeddedMetadata.Builder().lifespan(100, TimeUnit.MINUTES).build());
dc.put("k2", "v", new EmbeddedMetadata.Builder().build());
dc.put("k3", "v", new EmbeddedMetadata.Builder().maxIdle(100, TimeUnit.MINUTES).build());
dc.put("k4", "v", new EmbeddedMetadata.Builder()
.maxIdle(100, TimeUnit.MINUTES).lifespan(100, TimeUnit.MINUTES).build());
Set<String> expected = new HashSet<>();
expected.add("k1");
expected.add("k2");
expected.add("k3");
expected.add("k4");
for (InternalCacheEntry<String, String> ice : dc) {
assertTrue(expected.remove(ice.getKey()));
}
assertTrue("Did not see keys " + expected + " in iterator!", expected.isEmpty());
}
public void testKeys() {
dc.put("k1", "v1", new EmbeddedMetadata.Builder().lifespan(100, TimeUnit.MINUTES).build());
dc.put("k2", "v2", new EmbeddedMetadata.Builder().build());
dc.put("k3", "v3", new EmbeddedMetadata.Builder().maxIdle(100, TimeUnit.MINUTES).build());
dc.put("k4", "v4", new EmbeddedMetadata.Builder()
.maxIdle(100, TimeUnit.MINUTES).lifespan(100, TimeUnit.MINUTES).build());
Set<String> expected = new HashSet<>();
expected.add("k1");
expected.add("k2");
expected.add("k3");
expected.add("k4");
for (String o : dc.keySet()) assertTrue(expected.remove(o));
assertTrue("Did not see keys " + expected + " in iterator!", expected.isEmpty());
}
public void testValues() {
dc.put("k1", "v1", new EmbeddedMetadata.Builder().lifespan(100, TimeUnit.MINUTES).build());
dc.put("k2", "v2", new EmbeddedMetadata.Builder().build());
dc.put("k3", "v3", new EmbeddedMetadata.Builder().maxIdle(100, TimeUnit.MINUTES).build());
dc.put("k4", "v4", new EmbeddedMetadata.Builder()
.maxIdle(100, TimeUnit.MINUTES).lifespan(100, TimeUnit.MINUTES).build());
Set<String> expected = new HashSet<>();
expected.add("v1");
expected.add("v2");
expected.add("v3");
expected.add("v4");
for (String o : dc.values()) assertTrue(expected.remove(o));
assertTrue("Did not see keys " + expected + " in iterator!", expected.isEmpty());
}
public void testEntrySet() {
dc.put("k1", "v1", new EmbeddedMetadata.Builder().lifespan(100, TimeUnit.MINUTES).build());
dc.put("k2", "v2", new EmbeddedMetadata.Builder().build());
dc.put("k3", "v3", new EmbeddedMetadata.Builder().maxIdle(100, TimeUnit.MINUTES).build());
dc.put("k4", "v4", new EmbeddedMetadata.Builder()
.maxIdle(100, TimeUnit.MINUTES).lifespan(100, TimeUnit.MINUTES).build());
Set<Map.Entry> expected = new HashSet<>();
expected.add(CoreImmutables.immutableInternalCacheEntry(dc.get("k1")));
expected.add(CoreImmutables.immutableInternalCacheEntry(dc.get("k2")));
expected.add(CoreImmutables.immutableInternalCacheEntry(dc.get("k3")));
expected.add(CoreImmutables.immutableInternalCacheEntry(dc.get("k4")));
Set<Map.Entry> actual = new HashSet<>();
for (Map.Entry<String, String> o : dc.entrySet()) assertTrue(actual.add(o));
assertEquals("Expected to see keys " + expected + " but only saw " + actual, expected, actual);
}
public void testGetDuringKeySetLoop() {
for (int i = 0; i < 10; i++) dc.put(String.valueOf(i), "value", new EmbeddedMetadata.Builder().build());
int i = 0;
for (Object key : dc.keySet()) {
dc.peek(key); // calling get in this situations will result on corruption the iteration.
i++;
}
assertEquals(10, i);
}
public void testEntrySetStreamWithExpiredEntries() {
dc.put("k1", "v1", new EmbeddedMetadata.Builder().lifespan(100, TimeUnit.MILLISECONDS).build());
dc.put("k2", "v2", new EmbeddedMetadata.Builder().build());
dc.put("k3", "v3", new EmbeddedMetadata.Builder().maxIdle(200, TimeUnit.MILLISECONDS).build());
Set<Map.Entry<String, String>> expected = new HashSet<>();
Map.Entry<String, String> k1 = CoreImmutables.immutableInternalCacheEntry(dc.get("k1"));
expected.add(k1);
expected.add(CoreImmutables.immutableInternalCacheEntry(dc.get("k2")));
Map.Entry<String, String> k3 = CoreImmutables.immutableInternalCacheEntry(dc.get("k3"));
expected.add(k3);
List<Map.Entry<String, String>> results = Arrays.asList(dc.entrySet().stream().toArray(Map.Entry[]::new));
assertEquals(3, results.size());
assertArrayAndSetContainSame(expected, results);
timeService.advance(101);
results = Arrays.asList(dc.entrySet().stream().toArray(Map.Entry[]::new));
assertEquals(2, results.size());
expected.remove(k1);
assertArrayAndSetContainSame(expected, results);
timeService.advance(100);
results = Arrays.asList(dc.entrySet().stream().toArray(Map.Entry[]::new));
assertEquals(1, results.size());
expected.remove(k3);
assertArrayAndSetContainSame(expected, results);
}
private <E> void assertArrayAndSetContainSame(Set<E> expected, List<E> results) {
for (E result : results) {
assertTrue("Set didn't contain " + result, expected.contains(result));
}
}
}