package org.infinispan.api; import static org.infinispan.test.TestingUtil.extractComponent; import static org.infinispan.test.TestingUtil.wrapInboundInvocationHandler; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Mockito.CALLS_REAL_METHODS; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.withSettings; import static org.testng.Assert.assertNull; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.infinispan.Cache; import org.infinispan.commands.remote.CacheRpcCommand; import org.infinispan.configuration.cache.CacheMode; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.container.EntryFactory; import org.infinispan.context.InvocationContext; import org.infinispan.distribution.MagicKey; import org.infinispan.remoting.inboundhandler.DeliverOrder; import org.infinispan.remoting.inboundhandler.PerCacheInboundInvocationHandler; import org.infinispan.remoting.inboundhandler.Reply; import org.infinispan.statetransfer.StateResponseCommand; import org.infinispan.test.MultipleCacheManagersTest; import org.infinispan.test.TestingUtil; import org.mockito.Mockito; import org.testng.annotations.Test; /** * Tests if the keys are not wrapped in the non-owner nodes * * @author Pedro Ruivo * @since 6.0 */ @Test(groups = "functional", testName = "api.ConditionalOperationPrimaryOwnerFailTest") public class ConditionalOperationPrimaryOwnerFailTest extends MultipleCacheManagersTest { private static final String INITIAL_VALUE = "initial"; private static final String FINAL_VALUE = "final"; public void testEntryNotWrapped() throws Throwable { assertClusterSize("Wrong cluster size!", 3); final Object key = new MagicKey(cache(0), cache(1)); final Cache<Object, Object> futureBackupOwnerCache = cache(2); cache(0).put(key, INITIAL_VALUE); final PerCacheInboundInvocationHandler spyHandler = spyInvocationHandler(futureBackupOwnerCache); final EntryFactory spyEntryFactory = spyEntryFactory(futureBackupOwnerCache); //it blocks the StateResponseCommand.class final CountDownLatch latch1 = new CountDownLatch(1); final CountDownLatch latch2 = new CountDownLatch(1); doAnswer(invocation -> { CacheRpcCommand command = (CacheRpcCommand) invocation.getArguments()[0]; if (command instanceof StateResponseCommand) { log.debugf("Blocking command %s", command); latch2.countDown(); latch1.await(); } return invocation.callRealMethod(); }).when(spyHandler).handle(any(CacheRpcCommand.class), any(Reply.class), any(DeliverOrder.class)); doAnswer(invocation -> { InvocationContext context = (InvocationContext) invocation.getArguments()[0]; log.debugf("wrapEntryForWriting invoked with %s", context); Object mvccEntry = invocation.callRealMethod(); assertNull(mvccEntry, "Entry should not be wrapped!"); assertNull(context.lookupEntry(key), "Entry should not be wrapped!"); return mvccEntry; }).when(spyEntryFactory).wrapEntryForWriting(any(InvocationContext.class), any(), anyBoolean(), anyBoolean()); Future<?> killMemberResult = fork(() -> killMember(1)); //await until the key is received from state transfer (the command is blocked now...) latch2.await(30, TimeUnit.SECONDS); futureBackupOwnerCache.put(key, FINAL_VALUE); latch1.countDown(); killMemberResult.get(30, TimeUnit.SECONDS); } @Override protected void createCacheManagers() throws Throwable { ConfigurationBuilder builder = getDefaultClusteredCacheConfig(CacheMode.DIST_SYNC, false); builder.clustering() .hash().numOwners(2) .stateTransfer().fetchInMemoryState(true); createClusteredCaches(3, builder); } private EntryFactory spyEntryFactory(Cache<Object, Object> cache) { EntryFactory spy = spy(extractComponent(cache, EntryFactory.class)); TestingUtil.replaceComponent(cache, EntryFactory.class, spy, true); return spy; } private PerCacheInboundInvocationHandler spyInvocationHandler(Cache cache) { return wrapInboundInvocationHandler(cache, Mockito::spy); } }