package org.infinispan.atomic; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertTrue; import java.util.Map; import java.util.concurrent.Callable; import org.infinispan.Cache; import org.infinispan.atomic.impl.AtomicHashMapProxy; import org.infinispan.atomic.impl.FineGrainedAtomicHashMapProxy; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.container.DataContainer; import org.infinispan.eviction.EvictionStrategy; import org.infinispan.persistence.PersistenceUtil; import org.infinispan.persistence.dummy.DummyInMemoryStoreConfigurationBuilder; import org.infinispan.persistence.spi.AdvancedCacheLoader; import org.infinispan.test.MultipleCacheManagersTest; import org.infinispan.test.TestingUtil; import org.infinispan.test.fwk.CleanupAfterMethod; import org.infinispan.test.fwk.TestCacheManagerFactory; import org.testng.annotations.Test; /** * @author anistor@redhat.com * @since 5.3 */ @Test(groups = "functional", testName = "atomic.LocalDeltaAwareEvictionTest") @CleanupAfterMethod public class LocalDeltaAwareEvictionTest extends MultipleCacheManagersTest { protected static final String KEY1 = "key1"; protected static final String KEY2 = "key2"; protected boolean txEnabled = false; @Override protected void createCacheManagers() throws Throwable { ConfigurationBuilder configBuilder = TestCacheManagerFactory.getDefaultCacheConfiguration(true); configBuilder.eviction().maxEntries(1).strategy(EvictionStrategy.LRU) .persistence().addStore(DummyInMemoryStoreConfigurationBuilder.class); addClusterEnabledCacheManager(configBuilder); } /** * A generic accessor for a DeltaAware that consists of two String components. * Implementations of this interface will plug into the test and will drive access to the actual DeltaAware * instances allowing the test logic to be reused for different kinds of DeltaAware. * * @param <TDA> the type of DeltaAware * @param <TK> the type of cache key */ interface DeltaAwareAccessor<TDA, TK> { TDA createObject(Cache cache, TK key); TDA getObject(Cache cache, TK key); void putObject(Cache cache, TK key, TDA da); String getFirstComponent(TDA da); void setFirstComponent(TDA da, String value); String getSecondComponent(TDA da); void setSecondComponent(TDA da, String value); } protected Object withTx(int cacheIndex, Callable<Object> c) throws Exception { if (txEnabled) { return TestingUtil.withTx(cache(cacheIndex).getAdvancedCache().getTransactionManager(), c); } else { return c.call(); } } protected void assertNumberOfEntries(int cacheIndex) throws Exception { AdvancedCacheLoader loader = (AdvancedCacheLoader) TestingUtil.getCacheLoader(cache(cacheIndex)); assertEquals(2, PersistenceUtil.count(loader, null)); // two entries in store DataContainer dataContainer = cache(cacheIndex).getAdvancedCache().getDataContainer(); assertEquals(1, dataContainer.size()); // only one entry in memory (the other one was evicted) } protected void test(final DeltaAwareAccessor daa, final int nodeThatReads, final int nodeThatWrites) throws Exception { // create two delta-aware objects populated with initial values for components withTx(nodeThatWrites, new Callable<Object>() { @Override public Object call() { daa.createObject(cache(nodeThatWrites), KEY1); daa.createObject(cache(nodeThatWrites), KEY2); return null; } }); // check initial values not lost due to eviction assertInitialValues(daa, nodeThatWrites); if (nodeThatReads != nodeThatWrites) { assertInitialValues(daa, nodeThatReads); } // update first component of both objects withTx(nodeThatWrites, new Callable<Object>() { @Override public Object call() throws Exception { Object obj1 = daa.getObject(cache(nodeThatWrites), KEY1); daa.setFirstComponent(obj1, "** UPDATED** first component of object with key=key1"); daa.putObject(cache(nodeThatWrites), KEY1, obj1); Object obj2 = daa.getObject(cache(nodeThatWrites), KEY2); daa.setFirstComponent(obj2, "** UPDATED** first component of object with key=key2"); daa.putObject(cache(nodeThatWrites), KEY2, obj2); return null; } }); assertUpdatedValues(daa, nodeThatWrites); if (nodeThatReads != nodeThatWrites) { assertUpdatedValues(daa, nodeThatReads); } } protected void assertInitialValues(DeltaAwareAccessor daa, int cacheIndex) throws Exception { assertNumberOfEntries(cacheIndex); final Object obj1 = daa.getObject(cache(cacheIndex), KEY1); assertNotNull(obj1); assertEquals("first component of object with key=" + KEY1, daa.getFirstComponent(obj1)); assertEquals("second component of object with key=" + KEY1, daa.getSecondComponent(obj1)); final Object obj2 = daa.getObject(cache(cacheIndex), KEY2); assertNotNull(obj2); assertEquals("first component of object with key=" + KEY2, daa.getFirstComponent(obj2)); assertEquals("second component of object with key=" + KEY2, daa.getSecondComponent(obj2)); assertNumberOfEntries(cacheIndex); } protected void assertUpdatedValues(DeltaAwareAccessor daa, int nodeThatReads) throws Exception { assertNumberOfEntries(nodeThatReads); Object obj1 = daa.getObject(cache(nodeThatReads), KEY1); assertNotNull(obj1); assertEquals("** UPDATED** first component of object with key=" + KEY1, daa.getFirstComponent(obj1)); assertEquals("second component of object with key=" + KEY1, daa.getSecondComponent(obj1)); Object obj2 = daa.getObject(cache(nodeThatReads), KEY2); assertNotNull(obj2); assertEquals("** UPDATED** first component of object with key=" + KEY2, daa.getFirstComponent(obj2)); assertEquals("second component of object with key=" + KEY2, daa.getSecondComponent(obj2)); assertNumberOfEntries(nodeThatReads); } public void testDeltaAware() throws Exception { test(createDeltaAwareAccessor(), 0, 0); } protected DeltaAwareAccessor<TestDeltaAware, String> createDeltaAwareAccessor() { return new DeltaAwareAccessor<TestDeltaAware, String>() { @Override public TestDeltaAware createObject(Cache cache, String key) { TestDeltaAware da = new TestDeltaAware(); da.setFirstComponent("first component of object with key=" + key); da.setSecondComponent("second component of object with key=" + key); cache.put(key, da); return da; } @Override public TestDeltaAware getObject(Cache cache, String key) { return (TestDeltaAware) cache.get(key); } @Override public void putObject(Cache cache, String key, TestDeltaAware da) { cache.put(key, da); } @Override public String getFirstComponent(TestDeltaAware da) { return da.getFirstComponent(); } @Override public void setFirstComponent(TestDeltaAware da, String value) { da.setFirstComponent(value); } @Override public String getSecondComponent(TestDeltaAware da) { return da.getSecondComponent(); } @Override public void setSecondComponent(TestDeltaAware da, String value) { da.setSecondComponent(value); } }; } public void testAtomicMap() throws Exception { test(createAtomicMapAccessor(), 0, 0); } protected DeltaAwareAccessor<Map<String, String>, String> createAtomicMapAccessor() { return new DeltaAwareAccessor<Map<String, String>, String>() { @Override public Map<String, String> createObject(Cache cache, String key) { Map<String, String> map = AtomicMapLookup.getAtomicMap(cache, key); map.put("first", "first component of object with key=" + key); map.put("second", "second component of object with key=" + key); return map; } @Override public Map<String, String> getObject(Cache cache, String key) { return AtomicMapLookup.getAtomicMap(cache, key, false); } @Override public void putObject(Cache cache, String key, Map<String, String> da) { assertTrue(da instanceof AtomicHashMapProxy); // we do not actually put the map back into cache because it must already be there } @Override public String getFirstComponent(Map<String, String> da) { return da.get("first"); } @Override public void setFirstComponent(Map<String, String> da, String value) { da.put("first", value); } @Override public String getSecondComponent(Map<String, String> da) { return da.get("second"); } @Override public void setSecondComponent(Map<String, String> da, String value) { da.put("second", value); } }; } public void testFineGrainedAtomicMap() throws Exception { test(createFineGrainedAtomicMapAccessor(), 0, 0); } protected DeltaAwareAccessor<Map<String, String>, String> createFineGrainedAtomicMapAccessor() { return new DeltaAwareAccessor<Map<String, String>, String>() { @Override public Map<String, String> createObject(Cache cache, String key) { Map<String, String> map = AtomicMapLookup.getFineGrainedAtomicMap(cache, key); map.put("first", "first component of object with key=" + key); map.put("second", "second component of object with key=" + key); return map; } @Override public Map<String, String> getObject(Cache cache, String key) { return AtomicMapLookup.getFineGrainedAtomicMap(cache, key, false); } @Override public void putObject(Cache cache, String key, Map<String, String> da) { assertTrue(da instanceof FineGrainedAtomicHashMapProxy); // we do not actually put the map back into cache because it must already be there } @Override public String getFirstComponent(Map<String, String> da) { return da.get("first"); } @Override public void setFirstComponent(Map<String, String> da, String value) { da.put("first", value); } @Override public String getSecondComponent(Map<String, String> da) { return da.get("second"); } @Override public void setSecondComponent(Map<String, String> da, String value) { da.put("second", value); } }; } }