package org.infinispan.distexec;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.infinispan.Cache;
import org.infinispan.commands.ReplicableCommand;
import org.infinispan.commands.read.DistributedExecuteCommand;
import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.marshall.core.ExternalPojo;
import org.infinispan.remoting.inboundhandler.DeliverOrder;
import org.infinispan.remoting.responses.CacheNotFoundResponse;
import org.infinispan.remoting.responses.Response;
import org.infinispan.remoting.rpc.ResponseFilter;
import org.infinispan.remoting.rpc.ResponseMode;
import org.infinispan.remoting.transport.AbstractDelegatingTransport;
import org.infinispan.remoting.transport.Address;
import org.infinispan.remoting.transport.Transport;
import org.infinispan.test.MultipleCacheManagersTest;
import org.infinispan.test.TestingUtil;
import org.testng.AssertJUnit;
import org.testng.annotations.Test;
/**
* Tests dist.exec failover due to CacheNotFoundResponse or any other non SuccessfulResponse response.
*
* @author Vladimir Blagojevic
* @since 7.1
*/
@Test(groups = "functional", testName = "distexec.DistributedExecutorBadResponseFailoverTest")
public class DistributedExecutorBadResponseFailoverTest extends MultipleCacheManagersTest {
@Override
protected void createCacheManagers() throws Throwable {
ConfigurationBuilder builder = getDefaultClusteredCacheConfig(CacheMode.DIST_SYNC, false);
addClusterEnabledCacheManager(builder);
// this is the node sending DistributedExecuteCommand
final EmbeddedCacheManager cacheManager1 = manager(0);
TestingUtil.wrapGlobalComponent(cacheManager1, Transport.class,
(wrapOn, current) -> new CacheNotFoundResponseTransport(current), true);
addClusterEnabledCacheManager(builder);
addClusterEnabledCacheManager(builder);
defineConfigurationOnAllManagers(cacheName(), builder);
waitForClusterToForm(cacheName());
}
protected String cacheName() {
return "DistributedExecutorBadResponseFailoverTest";
}
public void testBasicTargetRemoteDistributedCallable() throws Exception {
long taskTimeout = TimeUnit.SECONDS.toMillis(15);
EmbeddedCacheManager cacheManager1 = manager(0);
final EmbeddedCacheManager cacheManager2 = manager(1);
Cache<Object, Object> cache1 = cacheManager1.getCache();
Cache<Object, Object> cache2 = cacheManager2.getCache();
DistributedExecutorService des = null;
try {
des = new DefaultExecutorService(cache1);
Address target = cache2.getAdvancedCache().getRpcManager().getAddress();
DistributedTaskBuilder<Integer> builder = des.createDistributedTaskBuilder(new SimpleCallable())
.failoverPolicy(DefaultExecutorService.RANDOM_NODE_FAILOVER)
.timeout(taskTimeout, TimeUnit.MILLISECONDS);
Future<Integer> future = des.submit(target, builder.build());
AssertJUnit.assertEquals((Integer) 1, future.get());
} catch (Exception ex) {
AssertJUnit.fail("Task did not failover properly " + ex);
} finally {
if (des != null)
des.shutdown();
}
}
static class SimpleCallable implements Callable<Integer>, Serializable, ExternalPojo {
/**
*
*/
private static final long serialVersionUID = -3130274337449595197L;
public SimpleCallable() {
}
@Override
public Integer call() throws Exception {
return 1;
}
}
private static class CacheNotFoundResponseTransport extends AbstractDelegatingTransport {
CacheNotFoundResponseTransport(Transport actual) {
super(actual);
}
@Override
public void start() {
// Do not start the transport a second time
}
@Override
public Map<Address, Response> invokeRemotely(Collection<Address> recipients, ReplicableCommand rpcCommand,
ResponseMode mode, long timeout, ResponseFilter responseFilter, DeliverOrder deliverOrder, boolean anycast)
throws Exception {
Map<Address, Response> properResponse = super.invokeRemotely(recipients, rpcCommand, mode, timeout,
responseFilter, deliverOrder, anycast);
// intercept the response we received for DistributedExecuteCommand
if (rpcCommand instanceof DistributedExecuteCommand) {
Map<Address, Response> cacheNotFoundResponse = new HashMap<>();
for (Entry<Address, Response> e : properResponse.entrySet()) {
// and augment it to return CacheNotFoundResponse (any other non SuccessfulResponse will do)
cacheNotFoundResponse.put(e.getKey(), CacheNotFoundResponse.INSTANCE);
}
return cacheNotFoundResponse;
} else {
return properResponse;
}
}
}
}