private boolean replace(Object key, Object expectedOldValue, Object newValue,
      Object callbackArg) {
    checkIfConcurrentMapOpsAllowed();
    if (newValue == null) {
      throw new NullPointerException();
    }

    long startPut = getStatisticsClock().getTime();
    validateArguments(key, newValue, callbackArg);
    checkReadiness();
    checkForLimitedOrNoAccess();

    @Released
    EntryEventImpl event = entryEventFactory.create(this, Operation.REPLACE, key, newValue,
        callbackArg, false, getMyId());

    try {
      if (generateEventID()) {
        event.setNewEventId(cache.getDistributedSystem());
      }

      discoverJTA();

      // In general, expectedOldValue null is used when there is no particular
      // old value expected (it can be anything). Here, however, if null
      // is passed as expectedOldValue, then it specifically means that the
      // oldValue must actually be null (i.e. INVALID). So here we
      // change an expectedOldValue of null to the invalid token
      if (expectedOldValue == null) {
        expectedOldValue = Token.INVALID;
      }

      if (!basicPut(event, false, true, expectedOldValue, false)) {
        return false;
      }
      if (!getDataView().isDeferredStats()) {
        getCachePerfStats().endPut(startPut, false);
      }
      return true;

    } catch (EntryNotFoundException ignore) {
      // put failed on server
      return false;
    } finally {
      event.release();
    }
  }
Same as {@link #replace(Object, Object, Object)} except a callback argument is supplied to be
passed on to <tt>CacheListener</tt>s and/or <tt>CacheWriter</tt>s.
geode-core/src/main/java/org/apache/geode/internal/cache/LocalRegion.java