package org.infinispan.commands.remote; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.Objects; import java.util.concurrent.CompletableFuture; import org.infinispan.commands.CommandsFactory; import org.infinispan.commands.read.GetCacheEntryCommand; import org.infinispan.commons.util.EnumUtil; import org.infinispan.container.InternalEntryFactory; import org.infinispan.container.entries.InternalCacheEntry; import org.infinispan.container.entries.MVCCEntry; import org.infinispan.context.Flag; import org.infinispan.context.InvocationContext; import org.infinispan.context.InvocationContextFactory; import org.infinispan.context.impl.FlagBitSets; import org.infinispan.interceptors.AsyncInterceptorChain; import org.infinispan.transaction.xa.GlobalTransaction; import org.infinispan.util.ByteString; import org.infinispan.util.logging.Log; import org.infinispan.util.logging.LogFactory; /** * Issues a remote get call. This is not a {@link org.infinispan.commands.VisitableCommand} and hence not passed up the * interceptor chain. * <p/> * * @author Mircea.Markus@jboss.com * @since 4.0 */ public class ClusteredGetCommand extends BaseClusteredReadCommand { public static final byte COMMAND_ID = 16; private static final Log log = LogFactory.getLog(ClusteredGetCommand.class); private static final boolean trace = log.isTraceEnabled(); private Object key; private InvocationContextFactory icf; private CommandsFactory commandsFactory; private AsyncInterceptorChain invoker; private InternalEntryFactory entryFactory; //only used by extended statistics. this boolean is local. private boolean isWrite; private ClusteredGetCommand() { super(null, EnumUtil.EMPTY_BIT_SET); // For command id uniqueness test } public ClusteredGetCommand(ByteString cacheName) { super(cacheName, EnumUtil.EMPTY_BIT_SET); } public ClusteredGetCommand(Object key, ByteString cacheName, long flags) { super(cacheName, flags); this.key = key; this.isWrite = false; } public void initialize(InvocationContextFactory icf, CommandsFactory commandsFactory, InternalEntryFactory entryFactory, AsyncInterceptorChain interceptorChain) { this.icf = icf; this.commandsFactory = commandsFactory; this.invoker = interceptorChain; this.entryFactory = entryFactory; } /** * Invokes a logical "get(key)" on a remote cache and returns results. */ @Override public CompletableFuture<Object> invokeAsync() throws Throwable { // make sure the get command doesn't perform a remote call // as our caller is already calling the ClusteredGetCommand on all the relevant nodes // CACHE_MODE_LOCAL is not used as it can be used when we want to ignore the ownership with respect to reads long flagBitSet = EnumUtil.bitSetOf(Flag.SKIP_REMOTE_LOOKUP); GetCacheEntryCommand command = commandsFactory.buildGetCacheEntryCommand(key, EnumUtil.mergeBitSets(flagBitSet, getFlagsBitSet())); command.setTopologyId(topologyId); InvocationContext invocationContext = icf.createRemoteInvocationContextForCommand(command, getOrigin()); CompletableFuture<Object> future = invoker.invokeAsync(invocationContext, command); return future.thenApply(rv -> { if (trace) log.tracef("Return value for key=%s is %s", key, rv); //this might happen if the value was fetched from a cache loader if (rv instanceof MVCCEntry) { MVCCEntry mvccEntry = (MVCCEntry) rv; return entryFactory.createValue(mvccEntry); } else if (rv instanceof InternalCacheEntry) { InternalCacheEntry internalCacheEntry = (InternalCacheEntry) rv; return internalCacheEntry.toInternalCacheValue(); } else { // null or Response return rv; } }); } @Deprecated public GlobalTransaction getGlobalTransaction() { return null; } @Override public byte getCommandId() { return COMMAND_ID; } @Override public void writeTo(ObjectOutput output) throws IOException { output.writeObject(key); output.writeLong(FlagBitSets.copyWithoutRemotableFlags(getFlagsBitSet())); } @Override public void readFrom(ObjectInput input) throws IOException, ClassNotFoundException { key = input.readObject(); setFlagsBitSet(input.readLong()); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ClusteredGetCommand that = (ClusteredGetCommand) o; return Objects.equals(key, that.key); } @Override public int hashCode() { return Objects.hashCode(key); } @Override public String toString() { return new StringBuilder() .append("ClusteredGetCommand{key=") .append(key) .append(", flags=").append(printFlags()) .append("}") .toString(); } public boolean isWrite() { return isWrite; } public void setWrite(boolean write) { isWrite = write; } public Object getKey() { return key; } @Override public boolean isReturnValueExpected() { return true; } @Override public boolean canBlock() { return false; } }