package org.infinispan.interceptors.base;
import java.util.List;
import org.infinispan.Cache;
import org.infinispan.cache.impl.CacheImpl;
import org.infinispan.commands.AbstractVisitor;
import org.infinispan.commands.FlagAffectedCommand;
import org.infinispan.commands.VisitableCommand;
import org.infinispan.commands.Visitor;
import org.infinispan.commands.read.GetKeyValueCommand;
import org.infinispan.commons.util.EnumUtil;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.context.Flag;
import org.infinispan.context.InvocationContext;
import org.infinispan.context.impl.FlagBitSets;
import org.infinispan.factories.annotations.Inject;
import org.infinispan.factories.scopes.Scope;
import org.infinispan.factories.scopes.Scopes;
import org.infinispan.interceptors.AsyncInterceptor;
import org.infinispan.interceptors.AsyncInterceptorChain;
import org.infinispan.interceptors.BaseAsyncInterceptor;
import org.infinispan.interceptors.InvocationStage;
import org.infinispan.interceptors.InterceptorChain;
import org.infinispan.interceptors.impl.SimpleAsyncInvocationStage;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
/**
* This is the base class for all interceptors to extend, and implements the {@link Visitor} interface allowing it to
* intercept invocations on {@link VisitableCommand}s.
* <p/>
* Commands are either created by the {@link CacheImpl} (for invocations on the {@link Cache} public interface), or
* by the {@link org.infinispan.remoting.inboundhandler.InboundInvocationHandler} for remotely originating invocations, and are passed up the interceptor chain
* by using the {@link InterceptorChain} helper class.
* <p/>
* When writing interceptors, authors can either override a specific visitXXX() method (such as {@link
* #visitGetKeyValueCommand(InvocationContext, GetKeyValueCommand)}) or the more generic {@link
* #handleDefault(InvocationContext, VisitableCommand)} which is the default behaviour of any visit method, as defined
* in {@link AbstractVisitor#handleDefault(InvocationContext, VisitableCommand)}.
* <p/>
* The preferred approach is to override the specific visitXXX() methods that are of interest rather than to override
* {@link #handleDefault(InvocationContext, VisitableCommand)} and then write a series of if statements or a switch
* block, if command-specific behaviour is needed.
* <p/>
*
* @author Mircea.Markus@jboss.com
* @see VisitableCommand
* @see Visitor
* @see InterceptorChain
* @deprecated Since 9.0, please extend {@link BaseAsyncInterceptor} instead.
*/
@Deprecated
@Scope(Scopes.NAMED_CACHE)
public abstract class CommandInterceptor extends AbstractVisitor implements AsyncInterceptor {
private AsyncInterceptorChain interceptorChain;
protected Configuration cacheConfiguration;
private static final Log log = LogFactory.getLog(CommandInterceptor.class);
private AsyncInterceptor nextInterceptor;
protected Log getLog() {
return log;
}
@Inject
public void injectConfiguration(Configuration configuration, AsyncInterceptorChain interceptorChain) {
this.cacheConfiguration = configuration;
this.interceptorChain = interceptorChain;
}
/**
* Retrieves the next interceptor in the chain.
* Since 9.0, it returns {@code null} if the next interceptor does not extend {@code CommandInterceptor}.
*
* @return the next interceptor in the chain.
*/
public final CommandInterceptor getNext() {
List<AsyncInterceptor> interceptors = interceptorChain.getInterceptors();
int myIndex = interceptors.indexOf(this);
if (myIndex < interceptors.size() - 1) {
AsyncInterceptor asyncInterceptor = interceptors.get(myIndex + 1);
if (asyncInterceptor instanceof CommandInterceptor)
return (CommandInterceptor) asyncInterceptor;
}
return null;
}
/**
* Note: Unlike {@link #getNext()}, this method does not ignore interceptors that do not extend
* {@code CommandInterceptor}
*
* @return true if there is another interceptor in the chain after this; false otherwise.
*/
public final boolean hasNext() {
List<AsyncInterceptor> interceptors = interceptorChain.getInterceptors();
int myIndex = interceptors.indexOf(this);
return myIndex < interceptors.size();
}
/**
* Does nothing since 9.0.
*/
public final void setNext(CommandInterceptor ignored) {
}
@Override
public final void setNextInterceptor(AsyncInterceptor interceptorStage) {
this.nextInterceptor = interceptorStage;
}
/**
* Invokes the next interceptor in the chain. This is how interceptor implementations should pass a call up the
* chain to the next interceptor.
*
* @param ctx invocation context
* @param command command to pass up the chain.
* @return return value of the invocation
* @throws Throwable in the event of problems
*/
public final Object invokeNextInterceptor(InvocationContext ctx, VisitableCommand command) throws Throwable {
Object maybeStage = nextInterceptor.visitCommand(ctx, command);
if (maybeStage instanceof SimpleAsyncInvocationStage) {
return ((InvocationStage) maybeStage).get();
} else {
return maybeStage;
}
}
/**
* The default behaviour of the visitXXX methods, which is to ignore the call and pass the call up to the next
* interceptor in the chain.
*
* @param ctx invocation context
* @param command command to invoke
* @return return value
* @throws Throwable in the event of problems
*/
@Override
protected Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable {
return invokeNextInterceptor(ctx, command);
}
protected final long getLockAcquisitionTimeout(FlagAffectedCommand command, boolean skipLocking) {
if (!skipLocking)
return command.hasAnyFlag(FlagBitSets.ZERO_LOCK_ACQUISITION_TIMEOUT) ?
0 : cacheConfiguration.locking().lockAcquisitionTimeout();
return -1;
}
protected final boolean hasSkipLocking(FlagAffectedCommand command) {
return command.hasAnyFlag(FlagBitSets.SKIP_LOCKING);
}
protected <K, V> Cache<K, V> getCacheWithFlags(Cache<K, V> cache, FlagAffectedCommand command) {
long flags = command.getFlagsBitSet();
if (flags != EnumUtil.EMPTY_BIT_SET) {
return cache.getAdvancedCache().withFlags(EnumUtil.enumArrayOf(flags, Flag.class));
} else {
return cache;
}
}
@Override
public Object visitCommand(InvocationContext ctx, VisitableCommand command)
throws Throwable {
return command.acceptVisitor(ctx, this);
}
}