private boolean processGatewayTag(EntryEvent cacheEvent) {
    // Gateway tags are installed in the server-side InternalRegion cache
    // modification methods. They do not have version numbers or distributed
    // member IDs. Instead they only have timestamps and distributed system IDs.

    // If there is a resolver plug-in, invoke it. Otherwise we use the timestamps and
    // distributed system IDs to determine whether to allow the event to proceed.

    final boolean isDebugEnabled = logger.isDebugEnabled();

    if (isRemoved() && !isTombstone()) {
      return true; // no conflict on a new entry
    }
    EntryEventImpl event = (EntryEventImpl) cacheEvent;
    VersionTag tag = event.getVersionTag();
    long stampTime = getVersionStamp().getVersionTimeStamp();
    long tagTime = tag.getVersionTimeStamp();
    int stampDsid = getVersionStamp().getDistributedSystemId();
    int tagDsid = tag.getDistributedSystemId();
    if (isDebugEnabled) {
      logger.debug(
          "processing gateway version information for {}.  Stamp dsid={} time={} Tag dsid={} time={}",
          event.getKey(), stampDsid, stampTime, tagDsid, tagTime);
    }
    if (tagTime == VersionTag.ILLEGAL_VERSION_TIMESTAMP) {
      return true; // no timestamp received from other system - just apply it
    }
    // According to GatewayConflictResolver's java doc, it will only be used on tag with different
    // distributed system id than stamp's
    if (stampDsid == -1) {
      return true;
    } else if (tagDsid == stampDsid) {
      if (tagTime >= stampTime) {
        return true;
      } else {
        throw new ConcurrentCacheModificationException("conflicting WAN event detected");
      }
    }
    GatewayConflictResolver resolver = event.getRegion().getCache().getGatewayConflictResolver();
    if (resolver != null) {
      if (isDebugEnabled) {
        logger.debug("invoking gateway conflict resolver");
      }
      final boolean[] disallow = new boolean[1];
      final Object[] newValue = new Object[] {this};
      GatewayConflictHelper helper = new GatewayConflictHelper() {
        @Override
        public void disallowEvent() {
          disallow[0] = true;
        }

        @Override
        public void changeEventValue(Object value) {
          newValue[0] = value;
        }
      };

      @Released
      TimestampedEntryEventImpl timestampedEvent = (TimestampedEntryEventImpl) event
          .getTimestampedEvent(tagDsid, stampDsid, tagTime, stampTime);

      // gateway conflict resolvers will usually want to see the old value
      if (!timestampedEvent.hasOldValue() && isRemoved()) {
        // OFFHEAP: since isRemoved I think getValue will never be stored off heap in this case
        timestampedEvent.setOldValue(getValue(timestampedEvent.getRegion()));
      }

      Throwable thr = null;
      try {
        resolver.onEvent(timestampedEvent, helper);
      } catch (CancelException cancelled) {
        throw cancelled;
      } catch (VirtualMachineError err) {
        SystemFailure.initiateFailure(err);
        // If this ever returns, rethrow the error. We're poisoned
        // now, so don't let this thread continue.
        throw err;
      } catch (Throwable t) {
        // Whenever you catch Error or Throwable, you must also
        // catch VirtualMachineError (see above). However, there is
        // _still_ a possibility that you are dealing with a cascading
        // error condition, so you also need to check to see if the JVM
        // is still usable:
        SystemFailure.checkFailure();
        logger.error("Exception occurred in GatewayConflictResolver", t);
        thr = t;
      } finally {
        timestampedEvent.release();
      }

      if (isDebugEnabled) {
        logger.debug("done invoking resolver", thr);
      }
      if (thr == null) {
        if (disallow[0]) {
          if (isDebugEnabled) {
            logger.debug("conflict resolver rejected the event for {}", event.getKey());
          }
          throw new ConcurrentCacheModificationException(
              "WAN conflict resolver rejected the operation");
        }

        tag.setAllowedByResolver(true);

        if (newValue[0] != this) {
          if (isDebugEnabled) {
            logger.debug("conflict resolver changed the value of the event for {}", event.getKey());
          }
          // the resolver changed the event value!
          event.setNewValue(newValue[0]);
        }
        // if nothing was done then we allow the event
        if (isDebugEnabled) {
          logger.debug("change was allowed by conflict resolver: {}", tag);
        }
        return true;
      }
    }
    if (isDebugEnabled) {
      logger.debug("performing normal WAN conflict check");
    }
    if (tagTime > stampTime || tagTime == stampTime && tagDsid >= stampDsid) {
      if (isDebugEnabled) {
        logger.debug("allowing event");
      }
      return true;
    }
    if (isDebugEnabled) {
      logger.debug("disallowing event for {}", event.getKey());
    }
    throw new ConcurrentCacheModificationException("conflicting WAN event detected");
  }
for an event containing a delta we must check to see if the tag's previous member id is the
stamp's member id and ensure that the version is only incremented by 1. Otherwise the delta is
being applied to a value that does not match the source of the delta.
geode-core/src/main/java/org/apache/geode/internal/cache/entries/AbstractRegionEntry.java