package org.infinispan.commands.read; import static org.infinispan.commons.util.Util.toStr; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; import org.infinispan.commands.AbstractTopologyAffectedCommand; import org.infinispan.commands.Visitor; import org.infinispan.commons.marshall.MarshallUtil; import org.infinispan.container.InternalEntryFactory; import org.infinispan.container.entries.CacheEntry; import org.infinispan.context.InvocationContext; import org.infinispan.context.impl.FlagBitSets; import org.infinispan.util.logging.Log; import org.infinispan.util.logging.LogFactory; /** * Retrieves multiple entries at once. * * @author Radim Vansa <rvansa@redhat.com> */ // TODO: revise the command hierarchy, e.g. this should not implement MetadataAwareCommand public class GetAllCommand extends AbstractTopologyAffectedCommand { public static final byte COMMAND_ID = 44; private static final Log log = LogFactory.getLog(GetAllCommand.class); private static final boolean trace = log.isTraceEnabled(); private Collection<?> keys; private boolean returnEntries; private /* transient */ InternalEntryFactory entryFactory; public GetAllCommand(Collection<?> keys, long flagsBitSet, boolean returnEntries, InternalEntryFactory entryFactory) { this.keys = keys; this.returnEntries = returnEntries; this.entryFactory = entryFactory; setFlagsBitSet(flagsBitSet); } GetAllCommand() { } @Override public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitGetAllCommand(ctx, this); } @Override public LoadType loadType() { return LoadType.PRIMARY; } @Override public Object perform(InvocationContext ctx) throws Throwable { Map<Object, Object> map = createMap(); for (Object key : keys) { CacheEntry entry = ctx.lookupEntry(key); if (entry == null) { throw new IllegalStateException("Entry for key " + toStr(key) + " not found"); } if (entry.isNull()) { if (trace) { log.tracef("Entry for key %s is null in current context", toStr(key)); } map.put(key, null); continue; } if (entry.isRemoved()) { if (trace) { log.tracef("Entry for key %s has been deleted and is of type %s", toStr(key), entry.getClass().getSimpleName()); } map.put(key, null); continue; } // Get cache entry instead of value if (returnEntries) { CacheEntry copy; if (ctx.isOriginLocal()) { copy = entryFactory.copy(entry); } else { copy = entry; } if (trace) { log.tracef("Found entry %s -> %s", toStr(key), entry); log.tracef("Returning copied entry %s", copy); } map.put(key, copy); } else { Object value = entry.getValue(); if (trace) { log.tracef("Found %s -> %s", toStr(key), toStr(value)); } map.put(key, value); } } return map; } @Override public byte getCommandId() { return COMMAND_ID; } @Override public void writeTo(ObjectOutput output) throws IOException { MarshallUtil.marshallCollection(keys, output); output.writeLong(FlagBitSets.copyWithoutRemotableFlags(getFlagsBitSet())); output.writeBoolean(returnEntries); } @Override public void readFrom(ObjectInput input) throws IOException, ClassNotFoundException { keys = MarshallUtil.unmarshallCollection(input, ArrayList::new); setFlagsBitSet(input.readLong()); returnEntries = input.readBoolean(); } @Override public boolean isReturnValueExpected() { return true; } @Override public boolean canBlock() { return false; } public boolean isReturnEntries() { return returnEntries; } public <V> Map<Object, V> createMap() { return new LinkedHashMap<>(); } public Collection<?> getKeys() { return keys; } public void setKeys(Collection<?> keys) { this.keys = keys; } @Override public String toString() { final StringBuilder sb = new StringBuilder("GetAllCommand{"); sb.append("keys=").append(toStr(keys)); sb.append(", returnEntries=").append(returnEntries); sb.append(", flags=").append(printFlags()); sb.append('}'); return sb.toString(); } }