package org.infinispan.server.test.client.hotrod;
import static org.infinispan.server.test.util.ITestUtils.isLocalMode;
import static org.infinispan.server.test.util.ITestUtils.sleepForSecs;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import org.infinispan.arquillian.core.RemoteInfinispanServer;
import org.infinispan.client.hotrod.Flag;
import org.infinispan.client.hotrod.MetadataValue;
import org.infinispan.client.hotrod.RemoteCache;
import org.infinispan.client.hotrod.RemoteCacheManager;
import org.infinispan.client.hotrod.ServerStatistics;
import org.infinispan.client.hotrod.VersionedValue;
import org.infinispan.client.hotrod.annotation.ClientListener;
import org.infinispan.client.hotrod.configuration.Configuration;
import org.infinispan.client.hotrod.configuration.ConfigurationBuilder;
import org.infinispan.client.hotrod.event.ClientCacheEntryCreatedEvent;
import org.infinispan.client.hotrod.event.ClientCacheEntryModifiedEvent;
import org.infinispan.client.hotrod.event.ClientCacheEntryRemovedEvent;
import org.infinispan.client.hotrod.event.ClientEvent;
import org.infinispan.client.hotrod.logging.Log;
import org.infinispan.client.hotrod.logging.LogFactory;
import org.infinispan.commons.marshall.jboss.GenericJBossMarshaller;
import org.infinispan.commons.util.CloseableIterator;
import org.infinispan.filter.KeyValueFilterConverterFactory;
import org.infinispan.notifications.cachelistener.filter.CacheEventConverterFactory;
import org.infinispan.notifications.cachelistener.filter.CacheEventFilterConverterFactory;
import org.infinispan.notifications.cachelistener.filter.CacheEventFilterFactory;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
/**
* Tests for HotRod client and its RemoteCache API. Subclasses must provide a
* way to get the list of remote HotRod servers and to assert the cache is
* empty.
* <p/>
* Subclasses may be used in Client-Server mode or Hybrid mode where HotRod
* server runs as a library deployed in an application server.
*
* @author Richard Achmatowicz
* @author Martin Gencur
* @author Jozef Vilkolak
*/
public abstract class AbstractRemoteCacheIT {
private static final Log log = LogFactory.getLog(AbstractRemoteCacheIT.class);
protected static String testCache = "default";
protected static RemoteCacheManager remoteCacheManager = null;
protected RemoteCache remoteCache;
protected final int ASYNC_OPS_ENTRY_LOAD = 10;
protected abstract List<RemoteInfinispanServer> getServers();
@Before
public void initialize() {
if (remoteCacheManager == null) {
Configuration config = createRemoteCacheManagerConfiguration();
remoteCacheManager = new RemoteCacheManager(config, true);
}
remoteCache = remoteCacheManager.getCache(testCache);
remoteCache.clear();
}
@AfterClass
public static void release() {
if (remoteCacheManager != null) {
remoteCacheManager.stop();
}
}
protected static Archive<?> createFilterArchive() {
return ShrinkWrap.create(JavaArchive.class, "filter.jar")
.addClasses(StaticCacheEventFilterFactory.class, DynamicCacheEventFilterFactory.class,
CustomPojoEventFilterFactory.class, Person.class)
.addAsServiceProvider(CacheEventFilterFactory.class,
StaticCacheEventFilterFactory.class, DynamicCacheEventFilterFactory.class,
CustomPojoEventFilterFactory.class);
}
protected static Archive<?> createConverterArchive() {
return ShrinkWrap.create(JavaArchive.class, "converter.jar")
.addClasses(StaticCacheEventConverterFactory.class, DynamicCacheEventConverterFactory.class,
CustomPojoEventConverterFactory.class, Person.class, CustomEvent.class)
.addAsServiceProvider(CacheEventConverterFactory.class,
StaticCacheEventConverterFactory.class, DynamicCacheEventConverterFactory.class,
CustomPojoEventConverterFactory.class);
}
protected static Archive<?> createFilterConverterArchive() {
return ShrinkWrap.create(JavaArchive.class, "filter-converter.jar")
.addClasses(FilterConverterFactory.class, CustomEvent.class,
CustomPojoFilterConverterFactory.class, Person.class, Id.class)
.addAsServiceProvider(CacheEventFilterConverterFactory.class, FilterConverterFactory.class,
CustomPojoFilterConverterFactory.class);
}
protected static Archive<?> createKeyValueFilterConverterArchive() {
return ShrinkWrap.create(JavaArchive.class, "key-value-filter-converter.jar")
.addClasses(TestKeyValueFilterConverterFactory.class, SampleEntity.class, Summary.class, SampleEntity.SampleEntityExternalizer.class, Summary.SummaryExternalizer.class)
.addAsServiceProvider(KeyValueFilterConverterFactory.class, TestKeyValueFilterConverterFactory.class);
}
private Configuration createRemoteCacheManagerConfiguration(int... hotrodPortOverrides) {
if (hotrodPortOverrides.length != 0) {
assert getServers().size() == hotrodPortOverrides.length : "The number of defined ports is different from server count";
}
ConfigurationBuilder config = new ConfigurationBuilder();
int index = 0;
for (RemoteInfinispanServer server : getServers()) {
int port = hotrodPortOverrides.length != 0 ? hotrodPortOverrides[index] : server.getHotrodEndpoint().getPort();
config.addServer()
.host(server.getHotrodEndpoint().getInetAddress().getHostName())
.port(port);
++index;
}
config.balancingStrategy("org.infinispan.server.test.client.hotrod.HotRodTestRequestBalancingStrategy")
// load balancing
.balancingStrategy("org.infinispan.client.hotrod.impl.transport.tcp.RoundRobinBalancingStrategy")
.forceReturnValues(false)
// TCP stuff
.tcpNoDelay(true)
.transportFactory("org.infinispan.client.hotrod.impl.transport.tcp.TcpTransportFactory")
// marshalling
// FIXME Workaround for ISPN-6367
.marshaller(new GenericJBossMarshaller(Thread.currentThread().getContextClassLoader()))
// executors
.asyncExecutorFactory().factoryClass("org.infinispan.client.hotrod.impl.async.DefaultAsyncExecutorFactory")
.addExecutorProperty("infinispan.client.hotrod.default_executor_factory.pool_size", "10")
.addExecutorProperty("infinispan.client.hotrod.default_executor_factory.queue_size", "100000")
//hashing
.keySizeEstimate(64)
.valueSizeEstimate(512);
return config.build();
}
private long numEntriesOnServer(int serverIndex) {
return getServers().get(serverIndex).
getCacheManager(isLocalMode() ? "local" : "clustered").
getCache(testCache).getNumberOfEntries();
}
@Test
public void testReplaceWithVersionWithLifespan() throws Exception {
int lifespanInSecs = 1;
assertNull(remoteCache.replace("aKey", "aValue"));
remoteCache.put("aKey", "aValue");
VersionedValue valueBinary = remoteCache.getVersioned("aKey");
assertTrue(remoteCache.replaceWithVersion("aKey", "aNewValue", valueBinary.getVersion(), lifespanInSecs));
// version should have changed; value should have changed
VersionedValue entry2 = remoteCache.getVersioned("aKey");
assertNotEquals(entry2.getVersion(), valueBinary.getVersion());
assertEquals("aNewValue", entry2.getValue());
sleepForSecs(lifespanInSecs + 1);
assertNull(remoteCache.getVersioned("aKey"));
}
@Test
public void testPut() throws Exception {
assertNull(remoteCache.put("aKey", "aValue"));
assertTrue(remoteCache.containsKey("aKey"));
assertEquals(remoteCache.get("aKey"), "aValue");
}
@Test
public void testPutWithLifespan() {
long lifespanInSecs = 1;
remoteCache.put("lkey", "value", lifespanInSecs, TimeUnit.SECONDS);
sleepForSecs(lifespanInSecs + 1);
assertNull(remoteCache.get("lkey"));
}
@Test
public void testSize() {
assertEquals(0, remoteCache.size());
// with force_return_value=false as default, this opertion
// should return null even if a previous value for aPut was there
assertNull(remoteCache.put("aKey", "aValue"));
assertTrue(remoteCache.containsKey("aKey"));
assertEquals(remoteCache.size(), 1);
// should be idempotent
assertEquals(remoteCache.size(), 1);
assertNull(remoteCache.put("anotherKey", "anotherValue"));
assertTrue(remoteCache.containsKey("anotherKey"));
assertEquals(remoteCache.size(), 2);
assertNull(remoteCache.remove("anotherKey"));
assertTrue(!remoteCache.containsKey("anotherKey"));
assertEquals(remoteCache.size(), 1);
assertNull(remoteCache.remove("aKey"));
assertTrue(!remoteCache.containsKey("aKey"));
assertEquals(remoteCache.size(), 0);
}
@Test
public void testIsEmpty() throws IOException {
assertTrue(remoteCache.isEmpty());
assertNull(remoteCache.put("aKey", "aValue"));
assertTrue(remoteCache.containsKey("aKey"));
assertTrue(!remoteCache.isEmpty());
assertNull(remoteCache.remove("aKey"));
assertTrue(!remoteCache.containsKey("aKey"));
assertTrue(remoteCache.isEmpty());
}
@Test
public void testContains() {
assertTrue(!remoteCache.containsKey("aKey"));
remoteCache.put("aKey", "aValue");
assertTrue(remoteCache.containsKey("aKey"));
}
@Test
public void testWithFlags() throws IOException {
assertNull(remoteCache.put("aKey", "aValue"));
assertTrue(remoteCache.containsKey("aKey"));
assertEquals("aValue", remoteCache.get("aKey"));
// should not return return old value
assertNull(remoteCache.put("aKey", "anotherValue"));
assertEquals("anotherValue", remoteCache.get("aKey"));
// now should return old value
assertEquals("anotherValue", remoteCache.withFlags(Flag.FORCE_RETURN_VALUE).put("aKey", "yetAnotherValue"));
}
@Test
public void testBulkOperations() {
Map<String, String> mapIn;
Map<String, String> mapOut = new HashMap<String, String>();
mapOut.put("aKey", "aValue");
mapOut.put("bKey", "bValue");
mapOut.put("cKey", "cValue");
remoteCache.putAll(mapOut);
mapIn = remoteCache.getBulk();
// check that the maps are equal
assertEquals(mapIn, mapOut);
}
@Test
public void testBulkOperationsWithLifespan() {
long lifespanInSecs = 1;
Map<String, String> mapIn = new HashMap<String, String>();
Map<String, String> mapOut = new HashMap<String, String>();
mapOut.put("aKey", "aValue");
mapOut.put("bKey", "bValue");
mapOut.put("cKey", "cValue");
remoteCache.putAll(mapOut, lifespanInSecs, TimeUnit.SECONDS);
// give the elements time to be evicted
sleepForSecs(lifespanInSecs + 1);
mapIn = remoteCache.getBulk();
assertEquals(mapIn.size(), 0);
}
@Test
public void testGetBulkWithLimit() {
Map<String, String> mapIn;
Map<String, String> mapOut = new HashMap<String, String>();
mapOut.put("aKey", "aValue");
mapOut.put("bKey", "bValue");
mapOut.put("cKey", "cValue");
remoteCache.putAll(mapOut);
mapIn = remoteCache.getBulk(2);
// we don't know which 2 entries will be retrieved
assertEquals(mapIn.size(), 2);
}
@Test
public void testGetName() {
// in hotrod protocol specification, the default cache is identified by an empty string
assertEquals(testCache, remoteCache.getName());
}
@Test
public void testKeySet() {
remoteCache.put("k1", "v1");
remoteCache.put("k2", "v2");
remoteCache.put("k3", "v3");
Set<String> expectedKeySet = new HashSet<String>();
expectedKeySet.add("k1");
expectedKeySet.add("k2");
expectedKeySet.add("k3");
assertEquals(expectedKeySet, remoteCache.keySet());
}
@Test
public void testGetWithMetadata() {
remoteCache.put("k1", "v1", 10000000, TimeUnit.MICROSECONDS); // setting only lifespan
remoteCache.put("k2", "v2", 10, TimeUnit.SECONDS, 10, TimeUnit.SECONDS); // lifespan + maxIdleTime
MetadataValue<String> k1 = remoteCache.getWithMetadata("k1");
MetadataValue<String> k2 = remoteCache.getWithMetadata("k2");
assertEquals(k1.getValue(), "v1");
// microseconds converted to seconds
assertEquals(k1.getLifespan(), 10);
assertEquals(k1.getMaxIdle(), -1);
assertEquals(k2.getValue(), "v2");
assertEquals(k2.getLifespan(), 10);
assertEquals(k2.getMaxIdle(), 10);
}
@Test
public void testRemoveAsync() throws Exception {
for (int i = 0; i <= ASYNC_OPS_ENTRY_LOAD; i++) {
remoteCache.put("key" + i, "value" + i);
}
Set<Future<?>> futures = new HashSet<Future<?>>();
for (int i = 0; i <= ASYNC_OPS_ENTRY_LOAD; i++) {
futures.add(remoteCache.removeAsync("key" + i));
}
for (Future<?> f : futures) {
f.get();
}
assertEquals(0, numEntriesOnServer(0));
}
@Test
public void testReplaceAsync() throws Exception {
remoteCache.clear();
for (int i = 0; i <= ASYNC_OPS_ENTRY_LOAD; i++) {
remoteCache.put("key" + i, "value" + i);
}
Set<Future<?>> futures = new HashSet<Future<?>>();
for (int i = 0; i <= ASYNC_OPS_ENTRY_LOAD; i++) {
futures.add(remoteCache.replaceAsync("key" + i, "newValue" + i, -1, TimeUnit.SECONDS, -1, TimeUnit.SECONDS));
}
for (Future<?> f : futures) {
f.get();
}
for (int i = 0; i <= ASYNC_OPS_ENTRY_LOAD; i++) {
assertEquals("newValue" + i, remoteCache.get("key" + i));
}
}
@Test
public void testGetVersionedCacheEntry() {
VersionedValue value = remoteCache.getVersioned("aKey");
assertNull("expected null but received: " + value, remoteCache.getVersioned("aKey"));
remoteCache.put("aKey", "aValue");
assertEquals("aValue", remoteCache.get("aKey"));
VersionedValue valueBinary = remoteCache.getVersioned("aKey");
assertNotNull(valueBinary);
assertEquals(valueBinary.getValue(), "aValue");
// now put the same value
remoteCache.put("aKey", "aValue");
VersionedValue entry2 = remoteCache.getVersioned("aKey");
assertEquals(entry2.getValue(), "aValue");
assertNotEquals(entry2.getVersion(), valueBinary.getVersion());
assertNotEquals(valueBinary, entry2);
// now put a different value
remoteCache.put("aKey", "anotherValue");
VersionedValue entry3 = remoteCache.getVersioned("aKey");
assertEquals(entry3.getValue(), "anotherValue");
assertNotEquals(entry3.getVersion(), entry2.getVersion());
assertNotEquals(entry3, entry2);
}
@Test
public void testReplace() {
// this should return null, indicating no k/v pair in the map
assertNull(remoteCache.replace("aKey", "anotherValue"));
remoteCache.put("aKey", "aValue");
assertNull(remoteCache.replace("aKey", "anotherValue"));
assertEquals(remoteCache.get("aKey"), "anotherValue");
}
@Test
public void testReplaceWithVersion() {
assertNull(remoteCache.replace("aKey", "aValue"));
remoteCache.put("aKey", "aValue");
VersionedValue valueBinary = remoteCache.getVersioned("aKey");
// replacement should take place (and so return true)
assertTrue(remoteCache.replaceWithVersion("aKey", "aNewValue", valueBinary.getVersion()));
// version should have changed; value should have changed
VersionedValue entry2 = remoteCache.getVersioned("aKey");
assertNotEquals(entry2.getVersion(), valueBinary.getVersion());
assertEquals(entry2.getValue(), "aNewValue");
// replacement should not take place because we have changed the value
assertTrue(!remoteCache.replaceWithVersion("aKey", "aNewValue", valueBinary.getVersion()));
}
@Test
public void testRemove() throws IOException {
assertNull(remoteCache.put("aKey", "aValue"));
assertEquals(remoteCache.get("aKey"), "aValue");
assertNull(remoteCache.remove("aKey"));
assertTrue(!remoteCache.containsKey("aKey"));
}
@Test
public void testRemoveWithVersion() {
assertTrue(!remoteCache.removeWithVersion("aKey", 12321212l));
remoteCache.put("aKey", "aValue");
VersionedValue valueBinary = remoteCache.getVersioned("aKey");
assertTrue(remoteCache.removeWithVersion("aKey", valueBinary.getVersion()));
remoteCache.put("aKey", "aNewValue");
VersionedValue entry2 = remoteCache.getVersioned("aKey");
assertNotEquals(entry2.getVersion(), valueBinary.getVersion());
assertEquals(entry2.getValue(), "aNewValue");
assertTrue(!remoteCache.removeWithVersion("aKey", valueBinary.getVersion()));
}
@Test
public void testPutIfAbsent() {
remoteCache.putIfAbsent("aKey", "aValue");
assertEquals(remoteCache.size(), 1);
assertEquals(remoteCache.get("aKey"), "aValue");
assertNull(remoteCache.putIfAbsent("aKey", "anotherValue"));
assertEquals(remoteCache.get("aKey"), "aValue");
}
@Test
public void testPutIfAbsentWithLifespan() throws Exception {
int lifespanInSecs = 1;
remoteCache.putIfAbsent("aKey", "aValue", lifespanInSecs, TimeUnit.SECONDS, -1, TimeUnit.SECONDS);
assertEquals(1, remoteCache.size());
assertEquals("aValue", remoteCache.get("aKey"));
sleepForSecs(lifespanInSecs + 1);
//verify the entry expired
assertEquals(null, remoteCache.get("akey"));
remoteCache.putIfAbsent("aKey", "aValue");
assertEquals(1, remoteCache.size());
assertEquals("aValue", remoteCache.get("aKey"));
assertEquals(null, remoteCache.putIfAbsent("aKey", "anotherValue", lifespanInSecs, TimeUnit.SECONDS, -1, TimeUnit.SECONDS));
sleepForSecs(lifespanInSecs + 1);
//verify the entry is still there because it was not put with lifespan since it had already existed
assertEquals("aValue", remoteCache.get("aKey"));
}
@Test
public void testClear() {
remoteCache.put("aKey", "aValue");
remoteCache.put("aKey2", "aValue");
remoteCache.clear();
assertTrue(!remoteCache.containsKey("aKey"));
assertTrue(!remoteCache.containsKey("aKey2"));
}
@Test
public void testGetRemoteCacheManager() {
RemoteCacheManager manager = null;
manager = remoteCache.getRemoteCacheManager();
assertEquals("getRemoteCachemanager() returned incorrect value", manager, remoteCacheManager);
}
@Test
public void testStats() {
ServerStatistics remoteStats = remoteCache.stats();
assertNotNull(remoteStats);
log.tracef("named stats = %s", remoteStats.getStatsMap());
}
@Test
public void testUnsupportedOperations() {
try {
remoteCache.remove("aKey", "aValue");
fail("call to remove() did not raise UnsupportedOperationException ");
} catch (UnsupportedOperationException uoe) {
// Unsupported operation exception correctly thrown
}
try {
remoteCache.removeAsync("aKey", "aValue");
fail("call to removeAsync() did not raise UnsupportedOperationException ");
} catch (UnsupportedOperationException uoe) {
// Unsupported operation exception correctly thrown
}
try {
remoteCache.replace("aKey", "oldValue", "newValue");
fail("call to replace() did not raise UnsupportedOperationException ");
} catch (UnsupportedOperationException uoe) {
// Unsupported operation exception correctly thrown
}
try {
remoteCache.replace("aKey", "oldValue", "newValue", -1, TimeUnit.SECONDS);
fail("call to replace() did not raise UnsupportedOperationException ");
} catch (UnsupportedOperationException uoe) {
// Unsupported operation exception correctly thrown
}
try {
remoteCache.replace("aKey", "oldValue", "newValue", -1, TimeUnit.SECONDS, -1, TimeUnit.SECONDS);
fail("call to replace() did not raise UnsupportedOperationException ");
} catch (UnsupportedOperationException uoe) {
// Unsupported operation exception correctly thrown
}
try {
remoteCache.replaceAsync("aKey", "oldValue", "newValue");
fail("call to replaceAsync() did not raise UnsupportedOperationException ");
} catch (UnsupportedOperationException uoe) {
// Unsupported operation exception correctly thrown
}
try {
remoteCache.replaceAsync("aKey", "oldValue", "newValue", -1, TimeUnit.SECONDS);
fail("call to replaceAsync() did not raise UnsupportedOperationException ");
} catch (UnsupportedOperationException uoe) {
// Unsupported operation exception correctly thrown
}
try {
remoteCache.replaceAsync("aKey", "oldValue", "newValue", -1, TimeUnit.SECONDS, -1, TimeUnit.SECONDS);
fail("call to replaceAsync() did not raise UnsupportedOperationException ");
} catch (UnsupportedOperationException uoe) {
// Unsupported operation exception correctly thrown
}
try {
remoteCache.containsValue("aValue");
fail("call to containsValue() did not raise UnsupportedOperationException ");
} catch (UnsupportedOperationException uoe) {
// Unsupported operation exception correctly thrown
}
try {
remoteCache.entrySet();
fail("call to entrySet() did not raise UnsupportedOperationException ");
} catch (UnsupportedOperationException uoe) {
// Unsupported operation exception correctly thrown
}
try {
remoteCache.values();
fail("call to values() did not raise UnsupportedOperationException ");
} catch (UnsupportedOperationException uoe) {
// Unsupported operation exception correctly thrown
}
}
@Test
public void testClearAsync() throws Exception {
fill(remoteCache, ASYNC_OPS_ENTRY_LOAD);
assertEquals(ASYNC_OPS_ENTRY_LOAD, numEntriesOnServer(0));
CompletableFuture<Void> future = remoteCache.clearAsync();
future.get();
assertEquals(0, numEntriesOnServer(0));
}
@Test
public void testPutAsync() throws Exception {
assertNull(remoteCache.get("k"));
Future<String> f = remoteCache.putAsync("k", "v");
assertFalse(f.isCancelled());
assertNull(f.get());
assertTrue(f.isDone());
assertEquals("v", remoteCache.get("k"));
}
@Test
public void testPutWithLifespanAsync() throws Exception {
long lifespanInSecs = 2;
Set<Future<?>> futures = new HashSet<Future<?>>();
for (int i = 0; i != ASYNC_OPS_ENTRY_LOAD; i++) {
futures.add(remoteCache.putAsync("key" + i, "value" + i, lifespanInSecs, TimeUnit.SECONDS, -1, TimeUnit.SECONDS));
}
for (Future<?> f : futures) {
f.get();
}
assertEquals(ASYNC_OPS_ENTRY_LOAD, numEntriesOnServer(0));
sleepForSecs(lifespanInSecs + 1);
for (int i = 0; i != ASYNC_OPS_ENTRY_LOAD; i++) {
assertEquals(null, remoteCache.get("key" + i));
}
}
@Test
public void testPutIfAbsentAsync() throws Exception {
Set<Future<?>> futures = new HashSet<Future<?>>();
for (int i = 0; i != ASYNC_OPS_ENTRY_LOAD; i++) {
futures.add(remoteCache.putIfAbsentAsync("key" + i, "value" + i));
}
// check that the puts completed successfully
for (Future<?> f : futures) {
f.get();
}
assertEquals(ASYNC_OPS_ENTRY_LOAD, numEntriesOnServer(0));
assertEquals("value" + (ASYNC_OPS_ENTRY_LOAD - 1), remoteCache.get("key" + (ASYNC_OPS_ENTRY_LOAD - 1)));
for (int i = 0; i != ASYNC_OPS_ENTRY_LOAD; i++) {
futures.add(remoteCache.putIfAbsentAsync("key" + i, "newValue" + i));
}
for (Future<?> f : futures) {
f.get();
}
for (int i = 0; i != ASYNC_OPS_ENTRY_LOAD; i++) {
assertEquals("value" + i, remoteCache.get("key" + i));
}
}
@Test
public void testPutIfAbsentWithLifespanAsync() throws Exception {
long lifespanInSecs = 2;
Set<Future<?>> futures = new HashSet<Future<?>>();
for (int i = 0; i != ASYNC_OPS_ENTRY_LOAD; i++) {
futures.add(remoteCache.putIfAbsentAsync("key" + i, "value" + i, lifespanInSecs, TimeUnit.SECONDS));
}
for (Future<?> f : futures) {
f.get();
}
assertEquals(ASYNC_OPS_ENTRY_LOAD, numEntriesOnServer(0));
assertEquals("value" + (ASYNC_OPS_ENTRY_LOAD - 1), remoteCache.get("key" + (ASYNC_OPS_ENTRY_LOAD - 1)));
sleepForSecs(lifespanInSecs + 1);
for (int i = 0; i != ASYNC_OPS_ENTRY_LOAD; i++) {
assertEquals(null, remoteCache.get("key" + i));
}
}
@Test
public void testReplaceWithVersionWithLifespanAsync() throws Exception {
int lifespanInSecs = 2;
assertNull(remoteCache.replace("aKey", "aValue"));
remoteCache.put("aKey", "aValue");
VersionedValue valueBinary = remoteCache.getVersioned("aKey");
CompletableFuture<Boolean> future = remoteCache.replaceWithVersionAsync("aKey", "aNewValue", valueBinary.getVersion(),
lifespanInSecs);
assertTrue(future.get());
// version should have changed; value should have changed
VersionedValue entry2 = remoteCache.getVersioned("aKey");
assertNotEquals(entry2.getVersion(), valueBinary.getVersion());
assertEquals("aNewValue", entry2.getValue());
sleepForSecs(lifespanInSecs + 1);
assertNull(remoteCache.getVersioned("aKey"));
}
@Test
public void testGetAsync() throws Exception {
fill(remoteCache, ASYNC_OPS_ENTRY_LOAD);
assertEquals(ASYNC_OPS_ENTRY_LOAD, numEntriesOnServer(0));
Set<Future<?>> futures = new HashSet<Future<?>>();
for (int i = 0; i != ASYNC_OPS_ENTRY_LOAD; i++) {
futures.add(remoteCache.getAsync("key" + i));
}
for (Future<?> f : futures) {
assertNotNull(f.get());
}
}
@Test
public void testBulkOperationsAsync() throws Exception {
Map<String, String> mapIn = new HashMap<String, String>();
Map<String, String> mapOut = new HashMap<String, String>();
fill(mapOut, ASYNC_OPS_ENTRY_LOAD);
CompletableFuture<Void> future = remoteCache.putAllAsync(mapOut);
future.get();
mapIn = remoteCache.getBulk();
assertEquals(mapOut, mapIn);
}
@Test
public void testBulkOperationsWithLifespanAsync() throws Exception {
long lifespanInSecs = 3;
Map<String, String> mapIn = new HashMap<String, String>();
Map<String, String> mapOut = new HashMap<String, String>();
fill(mapOut, ASYNC_OPS_ENTRY_LOAD);
CompletableFuture<Void> future = remoteCache.putAllAsync(mapOut, lifespanInSecs, TimeUnit.SECONDS);
future.get();
sleepForSecs(lifespanInSecs + 2);
mapIn = remoteCache.getBulk();
assertEquals(0, mapIn.size());
}
@Test
public void testReplaceWithVersionAsync() throws Exception {
assertNull(remoteCache.replace("aKey", "aValue"));
remoteCache.put("aKey", "aValue");
VersionedValue valueBinary = remoteCache.getVersioned("aKey");
// replacement should take place (and so return true)
CompletableFuture<Boolean> future = remoteCache.replaceWithVersionAsync("aKey", "aNewValue", valueBinary.getVersion());
assertTrue(future.get());
// version should have changed; value should have changed
VersionedValue entry2 = remoteCache.getVersioned("aKey");
assertNotEquals(entry2.getVersion(), valueBinary.getVersion());
assertEquals("aNewValue", entry2.getValue());
// replacement should not take place because we have changed the value
future = remoteCache.replaceWithVersionAsync("aKey", "aNewValue", valueBinary.getVersion());
assertFalse(future.get());
}
@Test
public void testRemoveWithVersionAsync() throws Exception {
CompletableFuture<Boolean> future = null;
future = remoteCache.removeWithVersionAsync("aKey", 12321212l);
assertTrue(!future.get());
remoteCache.put("aKey", "aValue");
VersionedValue valueBinary = remoteCache.getVersioned("aKey");
future = remoteCache.removeWithVersionAsync("aKey", valueBinary.getVersion());
assertTrue(future.get());
remoteCache.put("aKey", "aNewValue");
VersionedValue entry2 = remoteCache.getVersioned("aKey");
assertNotEquals(entry2.getVersion(), valueBinary.getVersion());
assertEquals(entry2.getValue(), "aNewValue");
future = remoteCache.removeWithVersionAsync("aKey", valueBinary.getVersion());
assertTrue(!future.get());
}
@Test
public void testGetProtocolVersion() throws Exception {
assertEquals("HotRod client, protocol version: 2.6", remoteCache.getProtocolVersion());
}
@Test
public void testPutGetCustomObject() throws Exception {
final Person p = new Person("Martin");
remoteCache.put("k1", p);
assertEquals(p, remoteCache.get("k1"));
}
@Test
public void testEventReceiveBasic() {
final EventLogListener eventListener = new EventLogListener();
remoteCache.addClientListener(eventListener);
try {
expectNoEvents(eventListener);
// Created events
remoteCache.put(1, "one");
expectOnlyCreatedEvent(1, eventListener);
remoteCache.put(2, "two");
expectOnlyCreatedEvent(2, eventListener);
// Modified events
remoteCache.put(1, "newone");
expectOnlyModifiedEvent(1, eventListener);
// Remove events
remoteCache.remove(1);
expectOnlyRemovedEvent(1, eventListener);
} finally {
remoteCache.removeClientListener(eventListener);
}
}
@Test
public void testEventReceiveConditional() {
final EventLogListener eventListener = new EventLogListener();
remoteCache.addClientListener(eventListener);
try {
expectNoEvents(eventListener);
// Put if absent
remoteCache.putIfAbsent(1, "one");
expectOnlyCreatedEvent(1, eventListener);
remoteCache.putIfAbsent(1, "again");
expectNoEvents(eventListener);
// Replace
remoteCache.replace(1, "newone");
expectOnlyModifiedEvent(1, eventListener);
// Replace with version
remoteCache.replaceWithVersion(1, "one", 0);
expectNoEvents(eventListener);
VersionedValue<String> versioned = remoteCache.getVersioned(1);
remoteCache.replaceWithVersion(1, "one", versioned.getVersion());
expectOnlyModifiedEvent(1, eventListener);
// Remove with version
remoteCache.removeWithVersion(1, 0);
expectNoEvents(eventListener);
versioned = remoteCache.getVersioned(1);
remoteCache.removeWithVersion(1, versioned.getVersion());
expectOnlyRemovedEvent(1, eventListener);
} finally {
remoteCache.removeClientListener(eventListener);
}
}
@Test
public void testEventFilteringStatic() {
final StaticFilteredEventLogListener eventListener = new StaticFilteredEventLogListener();
remoteCache.addClientListener(eventListener);
try {
expectNoEvents(eventListener);
remoteCache.put(1, "one");
expectNoEvents(eventListener);
remoteCache.put(2, "two");
expectOnlyCreatedEvent(2, eventListener);
remoteCache.remove(1);
expectNoEvents(eventListener);
remoteCache.remove(2);
expectOnlyRemovedEvent(2, eventListener);
} finally {
remoteCache.removeClientListener(eventListener);
}
}
@Test
public void testEventFilteringDynamic() {
final DynamicFilteredEventLogListener eventListener = new DynamicFilteredEventLogListener();
remoteCache.addClientListener(eventListener, new Object[]{3}, null);
try {
expectNoEvents(eventListener);
remoteCache.put(1, "one");
expectNoEvents(eventListener);
remoteCache.put(2, "two");
expectNoEvents(eventListener);
remoteCache.put(3, "three");
expectOnlyCreatedEvent(3, eventListener);
remoteCache.replace(1, "new-one");
expectNoEvents(eventListener);
remoteCache.replace(2, "new-two");
expectNoEvents(eventListener);
remoteCache.replace(3, "new-three");
expectOnlyModifiedEvent(3, eventListener);
remoteCache.remove(1);
expectNoEvents(eventListener);
remoteCache.remove(2);
expectNoEvents(eventListener);
remoteCache.remove(3);
expectOnlyRemovedEvent(3, eventListener);
} finally {
remoteCache.removeClientListener(eventListener);
}
}
@Test
public void testCustomEvents() {
final StaticCustomEventLogListener eventListener = new StaticCustomEventLogListener();
remoteCache.addClientListener(eventListener);
try {
eventListener.expectNoEvents();
remoteCache.put(1, "one");
eventListener.expectSingleCustomEvent(1, "one");
remoteCache.put(1, "newone");
eventListener.expectSingleCustomEvent(1, "newone");
remoteCache.remove(1);
eventListener.expectSingleCustomEvent(1, null);
} finally {
remoteCache.removeClientListener(eventListener);
}
}
@Test
public void testCustomEventsDynamic() {
final DynamicCustomEventLogListener eventListener = new DynamicCustomEventLogListener();
remoteCache.addClientListener(eventListener, null, new Object[]{2});
try {
eventListener.expectNoEvents();
remoteCache.put(1, "one");
eventListener.expectSingleCustomEvent(1, "one");
remoteCache.put(2, "two");
eventListener.expectSingleCustomEvent(2, null);
} finally {
remoteCache.removeClientListener(eventListener);
}
}
@Test
public void testCustomFilterEvents() {
final FilterCustomEventLogListener eventListener = new FilterCustomEventLogListener();
remoteCache.addClientListener(eventListener, new Object[]{3}, null);
try {
eventListener.expectNoEvents();
remoteCache.put(1, "one");
eventListener.expectSingleCustomEvent(1, "one");
remoteCache.put(1, "uno");
eventListener.expectSingleCustomEvent(1, "uno");
remoteCache.put(2, "two");
eventListener.expectSingleCustomEvent(2, "two");
remoteCache.put(2, "dos");
eventListener.expectSingleCustomEvent(2, "dos");
remoteCache.put(3, "three");
eventListener.expectSingleCustomEvent(3, null);
remoteCache.put(3, "tres");
eventListener.expectSingleCustomEvent(3, null);
remoteCache.remove(1);
eventListener.expectSingleCustomEvent(1, null);
remoteCache.remove(2);
eventListener.expectSingleCustomEvent(2, null);
remoteCache.remove(3);
eventListener.expectSingleCustomEvent(3, null);
} finally {
remoteCache.removeClientListener(eventListener);
}
}
@Test
public void testIterationWithCustomClasses() {
remoteCache.put("1", new SampleEntity("value1,value2"));
remoteCache.put("2", new SampleEntity("value3,value2"));
remoteCache.put("ignore", new SampleEntity("whatever"));
remoteCache.put("3", new SampleEntity("value7,value8"));
final Map<Object, Object> entryMap = new HashMap<>();
try(CloseableIterator<Entry<Object, Object>> closeableIterator = remoteCache.retrieveEntries("csv-key-value-filter-converter-factory", null, 10)) {
closeableIterator.forEachRemaining(new Consumer<Entry<Object, Object>>() {
@Override
public void accept(Entry<Object, Object> e) {
entryMap.put(e.getKey(), e.getValue());
}
});
}
assertEquals(3, entryMap.size());
assertEquals(Arrays.asList("value1","value2"), ((Summary) entryMap.get("1")).getAttributes());
assertEquals(Arrays.asList("value3","value2"), ((Summary) entryMap.get("2")).getAttributes());
assertEquals(Arrays.asList("value7","value8"), ((Summary) entryMap.get("3")).getAttributes());
}
@Test
public void testEventFilteringCustomPojo() {
final CustomPojoFilteredEventLogListener eventListener = new CustomPojoFilteredEventLogListener();
remoteCache.addClientListener(eventListener, new Object[]{"two"}, null);
try {
expectNoEvents(eventListener);
remoteCache.put(1, new Person("one"));
expectNoEvents(eventListener);
remoteCache.put(2, new Person("two"));
expectOnlyCreatedEvent(2, eventListener);
remoteCache.remove(1);
expectNoEvents(eventListener);
remoteCache.remove(2);
expectOnlyRemovedEvent(2, eventListener);
} finally {
remoteCache.removeClientListener(eventListener);
}
}
@Test
public void testCustomEventsCustomPojo() {
final CustomPojoCustomEventLogListener eventListener = new CustomPojoCustomEventLogListener();
remoteCache.addClientListener(eventListener, null, new Object[]{new Person("two")});
try {
eventListener.expectNoEvents();
remoteCache.put(1, new Person("one"));
eventListener.expectSingleCustomEvent(1, new Person("one"));
remoteCache.put(2, new Person("two"));
eventListener.expectSingleCustomEvent(2, null);
} finally {
remoteCache.removeClientListener(eventListener);
}
}
@Test
public void testCustomFilterEventsCustomPojo() {
final CustomPojoFilterCustomEventLogListener eventListener = new CustomPojoFilterCustomEventLogListener();
remoteCache.addClientListener(eventListener, new Object[]{new Id(3)}, null);
try {
eventListener.expectNoEvents();
remoteCache.put(new Id(1), new Person("one"));
eventListener.expectSingleCustomEvent(new Id(1), new Person("one"));
remoteCache.put(new Id(1), new Person("uno"));
eventListener.expectSingleCustomEvent(new Id(1), new Person("uno"));
remoteCache.put(new Id(2), new Person("two"));
eventListener.expectSingleCustomEvent(new Id(2), new Person("two"));
remoteCache.put(new Id(2), new Person("dos"));
eventListener.expectSingleCustomEvent(new Id(2), new Person("dos"));
remoteCache.put(new Id(3), new Person("three"));
eventListener.expectSingleCustomEvent(new Id(3), null);
remoteCache.put(new Id(3), new Person("tres"));
eventListener.expectSingleCustomEvent(new Id(3), null);
remoteCache.remove(new Id(1));
eventListener.expectSingleCustomEvent(new Id(1), null);
remoteCache.remove(new Id(2));
eventListener.expectSingleCustomEvent(new Id(2), null);
remoteCache.remove(new Id(3));
eventListener.expectSingleCustomEvent(new Id(3), null);
} finally {
remoteCache.removeClientListener(eventListener);
}
}
public static <K> void expectOnlyCreatedEvent(K key, EventLogListener eventListener) {
expectSingleEvent(key, eventListener, ClientEvent.Type.CLIENT_CACHE_ENTRY_CREATED);
expectNoEvents(eventListener, ClientEvent.Type.CLIENT_CACHE_ENTRY_MODIFIED);
expectNoEvents(eventListener, ClientEvent.Type.CLIENT_CACHE_ENTRY_REMOVED);
}
public static <K> void expectOnlyModifiedEvent(K key, EventLogListener eventListener) {
expectSingleEvent(key, eventListener, ClientEvent.Type.CLIENT_CACHE_ENTRY_MODIFIED);
expectNoEvents(eventListener, ClientEvent.Type.CLIENT_CACHE_ENTRY_CREATED);
expectNoEvents(eventListener, ClientEvent.Type.CLIENT_CACHE_ENTRY_REMOVED);
}
public static <K> void expectOnlyRemovedEvent(K key, EventLogListener eventListener) {
expectSingleEvent(key, eventListener, ClientEvent.Type.CLIENT_CACHE_ENTRY_REMOVED);
expectNoEvents(eventListener, ClientEvent.Type.CLIENT_CACHE_ENTRY_CREATED);
expectNoEvents(eventListener, ClientEvent.Type.CLIENT_CACHE_ENTRY_MODIFIED);
}
public static <K> void expectSingleEvent(K key, EventLogListener eventListener, ClientEvent.Type type) {
switch (type) {
case CLIENT_CACHE_ENTRY_CREATED:
ClientCacheEntryCreatedEvent createdEvent = eventListener.pollEvent(type);
assertEquals(key, createdEvent.getKey());
break;
case CLIENT_CACHE_ENTRY_MODIFIED:
ClientCacheEntryModifiedEvent modifiedEvent = eventListener.pollEvent(type);
assertEquals(key, modifiedEvent.getKey());
break;
case CLIENT_CACHE_ENTRY_REMOVED:
ClientCacheEntryRemovedEvent removedEvent = eventListener.pollEvent(type);
assertEquals(key, removedEvent.getKey());
break;
}
assertEquals(0, eventListener.queue(type).size());
}
public static void expectNoEvents(EventLogListener eventListener) {
expectNoEvents(eventListener, ClientEvent.Type.CLIENT_CACHE_ENTRY_CREATED);
expectNoEvents(eventListener, ClientEvent.Type.CLIENT_CACHE_ENTRY_MODIFIED);
expectNoEvents(eventListener, ClientEvent.Type.CLIENT_CACHE_ENTRY_REMOVED);
}
public static void expectNoEvents(EventLogListener eventListener, ClientEvent.Type type) {
switch (type) {
case CLIENT_CACHE_ENTRY_CREATED:
assertEquals(0, eventListener.createdEvents.size());
break;
case CLIENT_CACHE_ENTRY_MODIFIED:
assertEquals(0, eventListener.modifiedEvents.size());
break;
case CLIENT_CACHE_ENTRY_REMOVED:
assertEquals(0, eventListener.removedEvents.size());
break;
}
}
@ClientListener(filterFactoryName = "static-filter-factory")
public static class StaticFilteredEventLogListener extends EventLogListener {}
@ClientListener(filterFactoryName = "dynamic-filter-factory")
public static class DynamicFilteredEventLogListener extends EventLogListener {}
@ClientListener(converterFactoryName = "static-converter-factory")
public static class StaticCustomEventLogListener extends CustomEventLogListener {}
@ClientListener(converterFactoryName = "dynamic-converter-factory")
public static class DynamicCustomEventLogListener extends CustomEventLogListener {}
@ClientListener(filterFactoryName = "filter-converter-factory", converterFactoryName = "filter-converter-factory")
public static class FilterCustomEventLogListener extends CustomEventLogListener {}
@ClientListener(filterFactoryName = "pojo-filter-factory")
public static class CustomPojoFilteredEventLogListener extends EventLogListener {}
@ClientListener(converterFactoryName = "pojo-converter-factory")
public static class CustomPojoCustomEventLogListener extends CustomEventLogListener {}
@ClientListener(filterFactoryName = "pojo-filter-converter-factory", converterFactoryName = "pojo-filter-converter-factory")
public static class CustomPojoFilterCustomEventLogListener extends CustomEventLogListener {}
public static class Person implements Serializable {
final String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
if (!name.equals(person.name)) return false;
return true;
}
@Override
public int hashCode() {
return name.hashCode();
}
}
public static class Id implements Serializable {
final byte id;
public Id(int id) {
this.id = (byte) id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Id id1 = (Id) o;
if (id != id1.id) return false;
return true;
}
@Override
public int hashCode() {
return id;
}
}
protected <T extends Map<String, String>> void fill(T map, int entryCount) {
for (int i = 0; i != entryCount; i++) {
map.put("key" + i, "value" + i);
}
}
}