All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.infinispan.commands.write.ValueMatcher Maven / Gradle / Ivy

There is a newer version: 9.1.7.Final
Show newest version
package org.infinispan.commands.write;

import org.infinispan.Cache;
import org.infinispan.commons.equivalence.Equivalence;
import org.infinispan.commons.marshall.AbstractExternalizer;
import org.infinispan.commons.marshall.MarshallUtil;
import org.infinispan.container.entries.MVCCEntry;
import org.infinispan.marshall.core.Ids;

import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.HashSet;
import java.util.Set;

/**
 * A policy for determining if a write command should be executed based on the current value in the cache.
 *
 * When retrying conditional write commands in non-transactional caches, it is also used to determine the appropriate
 * return value. E.g. if a {@code putIfAbsent(k, v)} already succeeded on a backup owner which became the primary owner,
 * when retrying the command will find {@code v} in the cache but should return {@code null}. For non-conditional
 * commands it's impossible to know what the previous value was, so the command is allowed to return {@code v}.
 *
 * @author Dan Berindei
 * @since 6.0
 */
public enum ValueMatcher {
   /**
    * Always match. Used when the command is not conditional or when the value was already checked
    * (on the primary owner, in non-tx caches, or on the originator, in tx caches).
    * Also used when retrying {@link Cache#remove(Object)} operations.
    */
   MATCH_ALWAYS() {
      @Override
      public boolean matches(MVCCEntry existingEntry, Object expectedValue, Object newValue, Equivalence valueEquivalence) {
         return true;
      }

      @Override
      public boolean nonExistentEntryCanMatch() {
         return true;
      }

      @Override
      public ValueMatcher matcherForRetry() {
         return MATCH_ALWAYS;
      }
   },
   /**
    * Match when the existing value is equal to the expected value.
    * Used for {@link Cache#putIfAbsent(Object, Object)}, {@link Cache#replace(Object, Object, Object)},
    * and {@link Cache#remove(Object, Object)}.
    */
   MATCH_EXPECTED() {
      @Override
      public boolean matches(MVCCEntry existingEntry, Object expectedValue, Object newValue, Equivalence valueEquivalence) {
         return equal(extractValue(existingEntry), expectedValue, valueEquivalence);
      }

      @Override
      public boolean nonExistentEntryCanMatch() {
         return true;
      }

      @Override
      public ValueMatcher matcherForRetry() {
         return MATCH_EXPECTED_OR_NEW;
      }
   },
   /**
    * Match when the existing value is equal to the expected value or to the new value.
    * Used only in non-tx caches, when retrying a conditional command on the primary owner.
    */
   MATCH_EXPECTED_OR_NEW() {
      @Override
      public boolean matches(MVCCEntry existingEntry, Object expectedValue, Object newValue, Equivalence valueEquivalence) {
         return equal(extractValue(existingEntry), expectedValue, valueEquivalence) ||
               equal(extractValue(existingEntry), newValue, valueEquivalence);
      }

      @Override
      public boolean nonExistentEntryCanMatch() {
         return true;
      }

      @Override
      public ValueMatcher matcherForRetry() {
         return MATCH_EXPECTED_OR_NEW;
      }
   },

   MATCH_EXPECTED_OR_NULL() {
      @Override
      public boolean matches(MVCCEntry existingEntry, Object expectedValue, Object newValue, Equivalence valueEquivalence) {
         return newValue == null || equal(extractValue(existingEntry), expectedValue, valueEquivalence);
      }

      @Override
      public boolean nonExistentEntryCanMatch() {
         return true;
      }

      @Override
      public ValueMatcher matcherForRetry() {
         return MATCH_EXPECTED_OR_NULL;
      }
   },
   /**
    * Match any non-null value. Used for {@link Cache#replace(Object, Object)} and {@link Cache#remove(Object)}.
    */
   MATCH_NON_NULL() {
      @Override
      public boolean matches(MVCCEntry existingEntry, Object expectedValue, Object newValue, Equivalence valueEquivalence) {
         return extractValue(existingEntry) != null;
      }

      @Override
      public boolean nonExistentEntryCanMatch() {
         return false;
      }

      @Override
      public ValueMatcher matcherForRetry() {
         return MATCH_ALWAYS;
      }
   },
   /**
    * Never match. Only used in transactional mode, as unsuccessful commands are still sent remotely,
    * even though they should not be performed.
    */
   MATCH_NEVER() {
      @Override
      public boolean matches(MVCCEntry existingEntry, Object expectedValue, Object newValue, Equivalence valueEquivalence) {
         return false;
      }

      @Override
      public boolean nonExistentEntryCanMatch() {
         return false;
      }

      @Override
      public ValueMatcher matcherForRetry() {
         return MATCH_NEVER;
      }
   },;

   public abstract boolean matches(MVCCEntry existingEntry, Object expectedValue, Object newValue,
                          Equivalence valueEquivalence);

   public abstract boolean nonExistentEntryCanMatch();

   public abstract ValueMatcher matcherForRetry();

   protected Object extractValue(MVCCEntry mvccEntry) {
      // isRemoved() == true doesn't seem to imply isNull() == true, so we check it explicitly
      return (mvccEntry == null || mvccEntry.isRemoved()) ? null : mvccEntry.getValue();
   }

   protected boolean equal(Object a, Object b, Equivalence valueEquivalence) {
      // Compare by identity first to catch the case where both are null
      if (a == b) return true;
      return valueEquivalence != null ? valueEquivalence.equals(a, b) : (a != null && a.equals(b));
   }

   private static final ValueMatcher[] CACHED_VALUES = values();

   public static ValueMatcher valueOf(int ordinal) {
      return CACHED_VALUES[ordinal];
   }

   public static class Externalizer extends AbstractExternalizer {

      @Override
      public Integer getId() {
         return Ids.VALUE_MATCHER;
      }

      @Override
      public Set> getTypeClasses() {
         Set> classes = new HashSet<>(CACHED_VALUES.length);
         for (ValueMatcher valueMatcher : CACHED_VALUES) {
            classes.add(valueMatcher.getClass());
         }
         return classes;
      }

      @Override
      public void writeObject(ObjectOutput output, ValueMatcher valueMatcher) throws IOException {
         MarshallUtil.marshallEnum(valueMatcher, output);
      }

      @Override
      public ValueMatcher readObject(ObjectInput input) throws IOException, ClassNotFoundException {
         return MarshallUtil.unmarshallEnum(input, ValueMatcher::valueOf);
      }
   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy