package org.infinispan.test.concurrent; import java.util.List; import java.util.Map; import org.infinispan.Cache; import org.infinispan.commands.ReplicableCommand; import org.infinispan.factories.ComponentRegistry; import org.infinispan.remoting.responses.Response; import org.infinispan.remoting.rpc.RpcManager; import org.infinispan.remoting.transport.Address; import org.infinispan.test.TestingUtil; import org.infinispan.util.AbstractControlledRpcManager; /** * Replaces the {@link RpcManager} with a wrapper that can interact with a {@link StateSequencer} when a * command that matches a {@link CommandMatcher} is invoked remotely. * * @author Dan Berindei * @since 7.0 */ public class OutboundRpcSequencerAction { private final StateSequencer stateSequencer; private final Cache<?, ?> cache; private final CommandMatcher matcher; private SequencerRpcManager ourRpcManager; public OutboundRpcSequencerAction(StateSequencer stateSequencer, Cache<?, ?> cache, CommandMatcher matcher) { this.stateSequencer = stateSequencer; this.cache = cache; this.matcher = matcher; } /** * Set up a list of sequencer states before interceptor {@code interceptorClass} is called. * <p/> * Each invocation accepted by {@code matcher} will enter/exit the next state from the list, and does nothing after the list is exhausted. */ public OutboundRpcSequencerAction before(String state1, String... additionalStates) { replaceRpcManager(); ourRpcManager.beforeStates(StateSequencerUtil.concat(state1, additionalStates)); return this; } private void replaceRpcManager() { if (ourRpcManager == null) { ComponentRegistry componentRegistry = cache.getAdvancedCache().getComponentRegistry(); RpcManager rpcManager = componentRegistry.getComponent(RpcManager.class); ourRpcManager = new SequencerRpcManager(rpcManager, stateSequencer, matcher); TestingUtil.replaceComponent(cache, RpcManager.class, ourRpcManager, true); } } /** * Set up a list of sequencer states after interceptor {@code interceptorClass} has returned. * <p/> * Each invocation accepted by {@code matcher} will enter/exit the next state from the list, and does nothing after the list is exhausted. */ public OutboundRpcSequencerAction after(String state1, String... additionalStates) { replaceRpcManager(); ourRpcManager.afterStates(StateSequencerUtil.concat(state1, additionalStates)); return this; } public static class SequencerRpcManager extends AbstractControlledRpcManager { private final StateSequencer stateSequencer; private final CommandMatcher matcher; private volatile List<String> statesBefore; private volatile List<String> statesAfter; public SequencerRpcManager(RpcManager rpcManager, StateSequencer stateSequencer, CommandMatcher matcher) { super(rpcManager); this.stateSequencer = stateSequencer; this.matcher = matcher; } @Override protected Object beforeInvokeRemotely(ReplicableCommand command) { try { boolean accept = matcher.accept(command); StateSequencerUtil.advanceMultiple(stateSequencer, accept, statesBefore); return accept; } catch (Exception e) { throw new RuntimeException(e); } } @Override protected Map<Address, Response> afterInvokeRemotely(ReplicableCommand command, Map<Address, Response> responseMap, Object argument) { try { StateSequencerUtil.advanceMultiple(stateSequencer, (Boolean) argument, statesAfter); } catch (Exception e) { throw new RuntimeException(e); } return responseMap; } public void beforeStates(List<String> states) { this.statesBefore = StateSequencerUtil.listCopy(states); } public void afterStates(List<String> states) { this.statesAfter = StateSequencerUtil.listCopy(states); } } }