package org.infinispan.test.concurrent; import java.util.List; import org.infinispan.Cache; import org.infinispan.commands.VisitableCommand; import org.infinispan.context.InvocationContext; import org.infinispan.interceptors.AsyncInterceptor; import org.infinispan.interceptors.AsyncInterceptorChain; import org.infinispan.interceptors.BaseAsyncInterceptor; /** * Replaces an {@link AsyncInterceptor} with a wrapper that can interact with a {@link StateSequencer} when a * command that matches a {@link CommandMatcher} is visited. * * @author Dan Berindei * @since 7.0 */ public class InterceptorSequencerAction { private final StateSequencer stateSequencer; private final Cache<?, ?> cache; private final Class<? extends AsyncInterceptor> interceptorClass; private CommandMatcher matcher; private SequencerInterceptor ourInterceptor; public InterceptorSequencerAction(StateSequencer stateSequencer, Cache<?, ?> cache, Class<? extends AsyncInterceptor> interceptorClass, CommandMatcher matcher) { this.stateSequencer = stateSequencer; this.cache = cache; this.interceptorClass = interceptorClass; this.matcher = matcher; } /** * Set up a list of sequencer states before interceptor {@code interceptorClass} is called. */ public InterceptorSequencerAction before(String state1, String... additionalStates) { initOurInterceptor(); ourInterceptor.beforeStates(StateSequencerUtil.concat(state1, additionalStates)); return this; } // TODO Should we add beforeInvokeNext() and afterInvokeNext()? private void initOurInterceptor() { if (ourInterceptor == null) { ourInterceptor = SequencerInterceptor.createUniqueInterceptor(cache.getAdvancedCache().getAsyncInterceptorChain()); ourInterceptor.init(stateSequencer, matcher); cache.getAdvancedCache().getAsyncInterceptorChain().addInterceptorBefore(ourInterceptor, interceptorClass); } } /** * 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 InterceptorSequencerAction after(String state1, String... additionalStates) { initOurInterceptor(); ourInterceptor.afterStates(StateSequencerUtil.concat(state1, additionalStates)); return this; } public static class SequencerInterceptor extends BaseAsyncInterceptor { private static final Class[] uniqueInterceptorClasses = { U1.class, U2.class, U3.class, U4.class, U5.class, U6.class, U7.class, U8.class, U9.class }; private StateSequencer stateSequencer; private CommandMatcher matcher; private volatile List<String> statesBefore; private volatile List<String> statesAfter; public static SequencerInterceptor createUniqueInterceptor(AsyncInterceptorChain chain) { Class uniqueClass = findUniqueClass(chain); try { return (SequencerInterceptor) uniqueClass.newInstance(); } catch (Exception e) { throw new RuntimeException("Cannot instantiate unique interceptor", e); } } public static Class<?> findUniqueClass(AsyncInterceptorChain chain) { for (Class<? extends AsyncInterceptor> clazz : uniqueInterceptorClasses) { if (!chain.containsInterceptorType(clazz)) { return clazz; } } throw new IllegalStateException("Too many sequencer interceptors added to the same chain"); } public void init(StateSequencer stateSequencer, CommandMatcher matcher) { this.stateSequencer = stateSequencer; this.matcher = matcher; } @Override public Object visitCommand(InvocationContext ctx, VisitableCommand command) throws Throwable { boolean commandAccepted = matcher.accept(command); StateSequencerUtil.advanceMultiple(stateSequencer, commandAccepted, statesBefore); return invokeNextAndFinally(ctx, command, (rCtx, rCommand, rv, t) -> { StateSequencerUtil.advanceMultiple(stateSequencer, commandAccepted, statesAfter); }); } public void beforeStates(List<String> states) { this.statesBefore = StateSequencerUtil.listCopy(states); } public void afterStates(List<String> states) { this.statesAfter = StateSequencerUtil.listCopy(states); } public static class U1 extends SequencerInterceptor { } public static class U2 extends SequencerInterceptor { } public static class U3 extends SequencerInterceptor { } public static class U4 extends SequencerInterceptor { } public static class U5 extends SequencerInterceptor { } public static class U6 extends SequencerInterceptor { } public static class U7 extends SequencerInterceptor { } public static class U8 extends SequencerInterceptor { } public static class U9 extends SequencerInterceptor { } } }