@Test
  public void testCustomEntryTtl3() {

    final String name = getUniqueName();
    final int timeout1 = 20; // ms
    final int timeout2 = 40;
    final String key1 = "KEY1";
    final String value1 = "VALUE1";
    final String value2 = "VALUE2";

    RegionFactory<Object, Object> regionFactory =
        getCache().createRegionFactory(getRegionAttributes());
    ExpirationAttributes expire1 = new ExpirationAttributes(timeout1, ExpirationAction.INVALIDATE);
    regionFactory.setCustomEntryTimeToLive(new TestExpiry<>(key1, expire1));
    regionFactory.setStatisticsEnabled(true);
    TestCacheListenerEventCount list = new TestCacheListenerEventCount();
    // Disk regions are VERY slow, so we need to wait for the event...

    eventCount.set(0);
    regionFactory.addCacheListener(list);

    Region<Object, Object> region;
    System.setProperty(LocalRegion.EXPIRY_MS_PROPERTY, "true");
    try {
      region = createRegion(name, regionFactory);

      suspendExpiration();
      Region.Entry entry;
      eventCount.set(0);
      long tilt1;
      long tilt2;
      try {
        region.create(key1, value1);
        tilt1 = currentTimeMillis() + timeout1;
        entry = region.getEntry(key1);
        assertTrue(list.waitForInvocation(1000));
        Assert.assertTrue(value1.equals(entry.getValue()));
      } finally {
        permitExpiration();
      }
      waitForInvalidate(entry, tilt1, timeout1 / 2);
      await().alias("eventCount never became 1").until(() -> eventCount.get() == 1);
      eventCount.set(0);

      // Do it again with a put (I guess)
      suspendExpiration();
      try {
        region.put(key1, value1);
        tilt1 = currentTimeMillis() + timeout1;
        entry = region.getEntry(key1);
        Assert.assertTrue(value1.equals(entry.getValue()));
        assertTrue(list.waitForInvocation(10 * 1000));
      } finally {
        permitExpiration();
      }
      waitForInvalidate(entry, tilt1, timeout1 / 2);
      await().alias("eventCount never became 1").until(() -> eventCount.get() == 1);
      eventCount.set(0);

      // Change custom expiry for this region now...
      final String key2 = "KEY2";
      AttributesMutator mutt = region.getAttributesMutator();
      ExpirationAttributes expire2 =
          new ExpirationAttributes(timeout2, INVALIDATE);
      mutt.setCustomEntryTimeToLive(new TestExpiry<>(key2, expire2));

      suspendExpiration();
      try {
        region.put(key1, value1);
        region.put(key2, value2);
        tilt1 = currentTimeMillis() + timeout1;
        tilt2 = tilt1 + timeout2 - timeout1;
        entry = region.getEntry(key1);
        Assert.assertTrue(value1.equals(entry.getValue()));
        entry = region.getEntry(key2);
        Assert.assertTrue(value2.equals(entry.getValue()));
        assertTrue(list.waitForInvocation(1000));
      } finally {
        permitExpiration();
      }
      waitForInvalidate(entry, tilt2, timeout2 / 2);
      await().alias("eventCount never became 1").until(() -> eventCount.get() == 1);
      eventCount.set(0);
      // key1 should not be invalidated since we mutated to custom expiry to only expire key2
      entry = region.getEntry(key1);
      Assert.assertTrue(value1.equals(entry.getValue()));
      // now mutate back to key1 and change the action
      ExpirationAttributes expire3 = new ExpirationAttributes(timeout1, DESTROY);
      mutt.setCustomEntryTimeToLive(new TestExpiry<>(key1, expire3));
      waitForDestroy(entry, tilt1, timeout1 / 2);
    } finally {
      System.getProperties().remove(LocalRegion.EXPIRY_MS_PROPERTY);
    }
  }
Expire an entry with a custom expiration. Set a new custom expiration, create the same entry
again, make sure it observes the <em>new</em> expiration
geode-dunit/src/main/java/org/apache/geode/cache30/RegionTestCase.java