package org.infinispan.client.hotrod.retry; import static org.infinispan.server.hotrod.test.HotRodTestingUtil.hotRodCacheConfiguration; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.infinispan.AdvancedCache; import org.infinispan.Cache; import org.infinispan.commands.write.PutKeyValueCommand; import org.infinispan.configuration.cache.CacheMode; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.context.InvocationContext; import org.infinispan.interceptors.base.CommandInterceptor; import org.infinispan.remoting.transport.jgroups.SuspectException; import org.jgroups.SuspectedException; import org.testng.annotations.Test; /** * Test different server error situations and check how clients behave under * those circumstances. Also verify whether failover is happening accordingly. * * @author Galder ZamarreƱo * @since 4.2 */ @Test(groups = "functional", testName = "client.hotrod.retry.ServerFailureRetryTest") public class ServerFailureRetryTest extends AbstractRetryTest { @Override protected ConfigurationBuilder getCacheConfig() { return hotRodCacheConfiguration( getDefaultClusteredCacheConfig(CacheMode.REPL_SYNC, false)); } public void testRetryWithInfinispanSuspectException() { retryExceptions(false); } public void testRetryWithJGroupsSuspectedException() { retryExceptions(true); } private void retryExceptions(boolean throwJGroupsException) { AdvancedCache<?, ?> nextCache = cacheToHit(1); ErrorInducingInterceptor interceptor = new ErrorInducingInterceptor(throwJGroupsException); nextCache.getAsyncInterceptorChain().addInterceptor(interceptor, 1); try { remoteCache.put(1, "v1"); assertTrue(interceptor.suspectExceptionThrown); assertEquals("v1", remoteCache.get(1)); } finally { nextCache.getAsyncInterceptorChain().removeInterceptor(ErrorInducingInterceptor.class); } } public void testRetryCacheStopped() { // Put data in any of the cluster's default cache remoteCache.put(1, "v1"); assertEquals("v1", remoteCache.get(1)); // Find out what next cluster cache to be hit and stop it Cache<?, ?> cache = cacheToHit(2); try { cache.stop(); remoteCache.put(2, "v2"); assertEquals("v2", remoteCache.get(2)); } finally { cache.start(); } } // Listener callbacks can happen in the main thread or remote thread // depending on primary owner considerations, even for replicated caches. // Using an interceptor gives more flexibility by being able to put it // at the top of the interceptor stack, before remote/local separation public static class ErrorInducingInterceptor extends CommandInterceptor { volatile boolean suspectExceptionThrown; boolean throwJGroupsException; public ErrorInducingInterceptor(boolean throwJGroupsException) { this.throwJGroupsException = throwJGroupsException; } @Override public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { if (ctx.isOriginLocal()) { suspectExceptionThrown = true; throw throwJGroupsException ? new SuspectedException("Simulated suspicion") : new SuspectException("Simulated suspicion"); } return super.visitPutKeyValueCommand(ctx, command); } } }