package org.infinispan.commands.write;
import static org.infinispan.commons.util.Util.toStr;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Arrays;
import java.util.Collection;
import org.infinispan.commands.AbstractTopologyAffectedCommand;
import org.infinispan.commands.CommandInvocationId;
import org.infinispan.commands.Visitor;
import org.infinispan.commons.marshall.MarshallUtil;
import org.infinispan.commons.util.CollectionFactory;
import org.infinispan.commons.util.Util;
import org.infinispan.container.entries.MVCCEntry;
import org.infinispan.context.InvocationContext;
import org.infinispan.context.impl.FlagBitSets;
import org.infinispan.notifications.cachelistener.CacheNotifier;
import org.infinispan.util.concurrent.locks.RemoteLockCommand;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
/**
* Removes an entry from memory.
*
* @author Mircea.Markus@jboss.com
* @since 4.0
*/
public class InvalidateCommand extends AbstractTopologyAffectedCommand implements WriteCommand, RemoteLockCommand {
public static final int COMMAND_ID = 6;
private static final Log log = LogFactory.getLog(InvalidateCommand.class);
private static final boolean trace = log.isTraceEnabled();
protected Object[] keys;
protected CommandInvocationId commandInvocationId;
protected CacheNotifier notifier;
public InvalidateCommand() {
}
public InvalidateCommand(CacheNotifier notifier, long flagsBitSet, CommandInvocationId commandInvocationId, Object... keys) {
this.keys = keys;
this.notifier = notifier;
this.commandInvocationId = commandInvocationId;
setFlagsBitSet(flagsBitSet);
}
public InvalidateCommand(CacheNotifier notifier, long flagsBitSet, Collection<Object> keys, CommandInvocationId commandInvocationId) {
this(notifier, flagsBitSet, commandInvocationId, keys == null || keys.isEmpty() ? Util.EMPTY_OBJECT_ARRAY : keys.toArray(new Object[keys.size()]));
}
public void init(CacheNotifier notifier) {
this.notifier = notifier;
}
/**
* Performs an invalidation on a specified entry
*
* @param ctx invocation context
* @return null
*/
@Override
public Object perform(InvocationContext ctx) throws Throwable {
if (trace) {
log.tracef("Invalidating keys %s", toStr(Arrays.asList(keys)));
}
for (Object key : keys) {
MVCCEntry e = (MVCCEntry) ctx.lookupEntry(key);
if (e != null) {
notify(ctx, e, true);
e.setChanged(true);
e.setRemoved(true);
e.setCreated(false);
e.setValid(false);
}
}
return null;
}
protected void notify(InvocationContext ctx, MVCCEntry e, boolean pre) {
notifier.notifyCacheEntryInvalidated(e.getKey(), e.getValue(), e.getMetadata(), pre, ctx, this);
}
@Override
public byte getCommandId() {
return COMMAND_ID;
}
@Override
public boolean isReturnValueExpected() {
return false;
}
@Override
public boolean canBlock() {
return false;
}
@Override
public String toString() {
return "InvalidateCommand{keys=" +
toStr(Arrays.asList(keys)) +
'}';
}
@Override
public void writeTo(ObjectOutput output) throws IOException {
CommandInvocationId.writeTo(output, commandInvocationId);
MarshallUtil.marshallArray(keys, output);
output.writeLong(getFlagsBitSet());
}
@Override
public void readFrom(ObjectInput input) throws IOException, ClassNotFoundException {
commandInvocationId = CommandInvocationId.readFrom(input);
keys = MarshallUtil.unmarshallArray(input, Object[]::new);
setFlagsBitSet(input.readLong());
}
@Override
public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable {
return visitor.visitInvalidateCommand(ctx, this);
}
public Object[] getKeys() {
return keys;
}
@Override
public boolean isSuccessful() {
return true;
}
@Override
public boolean isConditional() {
return false;
}
@Override
public ValueMatcher getValueMatcher() {
return ValueMatcher.MATCH_ALWAYS;
}
@Override
public void setValueMatcher(ValueMatcher valueMatcher) {
}
@Override
public Collection<?> getAffectedKeys() {
return CollectionFactory.makeSet(keys);
}
@Override
public void fail() {
throw new UnsupportedOperationException();
}
@Override
public Collection<?> getKeysToLock() {
return Arrays.asList(keys);
}
@Override
public Object getKeyLockOwner() {
return commandInvocationId;
}
@Override
public boolean hasZeroLockAcquisition() {
return hasAnyFlag(FlagBitSets.ZERO_LOCK_ACQUISITION_TIMEOUT);
}
@Override
public boolean hasSkipLocking() {
return hasAnyFlag(FlagBitSets.SKIP_LOCKING);
}
@Override
public LoadType loadType() {
// TODO Return LoadType.OWNER only if there are invalidation listeners registered
return LoadType.OWNER;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
InvalidateCommand that = (InvalidateCommand) obj;
if (!hasSameFlags(that))
return false;
return Arrays.equals(keys, that.keys);
}
@Override
public int hashCode() {
return keys != null ? Arrays.hashCode(keys) : 0;
}
}