package org.infinispan.persistence;
import static org.testng.AssertJUnit.assertArrayEquals;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertNotNull;
import static org.testng.AssertJUnit.assertNull;
import static org.testng.AssertJUnit.assertTrue;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
import javax.transaction.TransactionManager;
import org.infinispan.Cache;
import org.infinispan.atomic.AtomicMap;
import org.infinispan.atomic.AtomicMapLookup;
import org.infinispan.commons.util.ByRef;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.configuration.cache.PersistenceConfigurationBuilder;
import org.infinispan.container.DataContainer;
import org.infinispan.container.entries.InternalCacheEntry;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.marshall.core.ExternalPojo;
import org.infinispan.marshall.core.MarshalledEntry;
import org.infinispan.persistence.manager.PersistenceManager;
import org.infinispan.persistence.manager.PersistenceManagerStub;
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;
/**
* This is a base functional test class containing tests that should be executed for each cache store/loader
* implementation. As these are functional tests, they should interact against Cache/CacheManager only and any access to
* the underlying cache store/loader should be done to verify contents.
*/
@Test(groups = {"unit", "smoke"}, testName = "persistence.BaseStoreFunctionalTest")
public abstract class BaseStoreFunctionalTest extends SingleCacheManagerTest {
protected abstract PersistenceConfigurationBuilder createCacheStoreConfig(PersistenceConfigurationBuilder persistence, boolean preload);
protected Object wrap(String key, String value) {
return value;
}
protected String unwrap(Object wrapped) {
return (String) wrapped;
}
protected BaseStoreFunctionalTest() {
cleanup = CleanupPhase.AFTER_METHOD;
}
@Override
protected void teardown() {
TestingUtil.clearContent(cacheManager);
super.teardown();
}
@Override
protected EmbeddedCacheManager createCacheManager() throws Exception {
return TestCacheManagerFactory.createCacheManager(false);
}
public void testTwoCachesSameCacheStore() {
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.read(cacheManager.getDefaultCacheConfiguration());
createCacheStoreConfig(cb.persistence(), false);
Configuration c = cb.build();
cacheManager.defineConfiguration("testTwoCachesSameCacheStore-1", c);
cacheManager.defineConfiguration("testTwoCachesSameCacheStore-2", c);
Cache<String, Object> first = cacheManager.getCache("testTwoCachesSameCacheStore-1");
Cache<String, Object> second = cacheManager.getCache("testTwoCachesSameCacheStore-2");
first.start();
second.start();
first.put("key", wrap("key", "val"));
assertEquals("val", unwrap(first.get("key")));
assertNull(second.get("key"));
second.put("key2", wrap("key2", "val2"));
assertEquals("val2", unwrap(second.get("key2")));
assertNull(first.get("key2"));
}
public void testPreloadAndExpiry() {
ConfigurationBuilder cb = TestCacheManagerFactory.getDefaultCacheConfiguration(false);
createCacheStoreConfig(cb.persistence(), true);
cacheManager.defineConfiguration("testPreloadAndExpiry", cb.build());
Cache<String, Object> cache = cacheManager.getCache("testPreloadAndExpiry");
cache.start();
assert cache.getCacheConfiguration().persistence().preload();
cache.put("k1", wrap("k1", "v"));
cache.put("k2", wrap("k2", "v"), 111111, TimeUnit.MILLISECONDS);
cache.put("k3", wrap("k3", "v"), -1, TimeUnit.MILLISECONDS, 222222, TimeUnit.MILLISECONDS);
cache.put("k4", wrap("k4", "v"), 333333, TimeUnit.MILLISECONDS, 444444, TimeUnit.MILLISECONDS);
assertCacheEntry(cache, "k1", "v", -1, -1);
assertCacheEntry(cache, "k2", "v", 111111, -1);
assertCacheEntry(cache, "k3", "v", -1, 222222);
assertCacheEntry(cache, "k4", "v", 333333, 444444);
cache.stop();
cache.start();
assertCacheEntry(cache, "k1", "v", -1, -1);
assertCacheEntry(cache, "k2", "v", 111111, -1);
assertCacheEntry(cache, "k3", "v", -1, 222222);
assertCacheEntry(cache, "k4", "v", 333333, 444444);
}
public void testPreloadStoredAsBinary() {
ConfigurationBuilder cb = TestCacheManagerFactory.getDefaultCacheConfiguration(false);
createCacheStoreConfig(cb.persistence(), true).storeAsBinary().enable();
cacheManager.defineConfiguration("testPreloadStoredAsBinary", cb.build());
Cache<String, Pojo> cache = cacheManager.getCache("testPreloadStoredAsBinary");
cache.start();
assert cache.getCacheConfiguration().persistence().preload();
assert cache.getCacheConfiguration().storeAsBinary().enabled();
cache.put("k1", new Pojo(1));
cache.put("k2", new Pojo(2), 111111, TimeUnit.MILLISECONDS);
cache.put("k3", new Pojo(3), -1, TimeUnit.MILLISECONDS, 222222, TimeUnit.MILLISECONDS);
cache.put("k4", new Pojo(4), 333333, TimeUnit.MILLISECONDS, 444444, TimeUnit.MILLISECONDS);
cache.stop();
cache.start();
assertEquals(4, cache.entrySet().size());
assertEquals(new Pojo(1), cache.get("k1"));
assertEquals(new Pojo(2), cache.get("k2"));
assertEquals(new Pojo(3), cache.get("k3"));
assertEquals(new Pojo(4), cache.get("k4"));
}
public static class Pojo implements Serializable, ExternalPojo {
private final int i;
public Pojo(int i) {
this.i = i;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Pojo pojo = (Pojo) o;
return i == pojo.i;
}
@Override
public int hashCode() {
return i;
}
}
public void testRestoreAtomicMap(Method m) {
cacheManager.defineConfiguration(m.getName(), configureCacheLoader(null, false).build());
Cache<String, Object> cache = cacheManager.getCache(m.getName());
AtomicMap<String, String> map = AtomicMapLookup.getAtomicMap(cache, m.getName());
map.put("a", "b");
//evict from memory
cache.evict(m.getName());
// now re-retrieve the map
assertEquals("b", AtomicMapLookup.getAtomicMap(cache, m.getName()).get("a"));
}
@Test(groups = "unstable")
public void testRestoreTransactionalAtomicMap(final Method m) throws Exception {
cacheManager.defineConfiguration(m.getName(), configureCacheLoader(null, false).build());
Cache<String, Object> cache = cacheManager.getCache(m.getName());
TransactionManager tm = cache.getAdvancedCache().getTransactionManager();
tm.begin();
final AtomicMap<String, String> map = AtomicMapLookup.getAtomicMap(cache, m.getName());
map.put("a", "b");
tm.commit();
//evict from memory
cache.evict(m.getName());
// now re-retrieve the map and make sure we see the diffs
assertEquals("b", AtomicMapLookup.getAtomicMap(cache, m.getName()).get("a"));
}
public void testStoreByteArrays(final Method m) throws PersistenceException {
ConfigurationBuilder base = new ConfigurationBuilder();
// we need to purge the container when loading, because we could try to compare
// some old entry using ByteArrayEquivalence and this throws ClassCastException
// for non-byte[] arguments
cacheManager.defineConfiguration(m.getName(), configureCacheLoader(base, true).build());
Cache<byte[], byte[]> cache = cacheManager.getCache(m.getName());
byte[] key = {1, 2, 3};
byte[] value = {4, 5, 6};
cache.put(key, value);
// Lookup in memory, sanity check
byte[] lookupKey = {1, 2, 3};
byte[] found = cache.get(lookupKey);
assertNotNull(found);
assertArrayEquals(value, found);
cache.evict(key);
// Lookup in cache store
found = cache.get(lookupKey);
assertNotNull(found);
assertArrayEquals(value, found);
}
public void testRemoveCache() {
ConfigurationBuilder cb = TestCacheManagerFactory.getDefaultCacheConfiguration(false);
createCacheStoreConfig(cb.persistence(), true);
EmbeddedCacheManager local = TestCacheManagerFactory.createCacheManager(cb);
try {
final String cacheName = "to-be-removed";
local.defineConfiguration(cacheName, local.getDefaultCacheConfiguration());
Cache<String, Object> cache = local.getCache(cacheName);
assertTrue(local.isRunning(cacheName));
cache.put("1", wrap("1", "v1"));
assertCacheEntry(cache, "1", "v1", -1, -1);
local.removeCache(cacheName);
assertFalse(local.isRunning(cacheName));
} finally {
TestingUtil.killCacheManagers(local);
}
}
public void testRemoveCacheWithPassivation() {
ConfigurationBuilder cb = TestCacheManagerFactory.getDefaultCacheConfiguration(false);
createCacheStoreConfig(cb.persistence().passivation(true), true);
EmbeddedCacheManager local = TestCacheManagerFactory.createCacheManager(cb);
try {
final String cacheName = "to-be-removed";
local.defineConfiguration(cacheName, local.getDefaultCacheConfiguration());
Cache<String, Object> cache = local.getCache(cacheName);
assertTrue(local.isRunning(cacheName));
cache.put("1", wrap("1", "v1"));
assertCacheEntry(cache, "1", "v1", -1, -1);
ByRef<Boolean> passivate = new ByRef<>(false);
PersistenceManager actual = cache.getAdvancedCache().getComponentRegistry().getComponent(PersistenceManager.class);
PersistenceManager stub = new PersistenceManagerStub() {
@Override
public void stop() {
actual.stop();
}
@Override
public void writeToAllNonTxStores(MarshalledEntry marshalledEntry, AccessMode modes) {
passivate.set(true);
}
};
cache.getAdvancedCache().getComponentRegistry().registerComponent(stub, PersistenceManager.class);
cache.getAdvancedCache().getComponentRegistry().rewire();
local.removeCache(cacheName);
assertFalse(local.isRunning(cacheName));
assertFalse(passivate.get());
assertEquals(0, actual.size());
} finally {
TestingUtil.killCacheManagers(local);
}
}
private ConfigurationBuilder configureCacheLoader(ConfigurationBuilder base, boolean purge) {
ConfigurationBuilder cfg = base == null ? new ConfigurationBuilder() : base;
cfg.transaction().transactionMode(TransactionMode.TRANSACTIONAL);
createCacheStoreConfig(cfg.persistence(), false);
cfg.persistence().stores().get(0).purgeOnStartup(purge);
return cfg;
}
private void assertCacheEntry(Cache cache, String key, String value, long lifespanMillis, long maxIdleMillis) {
DataContainer dc = cache.getAdvancedCache().getDataContainer();
InternalCacheEntry ice = dc.get(key);
assertNotNull(ice);
assertEquals(value, unwrap(ice.getValue()));
assertEquals(lifespanMillis, ice.getLifespan());
assertEquals(maxIdleMillis, ice.getMaxIdle());
if (lifespanMillis > -1) assert ice.getCreated() > -1 : "Lifespan is set but created time is not";
if (maxIdleMillis > -1) assert ice.getLastUsed() > -1 : "Max idle is set but last used is not";
}
}