package org.infinispan.client.hotrod.near;
import static org.infinispan.client.hotrod.near.MockNearCacheService.MockClearEvent;
import static org.infinispan.client.hotrod.near.MockNearCacheService.MockEvent;
import static org.infinispan.client.hotrod.near.MockNearCacheService.MockGetEvent;
import static org.infinispan.client.hotrod.near.MockNearCacheService.MockPutEvent;
import static org.infinispan.client.hotrod.near.MockNearCacheService.MockPutIfAbsentEvent;
import static org.infinispan.client.hotrod.near.MockNearCacheService.MockRemoveEvent;
import static org.infinispan.client.hotrod.test.HotRodClientTestingUtil.entryVersion;
import static org.infinispan.client.hotrod.test.HotRodClientTestingUtil.killRemoteCacheManager;
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.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import org.infinispan.Cache;
import org.infinispan.client.hotrod.RemoteCache;
import org.infinispan.client.hotrod.RemoteCacheManager;
import org.infinispan.client.hotrod.VersionedValue;
import org.infinispan.client.hotrod.configuration.ConfigurationBuilder;
import org.infinispan.client.hotrod.configuration.NearCacheConfiguration;
import org.infinispan.client.hotrod.configuration.NearCacheMode;
class AssertsNearCache<K, V> {
final RemoteCache<K, V> remote;
final Cache<byte[], ?> server;
final BlockingQueue<MockEvent> events;
final RemoteCacheManager manager;
final NearCacheMode nearCacheMode;
private AssertsNearCache(RemoteCacheManager manager, Cache<byte[], ?> server, BlockingQueue<MockEvent> events) {
this.manager = manager;
this.remote = manager.getCache();
this.server = server;
this.events = events;
this.nearCacheMode = manager.getConfiguration().nearCache().mode();
}
static <K, V> AssertsNearCache<K, V> create(Cache<byte[], ?> server, ConfigurationBuilder builder) {
final BlockingQueue<MockEvent> events = new ArrayBlockingQueue<>(128);
RemoteCacheManager manager = new RemoteCacheManager(builder.build()) {
@Override
protected NearCacheService<K, V> createNearCacheService(NearCacheConfiguration cfg) {
return new MockNearCacheService<K, V>(cfg, events, listenerNotifier);
}
};
return new AssertsNearCache<>(manager, server, events);
}
AssertsNearCache<K, V> get(K key, V expected) {
assertEquals(expected, remote.get(key));
return this;
}
AssertsNearCache<K, V> getVersioned(K key, V expected) {
VersionedValue<V> versioned = remote.getVersioned(key);
assertEquals(expected, versioned == null ? null : versioned.getValue());
return this;
}
AssertsNearCache<K, V> put(K key, V value) {
remote.put(key, value);
return this;
}
AssertsNearCache<K, V> remove(K key) {
remote.remove(key);
return this;
}
AssertsNearCache<K, V> expectNoNearEvents() {
assertEquals(events.toString(), 0, events.size());
return this;
}
AssertsNearCache<K, V> expectNearGetValueVersion(K key, V value) {
MockGetEvent get = assertGetKeyValue(key, value);
if (value != null) {
long serverVersion = entryVersion(server, key);
assertEquals(serverVersion, get.value.getVersion());
}
return this;
}
AssertsNearCache<K, V> expectNearGetValue(K key, V value) {
assertGetKeyValue(key, value);
return this;
}
AssertsNearCache<K, V> expectNearGetNull(K key) {
MockGetEvent get = assertGetKey(key);
assertNull(get.value);
return this;
}
@SafeVarargs
final AssertsNearCache<K, V> expectNearPut(K key, V value, AssertsNearCache<K, V>... affected) {
expectNearPutInClient(this, key, value);
for (AssertsNearCache<K, V> client : affected)
expectNearPutInClient(client, key, value);
return this;
}
private static <K, V> void expectNearPutInClient(AssertsNearCache<K, V> client, K key, V value) {
MockPutEvent put = pollEvent(client.events);
assertEquals(key, put.key);
assertEquals(value, put.value.getValue());
}
AssertsNearCache<K, V> expectNearPutIfAbsent(K key, V value) {
MockPutIfAbsentEvent put = pollEvent(events);
assertEquals(key, put.key);
assertEquals(value, put.value.getValue());
return this;
}
@SafeVarargs
final AssertsNearCache<K, V> expectNearRemove(K key, AssertsNearCache<K, V>... affected) {
expectLocalNearRemoveInClient(this, key);
for (AssertsNearCache<K, V> client : affected)
expectRemoteNearRemoveInClient(client, key);
return this;
}
@SafeVarargs
final AssertsNearCache<K, V> expectNearClear(AssertsNearCache<K, V>... affected) {
expectNearClearInClient(this);
for (AssertsNearCache<K, V> client : affected)
expectNearClearInClient(client);
return this;
}
void expectNearClearInClient(AssertsNearCache<K, V> client) {
MockEvent clear = pollEvent(client.events);
assertTrue("Unexpected event: " + clear, clear instanceof MockClearEvent);
}
void stop() {
killRemoteCacheManager(manager);
}
private static <K, V> void expectLocalNearRemoveInClient(AssertsNearCache<K, V> client, K key) {
if (client.nearCacheMode.invalidated()) {
// Preemptive remove
MockRemoveEvent preemptiveRemove = pollEvent(client.events);
assertEquals(key, preemptiveRemove.key);
}
// Remote event remove
MockRemoveEvent remoteRemove = pollEvent(client.events);
assertEquals(key, remoteRemove.key);
}
private static <K, V> void expectRemoteNearRemoveInClient(AssertsNearCache<K, V> client, K key) {
// Remote event remove
MockRemoveEvent remoteRemove = pollEvent(client.events);
assertEquals(key, remoteRemove.key);
}
private static <E extends MockEvent> E pollEvent(BlockingQueue<MockEvent> events) {
try {
@SuppressWarnings("unchecked")
E event = (E) events.poll(10, TimeUnit.SECONDS);
assertNotNull(event);
return event;
} catch (InterruptedException e) {
throw new AssertionError(e);
}
}
private MockGetEvent assertGetKey(K key) {
MockGetEvent get = pollEvent(events);
assertEquals(key, get.key);
return get;
}
private MockGetEvent assertGetKeyValue(K key, V value) {
MockGetEvent get = assertGetKey(key);
assertEquals(value, get.value == null ? null : get.value.getValue());
return get;
}
}