package org.infinispan.persistence;
import static org.infinispan.transaction.TransactionMode.NON_TRANSACTIONAL;
import static org.infinispan.transaction.TransactionMode.TRANSACTIONAL;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertNotNull;
import static org.testng.AssertJUnit.assertTrue;
import org.infinispan.Cache;
import org.infinispan.commands.VisitableCommand;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.container.DataContainer;
import org.infinispan.container.entries.InternalCacheValue;
import org.infinispan.context.InvocationContext;
import org.infinispan.interceptors.base.CommandInterceptor;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.marshall.core.MarshalledEntry;
import org.infinispan.persistence.dummy.DummyInMemoryStoreConfigurationBuilder;
import org.infinispan.persistence.spi.CacheLoader;
import org.infinispan.persistence.spi.PersistenceException;
import org.infinispan.test.SingleCacheManagerTest;
import org.infinispan.test.TestingUtil;
import org.infinispan.test.fwk.TestCacheManagerFactory;
import org.infinispan.transaction.TransactionMode;
import org.testng.annotations.Test;
/**
* @author Pedro Ruivo
* @since 5.2
*/
@Test(groups = "functional", testName = "persistence.PreloadWithAsyncStoreTest")
public class PreloadWithAsyncStoreTest extends SingleCacheManagerTest {
private static final Object[] KEYS = new Object[]{"key_1", "key_2", "key_3", "key_4"};
private static final Object[] VALUES = new Object[]{"value_1", "value_2", "value_3", "value_4"};
public void testtPreloadWithNonTransactionalCache() throws Exception {
doTest(CacheType.NO_TRANSACTIONAL);
}
public void testtPreloadWithTransactionalUsingSynchronizationCache() throws Exception {
doTest(CacheType.TRANSACTIONAL_SYNCHRONIZATION);
}
public void testPreloadWithTransactionalUsingXACache() throws Exception {
doTest(CacheType.TRANSACTIONAL_XA);
}
public void testPreloadWithTransactionalUsingXAAndRecoveryCache() throws Exception {
doTest(CacheType.TRANSACTIONAL_XA_RECOVERY);
}
@Override
protected EmbeddedCacheManager createCacheManager() throws Exception {
EmbeddedCacheManager cm = TestCacheManagerFactory.createClusteredCacheManager();
for (CacheType cacheType : CacheType.values()) {
ConfigurationBuilder builder = new ConfigurationBuilder();
builder.persistence()
.addStore(DummyInMemoryStoreConfigurationBuilder.class)
.preload(true)
.storeName(this.getClass().getName()).async().enable();
builder.transaction().transactionMode(cacheType.transactionMode).useSynchronization(cacheType.useSynchronization)
.recovery().enabled(cacheType.useRecovery);
builder.customInterceptors().addInterceptor().index(0).interceptor(new ExceptionTrackerInterceptor());
cm.defineConfiguration(cacheType.cacheName, builder.build());
}
return cm;
}
protected void doTest(CacheType cacheType) throws Exception {
final Cache<Object, Object> cache = cacheManager.getCache(cacheType.cacheName);
ExceptionTrackerInterceptor interceptor = getInterceptor(cache);
assertTrue("Preload should be enabled.", cache.getCacheConfiguration().persistence().preload());
assertTrue("Async Store should be enabled.", cache.getCacheConfiguration().persistence().usingAsyncStore());
CacheLoader loader = TestingUtil.getFirstLoader(cache);
assertNotInCacheAndStore(cache, loader, KEYS);
for (int i = 0; i < KEYS.length; ++i) {
cache.put(KEYS[i], VALUES[i]);
}
for (int i = 1; i < KEYS.length; i++) {
assertInCacheAndStore(cache, loader, KEYS[i], VALUES[i]);
}
DataContainer dataContainer = cache.getAdvancedCache().getDataContainer();
assertEquals("Wrong number of keys in data container after puts.", KEYS.length, dataContainer.size());
assertEquals("Some exceptions has been caught during the puts.", 0, interceptor.exceptionsCaught);
cache.stop();
assertEquals("Expected empty data container after stop.", 0, dataContainer.size());
assertEquals("Some exceptions has been caught during the stop.", 0, interceptor.exceptionsCaught);
cache.start();
assertTrue("Preload should be enabled after restart.", cache.getCacheConfiguration().persistence().preload());
assertTrue("Async Store should be enabled after restart.", cache.getCacheConfiguration().persistence().usingAsyncStore());
dataContainer = cache.getAdvancedCache().getDataContainer();
assertEquals("Wrong number of keys in data container after preload.", KEYS.length, dataContainer.size());
assertEquals("Some exceptions has been caught during the preload.", 0, interceptor.exceptionsCaught);
// Re-retrieve since the old reference might not be usable
loader = TestingUtil.getFirstLoader(cache);
for (int i = 1; i < KEYS.length; i++) {
assertInCacheAndStore(cache, loader, KEYS[i], VALUES[i]);
}
}
private void assertInCacheAndStore(Cache cache, CacheLoader loader, Object key, Object value) throws PersistenceException {
InternalCacheValue se = cache.getAdvancedCache().getDataContainer().get(key).toInternalCacheValue();
assertStoredEntry(se.getValue(), value, "Cache", key);
MarshalledEntry me = loader.load(key);
assertStoredEntry(me.getValue(), value, "Store", key);
}
private void assertStoredEntry(Object value, Object expectedValue, String src, Object key) {
assertNotNull(src + " entry for key " + key + " should NOT be null", value);
assertEquals(src + " should contain value " + expectedValue + " under key " + key + " but was " + value, expectedValue, value);
}
private <T> void assertNotInCacheAndStore(Cache cache, CacheLoader store, T... keys) throws PersistenceException {
for (Object key : keys) {
assertFalse("Cache should not contain key " + key, cache.getAdvancedCache().getDataContainer().containsKey(key));
assertFalse("Store should not contain key " + key, store.contains(key));
}
}
private ExceptionTrackerInterceptor getInterceptor(Cache cache) {
return cache.getAdvancedCache().getAsyncInterceptorChain()
.findInterceptorWithClass(ExceptionTrackerInterceptor.class);
}
private enum CacheType {
NO_TRANSACTIONAL("NO_TX"),
TRANSACTIONAL_SYNCHRONIZATION(TRANSACTIONAL, "TX_SYNC", true, false),
TRANSACTIONAL_XA(TRANSACTIONAL, "TX_XA", false, false),
TRANSACTIONAL_XA_RECOVERY(TRANSACTIONAL, "TX_XA_RECOVERY", false, true);
final TransactionMode transactionMode;
final String cacheName;
final boolean useSynchronization;
final boolean useRecovery;
CacheType(TransactionMode transactionMode, String cacheName, boolean useSynchronization,
boolean useRecovery) {
this.transactionMode = transactionMode;
this.cacheName = cacheName;
this.useSynchronization = useSynchronization;
this.useRecovery = useRecovery;
}
CacheType(String cacheName) {
//no tx cache. the boolean parameters are ignored.
this(NON_TRANSACTIONAL, cacheName, false, false);
}
}
private class ExceptionTrackerInterceptor extends CommandInterceptor {
private volatile int exceptionsCaught = 0;
@Override
protected Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable {
try {
return invokeNextInterceptor(ctx, command);
} catch (Throwable throwable) {
exceptionsCaught++;
throw throwable;
}
}
}
}