package org.infinispan.client.hotrod.retry; import static org.infinispan.server.hotrod.test.HotRodTestingUtil.hotRodCacheConfiguration; import static org.infinispan.test.TestingUtil.extractField; import static org.infinispan.test.TestingUtil.replaceField; import java.io.IOException; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.IntStream; import org.infinispan.client.hotrod.RemoteCache; import org.infinispan.client.hotrod.annotation.ClientCacheEntryCreated; import org.infinispan.client.hotrod.annotation.ClientListener; import org.infinispan.client.hotrod.event.ClientCacheEntryCreatedEvent; import org.infinispan.client.hotrod.event.ClientEvent; import org.infinispan.client.hotrod.event.ClientListenerNotifier; import org.infinispan.client.hotrod.exceptions.TransportException; import org.infinispan.client.hotrod.impl.protocol.Codec25; import org.infinispan.client.hotrod.impl.transport.Transport; import org.infinispan.client.hotrod.test.MultiHotRodServersTest; import org.infinispan.commons.marshall.Marshaller; import org.infinispan.configuration.cache.CacheMode; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.testng.annotations.Test; /** * Tests for a client with a listener when connection to the server drops. */ @Test(groups = "functional", testName = "client.hotrod.retry.ClientListenerRetryTest") @SuppressWarnings("unused") public class ClientListenerRetryTest extends MultiHotRodServersTest { private AtomicInteger counter = new AtomicInteger(0); private FailureInducingCodec failureInducingCodec = new FailureInducingCodec(); @Override protected void createCacheManagers() throws Throwable { createHotRodServers(2, getCacheConfiguration()); clients.forEach(rcm -> { Object listenerNotifier = extractField(rcm, "listenerNotifier"); replaceField(failureInducingCodec, "codec", listenerNotifier, ClientListenerNotifier.class); }); } private ConfigurationBuilder getCacheConfiguration() { ConfigurationBuilder builder = getDefaultClusteredCacheConfig(CacheMode.DIST_SYNC, false); return hotRodCacheConfiguration(builder); } @Test public void testConnectionDrop() throws Exception { RemoteCache<Integer, String> remoteCache = client(0).getCache(); Listener listener = new Listener(); remoteCache.addClientListener(listener); assertListenerActive(remoteCache, listener); failureInducingCodec.induceFailure(); addItems(remoteCache, 10); failureInducingCodec.resetFailure(); assertListenerActive(remoteCache, listener); } private void addItems(RemoteCache<Integer, String> cache, int items) { IntStream.range(0, items).forEach(i -> cache.put(counter.incrementAndGet(), "value")); } private void assertListenerActive(RemoteCache<Integer, String> cache, Listener listener) { int received = listener.getReceived(); eventually(() -> { cache.put(counter.incrementAndGet(), "value"); return listener.getReceived() > received; }); } @ClientListener private static class Listener { private final AtomicInteger count = new AtomicInteger(0); @ClientCacheEntryCreated public void handleCreatedEvent(ClientCacheEntryCreatedEvent<?> e) { count.incrementAndGet(); } int getReceived() { return count.intValue(); } } @Override protected int maxRetries() { return 10; } private static class FailureInducingCodec extends Codec25 { private volatile boolean failure; private final IOException failWith = new IOException("Connection reset by peer"); @Override public ClientEvent readEvent(Transport transport, byte[] expectedListenerId, Marshaller marshaller) { if (failure) { throw new TransportException(failWith, transport.getRemoteSocketAddress()); } return super.readEvent(transport, expectedListenerId, marshaller); } private void induceFailure() { failure = true; } private void resetFailure() { failure = false; } } }