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

com.gemstone.gemfire.TXTest Maven / Gradle / Ivy

There is a newer version: 2.0-BETA
Show newest version
/*
 * Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License. You
 * may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License. See accompanying
 * LICENSE file.
 */
package com.gemstone.gemfire;

import com.gemstone.gemfire.cache.*;
import com.gemstone.gemfire.cache.util.*;
import com.gemstone.gemfire.internal.cache.*;
//import com.gemstone.gemfire.internal.cache.lru.LRUClockNode;
import com.gemstone.gemfire.distributed.*;
import java.util.*;

import io.snappydata.test.dunit.DistributedTestBase;
import junit.framework.*;
import javax.transaction.Synchronization;
import com.gemstone.gemfire.cache.query.Query;
import com.gemstone.gemfire.cache.query.QueryService;
import com.gemstone.gemfire.cache.query.SelectResults;
import com.gemstone.gemfire.cache.query.QueryException;
import com.gemstone.gemfire.cache.query.Index;
import com.gemstone.gemfire.cache.query.IndexType;
import com.gemstone.gemfire.cache.query.internal.index.IndexManager;

// With java 1.5, please replace with System.nanoTime()
import com.gemstone.gemfire.internal.NanoTimer;

/**
 * Tests basic transaction functionality
 *
 * @author Darrel Schneider
 * @since 4.0
 *
 */
public class TXTest extends TestCase {

  protected int cbCount;
  protected TransactionEvent te;
  protected int listenerAfterCommit;
  protected int listenerAfterFailedCommit;
  protected int listenerAfterRollback;
  protected int listenerClose;
  protected GemFireCacheImpl cache;
  private CacheTransactionManager txMgr;
  protected Region region;

  public TXTest(String name) {
    super(name);
  }
  
  protected boolean isPR() {
    return (this.region instanceof PartitionedRegion);
  }

  public static void main(String[] args) {
    junit.textui.TestRunner.run(new TestSuite(TXTest.class));
  }

  ////////  Test methods


  private void createCache() throws Exception {
    Properties p = new Properties();
    p.setProperty("mcast-port", "0"); // loner
    this.cache = (GemFireCacheImpl)CacheFactory.create(DistributedSystem.connect(p));
    createRegion();
    this.txMgr = this.cache.getCacheTransactionManager();
    this.listenerAfterCommit = 0;
    this.listenerAfterFailedCommit = 0;
    this.listenerAfterRollback = 0;
    this.listenerClose = 0;
  }

  /**
   * 
   */
  protected void createRegion() throws Exception {
    AttributesFactory af = new AttributesFactory();
    af.setScope(Scope.DISTRIBUTED_NO_ACK);
    af.setConcurrencyChecksEnabled(false);  // test validation expects this behavior
    af.setIndexMaintenanceSynchronous(true);
    this.region = this.cache.createRegion("TXTest", af.create());
  }
  private void closeCache() {
    if (this.cache != null) {
      if (this.txMgr != null) {
        try {
          this.txMgr.rollback();
        } catch (IllegalTransactionStateException ignore) {
        }
      }
      this.region = null;
      this.txMgr = null;
      Cache c = this.cache;
      this.cache = null;
      c.close();
    }
  }
  
  @Override
  public void setUp() throws Exception {
    //(new RuntimeException("STACKTRACE: " + getName())).printStackTrace();
    /* (for DISABLED_testSimplePutsPerf)
    // disable stats; latter especially really screws numbers due to time calls
    System.setProperty("gemfire.statsDisabled", "true");
    System.setProperty("gemfire.enable-time-statistics", "false");
    System.setProperty("DistributionManager.VERBOSE", "false");
    */
    createCache();
  }

  @Override
  public void tearDown() throws Exception {
    closeCache();
    //(new RuntimeException("STACKTRACE: " + getName())).printStackTrace();
  }

  private void checkNoTxState() {
    assertEquals(null, this.txMgr.getTransactionId());
    assertTrue(!this.txMgr.exists());
    try {
      this.txMgr.commit();
      fail("expected ConflictException");
    } catch (ConflictException unexpected) {
      fail("did not expect " + unexpected);
    } catch (IllegalTransactionStateException expected) {
    }
    try {
      this.txMgr.rollback();
      fail("expected IllegalTransactionStateException");
    } catch (IllegalTransactionStateException expected) {
    }
  }

  public void DISABLED_testSimplePutsPerf() throws Exception {
    this.region = this.cache.createRegionFactory(RegionShortcut.LOCAL).create(
        "DefaultRegion");
    // warmups
    for (int t = 1; t <= 1000; ++t) {
      final String v = "val-" + t;
      this.txMgr.begin();
      for (int i = 1; i <= 1000; ++i) {
        this.region.put(t, v);
      }
      this.txMgr.commit();
    }
    // timed runs
    long start = System.nanoTime();
    for (int t = 1; t <= 10000; ++t) {
      final String v = "val-" + t;
      this.txMgr.begin();
      for (int i = 1; i <= 1000; ++i) {
        this.region.put(t, v);
      }
      this.txMgr.commit();
    }
    long end = System.nanoTime();
    System.out.println("total time taken: " + (end - start) + " nanos");
  }

  public void testSimpleOps() throws CacheException {
    final CachePerfStats stats = this.cache.getCachePerfStats();
    // See if things are ok when no transaction
    checkNoTxState();

    this.txMgr.begin();
    // now that a transaction exists make sure things behave as expected
    assertTrue(this.txMgr.getTransactionId() != null);
    assertTrue(this.txMgr.exists());
    try {
      this.txMgr.begin();
      fail("expected IllegalTransactionStateException");
    } catch (IllegalTransactionStateException expected) {
    }
    try {
      this.txMgr.commit();
    } catch (ConflictException unexpected) {
      fail("did not expect " + unexpected);
    }
    checkNoTxState();

    this.txMgr.begin();
    this.txMgr.rollback();
    checkNoTxState();

    this.region.put("uaKey", "val");

    {
      Region.Entry cmtre = this.region.getEntry("uaKey");
      cmtre.setUserAttribute("uaValue1");
      assertEquals("uaValue1", cmtre.getUserAttribute());

      int txRollbackChanges = stats.getTxRollbackChanges();
      int txCommitChanges = stats.getTxCommitChanges();
      int txFailureChanges = stats.getTxFailureChanges();
      this.txMgr.begin();
      Region.Entry re = this.region.getEntry("uaKey");
      assertEquals(this.region, re.getRegion());
      //if (isPR()) {
      this.region.put("1", "one");
      Region.Entry txre = this.region.getEntry("1");
      assertEquals(this.region, txre.getRegion());
      try {
        txre.setUserAttribute("uaValue2");
        fail("expected UnsupportedOperationException");
      } catch (UnsupportedOperationException e) {
        // expected
      }
      try {
        txre.getUserAttribute();
        fail("expected UnsupportedOperationException");
      } catch (UnsupportedOperationException e) {
        // expected
      }
      // Region entry's setUserAttribute should throw an exception within TX
      try {
        re.setUserAttribute("uaValue2");
        fail("expected UnsupportedOperationException");
      } catch (UnsupportedOperationException e) {
        // expected
      }
      // Region entry's getUserAttribute should be fine even within TX
      re.getUserAttribute();
      // now dropped support for UserAttribute in TX completely in new TX model
      /*
      } else {
        assertEquals("uaValue1", txre.getUserAttribute());
        txre.setUserAttribute("uaValue2");
        assertEquals("uaValue2", txre.getUserAttribute());
      }
      */
      this.txMgr.rollback();
      try {
        txre.getValue();
        fail("expected IllegalTransactionStateException");
      } catch (IllegalTransactionStateException ok) {
      }
      try {
        txre.isDestroyed();
        fail("expected IllegalTransactionStateException");
      } catch (IllegalTransactionStateException ok) {
      }
      try {
        txre.getUserAttribute();
        fail("expected UnsupportedOperationException");
      } catch (UnsupportedOperationException e) {
        // expected
      }
      try {
        txre.setUserAttribute("foo");
        fail("expected UnsupportedOperationException");
      } catch (UnsupportedOperationException e) {
        // expected
      }
      // operations on non-TX Region entry should all be fine
      assertEquals("val", re.getValue());
      assertFalse(re.isDestroyed());
      re.setUserAttribute("uaValue2");
      assertEquals("uaValue2", re.getUserAttribute());

      assertEquals(txRollbackChanges+1, stats.getTxRollbackChanges());
      assertEquals(txCommitChanges, stats.getTxCommitChanges());
      assertEquals(txFailureChanges, stats.getTxFailureChanges());

      assertEquals("uaValue2", cmtre.getUserAttribute());
    }

    {
      int txRollbackChanges = stats.getTxRollbackChanges();
      int txCommitChanges = stats.getTxCommitChanges();
      int txFailureChanges = stats.getTxFailureChanges();
      this.region.create("key1", "value1");
      this.txMgr.begin();
      this.region.invalidate("key1");
      this.txMgr.rollback();
      assertEquals(this.region.get("key1"), "value1");
      assertEquals(txRollbackChanges+1, stats.getTxRollbackChanges());
      assertEquals(txCommitChanges, stats.getTxCommitChanges());
      assertEquals(txFailureChanges, stats.getTxFailureChanges());

      txRollbackChanges = stats.getTxRollbackChanges();
      this.txMgr.begin();
      this.region.destroy("key1");
      this.txMgr.rollback();
      assertEquals(this.region.get("key1"), "value1");
      assertEquals(txRollbackChanges+1, stats.getTxRollbackChanges());
      assertEquals(txCommitChanges, stats.getTxCommitChanges());
      assertEquals(txFailureChanges, stats.getTxFailureChanges());

      txRollbackChanges = stats.getTxRollbackChanges();
      this.txMgr.begin();
      this.region.put("key1", "value2");
      this.txMgr.rollback();
      assertEquals(this.region.get("key1"), "value1");
      assertEquals(txRollbackChanges+1, stats.getTxRollbackChanges());
      assertEquals(txCommitChanges, stats.getTxCommitChanges());
      assertEquals(txFailureChanges, stats.getTxFailureChanges());
    }
  }

  public void testWriteOps() throws CacheException {
      this.txMgr.begin();
      this.region.put("key1", "value1");
      this.region.put("key2", "value2");
      assertTrue(this.region.containsKey("key1"));
      assertTrue(this.region.containsValueForKey("key1"));
      assertEquals("key1", this.region.getEntry("key1").getKey());
      assertEquals("value1", this.region.getEntry("key1").getValue());
      assertEquals("value1", this.region.get("key1"));
      assertTrue(this.region.containsKey("key2"));
      assertTrue(this.region.containsValueForKey("key2"));
      assertEquals("key2", this.region.getEntry("key2").getKey());
      assertEquals("value2", this.region.getEntry("key2").getValue());
      assertEquals("value2", this.region.get("key2"));
      this.txMgr.rollback();
      assertTrue(!this.region.containsKey("key1"));
      assertTrue(!this.region.containsKey("key2"));

      this.txMgr.begin();
      this.region.create("key1", "value1");
      assertTrue(this.region.containsKey("key1"));
      assertTrue(this.region.containsValueForKey("key1"));
      assertEquals("key1", this.region.getEntry("key1").getKey());
      assertEquals("value1", this.region.getEntry("key1").getValue());
      assertEquals("value1", this.region.get("key1"));
      this.txMgr.rollback();
      assertTrue(!this.region.containsKey("key1"));

      this.region.create("key1", "value1");
      this.txMgr.begin();
      this.region.put("key1", "value2");
      this.txMgr.rollback();
      assertTrue(this.region.containsKey("key1"));
      assertEquals("value1", this.region.get("key1"));
      this.region.localDestroy("key1");

      this.region.create("key1", "value1");
      this.txMgr.begin();
      this.region.localDestroy("key1");
      assertTrue(!this.region.containsKey("key1"));
      assertTrue(!this.region.containsValueForKey("key1"));
      this.txMgr.rollback();
      assertTrue(this.region.containsKey("key1"));
      this.region.localDestroy("key1");

      this.region.create("key1", "value1");
      this.txMgr.begin();
      this.region.destroy("key1");
      assertTrue(!this.region.containsKey("key1"));
      assertTrue(!this.region.containsValueForKey("key1"));
      this.txMgr.rollback();
      assertTrue(this.region.containsKey("key1"));
      this.region.localDestroy("key1");

      this.region.create("key1", "value1");
      this.txMgr.begin();
      assertTrue(this.region.containsValueForKey("key1"));
      this.region.localInvalidate("key1");
      assertTrue(this.region.containsKey("key1"));
      assertFalse(this.region.containsValueForKey("key1"));
      assertEquals(null, this.region.get("key1"));
      this.txMgr.rollback();
      assertTrue(this.region.containsValueForKey("key1"));
      this.region.localDestroy("key1");

      this.region.create("key1", "value1");
      this.txMgr.begin();
      assertTrue(this.region.containsValueForKey("key1"));
      this.region.invalidate("key1");
      assertTrue(this.region.containsKey("key1"));
      assertTrue(!this.region.containsValueForKey("key1"));
      assertEquals(null, this.region.get("key1"));
      this.txMgr.rollback();
      assertTrue(this.region.containsValueForKey("key1"));
      this.region.localDestroy("key1");

      // see if commits work
      assertTrue(!this.region.containsKey("key1"));
      assertTrue(!this.region.containsKey("key2"));
      this.txMgr.begin();
      this.region.create("key1", "value1");
      this.region.create("key2", "value2");
      this.txMgr.commit();
      assertTrue(this.region.containsKey("key1"));
      assertTrue(this.region.containsValueForKey("key1"));
      assertEquals("key1", this.region.getEntry("key1").getKey());
      assertEquals("value1", this.region.getEntry("key1").getValue());
      assertEquals("value1", this.region.get("key1"));
      assertTrue(this.region.containsKey("key2"));
      assertTrue(this.region.containsValueForKey("key2"));
      assertEquals("key2", this.region.getEntry("key2").getKey());
      assertEquals("value2", this.region.getEntry("key2").getValue());
      assertEquals("value2", this.region.get("key2"));
      this.region.localDestroy("key1");
      this.region.localDestroy("key2");
  }
  public void testTwoRegionTxs() throws CacheException {
    final CachePerfStats stats = this.cache.getCachePerfStats();
    int txCommitChanges;
    TransactionId myTxId;
    AttributesFactory af = new AttributesFactory();
    af.setScope(Scope.DISTRIBUTED_NO_ACK);
    Region reg1 = this.region;
    Region reg2 = this.cache.createRegion("TXTest2", af.create());
    
    this.txMgr.setListener(
      new TransactionListener() {
          public void afterCommit(TransactionEvent event) {
            listenerAfterCommit = 1;
            te = event;
          }

          public void afterFailedCommit(TransactionEvent event) {
            listenerAfterFailedCommit = 1;
            te = event;
          }

          public void afterRollback(TransactionEvent event) {
            listenerAfterRollback = 1;
            te = event;
          }
          public void close() {
            listenerClose = 1;
          }

        }
      );
    
    // see if commits work
    txCommitChanges = stats.getTxCommitChanges();
    assertTrue(!reg1.containsKey("key1"));
    assertTrue(!reg2.containsKey("key2"));
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.create("key1", "value1");
    reg2.create("key2", "value2");
    this.txMgr.commit();
    assertTrue(reg1.containsKey("key1"));
    assertTrue(reg1.containsValueForKey("key1"));
    assertEquals("key1", reg1.getEntry("key1").getKey());
    assertEquals("value1", reg1.getEntry("key1").getValue());
    assertEquals("value1", reg1.get("key1"));
    assertTrue(reg2.containsKey("key2"));
    assertTrue(reg2.containsValueForKey("key2"));
    assertEquals("key2", reg2.getEntry("key2").getKey());
    assertEquals("value2", reg2.getEntry("key2").getValue());
    assertEquals("value2", reg2.get("key2"));
    assertEquals(txCommitChanges+2, stats.getTxCommitChanges());
    {
      Collection creates = this.te.getCreateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(2, creates.size());
      Iterator it = creates.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1 || ev.getRegion() == reg2);
        if (ev.getRegion() == reg1) {
          assertEquals("key1", ev.getKey());
          assertEquals("value1", ev.getNewValue());
        } else {
          assertEquals("key2", ev.getKey());
          assertEquals("value2", ev.getNewValue());
        }
        assertEquals(null, ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");
    reg2.localDestroy("key2");

    reg2.localDestroyRegion();
  }
  
  public void testTxEvent() throws CacheException {
    TransactionId myTxId;
    Region reg1 = this.region;
    
    this.txMgr.setListener(
      new TransactionListener() {
          public void afterCommit(TransactionEvent event) {
            listenerAfterCommit = 1;
            te = event;
          }

          public void afterFailedCommit(TransactionEvent event) {
            listenerAfterFailedCommit = 1;
            te = event;
          }

          public void afterRollback(TransactionEvent event) {
            listenerAfterRollback = 1;
            te = event;
          }
          public void close() {
            listenerClose = 1;
          }

        }
      );

    // make sure each operation has the correct transaction event
    // check create
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.create("key1", "value1");
    this.txMgr.rollback();
    assertEquals(1, this.te.getEvents().size());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    {
      Cache teCache = this.te.getCache();
      assertEquals(teCache, this.cache);
      Collection creates = this.te.getCreateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, creates.size());
      Iterator it = creates.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals("value1", ev.getNewValue());
        assertEquals(null, ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }

    // check put of existing entry
    reg1.create("key1", "value0");
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.put("key1", "value1");
    this.txMgr.rollback();
    assertEquals(1, this.te.getEvents().size());
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    {
      Cache teCache = this.te.getCache();
      assertEquals(teCache, this.cache);
      Collection creates = this.te.getPutEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, creates.size());
      Iterator it = creates.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals("value1", ev.getNewValue());
        assertEquals("value0", ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // check put of non-existent entry
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.put("key1", "value0");
    this.txMgr.rollback();
    assertEquals(1, this.te.getEvents().size());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    {
      Cache teCache = this.te.getCache();
      assertEquals(teCache, this.cache);
      Collection creates = this.te.getCreateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, creates.size());
      Iterator it = creates.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals("value0", ev.getNewValue());
        assertEquals(null, ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }

    // check d invalidate of existing entry
    reg1.create("key1", "value0");
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.invalidate("key1");
    this.txMgr.rollback();
    assertEquals(1, this.te.getEvents().size());
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    {
      Cache teCache = this.te.getCache();
      assertEquals(teCache, this.cache);
      Collection creates = this.te.getInvalidateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, creates.size());
      Iterator it = creates.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals(null, ev.getNewValue());
        assertEquals("value0", ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // check l invalidate of existing entry
    reg1.create("key1", "value0");
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.localInvalidate("key1");
    this.txMgr.rollback();
    assertEquals(1, this.te.getEvents().size());
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    {
      Cache teCache = this.te.getCache();
      assertEquals(teCache, this.cache);
      Collection creates = this.te.getInvalidateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, creates.size());
      Iterator it = creates.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals(null, ev.getNewValue());
        assertEquals("value0", ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        if (!isPR()) assertTrue(!ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // check d destroy of existing entry
    reg1.create("key1", "value0");
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.destroy("key1");
    this.txMgr.rollback();
    assertEquals(1, this.te.getEvents().size());
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    {
      Cache teCache = this.te.getCache();
      assertEquals(teCache, this.cache);
      Collection creates = this.te.getDestroyEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, creates.size());
      Iterator it = creates.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals(null, ev.getNewValue());
        assertEquals("value0", ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // check l destroy of existing entry
    reg1.create("key1", "value0");
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.localDestroy("key1");
    this.txMgr.rollback();
    assertEquals(1, this.te.getEvents().size());
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    {
      Cache teCache = this.te.getCache();
      assertEquals(teCache, this.cache);
      Collection creates = this.te.getDestroyEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, creates.size());
      Iterator it = creates.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals(null, ev.getNewValue());
        assertEquals("value0", ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        if (!isPR()) assertTrue(!ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");
  }

  class CountingCallBackValidator {
    ArrayList asserts;
    final String createWriterAssert = "create writer Assert";
    final String createListenerAssert = "create listener Assert";
    final String updateWriterAssert = "update writer Assert";
    final String updateListenerAssert = "update listener Assert";
    final String invalAssert = "invalidate Assert";
    final String destroyWriterAssert = "destroy writer Assert";
    final String destroyListenerAssert = "destroy listener Assert";
    final String localDestroyWriterAssert = "local destroy writer Assert";
    final String localDestroyListenerAssert = "local destroy listener Assert";
    
    CountingCacheListener cl;
    CountingCacheWriter cw;
    CountingCallBackValidator(CountingCacheListener cl,
                              CountingCacheWriter cw) {
      this.cl = cl;  this.cw = cw;
      this.asserts = new ArrayList(8);
    }
    void assertCreateWriterCnt(int cnt) {assertCreateWriterCnt(cnt, true);}    
    void assertCreateWriterCnt(int cnt, boolean remember) {
      if (remember) {
        this.asserts.add(createWriterAssert);
        this.asserts.add(new Integer(cnt));
      }
      assertEquals(cnt, this.cw.getBeforeCreateCalls());      
    }
    void assertCreateListenerCnt(int cnt) {assertCreateListenerCnt(cnt, true);}
    void assertCreateListenerCnt(int cnt, boolean remember) {
      if (remember) {
        this.asserts.add(createListenerAssert);
        this.asserts.add(new Integer(cnt));
      }      
      assertEquals(cnt, this.cl.getAfterCreateCalls());
    }
    void assertDestroyWriterCnt(int cnt) {assertDestroyWriterCnt(cnt, true);}
    void assertDestroyWriterCnt(int cnt, boolean remember) {
      if (remember) {
        this.asserts.add(destroyWriterAssert);
        this.asserts.add(new Integer(cnt));
      }
      assertEquals(cnt, this.cw.getBeforeDestroyCalls(false));
    }
    void assertDestroyListenerCnt(int cnt) {assertDestroyListenerCnt(cnt, true);}
    void assertDestroyListenerCnt(int cnt, boolean remember) {
      if (remember) {
        this.asserts.add(destroyListenerAssert);
        this.asserts.add(new Integer(cnt));
      }      
      assertEquals(cnt, this.cl.getAfterDestroyCalls(false));
    }
    void assertLocalDestroyWriterCnt(int cnt) {assertLocalDestroyWriterCnt(cnt, true);}
    void assertLocalDestroyWriterCnt(int cnt, boolean remember) {
      if (remember) {
        this.asserts.add(localDestroyWriterAssert);
        this.asserts.add(new Integer(cnt));
      }
      assertEquals(0, this.cw.getBeforeDestroyCalls(true));
    }
    void assertLocalDestroyListenerCnt(int cnt) {assertLocalDestroyListenerCnt(cnt, true);}
    void assertLocalDestroyListenerCnt(int cnt, boolean remember) {
      if (remember) {
        this.asserts.add(localDestroyListenerAssert);
        this.asserts.add(new Integer(cnt));
      }
      assertEquals(cnt, this.cl.getAfterDestroyCalls(true));
    }

    void assertUpdateWriterCnt(int cnt) {assertUpdateWriterCnt(cnt, true);}
    void assertUpdateWriterCnt(int cnt, boolean remember) {
      if (remember) {
        this.asserts.add(updateWriterAssert);
        this.asserts.add(new Integer(cnt));
      }
      assertEquals(cnt, this.cw.getBeforeUpdateCalls());
    }
    void assertUpdateListenerCnt(int cnt) {assertUpdateListenerCnt(cnt, true);}
    void assertUpdateListenerCnt(int cnt, boolean remember) {
      if (remember) {
        this.asserts.add(updateListenerAssert);
        this.asserts.add(new Integer(cnt));
      }
      assertEquals(cnt, this.cl.getAfterUpdateCalls());
    }
    void assertInvalidateCnt(int cnt) {assertInvalidateCnt(cnt, true);}
    void assertInvalidateCnt(int cnt, boolean remember) {
      if (remember) {
        this.asserts.add(invalAssert);
        this.asserts.add(new Integer(cnt));
      }
      assertEquals(cnt, this.cl.getAfterInvalidateCalls());
    }    

    void reAssert() {
       Iterator assertItr = this.asserts.iterator();
       String assertType;
       Integer count;
       int cnt;
       while(assertItr.hasNext()) {
         assertType = (String) assertItr.next();
         assertTrue("CountingCallBackValidator reassert, did not have an associated count", assertItr.hasNext());
         count = (Integer) assertItr.next();
         cnt = count.intValue();
         if (assertType.equals(createWriterAssert)) {
           this.assertCreateWriterCnt(cnt, false);
         } else if (assertType.equals(createListenerAssert)){
           this.assertCreateListenerCnt(cnt, false);
         } else if (assertType.equals(updateWriterAssert)) {
           this.assertUpdateWriterCnt(cnt, false);
         } else if (assertType.equals(updateListenerAssert)){
           this.assertUpdateListenerCnt(cnt, false);
         } else if (assertType.equals(invalAssert)) {
           this.assertInvalidateCnt(cnt, false);
         } else if (assertType.equals(destroyWriterAssert)) {
           this.assertDestroyWriterCnt(cnt, false);
         } else if (assertType.equals(destroyListenerAssert)){
           this.assertDestroyListenerCnt(cnt, false);
         } else if (assertType.equals(localDestroyWriterAssert)) {
           this.assertLocalDestroyWriterCnt(cnt, false);
         } else if (assertType.equals(localDestroyListenerAssert)){
           this.assertLocalDestroyListenerCnt(cnt, false);
         } else {
           fail("CountingCallBackValidator reassert, unknown type");
         }
       }
    }
    void reset() {
      this.cl.reset(); this.cw.reset();
      this.asserts.clear();
    }
  }

  interface CountingCacheListener extends CacheListener {
    public int getAfterCreateCalls();
    public int getAfterUpdateCalls();
    public int getAfterInvalidateCalls();
    public int getAfterDestroyCalls(boolean fetchLocal);
    public void reset();
  }

  interface CountingCacheWriter extends CacheWriter {
    public int getBeforeCreateCalls();
    public int getBeforeUpdateCalls();
    public int getBeforeDestroyCalls(boolean fetchLocal);
    public void reset();
  }


  public void testTxAlgebra() throws CacheException {
    TransactionId myTxId;
    Region reg1 = this.region;
    
    this.txMgr.setListener(
      new TransactionListener() {
          public void afterCommit(TransactionEvent event) {
            listenerAfterCommit = 1;
            te = event;
          }

          public void afterFailedCommit(TransactionEvent event) {
            listenerAfterFailedCommit = 1;
            te = event;
          }

          public void afterRollback(TransactionEvent event) {
            listenerAfterRollback = 1;
            te = event;
          }
          public void close() {
            listenerClose = 1;
          }

        }
      );
    AttributesMutator mutator = this.region.getAttributesMutator();
    CountingCacheListener cntListener = new CountingCacheListener() {
        volatile int aCreateCalls, aUpdateCalls, aInvalidateCalls, aDestroyCalls, aLocalDestroyCalls;
        public void close() {}
        public void reset() {
          this.aCreateCalls = this.aUpdateCalls = this.aInvalidateCalls = 
            this.aDestroyCalls = this.aLocalDestroyCalls = 0;
        }
        public void afterCreate(EntryEvent e) {++this.aCreateCalls;}
        public void afterUpdate(EntryEvent e) {++this.aUpdateCalls;}
        public void afterInvalidate(EntryEvent e) {++this.aInvalidateCalls;}
        public void afterDestroy(EntryEvent e) {
          if (e.isDistributed()) {
            ++this.aDestroyCalls; 
          } else {
            ++this.aLocalDestroyCalls; 
          }
        }
        public void afterRegionInvalidate(RegionEvent e) {fail("Unexpected afterRegionInvalidate in testTxAlgebra");}
        public void afterRegionDestroy(RegionEvent e) {
          if (!e.getOperation().isClose()) {
            fail("Unexpected afterRegionDestroy in testTxAlgebra");
          }
        }
        public void afterRegionClear(RegionEvent event ) {}
        public void afterRegionCreate(RegionEvent event ) {}
        public void afterRegionLive(RegionEvent event ) {}
        public int getAfterCreateCalls(){return this.aCreateCalls;}
        public int getAfterUpdateCalls(){return this.aUpdateCalls;}
        public int getAfterInvalidateCalls(){return this.aInvalidateCalls;}
        public int getAfterDestroyCalls(boolean fetchLocal){
          return fetchLocal ? this.aLocalDestroyCalls : this.aDestroyCalls;
        }
      };
    mutator.setCacheListener(cntListener);
    CountingCacheWriter cntWriter = new CountingCacheWriter() {
        int bCreateCalls, bUpdateCalls, bDestroyCalls, bLocalDestroyCalls;
        public void close() {}
        public void reset() {
          this.bCreateCalls = this.bUpdateCalls = this.bDestroyCalls = 
            this.bLocalDestroyCalls = 0;
        }
        public void beforeCreate(EntryEvent e) {++this.bCreateCalls;}
        public void beforeUpdate(EntryEvent e) {++this.bUpdateCalls;}
        public void beforeDestroy(EntryEvent e) {++this.bDestroyCalls;}
        public void beforeRegionDestroy(RegionEvent e) {fail("Unexpected beforeRegionDestroy in testTxAlgebra");}
        public void beforeRegionClear(RegionEvent e) {fail("Unexpected beforeRegionClear in testTxAlgebra");}
        public int getBeforeCreateCalls(){return this.bCreateCalls;}
        public int getBeforeUpdateCalls(){return this.bUpdateCalls;}
        public int getBeforeDestroyCalls(boolean fetchLocal){
          return fetchLocal ? this.bLocalDestroyCalls : this.bDestroyCalls;
        }
      };
    mutator.setCacheWriter(cntWriter);

    CountingCallBackValidator callbackVal = 
      new CountingCallBackValidator(cntListener, cntWriter);

    // make sure each op sequence has the correct affect transaction event
    // check C + C -> EX
    // check C + P -> C
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.create("key1", "value1");
    callbackVal.assertCreateWriterCnt(1);
    try {
      reg1.create("key1", "value2");
      fail("expected EntryExistsException");
    } catch (EntryExistsException ok) {
    }
    callbackVal.assertCreateWriterCnt(1, /*remember*/ false);
    reg1.put("key1", "value2");
    callbackVal.assertUpdateWriterCnt(1);
    assertEquals("value2", reg1.getEntry("key1").getValue());    
    // Make sure listener callbacks were not triggered before commit
    callbackVal.assertCreateListenerCnt(0, false);
    callbackVal.assertUpdateListenerCnt(0);
    this.txMgr.commit();
    callbackVal.assertCreateListenerCnt(1);
    callbackVal.reAssert();
    assertEquals("value2", reg1.getEntry("key1").getValue());
    assertEquals(1, this.te.getEvents().size());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    {
      Collection events = this.te.getCreateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals("value2", ev.getNewValue());
        assertEquals(null, ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // Check C + DI -> C
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.create("key1", "value1");
    callbackVal.assertCreateWriterCnt(1);
    reg1.invalidate("key1");
    callbackVal.assertInvalidateCnt(0, false);
    assertTrue(reg1.containsKey("key1"));
    assertTrue(!reg1.containsValueForKey("key1"));
    callbackVal.assertCreateListenerCnt(0, false);
    this.txMgr.commit();
    callbackVal.assertCreateListenerCnt(1);
    callbackVal.reAssert();
    assertTrue(reg1.containsKey("key1"));
    assertTrue(!reg1.containsValueForKey("key1"));
    assertEquals(1, this.te.getEvents().size());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    {
      Collection events = this.te.getCreateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals(null, ev.getNewValue());
        assertEquals(null, ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // @todo mitch implement the following
    // check LI + DI -> NOOP
    // check DI + LI -> NOOP
    // check DI + DI -> NOOP
    // check LI + LI -> NOOP

    // check C + DD -> NOOP
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.create("key1", "value0");
    callbackVal.assertCreateWriterCnt(1);
    reg1.destroy("key1");
    callbackVal.assertDestroyWriterCnt(1);
    assertTrue(!reg1.containsKey("key1"));
    callbackVal.assertDestroyListenerCnt(0);
    this.txMgr.commit();
    callbackVal.assertDestroyListenerCnt(0);
    callbackVal.assertCreateListenerCnt(0);
    callbackVal.reAssert();
    assertTrue(!reg1.containsKey("key1"));
    assertEquals(0, this.te.getEvents().size());
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());

    // Check C + LI -> C
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.create("key1", "value1");
    callbackVal.assertCreateWriterCnt(1);
    reg1.localInvalidate("key1");
    callbackVal.assertInvalidateCnt(0);
    assertTrue(reg1.containsKey("key1"));
    assertTrue(!reg1.containsValueForKey("key1"));
    this.txMgr.commit();
    callbackVal.assertCreateListenerCnt(1);
    callbackVal.reAssert();
    assertTrue(reg1.containsKey("key1"));
    assertTrue(!reg1.containsValueForKey("key1"));
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getCreateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals(null, ev.getNewValue());
        assertEquals(null, ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // Check C + LI + C -> EX
    // Check C + LI + P -> C
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.create("key1", "value1");
    callbackVal.assertCreateWriterCnt(1);
    reg1.localInvalidate("key1");
    callbackVal.assertInvalidateCnt(0);
    try {
      reg1.create("key1", "ex");
      fail("expected EntryExistsException");
    } catch (EntryExistsException ok) {
    }
    callbackVal.assertCreateWriterCnt(1, /*remember*/ false);
    reg1.put("key1", "value2");
    callbackVal.assertUpdateWriterCnt(1);
    assertTrue(reg1.containsKey("key1"));
    assertEquals("value2", reg1.getEntry("key1").getValue());
    callbackVal.assertUpdateListenerCnt(0);
    callbackVal.assertCreateListenerCnt(0, false);
    this.txMgr.commit();    
    callbackVal.assertCreateListenerCnt(1);
    callbackVal.reAssert();
    assertTrue(reg1.containsKey("key1"));
    assertEquals("value2", reg1.getEntry("key1").getValue());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getCreateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals("value2", ev.getNewValue());
        assertEquals(null, ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // Check C + LI + LD -> NOOP
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.create("key1", "value1");
    callbackVal.assertCreateWriterCnt(1);
    reg1.localInvalidate("key1");
    callbackVal.assertInvalidateCnt(0);
    reg1.localDestroy("key1");
    callbackVal.assertLocalDestroyWriterCnt(1);
    assertTrue(!reg1.containsKey("key1"));
    this.txMgr.commit();
    callbackVal.assertCreateListenerCnt(0);
    callbackVal.assertDestroyListenerCnt(0);
    callbackVal.reAssert();
    assertTrue(!reg1.containsKey("key1"));
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(0, this.te.getEvents().size());

    // Check C + LI + DD -> NOOP
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.create("key1", "value1");
    callbackVal.assertCreateWriterCnt(1);
    reg1.localInvalidate("key1");
    callbackVal.assertInvalidateCnt(0);
    reg1.destroy("key1");
    callbackVal.assertDestroyWriterCnt(1);
    assertTrue(!reg1.containsKey("key1"));
    this.txMgr.commit();
    callbackVal.assertDestroyListenerCnt(0);
    callbackVal.assertCreateListenerCnt(0);
    callbackVal.reAssert();
    assertTrue(!reg1.containsKey("key1"));
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(0, this.te.getEvents().size());

    // check C + LD -> NOOP
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.create("key1", "value0");
    callbackVal.assertCreateWriterCnt(1);
    reg1.localDestroy("key1");
    callbackVal.assertLocalDestroyWriterCnt(1);
    assertTrue(!reg1.containsKey("key1"));
    this.txMgr.commit();
    callbackVal.assertCreateListenerCnt(0);
    callbackVal.assertLocalDestroyListenerCnt(0);
    callbackVal.reAssert();
    assertTrue(!reg1.containsKey("key1"));
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(0, this.te.getEvents().size());

    // check C + LD + D -> EX
    // check C + LD + I -> EX
    // check C + LD + C -> C
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.create("key1", "value0");
    callbackVal.assertCreateWriterCnt(1, /*remember*/ false);
    reg1.localDestroy("key1");
    callbackVal.assertLocalDestroyWriterCnt(1);
    assertTrue(!reg1.containsKey("key1"));
    try {
      reg1.localDestroy("key1");
      fail("expected EntryNotFoundException");
    } catch (EntryNotFoundException ok) {
    }
    callbackVal.assertLocalDestroyWriterCnt(1, /*remember*/ false);
    try {
      reg1.destroy("key1");
      fail("expected EntryNotFoundException");
    } catch (EntryNotFoundException ok) {
    }
    callbackVal.assertDestroyWriterCnt(0);
    try {
      reg1.localInvalidate("key1");
      fail("expected EntryNotFoundException");
    } catch (EntryNotFoundException ok) {
    }
    callbackVal.assertInvalidateCnt(0);
    try {
      reg1.invalidate("key1");
      fail("expected EntryNotFoundException");
    } catch (EntryNotFoundException ok) {
    }
    callbackVal.assertInvalidateCnt(0, /*remember*/ false);
    reg1.create("key1", "value3");
    callbackVal.assertCreateWriterCnt(2);
    assertEquals("value3", reg1.getEntry("key1").getValue());
    this.txMgr.commit();
    callbackVal.assertCreateListenerCnt(1);
    callbackVal.reAssert();
    assertEquals("value3", reg1.getEntry("key1").getValue());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getCreateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals("value3", ev.getNewValue());
        assertEquals(null, ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // check C + LD + P -> C
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.create("key1", "value0");
    callbackVal.assertCreateWriterCnt(1, /*remember*/ false);
    reg1.localDestroy("key1");
    callbackVal.assertLocalDestroyWriterCnt(1);
    reg1.put("key1", "value3");
    callbackVal.assertCreateWriterCnt(2);
    assertEquals("value3", reg1.getEntry("key1").getValue());
    this.txMgr.commit();
    callbackVal.assertCreateListenerCnt(1);
    callbackVal.assertUpdateListenerCnt(0);
    callbackVal.reAssert();
    assertEquals("value3", reg1.getEntry("key1").getValue());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getCreateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals("value3", ev.getNewValue());
        assertEquals(null, ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // check put of existing entry
    // check P + C -> EX
    // check P + P -> P
    reg1.create("key1", "value0");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.put("key1", "value1");
    callbackVal.assertUpdateWriterCnt(1, /*remember*/ false);
    try {
      reg1.create("key1", "value2");
      fail("expected EntryExistsException");
    } catch (EntryExistsException ok) {
    }
    callbackVal.assertUpdateWriterCnt(1, /*remember*/ false);
    callbackVal.assertCreateWriterCnt(0);
    reg1.put("key1", "value3");
    callbackVal.assertUpdateWriterCnt(2);
    assertEquals("value3", reg1.getEntry("key1").getValue());
    this.txMgr.commit();
    callbackVal.assertCreateListenerCnt(0);
    callbackVal.assertUpdateListenerCnt(1);
    callbackVal.reAssert();
    assertEquals("value3", reg1.getEntry("key1").getValue());
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getPutEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals("value3", ev.getNewValue());
        assertEquals("value0", ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // check P + DI -> DI
    reg1.create("key1", "value0");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.put("key1", "value1");
    callbackVal.assertUpdateWriterCnt(1);
    reg1.invalidate("key1");
    callbackVal.assertInvalidateCnt(0, false);
    assertTrue(reg1.containsKey("key1"));
    assertTrue(!reg1.containsValueForKey("key1"));
    this.txMgr.commit();
    callbackVal.assertInvalidateCnt(1);
    callbackVal.assertUpdateListenerCnt(0);
    callbackVal.reAssert();
    assertTrue(reg1.containsKey("key1"));
    assertTrue(!reg1.containsValueForKey("key1"));
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getInvalidateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals(null, ev.getNewValue());
        assertEquals("value0", ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // check P + DD -> D
    reg1.create("key1", "value0");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.put("key1", "value1");
    callbackVal.assertUpdateWriterCnt(1);
    reg1.destroy("key1");
    callbackVal.assertDestroyWriterCnt(1);
    assertTrue(!reg1.containsKey("key1"));
    this.txMgr.commit();
    callbackVal.assertUpdateListenerCnt(0);
    callbackVal.assertLocalDestroyListenerCnt(0);
    callbackVal.assertDestroyListenerCnt(1);
    callbackVal.reAssert();
    assertTrue(!reg1.containsKey("key1"));
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getDestroyEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals(null, ev.getNewValue());
        assertEquals("value0", ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }

    // check P + LI -> LI
    reg1.create("key1", "value0");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.put("key1", "value1");
    callbackVal.assertUpdateWriterCnt(1);
    reg1.localInvalidate("key1");
    callbackVal.assertInvalidateCnt(0);
    assertTrue(reg1.containsKey("key1"));
    assertTrue(!reg1.containsValueForKey("key1"));
    this.txMgr.commit();
    callbackVal.assertUpdateListenerCnt(1);
    callbackVal.reAssert();
    assertTrue(reg1.containsKey("key1"));
    assertEquals(null, reg1.getEntry("key1").getValue());
    assertTrue(!reg1.containsValueForKey("key1"));
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getInvalidateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals(null, ev.getNewValue());
        assertEquals("value0", ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(!ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // Check P + LI + C -> EX
    // Check P + LI + P -> P
    reg1.create("key1", "value0");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.put("key1", "value1");
    callbackVal.assertUpdateWriterCnt(1, /*remember*/ false);
    reg1.localInvalidate("key1");
    callbackVal.assertInvalidateCnt(0);
    try {
      reg1.create("key1", "ex");
      fail("expected EntryExistsException");
    } catch (EntryExistsException ok) {
    }
    callbackVal.assertCreateWriterCnt(0);
    callbackVal.assertUpdateWriterCnt(1, /*remember*/ false);
    reg1.put("key1", "value2");
    callbackVal.assertUpdateWriterCnt(2);
    assertTrue(reg1.containsKey("key1"));
    assertEquals("value2", reg1.getEntry("key1").getValue());
    this.txMgr.commit();
    callbackVal.assertUpdateListenerCnt(1);
    callbackVal.reAssert();
    assertTrue(reg1.containsKey("key1"));
    assertEquals("value2", reg1.getEntry("key1").getValue());
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getPutEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals("value2", ev.getNewValue());
        assertEquals("value0", ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // Check P + LI + LD -> LD
    reg1.create("key1", "value0");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.put("key1", "value1");
    callbackVal.assertUpdateWriterCnt(1);
    reg1.localInvalidate("key1");
    callbackVal.assertInvalidateCnt(0);
    reg1.localDestroy("key1");
    callbackVal.assertLocalDestroyWriterCnt(1);
    assertTrue(!reg1.containsKey("key1"));
    this.txMgr.commit();
    callbackVal.assertUpdateListenerCnt(0);
    callbackVal.assertDestroyListenerCnt(0);
    callbackVal.assertLocalDestroyListenerCnt(1);
    callbackVal.reAssert();
    assertTrue(!reg1.containsKey("key1"));
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getDestroyEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals(null, ev.getNewValue());
        assertEquals("value0", ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(!ev.isDistributed());
      }
    }

    // Check P + LI + DD -> DD
    reg1.create("key1", "value0");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.put("key1", "value1");
    callbackVal.assertUpdateWriterCnt(1);
    reg1.localInvalidate("key1");
    callbackVal.assertInvalidateCnt(0);
    reg1.destroy("key1");
    callbackVal.assertDestroyWriterCnt(1);
    assertTrue(!reg1.containsKey("key1"));
    this.txMgr.commit();
    callbackVal.assertUpdateListenerCnt(0);
    callbackVal.assertDestroyListenerCnt(1);
    callbackVal.reAssert();
    assertTrue(!reg1.containsKey("key1"));
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getDestroyEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals(null, ev.getNewValue());
        assertEquals("value0", ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }

    // check P + LD -> LD
    reg1.create("key1", "value0");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.put("key1", "value1");
    callbackVal.assertUpdateWriterCnt(1);
    reg1.localDestroy("key1");
    callbackVal.assertLocalDestroyWriterCnt(1);
    assertTrue(!reg1.containsKey("key1"));
    this.txMgr.commit();
    callbackVal.assertUpdateListenerCnt(0);
    callbackVal.assertDestroyListenerCnt(0);
    callbackVal.assertLocalDestroyListenerCnt(1);
    callbackVal.reAssert();
    assertTrue(!reg1.containsKey("key1"));
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getDestroyEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals(null, ev.getNewValue());
        assertEquals("value0", ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(!ev.isDistributed());
      }
    }

    // check P + LD + D -> EX
    // check P + LD + I -> EX
    // check P + LD + C -> C
    reg1.create("key1", "value0");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.put("key1", "value1");
    callbackVal.assertUpdateWriterCnt(1);
    reg1.localDestroy("key1");
    callbackVal.assertLocalDestroyWriterCnt(1);
    assertTrue(!reg1.containsKey("key1"));
    try {
      reg1.localDestroy("key1");
      fail("expected EntryNotFoundException");
    } catch (EntryNotFoundException ok) {
    }
    callbackVal.assertLocalDestroyWriterCnt(1, /*remember*/ false);
    try {
      reg1.destroy("key1");
      fail("expected EntryNotFoundException");
    } catch (EntryNotFoundException ok) {
    }
    callbackVal.assertDestroyWriterCnt(0);
    try {
      reg1.localInvalidate("key1");
      fail("expected EntryNotFoundException");
    } catch (EntryNotFoundException ok) {
    }
    callbackVal.assertInvalidateCnt(0, /*remember*/ false);
    try {
      reg1.invalidate("key1");
      fail("expected EntryNotFoundException");
    } catch (EntryNotFoundException ok) {
    }
    callbackVal.assertInvalidateCnt(0);
    reg1.create("key1", "value3");
    callbackVal.assertCreateWriterCnt(1);
    assertEquals("value3", reg1.getEntry("key1").getValue());
    this.txMgr.commit();
    callbackVal.assertLocalDestroyListenerCnt(0);
    callbackVal.assertUpdateListenerCnt(1);
    callbackVal.reAssert();
    assertEquals("value3", reg1.getEntry("key1").getValue());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getCreateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals("value3", ev.getNewValue());
        assertEquals(null, ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // check P + LD + P -> C
    reg1.create("key1", "value0");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.put("key1", "value1");
    callbackVal.assertUpdateWriterCnt(1, /*remember*/ false);
    reg1.localDestroy("key1");
    callbackVal.assertLocalDestroyWriterCnt(1);
    reg1.put("key1", "value3");
    callbackVal.assertCreateWriterCnt(1);
    assertEquals("value3", reg1.getEntry("key1").getValue());
    this.txMgr.commit();
    callbackVal.assertLocalDestroyListenerCnt(0);
    callbackVal.assertUpdateListenerCnt(1);
    callbackVal.reAssert();
    assertEquals("value3", reg1.getEntry("key1").getValue());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getCreateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals("value3", ev.getNewValue());
        assertEquals(null, ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // check DI + C -> EX
    // check DI + P -> P
    reg1.create("key1", "value0");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.invalidate("key1");
    callbackVal.assertInvalidateCnt(0);
    try {
      reg1.create("key1", "value1");
      fail("expected EntryExistsException");
    } catch (EntryExistsException ok) {
    }
    callbackVal.assertCreateWriterCnt(0);
    reg1.put("key1", "value2");
    callbackVal.assertUpdateWriterCnt(1);
    assertEquals("value2", reg1.getEntry("key1").getValue());
    this.txMgr.commit();
    callbackVal.assertUpdateListenerCnt(1);
    callbackVal.reAssert();
    assertEquals("value2", reg1.getEntry("key1").getValue());
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getPutEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals("value2", ev.getNewValue());
        assertEquals("value0", ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // check DI + DD -> D
    reg1.create("key1", "value0");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.invalidate("key1");
    callbackVal.assertInvalidateCnt(0);
    reg1.destroy("key1");
    callbackVal.assertDestroyWriterCnt(1);
    assertTrue(!reg1.containsKey("key1"));
    this.txMgr.commit();
    callbackVal.assertDestroyListenerCnt(1);
    callbackVal.reAssert();
    assertTrue(!reg1.containsKey("key1"));
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getDestroyEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals(null, ev.getNewValue());
        assertEquals("value0", ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }

    // check DI + LD -> LD
    reg1.create("key1", "value0");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.invalidate("key1");
    callbackVal.assertInvalidateCnt(0);
    reg1.localDestroy("key1");
    callbackVal.assertLocalDestroyWriterCnt(1);
    assertTrue(!reg1.containsKey("key1"));
    this.txMgr.commit();
    callbackVal.assertDestroyListenerCnt(0);    
    callbackVal.assertLocalDestroyListenerCnt(1);
    callbackVal.reAssert();
    assertTrue(!reg1.containsKey("key1"));
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getDestroyEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals(null, ev.getNewValue());
        assertEquals("value0", ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(!ev.isDistributed());
      }
    }

    // check DI + LD + D -> EX
    // check DI + LD + I -> EX
    // check DI + LD + C -> C
    reg1.create("key1", "value0");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.invalidate("key1");
    callbackVal.assertInvalidateCnt(0, false);
    reg1.localDestroy("key1");
    callbackVal.assertLocalDestroyWriterCnt(1);
    assertTrue(!reg1.containsKey("key1"));
    try {
      reg1.localDestroy("key1");
      fail("expected EntryNotFoundException");
    } catch (EntryNotFoundException ok) {
    }
    callbackVal.assertLocalDestroyWriterCnt(1, /*remember*/ false);
    try {
      reg1.destroy("key1");
      fail("expected EntryNotFoundException");
    } catch (EntryNotFoundException ok) {
    }
    callbackVal.assertDestroyWriterCnt(0);
    try {
      reg1.localInvalidate("key1");
      fail("expected EntryNotFoundException");
    } catch (EntryNotFoundException ok) {
    }
    callbackVal.assertInvalidateCnt(0, /*remember*/ false);
    try {
      reg1.invalidate("key1");
      fail("expected EntryNotFoundException");
    } catch (EntryNotFoundException ok) {
    }
    callbackVal.assertInvalidateCnt(0, /*remember*/ false);
    reg1.create("key1", "value3");
    callbackVal.assertCreateWriterCnt(1);
    assertEquals("value3", reg1.getEntry("key1").getValue());
    this.txMgr.commit();
    callbackVal.assertInvalidateCnt(0);
    callbackVal.assertDestroyListenerCnt(0);
    callbackVal.assertLocalDestroyListenerCnt(0);
    callbackVal.assertCreateListenerCnt(0);
    callbackVal.assertUpdateListenerCnt(1);
    callbackVal.reAssert();
    assertEquals("value3", reg1.getEntry("key1").getValue());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getCreateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals("value3", ev.getNewValue());
        assertEquals(null, ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // check DI + LD + P -> C
    reg1.create("key1", "value0");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.invalidate("key1", "value1");
    callbackVal.assertInvalidateCnt(0, false);
    reg1.localDestroy("key1");
    callbackVal.assertLocalDestroyWriterCnt(1);
    reg1.put("key1", "value3");
    callbackVal.assertCreateWriterCnt(1);
    assertEquals("value3", reg1.getEntry("key1").getValue());
    this.txMgr.commit();
    callbackVal.assertInvalidateCnt(0);
    callbackVal.assertCreateListenerCnt(0);
    callbackVal.assertLocalDestroyListenerCnt(0);
    callbackVal.assertUpdateListenerCnt(1);
    callbackVal.reAssert();
    assertEquals("value3", reg1.getEntry("key1").getValue());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getCreateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals("value3", ev.getNewValue());
        assertEquals(null, ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // check LI + C -> EX
    // check LI + P -> P
    reg1.create("key1", "value0");
    this.txMgr.begin();
    callbackVal.reset();
    myTxId = this.txMgr.getTransactionId();
    reg1.localInvalidate("key1");
    callbackVal.assertInvalidateCnt(0, false);
    try {
      reg1.create("key1", "value1");
      fail("expected EntryExistsException");
    } catch (EntryExistsException ok) {
    }
    callbackVal.assertCreateWriterCnt(0);
    reg1.put("key1", "value2");
    callbackVal.assertUpdateWriterCnt(1);
    assertEquals("value2", reg1.getEntry("key1").getValue());
    this.txMgr.commit();
    callbackVal.assertUpdateListenerCnt(1);
    callbackVal.assertInvalidateCnt(0);
    callbackVal.reAssert();
    assertEquals("value2", reg1.getEntry("key1").getValue());
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getPutEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals("value2", ev.getNewValue());
        assertEquals("value0", ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // check LI + DD -> DD
    reg1.create("key1", "value0");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.localInvalidate("key1");
    callbackVal.assertInvalidateCnt(0, false);
    reg1.destroy("key1");
    callbackVal.assertDestroyWriterCnt(1);
    assertTrue(!reg1.containsKey("key1"));
    this.txMgr.commit();
    callbackVal.assertInvalidateCnt(0);
    callbackVal.assertDestroyListenerCnt(1);
    callbackVal.reAssert();
    assertTrue(!reg1.containsKey("key1"));
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getDestroyEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals(null, ev.getNewValue());
        assertEquals("value0", ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }

    // check LI + LD -> LD
    reg1.create("key1", "value0");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.localInvalidate("key1");
    callbackVal.assertInvalidateCnt(0, false);
    reg1.localDestroy("key1");
    callbackVal.assertLocalDestroyWriterCnt(1);
    assertTrue(!reg1.containsKey("key1"));
    this.txMgr.commit();
    callbackVal.assertInvalidateCnt(0);
    callbackVal.assertLocalDestroyListenerCnt(1);
    callbackVal.reAssert();
    assertTrue(!reg1.containsKey("key1"));
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getDestroyEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals(null, ev.getNewValue());
        assertEquals("value0", ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(!ev.isDistributed());
      }
    }
    
    // check LI + LD + D -> EX
    // check LI + LD + I -> EX
    // check LI + LD + C -> C
    reg1.create("key1", "value0");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.localInvalidate("key1");
    callbackVal.assertInvalidateCnt(0, false);
    reg1.localDestroy("key1");
    callbackVal.assertLocalDestroyWriterCnt(1);
    assertTrue(!reg1.containsKey("key1"));
    try {
      reg1.localDestroy("key1");
      fail("expected EntryNotFoundException");
    } catch (EntryNotFoundException ok) {
    }
    callbackVal.assertLocalDestroyWriterCnt(1, /*remember*/ false);
    try {
      reg1.destroy("key1");
      fail("expected EntryNotFoundException");
    } catch (EntryNotFoundException ok) {
    }
    callbackVal.assertDestroyWriterCnt(0);
    try {
      reg1.localInvalidate("key1");
      fail("expected EntryNotFoundException");
    } catch (EntryNotFoundException ok) {
    }
    callbackVal.assertInvalidateCnt(0, /*remember*/ false);
    try {
      reg1.invalidate("key1");
      fail("expected EntryNotFoundException");
    } catch (EntryNotFoundException ok) {
    }
    callbackVal.assertInvalidateCnt(0, /*remember*/ false);
    reg1.create("key1", "value3");
    callbackVal.assertCreateWriterCnt(1);
    assertEquals("value3", reg1.getEntry("key1").getValue());
    this.txMgr.commit();
    callbackVal.assertInvalidateCnt(0);
    callbackVal.assertDestroyListenerCnt(0);
    callbackVal.assertLocalDestroyListenerCnt(0);
    callbackVal.assertCreateListenerCnt(0);
    callbackVal.assertUpdateListenerCnt(1);
    callbackVal.reAssert();
    assertEquals("value3", reg1.getEntry("key1").getValue());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getCreateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals("value3", ev.getNewValue());
        assertEquals(null, ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // check LI + LD + P -> C
    reg1.create("key1", "value0");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.localInvalidate("key1", "value1");
    callbackVal.assertInvalidateCnt(0, false);
    reg1.localDestroy("key1");
    callbackVal.assertLocalDestroyWriterCnt(1);
    reg1.put("key1", "value3");
    callbackVal.assertCreateWriterCnt(1);
    assertEquals("value3", reg1.getEntry("key1").getValue());
    this.txMgr.commit();
    callbackVal.assertLocalDestroyListenerCnt(0);
    callbackVal.assertCreateListenerCnt(0);
    callbackVal.assertInvalidateCnt(0);
    callbackVal.assertUpdateListenerCnt(1);
    callbackVal.reAssert();
    assertEquals("value3", reg1.getEntry("key1").getValue());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getCreateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals("value3", ev.getNewValue());
        assertEquals(null, ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // check init state LI + P + I -> I token (bug 33073)
    reg1.create("key1", "value0");
    reg1.localInvalidate("key1");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.put("key1", "value1");
    callbackVal.assertUpdateWriterCnt(1);
    assertEquals("value1", reg1.getEntry("key1").getValue());
    reg1.invalidate("key1");
    callbackVal.assertInvalidateCnt(0, false);
    this.txMgr.commit();
    callbackVal.assertUpdateListenerCnt(0);
    callbackVal.assertInvalidateCnt(1);
    callbackVal.reAssert();
    assertNull(reg1.getEntry("key1").getValue());
    {
      // Special check to assert Invaldate token
      LocalRegion.NonTXEntry nonTXe = (LocalRegion.NonTXEntry) reg1.getEntry("key1");
      assertTrue(nonTXe.getRegionEntry().isInvalid());
    }
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getInvalidateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertNull(ev.getNewValue());
        assertNull(ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    
    // check init state I + P + LI -> LI token (bug 33073)
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.put("key1", "value1");
    callbackVal.assertUpdateWriterCnt(1);
    assertEquals("value1", reg1.getEntry("key1").getValue());
    reg1.localInvalidate("key1");
    callbackVal.assertInvalidateCnt(0, false);
    this.txMgr.commit();
    callbackVal.assertInvalidateCnt(0);
    callbackVal.assertUpdateListenerCnt(1);
    callbackVal.assertCreateListenerCnt(0);
    callbackVal.reAssert();
    assertNull(reg1.getEntry("key1").getValue());
    {
      // Special check to assert Local Invaldate token
      LocalRegion.NonTXEntry nonTXe = (LocalRegion.NonTXEntry) reg1.getEntry("key1");
      assertTrue(nonTXe.getRegionEntry().isInvalid());
    }
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getInvalidateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertNull(ev.getNewValue());
        assertNull(ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(!ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // check DD + C -> C
    reg1.create("key1", "value0");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.destroy("key1");
    callbackVal.assertDestroyWriterCnt(1);
    reg1.create("key1", "value1");
    callbackVal.assertCreateWriterCnt(1);
    assertEquals("value1", reg1.getEntry("key1").getValue());
    this.txMgr.commit();
    callbackVal.assertDestroyListenerCnt(0);
    callbackVal.assertCreateListenerCnt(0);
    callbackVal.assertUpdateListenerCnt(1);
    callbackVal.reAssert();
    assertEquals("value1", reg1.getEntry("key1").getValue());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getCreateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals("value1", ev.getNewValue());
        assertEquals(null, ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // check DD + P -> C
    reg1.create("key1", "value0");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.destroy("key1");
    callbackVal.assertDestroyWriterCnt(1);
    reg1.put("key1", "value1");
    callbackVal.assertCreateWriterCnt(1);
    assertEquals("value1", reg1.getEntry("key1").getValue());
    this.txMgr.commit();
    callbackVal.assertDestroyListenerCnt(0);
    callbackVal.assertCreateListenerCnt(0);
    callbackVal.assertUpdateListenerCnt(1);
    callbackVal.reAssert();
    assertEquals("value1", reg1.getEntry("key1").getValue());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getCreateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals("value1", ev.getNewValue());
        assertEquals(null, ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // check LD + C -> C
    reg1.create("key1", "value0");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.localDestroy("key1");
    callbackVal.assertLocalDestroyWriterCnt(1);
    reg1.create("key1", "value1");
    callbackVal.assertCreateWriterCnt(1);
    assertEquals("value1", reg1.getEntry("key1").getValue());
    this.txMgr.commit();
    callbackVal.assertLocalDestroyListenerCnt(0);
    callbackVal.assertCreateListenerCnt(0);
    callbackVal.assertUpdateListenerCnt(1);
    callbackVal.reAssert();
    assertEquals("value1", reg1.getEntry("key1").getValue());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getCreateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals("value1", ev.getNewValue());
        assertEquals(null, ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");

    // check LD + P -> C
    reg1.create("key1", "value0");
    callbackVal.reset();
    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    reg1.localDestroy("key1");
    callbackVal.assertLocalDestroyWriterCnt(1);
    reg1.put("key1", "value1");
    callbackVal.assertCreateWriterCnt(1);
    assertEquals("value1", reg1.getEntry("key1").getValue());
    this.txMgr.commit();
    callbackVal.assertLocalDestroyListenerCnt(0);
    callbackVal.assertCreateListenerCnt(0);
    callbackVal.assertUpdateListenerCnt(1);
    callbackVal.reAssert();
    assertEquals("value1", reg1.getEntry("key1").getValue());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(1, this.te.getEvents().size());
    {
      Collection events = this.te.getCreateEvents();
      assertEquals(myTxId, this.te.getTransactionId());
      assertEquals(1, events.size());
      Iterator it = events.iterator();
      while (it.hasNext()) {
        EntryEvent ev = (EntryEvent)it.next();
        assertEquals(myTxId, ev.getTransactionId());
        assertTrue(ev.getRegion() == reg1);
        assertEquals("key1", ev.getKey());
        assertEquals("value1", ev.getNewValue());
        assertEquals(null, ev.getOldValue());
        assertTrue(!ev.isLocalLoad());
        assertTrue(!ev.isNetLoad());
        assertTrue(!ev.isLoad());
        assertTrue(!ev.isNetSearch());
        assertEquals(null, ev.getCallbackArgument());
        assertEquals(true, ev.isCallbackArgumentAvailable());
        assertTrue(!ev.isOriginRemote());
        assertTrue(!ev.isExpiration());
        assertTrue(ev.isDistributed());
      }
    }
    reg1.localDestroy("key1");
  }

  protected void doNonTxInvalidateRegionOp(CachePerfStats stats) throws Exception {
    int txRollbackChanges = stats.getTxRollbackChanges();
    this.region.create("key1", "value1");
    this.region.create("key2", "value2");
    this.txMgr.begin();
    try {
      this.region.localInvalidateRegion();
      fail("Should have gotten an UnsupportedOperationInTransactionException");
    } catch(UnsupportedOperationInTransactionException ee) {
      // this is expected
    }
    this.txMgr.rollback();
    assertEquals("value1", this.region.get("key1"));
    assertEquals("value2", this.region.get("key2"));
    assertEquals(txRollbackChanges, stats.getTxRollbackChanges());

    this.region.put("key1", "value1");
    this.region.put("key2", "value2");
    this.txMgr.begin();
    try {
      this.region.invalidateRegion();
      fail("Should have gotten an UnsupportedOperationInTransactionException");
    } catch(UnsupportedOperationInTransactionException ee) {
      // this is expected
    }
    this.txMgr.rollback();
    assertEquals("value1", this.region.get("key1"));
    assertEquals("value2", this.region.get("key2"));
    assertEquals(txRollbackChanges, stats.getTxRollbackChanges());

  }
  protected void doNonTxDestroyRegionOp(CachePerfStats stats) throws Exception {
    int txRollbackChanges = stats.getTxRollbackChanges();
    this.region.put("key1", "value1");
    this.region.put("key2", "value2");
    this.txMgr.begin();
    try {
      this.region.localDestroyRegion();
      fail("Should have gotten an UnsupportedOperationInTransactionException");
    } catch(UnsupportedOperationInTransactionException ee) {
      // this is expected
    }
    this.txMgr.rollback();
    assertTrue(!this.region.isDestroyed());
    assertEquals(txRollbackChanges, stats.getTxRollbackChanges());
      
    this.txMgr.begin();
    try {
      this.region.destroyRegion();
      fail("Should have gotten an UnsupportedOperationInTransactionException");
    } catch(UnsupportedOperationInTransactionException ee) {
      // this is expected
    }
    this.txMgr.rollback();
    assertTrue(!this.region.isDestroyed());
    assertEquals(txRollbackChanges, stats.getTxRollbackChanges());
  }

  public void testNonTxRegionOps() throws Exception {
    final CachePerfStats stats = this.cache.getCachePerfStats();
    doNonTxInvalidateRegionOp(stats);
    doNonTxDestroyRegionOp(stats);
  }

  public void testEntryNotFound() {
    // make sure operations that should fail with EntryNotFoundException
    // do so when done transactionally
    try {
      try {
        this.region.destroy("noEntry");
        fail("expected EntryNotFoundException");
      } catch (EntryNotFoundException expected) {
      }
      try {
        this.region.localDestroy("noEntry");
        fail("expected EntryNotFoundException");
      } catch (EntryNotFoundException expected) {
      }
      try {
        this.region.invalidate("noEntry");
        fail("expected EntryNotFoundException");
      } catch (EntryNotFoundException expected) {
      }
      try {
        this.region.localInvalidate("noEntry");
        fail("expected EntryNotFoundException");
      } catch (EntryNotFoundException expected) {
      }

      this.txMgr.begin();
      try {
        this.region.destroy("noEntry");
        fail("expected EntryNotFoundException");
      } catch (EntryNotFoundException expected) {
      }
      this.txMgr.rollback();
      this.txMgr.begin();
      try {
        this.region.localDestroy("noEntry");
        fail("expected EntryNotFoundException");
      } catch (EntryNotFoundException expected) {
      }
      this.txMgr.rollback();
      this.txMgr.begin();
      try {
        this.region.invalidate("noEntry");
        fail("expected EntryNotFoundException");
      } catch (EntryNotFoundException expected) {
      }
      this.txMgr.rollback();
      this.txMgr.begin();
      try {
        this.region.localInvalidate("noEntry");
        fail("expected EntryNotFoundException");
      } catch (EntryNotFoundException expected) {
      }
      this.txMgr.rollback();

      // check to see if EntryExistsException works in transactions
      this.region.create("key1", "value1");
      try {
      this.region.create("key1", "value2");
      fail("expected EntryExistsException");
      } catch (EntryExistsException expected) {
      }
      this.txMgr.begin();
      try {
        this.region.create("key1", "value2");
        fail("expected EntryExistsException");
      } catch (EntryExistsException expected) {
      }
      this.txMgr.rollback();
    } catch (CacheException ex) {
      fail("unexpected " + ex);
    }
  }
  
  public void testListener() {
    assertTrue(this.txMgr.getListener() == null);
    TransactionListener oldListener = this.txMgr.setListener(
      new TransactionListener() {
          public void afterCommit(TransactionEvent event) {
            listenerAfterCommit = 1;
            te = event;
          }

          public void afterFailedCommit(TransactionEvent event) {
            listenerAfterFailedCommit = 1;
            te = event;
          }

          public void afterRollback(TransactionEvent event) {
            listenerAfterRollback = 1;
            te = event;
          }
          public void close() {
            listenerClose = 1;
          }

        }
      );
    assertTrue(oldListener == null);
    this.txMgr.begin();
    TransactionId myTxId = this.txMgr.getTransactionId();
    assertEquals(0, this.listenerAfterRollback);
    this.txMgr.rollback();
    assertEquals(1, this.listenerAfterRollback);
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(0, this.te.getEvents().size());
    assertEquals(myTxId, this.te.getTransactionId());

    this.txMgr.begin();
    myTxId = this.txMgr.getTransactionId();
    try {
      assertEquals(0, this.listenerAfterCommit);
      this.txMgr.commit();
    } catch (ConflictException unexpected) {
      fail("did not expect " + unexpected);
    }
    assertEquals(1, this.listenerAfterCommit);
    assertEquals(0, this.te.getCreateEvents().size());
    assertEquals(0, this.te.getPutEvents().size());
    assertEquals(0, this.te.getInvalidateEvents().size());
    assertEquals(0, this.te.getDestroyEvents().size());
    assertEquals(0, this.te.getEvents().size());
    assertEquals(myTxId, this.te.getTransactionId());

    assertEquals(0, this.listenerClose);
    oldListener = this.txMgr.setListener(
      new TransactionListener() {
          public void afterCommit(TransactionEvent event) {
            listenerAfterCommit = 2;
            te = event;
          }
          public void afterFailedCommit(TransactionEvent event) {
            listenerAfterFailedCommit = 2;
          }
          public void afterRollback(TransactionEvent event) {
            listenerAfterRollback = 2;
            te = event;
          }
          public void close() {
            listenerClose = 2;
          }
        }
      );
    assertEquals(1, this.listenerClose);

    this.txMgr.begin();
    assertEquals(1, this.listenerAfterRollback);
    this.txMgr.rollback();
    assertEquals(2, this.listenerAfterRollback);
    this.txMgr.begin();
    this.txMgr.setListener(oldListener);
    assertEquals(2, this.listenerClose);
    this.txMgr.rollback();
    assertEquals(1, this.listenerAfterRollback);

    closeCache();
    assertEquals(1, this.listenerClose);
  }

  // make sure standard Cache(Listener,Writer)
  // are not called during rollback
  public void testNoCallbacksOnRollback() throws CacheException {
    // install listeners
    AttributesMutator mutator = this.region.getAttributesMutator();
    mutator.setCacheListener(new CacheListenerAdapter() {
        @Override
        public void close() {cbCount++;}
        @Override
        public void afterCreate(EntryEvent event) {cbCount++;}
        @Override
        public void afterUpdate(EntryEvent event) {cbCount++;}
        @Override
        public void afterInvalidate(EntryEvent event) {cbCount++;}
        @Override
        public void afterDestroy(EntryEvent event) {cbCount++;}
        @Override
        public void afterRegionInvalidate(RegionEvent event) {cbCount++;}
        @Override
        public void afterRegionDestroy(RegionEvent event) {cbCount++;}
      });
    mutator.setCacheWriter(new CacheWriter() {
        public void close() {cbCount++;}
        public void beforeUpdate(EntryEvent event)
          throws CacheWriterException {cbCount++;}
        public void beforeCreate(EntryEvent event)
          throws CacheWriterException {cbCount++;}
        public void beforeDestroy(EntryEvent event)
          throws CacheWriterException {cbCount++;}
        public void beforeRegionDestroy(RegionEvent event)
          throws CacheWriterException {cbCount++;}
        public void beforeRegionClear(RegionEvent event)
          throws CacheWriterException {cbCount++;}
      });

    this.txMgr.begin();
    this.region.create("key1", "value1");
    this.cbCount = 0;
    this.txMgr.rollback();
    assertEquals(0, this.cbCount);

    this.cbCount = 0;
    this.region.create("key1", "value1");
    // do a santify check to make sure callbacks are installed
    assertEquals(2, this.cbCount); // 2 -> 1writer + 1listener

    this.txMgr.begin();
    this.region.put("key1", "value2");
    this.cbCount = 0;
    this.txMgr.rollback();
    assertEquals(0, this.cbCount);
    this.region.localDestroy("key1");

    this.region.create("key1", "value1");
    this.txMgr.begin();
    this.region.localDestroy("key1");
    this.cbCount = 0;
    this.txMgr.rollback();
    assertEquals(0, this.cbCount);

    this.region.put("key1", "value1");
    this.txMgr.begin();
    this.region.destroy("key1");
    this.cbCount = 0;
    this.txMgr.rollback();
    assertEquals(0, this.cbCount);

    this.region.put("key1", "value1");
    this.txMgr.begin();
    this.region.localInvalidate("key1");
    this.cbCount = 0;
    this.txMgr.rollback();
    assertEquals(0, this.cbCount);
    this.region.localDestroy("key1");

    this.region.put("key1", "value1");
    this.txMgr.begin();
    this.region.invalidate("key1");
    this.cbCount = 0;
    this.txMgr.rollback();
    assertEquals(0, this.cbCount);

    this.region.localDestroy("key1");
  }

  // 
  // TXCallBackValidator is a container for holding state for validating Cache
  // callbacks
  // 
  class TXCallBackValidator {
    boolean passedValidation;
    boolean suspendValidation;
    int expectedCallCount;
    Object key;
    Object oldVal;
    boolean oldValIdentCheck;
    Object newVal;
    boolean newValIdentCheck;
    TransactionId txId;
    boolean isDistributed;
    boolean isLocalLoad;
    boolean isCreate;
    boolean isUpdate;
    boolean isDestroyed;
    boolean isInvalidate;
    Object callBackArg;

    // EntryEvent, CallCount validator for 
    // callbacks (CacheWriter, CacheListener
    boolean validate(EntryEvent event, int cnt) {
      if (this.isSuspendValidation()) {
        return true;
      }
      this.passedValidation = false;
      assertEquals("Expected Call Count Assertion!", this.expectedCallCount, cnt);

      assertTrue(!event.isExpiration());
      assertTrue(!event.isNetLoad());
      assertEquals("isLoad Assertion!", this.isLoad(), event.isLoad());
      assertEquals("isLocalLoad Assertion!", this.isLoad(), event.isLocalLoad());
      assertTrue(!event.isNetSearch());
      assertTrue(!event.isOriginRemote());
      assertNotNull(event.getRegion());
      assertNotNull(event.getRegion().getCache());
      assertNotNull(event.getRegion().getCache().getCacheTransactionManager());
      assertEquals(this.getTXId(), event.getTransactionId());
      // assertEquals(event.getTransactionId(), event.getRegion().getCache().getCacheTransactionManager().getTransactionId(), );
      if (!isPR()) assertEquals("IsDistributed Assertion!",  this.isDistributed(), event.isDistributed());
      assertEquals(this.getKey(), event.getKey());
      assertSame(this.getCallBackArg(), event.getCallbackArgument());
      if (newValIdentCheck) {
        assertSame(newVal, event.getNewValue());
      } else {
        assertEquals(newVal, event.getNewValue());
      }
      if (oldValIdentCheck) {
        assertSame(oldVal, event.getOldValue());
      } else {
        assertEquals(oldVal, event.getOldValue());
      }
      this.passedValidation = true;
      return true;
    }

    int setExpectedCount(int newVal) {
      int oldVal = this.expectedCallCount;
      this.expectedCallCount = newVal;
      return oldVal;
    }
//     int getCount() {return this.callCount;}
//     int setCount(int newVal) {
//       int oldVal = this.callCount;
//       this.callCount = newVal;
//       return oldVal;
//     }
    void setKey(Object key) {this.key = key;}
    Object getKey() {return this.key;}
    void setOldValue(Object val, boolean checkWithIdentity) {
      this.oldVal = val;
      this.oldValIdentCheck = checkWithIdentity;
    }
    Object getOldValue() {return this.oldVal;}
    void setNewValue(Object val, boolean checkWithIdentity) {
      this.newVal = val; 
      this.newValIdentCheck = checkWithIdentity;
    }
    Object getNewValue() {return this.newVal;}
    TransactionId setTXId(TransactionId txId) {
      TransactionId old = this.txId;
      this.txId = txId; 
      return old;
    }
    TransactionId getTXId() {return this.txId;}
    void setIsDistributed(boolean isDistributed) {
      this.isDistributed = isDistributed;
    }
    Object getCallBackArg() {return this.callBackArg;}
    void setCallBackArg(Object callBackArg) {
      this.callBackArg = callBackArg; 
    }

    boolean isDistributed() {return this.isDistributed;}

    void setIsCreate(boolean isCreate) {this.isCreate = isCreate;}
    boolean isCreate() {return this.isCreate;}
    void setIsUpdate(boolean isUpdate) {this.isUpdate = isUpdate;}
    boolean isUpdate() {return this.isUpdate;}
    void setIsDestroy(boolean isDestroyed) {this.isDestroyed = isDestroyed;}
    boolean isDestroy() {return this.isDestroyed;}
    void setIsInvalidate(boolean isInvalidate) {this.isInvalidate = isInvalidate;}
    boolean isInvalidate() {return this.isInvalidate;}
    void setIsLoad(boolean isLoad) {this.isLocalLoad = isLoad;}
    boolean isLoad() {return this.isLocalLoad;}

    boolean suspendValidation(boolean toggle) {
      boolean oldVal = this.suspendValidation;
      this.suspendValidation = toggle;
      return oldVal;
    }
    boolean isSuspendValidation() {
      return this.suspendValidation;
    }
    void setPassedValidation(boolean passedValidation) {this.passedValidation = passedValidation;}
    boolean passedValidation() {return this.passedValidation;}
  }
  interface ValidatableCacheListener extends CacheListener {
    public void setValidator(TXCallBackValidator v);
    public void validate();
    public void validateNoEvents();
    public void reset();
    public void setExpectedCount(int count);
    public int getCallCount();
  }
  interface ValidatableCacheWriter extends CacheWriter {
    public void setValidator(TXCallBackValidator v);
    public int getCallCount();
    public void localDestroyMakeup(int count);
    public void validate();
    public void reset();
    public void validateNoEvents();
  }

  // Test to make sure CacheListener callbacks are called in place with
  // the CacheEvents properly constructed
  public void testCacheCallbacks() throws CacheException {
    final String key1 = "Key1";
    final String value1 = "value1";
    final String value2 = "value2";
    final String callBackArg = "call back arg";
    // install listeners
    AttributesMutator mutator = this.region.getAttributesMutator();

    TXCallBackValidator cbv = new TXCallBackValidator();
    
    // Cache Listener
    ValidatableCacheListener vCl = new ValidatableCacheListener() {
        TXCallBackValidator v;
        int callCount;
        int prevCallCount;
        EntryEvent lastEvent;
        public void validate() {
          this.v.validate(this.lastEvent, this.callCount);
        }
        public void validate(EntryEvent event) {
          this.v.validate(event, ++this.callCount);
        }
        public void setValidator(TXCallBackValidator v) {this.v = v;}
        public void close() {}
        public void afterCreate(EntryEvent event) {
          lastEvent = event;
          if (this.v.isSuspendValidation()) {return;}
          this.validate(event);
          this.v.setPassedValidation(false);
          assertTrue("IsCreate Assertion!", this.v.isCreate());
          assertTrue(event.getRegion().containsKey(this.v.getKey()));
          assertTrue(event.getRegion().containsValueForKey(this.v.getKey()));
          assertNotNull(event.getRegion().getEntry(event.getKey()).getValue());
          this.v.setPassedValidation(true);
        }
        public void afterUpdate(EntryEvent event) {
          lastEvent = event;
          if (this.v.isSuspendValidation()) {return;}
          validate(event);
          this.v.setPassedValidation(false);
          assertTrue("IsUpdate Assertion!", this.v.isUpdate());
          assertTrue(event.getRegion().containsKey(this.v.getKey()));
          assertTrue(event.getRegion().containsValueForKey(this.v.getKey()));
          assertNotNull(event.getRegion().getEntry(event.getKey()).getValue());
          this.v.setPassedValidation(true);
        } 
        public void afterInvalidate(EntryEvent event) {
          lastEvent = event;
          if (this.v.isSuspendValidation()) {return;}
          validate(event);
          this.v.setPassedValidation(false);
          assertTrue("IsInvaldiate Assertion!", this.v.isInvalidate());
          assertTrue(event.getRegion().containsKey(this.v.getKey()));
          assertTrue(!event.getRegion().containsValueForKey(this.v.getKey()));
          assertNull(event.getRegion().getEntry(event.getKey()).getValue());
          this.v.setPassedValidation(true);
        }
        public void afterDestroy(EntryEvent event) {
          lastEvent = event;
          if (this.v.isSuspendValidation()) {return;}
          validate(event);
          this.v.setPassedValidation(false);
          assertTrue("IsDestroy Assertion!", this.v.isDestroy());
          assertTrue(!event.getRegion().containsKey(this.v.getKey()));
          assertTrue(!event.getRegion().containsValueForKey(this.v.getKey()));
          assertNull(event.getRegion().getEntry(event.getKey()));
          this.v.setPassedValidation(true);
        }
        public void afterRegionInvalidate(RegionEvent event) {
          fail("Unexpected invokation of afterRegionInvalidate");
        }
        public void afterRegionDestroy(RegionEvent event) {
          if (!event.getOperation().isClose()) {
            fail("Unexpected invokation of afterRegionDestroy");
          }
        }
        public void afterRegionClear(RegionEvent event) {
          
        }
	public void afterRegionCreate(RegionEvent event) {
	}
	public void afterRegionLive(RegionEvent event) {
	}
        public void reset() {
          lastEvent = null;
          prevCallCount = callCount;
        }
        public void validateNoEvents() {
          assertNull("Did not expect listener callback",lastEvent);
          assertEquals(prevCallCount, callCount);
        }
        public void setExpectedCount(int count) {
          callCount = count;
        }
        public int getCallCount() {
          return callCount;
        }
      };

    vCl.setValidator(cbv);
    mutator.setCacheListener(vCl);

    // CacheWriter
    ValidatableCacheWriter vCw = new ValidatableCacheWriter() {
        TXCallBackValidator v;
        int callCount;
        int prevCallCount;
        EntryEvent lastEvent;
        public int getCallCount() { return this.callCount; }
        public void localDestroyMakeup(int count) { this.callCount += count; }
        public void validate() {
          this.v.validate(this.lastEvent, this.callCount);
        }
        public void validate(EntryEvent event) {
          this.v.validate(event, ++this.callCount);
        }
        public void setValidator(TXCallBackValidator v) {this.v = v;}
        public void close() {}
        public void beforeCreate(EntryEvent event) {
          lastEvent = event;
          if (this.v.isSuspendValidation()) {return;}
          validate(event);
          this.v.setPassedValidation(false);
          assertTrue("IsCreate Assertion!", this.v.isCreate());
          assertFalse(event.getRegion().containsKey(this.v.getKey()));
          assertFalse(event.getRegion().containsValueForKey(this.v.getKey()));
          assertNull(event.getRegion().getEntry(event.getKey()));
          this.v.setPassedValidation(true);
        }
        public void beforeUpdate(EntryEvent event) {
          lastEvent = event;
          if (this.v.isSuspendValidation()) {return;}
          validate(event);
          this.v.setPassedValidation(false);
          assertTrue("IsUpdate Assertion!", this.v.isUpdate());
          assertTrue(event.getRegion().containsKey(this.v.getKey()));
          // Can not assert the following line, as the value being update may be invalide
          // assertTrue(event.getRegion().containsValueForKey(this.v.getKey()));
          this.v.setPassedValidation(true);
        } 
        public void beforeDestroy(EntryEvent event) {
          lastEvent = event;
          if (this.v.isSuspendValidation()) {return;}
          validate(event);
          this.v.setPassedValidation(false);
          assertTrue("IsDestroy Assertion!", this.v.isDestroy());
          assertTrue(event.getRegion().containsKey(this.v.getKey()));
          this.v.setPassedValidation(true);
        }
        public void beforeRegionDestroy(RegionEvent event) {
          fail("Unexpected invocation of beforeRegionDestroy");
        }
        public void beforeRegionClear(RegionEvent event) {
          fail("Unexpected invocation of beforeRegionClear");
        }
        public void reset() {
          lastEvent = null;
          prevCallCount = callCount;
        }
        public void validateNoEvents() {
          assertNull("Did not expect a writer event",lastEvent);
          assertEquals(prevCallCount, callCount);
        }
      };
    vCw.setValidator(cbv);
    mutator.setCacheWriter(vCw);

    // Cache Loader 
    mutator.setCacheLoader(new CacheLoader() {
        int count = 0;
        public Object load(LoaderHelper helper) throws CacheLoaderException {return new Integer(count++);}
        public void close() {}
      });
    
    // Use this to track the number of callout method invocations
    int appCallCount = 1;

    // Create => beforeCreate/afterCreate tests
    cbv.setKey(key1);
    cbv.setCallBackArg(callBackArg);
    cbv.setNewValue(value1, false);
    cbv.setOldValue(null, true);
    cbv.setIsDistributed(true);
    cbv.setIsLoad(false);
    cbv.setIsCreate(true);
    cbv.setIsUpdate(false);
    // Test non-transactional create expecting beforeCreate/afterCreate call
    cbv.setTXId(txMgr.getTransactionId());
    cbv.setExpectedCount(appCallCount++);
    this.region.create(key1, value1, callBackArg);
    assertTrue("Non-TX Create Validation Assertion", cbv.passedValidation());
    cbv.suspendValidation(true);
    this.region.localDestroy(key1);
    cbv.suspendValidation(false);
    // Test transactional create expecting afterCreate call
    this.txMgr.begin();
    cbv.setTXId(txMgr.getTransactionId());
    cbv.setExpectedCount(appCallCount++);
    this.region.create(key1, value1, callBackArg);
    this.txMgr.commit();
    assertTrue("TX Create Validation Assertion", cbv.passedValidation());
    cbv.suspendValidation(true);
    this.region.localDestroy(key1);

    // Put => afterCreate tests
    cbv.suspendValidation(false);
    cbv.setNewValue(value2, false);
    cbv.setOldValue(null, true);
    cbv.setIsDistributed(true);
    cbv.setIsLoad(false);
    cbv.setIsCreate(true);
    cbv.setIsUpdate(false);
    // Test non-transactional put expecting afterCreate call due to no
    // previous Entry
    cbv.setTXId(txMgr.getTransactionId());
    cbv.setExpectedCount(appCallCount++);
    this.region.put(key1, value2, callBackArg);
    assertTrue("Non-TX Put->Create Validation Assertion", cbv.passedValidation());
    cbv.suspendValidation(true);
    this.region.localDestroy(key1);
    cbv.suspendValidation(false);
    // Test transactional put expecting afterCreate call due to no
    // previous Entry
    this.txMgr.begin();
    cbv.setTXId(txMgr.getTransactionId());
    cbv.setExpectedCount(appCallCount++);
    this.region.put(key1, value2, callBackArg);
    this.txMgr.commit();
    assertTrue("TX Put->Create Validation Assertion", cbv.passedValidation());

    // Put => afterUpdate tests
    cbv.setNewValue(value1, false);
    cbv.setOldValue(value2, false);
    cbv.setIsDistributed(true);
    cbv.setIsLoad(false);
    cbv.setIsCreate(false);
    cbv.setIsUpdate(true);
    // Test non-transactional put expecting afterUpdate call due to 
    // previous Entry
    cbv.setTXId(txMgr.getTransactionId());
    cbv.setExpectedCount(appCallCount++);
    this.region.put(key1, value1, callBackArg);
    assertTrue("Non-TX Put->Update Validation Assertion", cbv.passedValidation());
    cbv.suspendValidation(true);
    this.region.localDestroy(key1);
    this.region.put(key1, value2);
    cbv.suspendValidation(false);
    // Test transactional put expecting afterUpdate call due to
    // previous Entry
    this.txMgr.begin();
    cbv.setTXId(txMgr.getTransactionId());
    cbv.setExpectedCount(appCallCount++);
    this.region.put(key1, value1, callBackArg);
    this.txMgr.commit();
    assertTrue("TX Put->Update Validation Assertion", cbv.passedValidation());

    // LocalDestroy => afterDestroy, non-distributed tests
    cbv.setNewValue(null, true);
    cbv.setOldValue(value1, false);
    cbv.setIsDistributed(false);
    cbv.setIsLoad(false);
    cbv.setIsDestroy(true);
    cbv.setIsUpdate(false);
    // Test non-transactional localDestroy, expecting afterDestroy,
    // non-distributed
    cbv.setTXId(txMgr.getTransactionId());
    cbv.setExpectedCount(appCallCount++);
    this.region.localDestroy(key1, callBackArg);
    if (!isPR()) vCw.localDestroyMakeup(1); // Account for cacheWriter not begin called
    assertTrue("Non-TX LocalDestroy Validation Assertion", cbv.passedValidation());
    cbv.suspendValidation(true);
    this.region.create(key1, value1);
    cbv.suspendValidation(false);
    // Test transactional localDestroy expecting afterDestroy,
    // non-distributed
    this.txMgr.begin();
    cbv.setTXId(txMgr.getTransactionId());
    cbv.setExpectedCount(appCallCount++);
    this.region.localDestroy(key1, callBackArg);
    if (!isPR()) vCw.localDestroyMakeup(1); // Account for cacheWriter not begin called
    this.txMgr.commit();
    assertTrue("TX LocalDestroy Validation Assertion", cbv.passedValidation());

    // Destroy => afterDestroy, distributed tests
    cbv.setNewValue(null, true);
    cbv.setOldValue(value1, false);
    cbv.setIsDistributed(true);
    cbv.setIsLoad(false);
    cbv.setIsDestroy(true);
    cbv.suspendValidation(true);
    this.region.create(key1, value1);
    cbv.suspendValidation(false);
    // Test non-transactional Destroy, expecting afterDestroy,
    // distributed
    cbv.setTXId(txMgr.getTransactionId());
    cbv.setExpectedCount(appCallCount++);
    this.region.destroy(key1, callBackArg);
    assertTrue("Non-TX Destroy Validation Assertion", cbv.passedValidation());
    cbv.suspendValidation(true);
    this.region.create(key1, value1);
    cbv.suspendValidation(false);
    // Test transactional Destroy, expecting afterDestroy,
    // distributed
    this.txMgr.begin();
    cbv.setTXId(txMgr.getTransactionId());
    cbv.setExpectedCount(appCallCount++);
    this.region.destroy(key1, callBackArg);
    this.txMgr.commit();
    assertTrue("TX Destroy Validation Assertion", cbv.passedValidation());

    // localInvalidate => afterInvalidate,  non-distributed tests
    cbv.setNewValue(null, true);
    cbv.setOldValue(value1, false);
    cbv.setIsDistributed(false);
    cbv.setIsLoad(false);
    cbv.setIsInvalidate(true);
    cbv.setIsDestroy(false);
    cbv.suspendValidation(true);
    this.region.create(key1, value1);
    cbv.suspendValidation(false);
    // Test non-transactional localInvalidate, expecting afterInvalidate
    // non-distributed
    cbv.setTXId(txMgr.getTransactionId());
    cbv.setExpectedCount(appCallCount++);
    this.region.localInvalidate(key1, callBackArg);
    assertTrue("Non-TX LocalInvalidate Validation Assertion", cbv.passedValidation());
    vCw.localDestroyMakeup(1); // Account for cacheWriter not begin called
    cbv.suspendValidation(true);
    this.region.put(key1, value1);
    cbv.suspendValidation(false);
    // Test transactional localInvalidate, expecting afterInvalidate
    // non-distributed
    this.txMgr.begin();
    cbv.setTXId(txMgr.getTransactionId());
    cbv.setExpectedCount(appCallCount++);
    this.region.localInvalidate(key1, callBackArg);
    this.txMgr.commit();
    assertTrue("TX LocalInvalidate Validation Assertion", cbv.passedValidation());
    vCw.localDestroyMakeup(1); // Account for cacheWriter not begin called
    cbv.suspendValidation(true);
    this.region.localDestroy(key1);

    // Invalidate => afterInvalidate, distributed tests
    cbv.setNewValue(null, true);
    cbv.setOldValue(value1, false);
    cbv.setIsDistributed(true);
    cbv.setIsLoad(false);
    cbv.suspendValidation(true);
    this.region.create(key1, value1);
    cbv.suspendValidation(false);
    // Test non-transactional Invalidate, expecting afterInvalidate
    // distributed
    cbv.setTXId(txMgr.getTransactionId());
    cbv.setExpectedCount(appCallCount++);
    this.region.invalidate(key1, callBackArg);
    vCw.localDestroyMakeup(1); // Account for cacheWriter not begin called
    assertTrue("Non-TX Invalidate Validation Assertion", cbv.passedValidation());
    cbv.suspendValidation(true);
    this.region.put(key1, value1);
    cbv.suspendValidation(false);
    // Test transactional Invalidate, expecting afterInvalidate
    // distributed
    this.txMgr.begin();
    cbv.setTXId(txMgr.getTransactionId());
    cbv.setExpectedCount(appCallCount++);
    this.region.invalidate(key1, callBackArg);
    this.txMgr.commit();
    vCw.localDestroyMakeup(1); // Account for cacheWriter not begin called
    assertTrue("TX Invalidate Validation Assertion", cbv.passedValidation());
    cbv.suspendValidation(true);
    this.region.localDestroy(key1);
    cbv.suspendValidation(false);

    // Create load Event tests
    int loaderValCheck=0;
    cbv.setNewValue(new Integer(loaderValCheck++), false);
    cbv.setCallBackArg(null);
    cbv.setOldValue(null, false);
    cbv.setIsDistributed(true);
    cbv.setIsCreate(true);
    cbv.setIsUpdate(false);
    cbv.setIsLoad(true);
    // Test non-transactional load, expecting afterCreate distributed
    cbv.setTXId(txMgr.getTransactionId());
    cbv.setExpectedCount(appCallCount++);
    this.region.get(key1);
    assertTrue("Non-TX Invalidate Validation Assertion", cbv.passedValidation());
    vCl.validate(); vCw.validate();
    cbv.suspendValidation(true);
    this.region.localDestroy(key1);
    cbv.suspendValidation(false);
    // Test transactional load, expecting afterCreate distributed
    vCl.reset();
    this.txMgr.begin();
    cbv.setTXId(txMgr.getTransactionId());
    cbv.setNewValue(new Integer(loaderValCheck++), false);
    cbv.setExpectedCount(appCallCount++);
    this.region.get(key1);
    this.txMgr.rollback();
    assertTrue("TX Invalidate Validation Assertion", cbv.passedValidation());
    vCw.validate();
    vCl.validateNoEvents();
    vCl.setExpectedCount(vCl.getCallCount()+1);
    
    this.txMgr.begin();
    cbv.setTXId(txMgr.getTransactionId());
    cbv.setNewValue(new Integer(loaderValCheck++), false);
    cbv.setExpectedCount(appCallCount++);
    this.region.get(key1);
    vCw.validate();
    vCw.reset();
    this.txMgr.commit();
    vCw.validateNoEvents();
    assertTrue("TX Invalidate Validation Assertion", cbv.passedValidation());
    vCl.validate();
    cbv.suspendValidation(true);
    this.region.localDestroy(key1);
    cbv.suspendValidation(false);

    // Update load Event tests
    cbv.suspendValidation(true);
    this.region.create(key1, null);
    cbv.suspendValidation(false);
    assertTrue(this.region.containsKey(key1));
    assertTrue(!this.region.containsValueForKey(key1));
    cbv.setNewValue(new Integer(loaderValCheck++), false);
    cbv.setOldValue(null, false);
    cbv.setIsDistributed(true);
    cbv.setCallBackArg(null);
    cbv.setIsCreate(false);
    cbv.setIsUpdate(true);
    cbv.setIsLoad(true);
    // Test non-transactional load, expecting afterUpdate distributed
    cbv.setTXId(txMgr.getTransactionId());
    cbv.setExpectedCount(appCallCount++);
    this.region.get(key1);
    assertTrue("Non-TX Invalidate Validation Assertion", cbv.passedValidation());     
    vCw.validate();
    vCl.validate();
    cbv.suspendValidation(true);
    this.region.invalidate(key1);
    cbv.suspendValidation(false);
    assertTrue(this.region.containsKey(key1));
    assertTrue(!this.region.containsValueForKey(key1));
    // Test transactional load, expecting afterUpdate distributed
    this.txMgr.begin();
    cbv.setTXId(txMgr.getTransactionId());
    cbv.setExpectedCount(appCallCount++);
    cbv.setNewValue(new Integer(loaderValCheck++), false);
    this.region.get(key1);
    vCw.validate();
    vCw.reset();
    this.txMgr.commit();
    vCw.validateNoEvents();
    vCl.validate();
    
    cbv.suspendValidation(true);
    this.region.invalidate(key1);
    cbv.suspendValidation(false);
    vCl.reset();
    this.txMgr.begin();
    cbv.setTXId(txMgr.getTransactionId());
    cbv.setExpectedCount(appCallCount++);
    cbv.setNewValue(new Integer(loaderValCheck++), false);
    this.region.get(key1);
    this.txMgr.rollback();
    assertTrue("TX Invalidate Validation Assertion", cbv.passedValidation());    
    vCw.validate();
    vCl.validateNoEvents();
  }

  public void testEntryCount() throws CacheException {
    LocalRegion reg1 = (LocalRegion)this.region;

    assertEquals(0, reg1.entryCount());
    reg1.create("key1", "value1");
    assertEquals(1, reg1.entryCount());
    reg1.create("key2", "value2");
    assertEquals(2, reg1.entryCount());
    reg1.localDestroy("key2");
    assertEquals(1, reg1.entryCount());

    reg1.create("key3", "value3");
    assertEquals(2, reg1.entryCount());
    
    this.txMgr.begin();
    this.region.create("key2", "value2");
    assertEquals(3, reg1.entryCount());
    reg1.localDestroy("key2");
    assertEquals(2, reg1.entryCount());

    this.region.create("key2", "value2");
    assertEquals(3, reg1.entryCount());
    reg1.destroy("key2");
    assertEquals(2, reg1.entryCount());

    reg1.localInvalidate("key1");
    assertEquals(2, reg1.entryCount());
    reg1.put("key1", "value2");
    assertEquals(2, reg1.entryCount());
    this.region.create("key2", "value2");
    assertEquals(3, reg1.entryCount());

    reg1.localDestroy("key3");
    assertEquals(2, reg1.entryCount());
    reg1.create("key3", "value3");
    assertEquals(3, reg1.entryCount());

    this.txMgr.rollback();

    assertEquals(2, reg1.entryCount());
  }

  private void checkCollectionSize(int expectedSize) {
    checkCollectionSize(expectedSize, expectedSize, expectedSize);
  }
  private void checkCollectionSize(int expectedSize, int expectedRecSize) {
    checkCollectionSize(expectedSize, expectedRecSize, expectedSize);
  }
  private void checkCollectionSize(int expectedSize,
                                   int expectedRecSize,
                                   int expectedValuesSize)
  {
    int size = 0;
    for (Object key : this.region.entrySet(false)) {
      size++;
    }
    assertEquals(expectedSize, size);
    assertEquals(expectedSize, this.region.keySet().size());
    assertEquals(expectedValuesSize, this.region.values().size());
    assertEquals(expectedSize, this.region.entrySet(false).size());
    assertEquals(expectedRecSize, this.region.entrySet(true).size());
  }
  
  public void testCollections() throws CacheException {
    Region reg1 = this.region;

    checkSubRegionCollecection(reg1);

    {
      Collection nonTxKeys = reg1.keySet();
      Collection nonTxValues = reg1.values();
      this.txMgr.begin();
      reg1.create("key1", "value1");
      Collection txKeys = reg1.keySet();
      Collection txValues = reg1.values();
      /* [sumedh] No longer fail this scenario to avoid the overhead of
       * ThreadLocal lookup in every iteration. Besides does not look to
       * be a harmful usage in any case.
       */
      nonTxKeys.size();
      nonTxValues.size();
      /*
      try {
        nonTxKeys.size();
        fail();
      } catch (IllegalTransactionStateException expected) {
        TransactionId txid = this.txMgr.getTransactionId();
        assertEquals(LocalizedStrings
            .LocalRegion_NON_TRANSACTIONAL_REGION_COLLECTION_IS_BEING_USED_IN_A_TRANSACTION
                .toLocalizedString(txid), expected.getMessage());
      }
      assertEquals(1, txKeys.size());
      try {
        nonTxValues.size();
        fail();
      } catch (IllegalTransactionStateException expected) {
        TransactionId txid = this.txMgr.getTransactionId();
        assertEquals(LocalizedStrings
            .LocalRegion_NON_TRANSACTIONAL_REGION_COLLECTION_IS_BEING_USED_IN_A_TRANSACTION
                .toLocalizedString(txid), expected.getMessage());
      }
      */
      assertEquals(1, txValues.size());
      assertTrue(txKeys.contains("key1"));
      {
        Iterator txIt = txKeys.iterator();
        assertTrue(txIt.hasNext());
        assertTrue(txIt.hasNext());
        assertEquals("key1", txIt.next());
        assertTrue(!txIt.hasNext());
      }
      assertTrue(txValues.contains("value1"));
      {
        Iterator txIt = txValues.iterator();
        assertTrue(txIt.hasNext());
        assertTrue(txIt.hasNext());
        assertEquals("value1", txIt.next());
        assertTrue(!txIt.hasNext());
      }
      reg1.invalidate("key1");
//      assertEquals(0, nonTxKeys.size());
      assertEquals(1, txKeys.size());
//      assertEquals(0, nonTxValues.size());
      assertEquals(0, txValues.size());
      assertTrue(txKeys.contains("key1"));
      assertTrue(!txValues.contains("value1"));
      reg1.create("key2", "value2");
      reg1.create("key3", "value3");
//      assertEquals(0, nonTxKeys.size());
      assertEquals(3, txKeys.size());
//      assertEquals(0, nonTxValues.size());
      assertEquals(2, txValues.size());
      reg1.put("key1", "value1");
//      assertEquals(0, nonTxKeys.size());
      assertEquals(3, txKeys.size());
//      assertEquals(0, nonTxValues.size());
      assertEquals(3, txValues.size());
      reg1.localInvalidate("key2");
//      assertEquals(0, nonTxValues.size());
      assertEquals(2, txValues.size());
      reg1.invalidate("key1");
//      assertEquals(0, nonTxValues.size());
      assertEquals(1, txValues.size());
      reg1.destroy("key2");
      reg1.destroy("key3");
      assertEquals(1, txKeys.size());
      
      reg1.destroy("key1");
      assertEquals(0, txKeys.size());
      assertTrue(!txKeys.contains("key1"));
      Iterator txIt = txKeys.iterator();
      assertTrue(!txIt.hasNext());
      this.txMgr.rollback();
      try {
        txKeys.size();
        fail("expected IllegalTransactionStateException");
      } catch (IllegalTransactionStateException ok) {
      }
      try {
        txKeys.isEmpty();
        fail("expected IllegalTransactionStateException");
      } catch (IllegalTransactionStateException ok) {
      }
      try {
        txKeys.contains("key1");
        fail("expected IllegalTransactionStateException");
      } catch (IllegalTransactionStateException ok) {
      }
      try {
        txKeys.iterator();
        fail("expected IllegalTransactionStateException");
      } catch (IllegalTransactionStateException ok) {
      }
      try {
        txIt.hasNext();
        fail("expected IllegalTransactionStateException");
      } catch (IllegalTransactionStateException ok) {
      }
    }
    {
//      Collection nonTxValues = reg1.values();
      this.txMgr.begin();
      reg1.create("key1", "value1");
      Collection txValues = reg1.values();
//      assertEquals(0, nonTxValues.size());
      assertEquals(1, txValues.size());
      assertTrue(txValues.contains("value1"));
      {
        Iterator txIt = txValues.iterator();
        assertTrue(txIt.hasNext());
        assertEquals("value1", txIt.next());
        assertTrue(!txIt.hasNext());
      }
      reg1.destroy("key1");
      assertEquals(0, txValues.size());
      assertTrue(!txValues.contains("value1"));
      assertTrue(!txValues.iterator().hasNext());
      assertTrue(!txValues.iterator().hasNext());
      this.txMgr.rollback();
    }
    {
      Collection nonTxEntries = reg1.entrySet(false);
      this.txMgr.begin();
      reg1.create("key1", "value1");
      Collection txEntries = reg1.entrySet(false);
      // non-TX collections can now be used in a transactional context
      nonTxEntries.size();
      /*
      try {
        nonTxEntries.size();
        fail();
      } catch (IllegalTransactionStateException expected) {
        TransactionId txid = this.txMgr.getTransactionId();
        assertEquals(LocalizedStrings
            .LocalRegion_NON_TRANSACTIONAL_REGION_COLLECTION_IS_BEING_USED_IN_A_TRANSACTION
                .toLocalizedString(txid), expected.getMessage());
      }
      */
      assertEquals(1, txEntries.size());
      assertTrue(txEntries.contains(reg1.getEntry("key1")));
      {
        Iterator txIt = txEntries.iterator();
        assertTrue(txIt.hasNext());
        assertTrue(txIt.hasNext());
        assertEquals(reg1.getEntry("key1"), txIt.next());
        assertTrue(!txIt.hasNext());
        assertTrue(!txIt.hasNext());
      }
      reg1.destroy("key1");
      assertEquals(0, txEntries.size());
      assertTrue(!txEntries.iterator().hasNext());
      this.txMgr.rollback();
    }
    {
      Collection nonTxKeys = reg1.keySet();
      Collection nonTxValues = reg1.values();
      
      assertEquals(0, nonTxKeys.size());
      assertEquals(0, nonTxValues.size());
      reg1.create("key1", "value1");
      assertEquals(1, nonTxKeys.size());
      assertEquals(1, nonTxValues.size());
      reg1.invalidate("key1");
      assertEquals(1, nonTxKeys.size());
      assertEquals(0, nonTxValues.size());

      this.txMgr.begin();
      Collection txKeys = reg1.keySet();
      Collection txValues = reg1.values();
      assertEquals(1, txKeys.size());
      assertEquals(0, txValues.size());
      reg1.put("key1", "txValue1");
      assertEquals(1, txKeys.size());
      assertEquals(1, txValues.size());
      assertTrue(txValues.iterator().hasNext());
      assertEquals("txValue1", txValues.iterator().next());
//      assertEquals(0, nonTxValues.size());
      // non-TX collections can now be used in a transactional context
      assertFalse(nonTxValues.iterator().hasNext());
      /*
      try {
        nonTxValues.iterator().hasNext();
      } catch (IllegalTransactionStateException expected) {
        TransactionId txid = this.txMgr.getTransactionId();
        assertEquals(LocalizedStrings
            .LocalRegion_NON_TRANSACTIONAL_REGION_COLLECTION_IS_BEING_USED_IN_A_TRANSACTION
                .toLocalizedString(txid), expected.getMessage());
      }
      */

      reg1.localInvalidate("key1");
      assertEquals(0, txValues.size());
      assertTrue(!txValues.iterator().hasNext());
      this.txMgr.rollback();
    }
  }

  /**
   * @param reg1
   */
  protected void checkSubRegionCollecection(Region reg1) {
    AttributesFactory af = new AttributesFactory();
    af.setScope(Scope.DISTRIBUTED_NO_ACK);
    Region sub1 = this.region.createSubregion("collectionSub1", af.create());
    af = new AttributesFactory();
    Region sub2 = this.region.createSubregion("collectionSub2", af.create());
    af = new AttributesFactory();
    af.setScope(Scope.LOCAL);
    Region sub2_1 = sub2.createSubregion("collectionSub2_1", af.create());

    checkCollectionSize(0);
    try {
      this.region.keySet().iterator().next();
      fail();
    } catch (NoSuchElementException expected) {
      assertNull(expected.getMessage());
    }
    try {
      this.region.values().iterator().next();
      fail();
    } catch (NoSuchElementException expected) {
      assertNull(expected.getMessage());
    }
    try {
      this.region.entrySet().iterator().next();
      fail();
    } catch (NoSuchElementException expected) {
      assertNull(expected.getMessage());
    }
    reg1.create("key1", "value1");
    checkCollectionSize(1); 
    {
      Iterator it = this.region.keySet().iterator();
      it.next(); 
      try {
        it.next();
        fail();
      } catch (NoSuchElementException expected) {
        assertNull(expected.getMessage());
      }
    }
    {
      Iterator it = this.region.values().iterator();
      it.next(); 
      try {
        it.next();
        fail();
      } catch (NoSuchElementException expected) {
        assertNull(expected.getMessage());
      }
    }
    {
      Iterator it = this.region.entrySet().iterator();
      it.next(); 
      try {
        it.next();
        fail();
      } catch (NoSuchElementException expected) {
        assertNull(expected.getMessage());
      }
    }

    reg1.create("key2", "value2");
    checkCollectionSize(2);
    reg1.localInvalidate("key2");
    checkCollectionSize(2, 2, 1);
    reg1.localInvalidate("key1");
    checkCollectionSize(2, 2, 0);
    reg1.localDestroy("key2");
    checkCollectionSize(1, 1, 0);
    reg1.localDestroy("key1");
    checkCollectionSize(0);


    // Non-TX recursive checks
    sub2_1.create("key6", "value6");
    checkCollectionSize(0, 1);
    assertEquals(0, sub2.entrySet(false).size());
    assertEquals(1, sub2.entrySet(true).size());
    assertEquals(1, sub2_1.entrySet(true).size());
    sub2.create("key5", "value5");
    checkCollectionSize(0, 2);
    assertEquals(1, sub2.entrySet(false).size());
    assertEquals(2, sub2.entrySet(true).size());
    sub1.create("key4", "value4");
    checkCollectionSize(0, 3);
    assertEquals(1, sub1.entrySet(false).size());
    assertEquals(1, sub1.entrySet(true).size());
    reg1.put("key1", "value1");
    checkCollectionSize(1, 4);
    sub2.localDestroy("key5");
    checkCollectionSize(1, 3);
    assertEquals(0, sub2.entrySet(false).size());
    assertEquals(1, sub2.entrySet(true).size());
    assertEquals(1, sub2_1.entrySet(false).size());
    assertEquals(1, sub2_1.entrySet(true).size());
    sub2_1.localDestroy("key6");
    checkCollectionSize(1, 2);
    assertEquals(0, sub2.entrySet(false).size());
    assertEquals(0, sub2.entrySet(true).size());
    assertEquals(0, sub2_1.entrySet(false).size());
    sub1.localDestroy("key4");
    checkCollectionSize(1, 1);
    assertEquals(0, sub1.entrySet(false).size());
    assertEquals(0, sub1.entrySet(true).size());

    reg1.create("key3", "value3");
    sub1.create("key4", "value4");
    sub2.create("key5", "value5");
    sub2_1.create("key6", "value6");
    checkCollectionSize(2, 5);
    
    this.txMgr.begin();
    this.region.create("key2", "value2");
    checkCollectionSize(3, 6);
    reg1.localDestroy("key2");
    checkCollectionSize(2, 5);
    this.region.create("key2", "value2");
    checkCollectionSize(3, 6);
    reg1.destroy("key2");
    checkCollectionSize(2, 5);
    reg1.put("key1", "value2");
    checkCollectionSize(2, 5);
    this.region.create("key2", "value2");
    checkCollectionSize(3, 6);
    reg1.localDestroy("key3");
    checkCollectionSize(2, 5);
    reg1.create("key3", "value3");
    checkCollectionSize(3, 6);

    // TX recursive checks
    sub2.destroy("key5");
    checkCollectionSize(3, 5);
    assertEquals(1, sub1.entrySet(false).size());
    assertEquals(1, sub1.entrySet(true).size());
    assertEquals(0, sub2.entrySet(false).size());
    assertEquals(1, sub2.entrySet(true).size());
    assertEquals(1, sub2_1.entrySet(false).size());
    assertEquals(1, sub2_1.entrySet(true).size());
    sub2_1.destroy("key6");
    checkCollectionSize(3, 4);
    assertEquals(1, sub1.entrySet(false).size());
    assertEquals(1, sub1.entrySet(true).size());
    assertEquals(0, sub2.entrySet(false).size());
    assertEquals(0, sub2.entrySet(true).size());
    assertEquals(0, sub2_1.entrySet(false).size());
    assertEquals(0, sub2_1.entrySet(true).size());
    sub1.localDestroy("key4");
    checkCollectionSize(3, 3);
    assertEquals(0, sub1.entrySet(false).size());
    assertEquals(0, sub1.entrySet(true).size());
    assertEquals(0, sub2.entrySet(false).size());
    assertEquals(0, sub2.entrySet(true).size());
    assertEquals(0, sub2_1.entrySet(false).size());
    assertEquals(0, sub2_1.entrySet(true).size());
    sub2.put("key5", "value5");
    checkCollectionSize(3, 4);
    assertEquals(0, sub1.entrySet(false).size());
    assertEquals(0, sub1.entrySet(true).size());
    assertEquals(1, sub2.entrySet(false).size());
    assertEquals(1, sub2.entrySet(true).size());
    assertEquals(0, sub2_1.entrySet(false).size());
    assertEquals(0, sub2_1.entrySet(true).size());
    sub2_1.put("key6", "value6");
    checkCollectionSize(3, 5);
    assertEquals(0, sub1.entrySet(false).size());
    assertEquals(0, sub1.entrySet(true).size());
    assertEquals(1, sub2.entrySet(false).size());
    assertEquals(2, sub2.entrySet(true).size());
    assertEquals(1, sub2_1.entrySet(false).size());
    assertEquals(1, sub2_1.entrySet(true).size());
    sub1.put("key4", "value4");
    checkCollectionSize(3, 6);
    assertEquals(1, sub1.entrySet(false).size());
    assertEquals(1, sub1.entrySet(true).size());
    assertEquals(1, sub2.entrySet(false).size());
    assertEquals(2, sub2.entrySet(true).size());
    assertEquals(1, sub2_1.entrySet(false).size());
    assertEquals(1, sub2_1.entrySet(true).size());
    sub2_1.put("key7", "value7");
    checkCollectionSize(3, 7);
    assertEquals(1, sub1.entrySet(false).size());
    assertEquals(1, sub1.entrySet(true).size());
    assertEquals(1, sub2.entrySet(false).size());
    assertEquals(3, sub2.entrySet(true).size());
    assertEquals(2, sub2_1.entrySet(false).size());
    assertEquals(2, sub2_1.entrySet(true).size());

    this.txMgr.rollback();
    checkCollectionSize(2, 5);

    // disabling these in a TX because they throw and don't work now!
    //this.txMgr.begin();
    sub2.destroyRegion();
    checkCollectionSize(2, 3);
    sub1.destroyRegion();
    checkCollectionSize(2);
    //this.txMgr.rollback();
    
    reg1.localDestroy("key1");
    reg1.localDestroy("key3");
    checkCollectionSize(0);
  }

  /**
   * disabled till non-TX, TX conflict detection is not complete.
   */
  public void DISABLED_TILL_NEW_TX_IMPL_COMPLETE_testLoader() throws CacheException {
    LocalRegion reg1 = (LocalRegion)this.region;
    AttributesMutator mutator = reg1.getAttributesMutator();
    mutator.setCacheLoader(new CacheLoader() {
        int count = 0;
        public Object load(LoaderHelper helper)
          throws CacheLoaderException
        {
          count++;
          return "LV " + count;
        }
        public void close() {}
      });
    if (isPR()) ((PartitionedRegion)reg1).setHaveCacheLoader();
    assertTrue(!reg1.containsKey("key1"));
    assertEquals("LV 1", reg1.get("key1"));
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 1", reg1.getEntry("key1").getValue());
    reg1.localDestroy("key1");
    
    // TX load: only TX
    this.txMgr.begin();
    assertTrue(!reg1.containsKey("key1"));
    assertEquals("LV 2", reg1.get("key1"));
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 2", reg1.getEntry("key1").getValue());
    this.txMgr.rollback();
    assertTrue(!reg1.containsKey("key1"));
    // assertEquals("LV 2", reg1.getEntry("key1").getValue());
    // reg1.localDestroy("key1");
    // TX load: commit check
    this.txMgr.begin();
    assertTrue(!reg1.containsKey("key1"));
    assertEquals("LV 3", reg1.get("key1"));
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 3", reg1.getEntry("key1").getValue());
    this.txMgr.commit();
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 3", reg1.getEntry("key1").getValue());
    reg1.localDestroy("key1");
    // TX load YES conflict: no-initial state, tx create, committed load
    {
      final TXManagerImpl txMgrImpl = (TXManagerImpl)this.txMgr;
      TXStateInterface tx;
      this.txMgr.begin();
      reg1.create("key1", "txValue");
      assertEquals("txValue", reg1.getEntry("key1").getValue());
      tx = txMgrImpl.internalSuspend();
      assertTrue(!reg1.containsKey("key1"));
      assertEquals("LV 4", reg1.get("key1"));
      assertTrue(reg1.containsKey("key1"));
      txMgrImpl.resume(tx);
      assertEquals("txValue", reg1.getEntry("key1").getValue());
      assertEquals("txValue", reg1.get("key1"));
      try {
        this.txMgr.commit();   
        fail("Should have thrown a conflict");
      } catch(ConflictException cce) {
        // this is what we want
      }
      assertEquals("LV 4", reg1.getEntry("key1").getValue());
      assertEquals("LV 4", reg1.get("key1"));
      reg1.localDestroy("key1");
    }
    // TX load no conflict: load initial state, tx update
    assertEquals("LV 5", reg1.get("key1"));
    this.txMgr.begin();
    reg1.put("key1", "txValue");
    assertEquals("txValue", reg1.get("key1"));
    assertEquals("txValue", reg1.getEntry("key1").getValue());
    this.txMgr.commit();    // no conflict! Make sure committed value overrode initial state
    assertEquals("txValue", reg1.getEntry("key1").getValue());
    assertEquals("txValue", reg1.get("key1"));
    reg1.localDestroy("key1");
    // TX load no conflict: load initial state, tx load
    assertEquals("LV 6", reg1.get("key1"));
    this.txMgr.begin();
    reg1.localInvalidate("key1");
    assertEquals("LV 7", reg1.get("key1"));
    assertEquals("LV 7", reg1.getEntry("key1").getValue());
    this.txMgr.commit(); // no conflict! Make sure committed value overrode initial state
    assertEquals("LV 7", reg1.getEntry("key1").getValue());
    assertEquals("LV 7", reg1.get("key1"));
    reg1.localDestroy("key1");

    // TX load no conflict: no initial state, tx load, committed create
    {
      final TXManagerImpl txMgrImpl = (TXManagerImpl)this.txMgr;
      TXStateInterface tx;
      this.txMgr.begin();
      assertEquals("LV 8", reg1.get("key1"));
      assertEquals("LV 8", reg1.getEntry("key1").getValue());
      tx = txMgrImpl.internalSuspend();
      assertTrue(!reg1.containsKey("key1"));
      reg1.create("key1", "txValue");
      assertTrue(reg1.containsKey("key1"));
      assertEquals("txValue", reg1.get("key1"));
      txMgrImpl.resume(tx);
      assertEquals("LV 8", reg1.getEntry("key1").getValue());
      try {
        this.txMgr.commit();  // should conflict
        fail("Should have thrown cce");
      } catch(ConflictException cce) {
        // this is what we want
      }
      assertEquals("txValue", reg1.getEntry("key1").getValue());
      assertEquals("txValue", reg1.get("key1"));
      reg1.localDestroy("key1");
    }
    // TX load conflict: no-inital state, tx load->update, committed update
    {
      final TXManagerImpl txMgrImpl = (TXManagerImpl)this.txMgr;
      TXStateInterface tx;
      this.txMgr.begin();
      reg1.create("key1", "txValue");
      tx = txMgrImpl.internalSuspend();
      assertTrue(!reg1.containsKey("key1"));
      // new transaction, load(create) + put
      this.txMgr.begin();
      assertEquals("LV 9", reg1.get("key1"));
      assertEquals("LV 9", reg1.getEntry("key1").getValue());
      reg1.put("key1", "txValue2");
      assertEquals("txValue2", reg1.get("key1"));
      assertEquals("txValue2", reg1.getEntry("key1").getValue());
      this.txMgr.commit();
      assertTrue(reg1.containsKey("key1"));
      assertEquals("txValue2", reg1.get("key1"));
      assertEquals("txValue2", reg1.getEntry("key1").getValue());
      txMgrImpl.resume(tx);
      assertEquals("txValue", reg1.getEntry("key1").getValue());
      assertEquals("txValue", reg1.get("key1"));
      try {
        this.txMgr.commit();
        fail("expected ConflictException!");
      } catch (ConflictException expected) {
      }
      assertTrue(reg1.containsKey("key1"));
      assertEquals("txValue2", reg1.get("key1"));
      assertEquals("txValue2", reg1.getEntry("key1").getValue());
      reg1.localDestroy("key1");
    }

    // TX load repeat: no-initial state, tx load->get
    this.txMgr.begin();
    assertTrue(!reg1.containsKey("key1"));
    assertEquals("LV 10", reg1.get("key1")); // first invocation
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 10", reg1.getEntry("key1").getValue());
    assertEquals("LV 10", reg1.get("key1")); // second invocation
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 10", reg1.getEntry("key1").getValue());
    this.txMgr.rollback();

    // TX load repeat: no-initial state, tx load->localDestory->load
    this.txMgr.begin();
    assertTrue(!reg1.containsKey("key1"));
    assertEquals("LV 11", reg1.get("key1")); // first invocation
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 11", reg1.getEntry("key1").getValue());
    reg1.localDestroy("key1");
    assertEquals("LV 12", reg1.get("key1")); // second invocation
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 12", reg1.getEntry("key1").getValue());
    this.txMgr.rollback();

    // TX load repeat: no-initial state: tx load->destroy->load
    this.txMgr.begin();
    assertTrue(!reg1.containsKey("key1"));
    assertEquals("LV 13", reg1.get("key1"));    // first invocation
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 13", reg1.getEntry("key1").getValue());
    reg1.destroy("key1");
    assertEquals("LV 14", reg1.get("key1"));    // second invocation
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 14", reg1.getEntry("key1").getValue());
    this.txMgr.rollback();

    // TX load repeat: no-initial state, tx load->localInvalidate->load
    this.txMgr.begin();
    assertTrue(!reg1.containsKey("key1"));
    assertEquals("LV 15", reg1.get("key1"));    // first invocation
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 15", reg1.getEntry("key1").getValue());
    reg1.localInvalidate("key1");
    assertEquals("LV 16", reg1.get("key1"));    // second invocation
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 16", reg1.getEntry("key1").getValue());
    this.txMgr.rollback();

    // TX load repeat: no-initial, tx load->invalidate->load
    this.txMgr.begin();
    assertTrue(!reg1.containsKey("key1"));
    assertEquals("LV 17", reg1.get("key1"));    // first invocation
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 17", reg1.getEntry("key1").getValue());
    reg1.invalidate("key1");
    assertEquals("LV 18", reg1.get("key1"));    // second invocation
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 18", reg1.getEntry("key1").getValue());
    this.txMgr.rollback();

    // TX load repeat: invalid entry initial state, tx load->get
    reg1.create("key1", null);
    this.txMgr.begin();
    assertTrue(reg1.containsKey("key1"));
    assertNull(reg1.getEntry("key1").getValue());
    assertEquals("LV 19", reg1.get("key1"));    // first invocation
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 19", reg1.getEntry("key1").getValue());
    assertEquals("LV 19", reg1.get("key1"));    // second invocation
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 19", reg1.getEntry("key1").getValue());
    this.txMgr.rollback();

    // TX load repeat: invalid entry initial state, tx load->localDestory->load
    this.txMgr.begin();
    assertTrue(reg1.containsKey("key1"));
    assertNull(reg1.getEntry("key1").getValue());
    assertEquals("LV 20", reg1.get("key1"));    // first invocation
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 20", reg1.getEntry("key1").getValue());
    reg1.localDestroy("key1");
    assertEquals("LV 21", reg1.get("key1"));    // second invocation
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 21", reg1.getEntry("key1").getValue());
    this.txMgr.rollback();

    // TX load repeat: invalid entry initial state: tx load->destroy->load
    this.txMgr.begin();
    assertTrue(reg1.containsKey("key1"));
    assertNull(reg1.getEntry("key1").getValue());
    assertEquals("LV 22", reg1.get("key1"));    // first invocation
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 22", reg1.getEntry("key1").getValue());
    reg1.destroy("key1");
    assertEquals("LV 23", reg1.get("key1"));    // second invocation
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 23", reg1.getEntry("key1").getValue());
    this.txMgr.rollback();

    // TX load repeat: invalid entry initial state, tx load->localInvalidate->load
    this.txMgr.begin();
    assertTrue(reg1.containsKey("key1"));
    assertNull(reg1.getEntry("key1").getValue());
    assertEquals("LV 24", reg1.get("key1"));    // first invocation
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 24", reg1.getEntry("key1").getValue());
    reg1.localInvalidate("key1");
    assertEquals("LV 25", reg1.get("key1"));    // second invocation
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 25", reg1.getEntry("key1").getValue());
    this.txMgr.rollback();

    // TX load repeat: invalid entry initial state, tx load->invalidate->load
    this.txMgr.begin();
    assertTrue(reg1.containsKey("key1"));
    assertNull(reg1.getEntry("key1").getValue());
    assertEquals("LV 26", reg1.get("key1"));    // first invocation
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 26", reg1.getEntry("key1").getValue());
    reg1.invalidate("key1");
    assertEquals("LV 27", reg1.get("key1"));    // second invocation
    assertTrue(reg1.containsKey("key1"));
    assertEquals("LV 27", reg1.getEntry("key1").getValue());
    this.txMgr.rollback();

    // Make sure a load does not conflict with the region being destroyed
    this.txMgr.begin();
    assertEquals("LV 28", reg1.get("key2"));

    this.txMgr.commit();
    reg1.localDestroyRegion(); // non-tx region op
    // reg1 is now destroyed
  }

  /**
   * disabled till non-TX, TX conflict detection is not complete.
   */
  public void DISABLED_TILL_NEW_TX_IMPL_COMPLETE_testStats() throws CacheException {
    final int SLEEP_MS = 250;
    //final int OP_TIME = 0; // ns // changed form 10 to 0 because on fater platforms
    // and low resolution clocks this test will fail.
    final CachePerfStats stats = this.cache.getCachePerfStats();

    class statsValidator {
      long txSuccessLifeTime;
      long txFailedLifeTime;
      long txRollbackLifeTime;
      int txCommits;
      int txFailures;
      int txRollbacks;
      long txCommitTime;
      long txFailureTime;
      long txRollbackTime;
      int txCommitChanges;
      int txFailureChanges;
      int txRollbackChanges;
      
      CachePerfStats stats;
      statsValidator(CachePerfStats stats) {
        this.stats = stats;
      }
      void reset() {
        this.txSuccessLifeTime = this.stats.getTxSuccessLifeTime();
        this.txFailedLifeTime = this.stats.getTxFailedLifeTime();
        this.txRollbackLifeTime = this.stats.getTxRollbackLifeTime();
        this.txCommits = this.stats.getTxCommits();
        this.txFailures = this.stats.getTxFailures();
        this.txRollbacks = this.stats.getTxRollbacks();
        this.txCommitTime = this.stats.getTxCommitTime();
        this.txFailureTime = this.stats.getTxFailureTime();
        this.txRollbackTime = this.stats.getTxRollbackTime();
        this.txCommitChanges = this.stats.getTxCommitChanges();
        this.txFailureChanges = this.stats.getTxFailureChanges();
        this.txRollbackChanges = this.stats.getTxRollbackChanges();
      }
      void setTxSuccessLifeTime(long txSuccessLifeTime) { this.txSuccessLifeTime = txSuccessLifeTime; }
      void setTxFailedLifeTime(long txFailedLifeTime) { this.txFailedLifeTime = txFailedLifeTime; }
      void setTxRollbackLifeTime(long txRollbackLifeTime) { this.txRollbackLifeTime = txRollbackLifeTime; }
      void setTxCommits(int txCommits) { this.txCommits = txCommits; }
      void setTxFailures(int txFailures) { this.txFailures = txFailures; }
      void setTxRollbacks(int txRollbacks) { this.txRollbacks = txRollbacks; }
      void setTxCommitTime(long txCommitTime) { this.txCommitTime = txCommitTime; }
      void setTxFailureTime(long txFailureTime) { this.txFailureTime = txFailureTime; }
      void setTxRollbackTime(long txRollbackTime) { this.txRollbackTime = txRollbackTime; }
      void setTxCommitChanges(int txCommitChanges) { this.txCommitChanges = txCommitChanges; }
      void setTxFailureChanges(int txFailureChanges) { this.txFailureChanges = txFailureChanges; }
      void setTxRollbackChanges(int txRollbackChanges) {this.txRollbackChanges = txRollbackChanges; }
      void assertValid() {
        assertEquals(this.txRollbacks, this.stats.getTxRollbacks());
        assertEquals(this.txRollbackChanges, this.stats.getTxRollbackChanges());
        if (Boolean.getBoolean("gemfire.cache.enable-time-statistics")) {
          assertTrue(this.txRollbackTime <= this.stats.getTxRollbackTime());
          // assertTrue(this.txRollbackLifeTime+((SLEEP_MS-10)*1000000) <= this.stats.getTxRollbackLifeTime());
	  assertTrue("RollbackLifeTime " + this.txRollbackLifeTime  
		    + " is not <= " + this.stats.getTxRollbackLifeTime(), 
              this.txRollbackLifeTime <= this.stats.getTxRollbackLifeTime());
          assertTrue(this.txCommitTime <= this.stats.getTxCommitTime());
          assertTrue(this.txSuccessLifeTime <= this.stats.getTxSuccessLifeTime());
          assertTrue(this.txFailureTime <= this.stats.getTxFailureTime());
          assertTrue("FailedLifeTime " + this.txFailedLifeTime 
              + " is not <= " + this.stats.getTxFailedLifeTime(),
              this.txFailedLifeTime <= this.stats.getTxFailedLifeTime());
        }
        
        assertEquals(this.txCommits, this.stats.getTxCommits());
        assertEquals(this.txCommitChanges, this.stats.getTxCommitChanges());
        
        assertEquals(this.txFailures, this.stats.getTxFailures());
        assertEquals(this.txFailureChanges, this.stats.getTxFailureChanges());
      }
    }

    statsValidator statsVal = new statsValidator(stats);
    // Zero and non-zero rollback stats test
    int i;
    long testRollbackLifeTime=0, testTotalTx=0;
    for(i=0; i<2; ++i) {    
      statsVal.reset();
      statsVal.setTxRollbacks(stats.getTxRollbacks()+1);
      statsVal.setTxRollbackLifeTime(stats.getTxRollbackLifeTime()+((SLEEP_MS-20)*1000000));
      final long beforeBegin = NanoTimer.getTime();
      this.txMgr.begin();
      final long afterBegin = NanoTimer.getTime();
      pause(SLEEP_MS);
      if (i>0) {
        statsVal.setTxRollbackChanges(stats.getTxRollbackChanges()+2);
        this.region.put("stats1", "stats rollback1");    
        this.region.put("stats2", "stats rollback2");    
      }
      statsVal.setTxRollbackTime(stats.getTxRollbackTime());
      final long beforeRollback = NanoTimer.getTime();
      this.txMgr.rollback();
      final long afterRollback = NanoTimer.getTime();
      final long statsRollbackLifeTime = stats.getTxRollbackLifeTime();
      testRollbackLifeTime += beforeRollback - afterBegin;
      // bruce - time based stats are disabled by default
      String p = (String)cache.getDistributedSystem()
          .getProperties().get("gemfire.enable-time-statistics");
      if (p != null && Boolean.getBoolean(p)) {
        assertTrue("Local RollbackLifeTime assertion:  " 
                + testRollbackLifeTime 
      	        + " is not <= " + statsRollbackLifeTime,
        	 testRollbackLifeTime <= statsRollbackLifeTime);
      }
      testTotalTx += afterRollback - beforeBegin;
      final long totalTXMinusRollback = testTotalTx - stats.getTxRollbackTime();
      if (Boolean.getBoolean("gemfire.cache.enable-time-statistics")) {
        assertTrue("Total Tx Minus Rollback assertion:  " 
		 +  totalTXMinusRollback
		 + " is not >= " + statsRollbackLifeTime,
		 totalTXMinusRollback >= statsRollbackLifeTime);
      }
      statsVal.assertValid();
    }

    // Zero and non-zero commit stats test
    for(i=0; i<2; ++i) {
      statsVal.reset();
      statsVal.setTxCommits(stats.getTxCommits()+1);
      statsVal.setTxSuccessLifeTime(stats.getTxSuccessLifeTime()+((SLEEP_MS-10)*1000000));
      this.txMgr.begin();
      pause(SLEEP_MS);
      if (i>0) {
        statsVal.setTxCommitChanges(stats.getTxCommitChanges()+2);
        this.region.put("stats1", "commit1");
        this.region.put("stats2", "commit2");
      }
      try {
        statsVal.setTxCommitTime(stats.getTxCommitTime());
        this.txMgr.commit();
      } catch (ConflictException ex) {
        fail("unexpected " + ex);
      }
      statsVal.assertValid();
    }

    // Non-zero failed commit stats
    TXManagerImpl txMgrImpl = (TXManagerImpl)this.txMgr;
    statsVal.reset();
    statsVal.setTxFailures(stats.getTxFailures()+1);
    statsVal.setTxFailureChanges(stats.getTxFailureChanges()+2);
    statsVal.setTxFailedLifeTime(stats.getTxFailedLifeTime()+((SLEEP_MS-20)*1000000));
    this.region.put("stats3", "stats fail3");
    this.txMgr.begin();
    this.region.put("stats1", "stats fail1");
    this.region.put("stats2", "stats fail2");
    try {
      this.region.create("stats3", "try stats3");
      fail("expected EntryExistsException");
    } catch (EntryExistsException ok) {
    } 
    // begin other tx simulation
    TXStateInterface tx = txMgrImpl.internalSuspend();
    this.region.put("stats1", "stats success1");
    this.region.put("stats2", "stats success2");
    txMgrImpl.resume(tx);
    // end other tx simulation
    pause(SLEEP_MS);
    try {
      statsVal.setTxFailureTime(stats.getTxFailureTime());
      this.txMgr.commit();
      fail("expected ConflictException");
    } catch (ConflictException ex) {
      // expected failure
    }
    statsVal.assertValid();
  }

  public void testCacheStats() throws CacheException {
    CachePerfStats cacheStats = this.cache.getCachePerfStats();
    // quick sanity check to make sure perf stats work non-tx
    int creates;
    int destroys;
    int puts;
    int invalidates;

    creates = cacheStats.getCreates();
    destroys = cacheStats.getDestroys();
    puts = cacheStats.getPuts();
    invalidates = cacheStats.getInvalidates();
    this.region.create("key1", "value1");
    assertEquals(creates+1, cacheStats.getCreates());
    assertEquals(destroys, cacheStats.getDestroys());
    assertEquals(puts+1, cacheStats.getPuts());
    assertEquals(invalidates, cacheStats.getInvalidates());

    creates = cacheStats.getCreates();
    destroys = cacheStats.getDestroys();
    puts = cacheStats.getPuts();
    invalidates = cacheStats.getInvalidates();
    this.region.put("key1", "value2");
    assertEquals(creates, cacheStats.getCreates());
    assertEquals(destroys, cacheStats.getDestroys());
    assertEquals(puts+1, cacheStats.getPuts());
    assertEquals(invalidates, cacheStats.getInvalidates());

    creates = cacheStats.getCreates();
    destroys = cacheStats.getDestroys();
    puts = cacheStats.getPuts();
    invalidates = cacheStats.getInvalidates();
    this.region.invalidate("key1");
    assertEquals(creates, cacheStats.getCreates());
    assertEquals(destroys, cacheStats.getDestroys());
    assertEquals(puts, cacheStats.getPuts());
    assertEquals(invalidates+1, cacheStats.getInvalidates());

    creates = cacheStats.getCreates();
    destroys = cacheStats.getDestroys();
    puts = cacheStats.getPuts();
    invalidates = cacheStats.getInvalidates();
    this.region.destroy("key1");
    assertEquals(creates, cacheStats.getCreates());
    assertEquals(destroys+1, cacheStats.getDestroys());
    assertEquals(puts, cacheStats.getPuts());
    assertEquals(invalidates, cacheStats.getInvalidates());

    // now make sure they do not change from tx ops and from rollbacks
    creates = cacheStats.getCreates();
    destroys = cacheStats.getDestroys();
    puts = cacheStats.getPuts();
    invalidates = cacheStats.getInvalidates();
    this.txMgr.begin();
    this.region.create("key1", "value1");
    this.region.put("key1", "value2");
    this.region.invalidate("key1");
    this.region.put("key1", "value3");
    this.region.localInvalidate("key1");
    this.region.put("key1", "value4");
    this.region.localDestroy("key1");
    this.region.put("key1", "value5");
    this.region.destroy("key1");
    this.txMgr.rollback();
    assertEquals(creates, cacheStats.getCreates());
    assertEquals(destroys, cacheStats.getDestroys());
    assertEquals(puts, cacheStats.getPuts());
    assertEquals(invalidates, cacheStats.getInvalidates());

    // now make sure they do change when a commit is done
    creates = cacheStats.getCreates();
    destroys = cacheStats.getDestroys();
    puts = cacheStats.getPuts();
    invalidates = cacheStats.getInvalidates();
    this.txMgr.begin();
    this.region.create("key1", "value1");
    assertEquals(creates, cacheStats.getCreates());
    assertEquals(puts, cacheStats.getPuts());
    this.txMgr.commit();
    assertEquals(creates+1, cacheStats.getCreates());
    assertEquals(destroys, cacheStats.getDestroys());
    assertEquals(puts+1, cacheStats.getPuts());
    assertEquals(invalidates, cacheStats.getInvalidates());

    creates = cacheStats.getCreates();
    destroys = cacheStats.getDestroys();
    puts = cacheStats.getPuts();
    invalidates = cacheStats.getInvalidates();
    this.txMgr.begin();
    this.region.put("key1", "value1");
    assertEquals(puts, cacheStats.getPuts());
    this.txMgr.commit();
    assertEquals(creates, cacheStats.getCreates());
    assertEquals(destroys, cacheStats.getDestroys());
    assertEquals(puts+1, cacheStats.getPuts());
    assertEquals(invalidates, cacheStats.getInvalidates());

    creates = cacheStats.getCreates();
    destroys = cacheStats.getDestroys();
    puts = cacheStats.getPuts();
    invalidates = cacheStats.getInvalidates();
    this.txMgr.begin();
    this.region.localInvalidate("key1");
    assertEquals(invalidates, cacheStats.getInvalidates());
    this.txMgr.commit();
    assertEquals(creates, cacheStats.getCreates());
    assertEquals(destroys, cacheStats.getDestroys());
    assertEquals(puts, cacheStats.getPuts());
    assertEquals(invalidates+1, cacheStats.getInvalidates());

    creates = cacheStats.getCreates();
    destroys = cacheStats.getDestroys();
    puts = cacheStats.getPuts();
    invalidates = cacheStats.getInvalidates();
    this.txMgr.begin();
    this.region.localDestroy("key1");
    assertEquals(destroys, cacheStats.getDestroys());
    this.txMgr.commit();
    assertEquals(creates, cacheStats.getCreates());
    assertEquals(destroys+1, cacheStats.getDestroys());
    assertEquals(puts, cacheStats.getPuts());
    assertEquals(invalidates, cacheStats.getInvalidates());
  }

  private void pause(int msWait) {
    try {
      Thread.sleep(msWait);
    } catch (InterruptedException ignore) {
      fail("interrupted");
    }
  }
  
  public void testSuspendResume() {
    TXManagerImpl txMgrImpl = (TXManagerImpl)this.txMgr;
    assertTrue(!this.txMgr.exists());
    assertEquals(null, txMgrImpl.internalSuspend());
    TXStateProxy txProxy = null;
    txMgrImpl.resume(txProxy);
    assertTrue(!this.txMgr.exists());

    this.txMgr.begin();
    TransactionId origId = this.txMgr.getTransactionId();
    assertTrue(this.txMgr.exists());
    {
      TXStateInterface tx = txMgrImpl.internalSuspend();
      assertTrue(!this.txMgr.exists());
      this.txMgr.begin();
      try {
        txMgrImpl.resume(tx);
        fail("expected IllegalTransactionStateException");
      } catch (IllegalTransactionStateException expected) {
      }
      this.txMgr.rollback();
      assertTrue(!this.txMgr.exists());
      txMgrImpl.resume(tx);
    }
    assertTrue(this.txMgr.exists());
    assertEquals(origId, this.txMgr.getTransactionId());
    this.txMgr.rollback();
  }

  public void testPublicSuspendResume() {
    CacheTransactionManager txMgr = this.txMgr;
    assertTrue(!this.txMgr.exists());
    assertEquals(null, txMgr.suspend());
    TransactionId txId = null;
    try {
      txMgr.resume(txId);
      fail("expected IllegalStateException");
    } catch (IllegalStateException e) {
    }
    assertTrue(!this.txMgr.exists());

    this.txMgr.begin();
    TransactionId origId = this.txMgr.getTransactionId();
    assertTrue(this.txMgr.exists());
    {
      TransactionId tx = txMgr.suspend();
      assertTrue(!this.txMgr.exists());
      this.txMgr.begin();
      try {
        txMgr.resume(tx);
        fail("expected IllegalStateException");
      } catch (IllegalStateException expected) {
      }
      this.txMgr.rollback();
      assertTrue(!this.txMgr.exists());
      txMgr.resume(tx);
    }
    assertTrue(this.txMgr.exists());
    assertEquals(origId, this.txMgr.getTransactionId());
    this.txMgr.rollback();
    
  }
  
  public void testCheckNoTX() {
    {
      AttributesFactory af = new AttributesFactory();
      af.setScope(Scope.GLOBAL);
      Region gr = null;
      try {
        gr = this.cache.createRegion("GLOBALTXTest", af.create());
      } catch (CacheException ex) {
        fail("did not expect " + ex);
      }
      try {
        gr.put("foo", "bar1");
      } catch (Exception ex) {
        fail("did not expect " + ex);
      }
      this.txMgr.begin();
      try {
        gr.put("foo", "bar2");
        fail("expected UnsupportedOperationException");
      } catch (UnsupportedOperationException expected) {
      } catch (Exception ex) {
        fail("did not expect " + ex);
      }
      this.txMgr.rollback();

      gr.destroyRegion();

      // should work for a GLOBAL REPLICATED region
      // TODO: TX: disallowed for global regions; find out why
      /*
      af.setScope(Scope.GLOBAL);
      af.setDataPolicy(DataPolicy.REPLICATE);
      try {
        gr = this.cache.createRegion("GLOBALTXTest", af.create());
      } catch (CacheException ex) {
        fail("did not expect " + ex);
      }
      try {
        gr.put("foo", "bar1");
      } catch (Exception ex) {
        fail("did not expect " + ex);
      }
      this.txMgr.begin();
      gr.put("foo", "bar2");
      assertEquals("bar2", gr.get("foo"));
      this.txMgr.commit();
      assertEquals("bar2", gr.get("foo"));
      // now with rollback
      this.txMgr.begin();
      gr.put("foo", "bar3");
      assertEquals("bar3", gr.get("foo"));
      this.txMgr.rollback();
      assertEquals("bar2", gr.get("foo"));
      */
    }


    {
      DiskStoreFactory dsf = this.cache.createDiskStoreFactory();
      dsf.create("testCheckNoTX");
      AttributesFactory af = new AttributesFactory();
      af.setScope(Scope.LOCAL);
      af.setDataPolicy(DataPolicy.PERSISTENT_REPLICATE);
      af.setDiskStoreName("testCheckNoTX");
      Region dr = null;
      try {
        dr = this.cache.createRegion("DiskTXTest", af.create());
      } catch (CacheException ex) {
        fail("did not expect " + ex);
      }
      try {
        try {
          dr.put("foo", "bar1");
        } catch (Exception ex) {
          fail("did not expect " + ex);
        }
        this.txMgr.begin();
        try {
          dr.put("foo", "bar2");
          fail("expected UnsupportedOperationException");
        } catch (UnsupportedOperationException expected) {
        } catch (Exception ex) {
          fail("did not expect " + ex);
        }
        this.txMgr.rollback();
      } finally {
        dr.localDestroyRegion();
      }
    }
  }

  public void DISABLED_TILL_NEW_TX_IMPL_COMPLETE_testRepeatableRead() throws CacheException {
    final TXManagerImpl txMgrImpl = (TXManagerImpl)this.txMgr;
    TXStateInterface tx;
    
    // try repeating a get and make sure it doesn't cause a conflict
    this.region.put("key1", "value1"); // non-tx
    txMgrImpl.begin();
    assertEquals("value1", this.region.get("key1"));
    tx = txMgrImpl.internalSuspend();
    this.region.put("key1", "value2"); // non-tx
    txMgrImpl.resume(tx);

    assertEquals("value1", this.region.get("key1"));
    txMgrImpl.commit();
    
    // try repeating a get and modify the entry and make sure it causes a conflict
    this.region.put("key1", "value1"); // non-tx
    txMgrImpl.begin();
    assertEquals("value1", this.region.get("key1"));
    tx = txMgrImpl.internalSuspend();
    this.region.put("key1", "value2"); // non-tx
    txMgrImpl.resume(tx);
    assertEquals("value1", this.region.get("key1"));
    this.region.put("key1", "value3");
    assertEquals("value3", this.region.get("key1"));
    try {
      txMgrImpl.commit();
      fail("expected ConflictException");
    } catch (ConflictException ex) {
    }

    // try repeating a getEntry and make sure it doesn't cause a conflict
    this.region.put("key1", "value1"); // non-tx
    txMgrImpl.begin();
    this.region.getEntry("key1");
    tx = txMgrImpl.internalSuspend();
    this.region.put("key1", "value2"); // non-tx
    txMgrImpl.resume(tx);

    assertEquals("value1", this.region.get("key1"));
    txMgrImpl.commit();
    
    // try repeating a getEntry and modify the entry and make sure it causes a conflict
    this.region.put("key1", "value1"); // non-tx
    txMgrImpl.begin();
    this.region.getEntry("key1");
    tx = txMgrImpl.internalSuspend();
    this.region.put("key1", "value2"); // non-tx
    txMgrImpl.resume(tx);
    this.region.put("key1", "value3");
    try {
      txMgrImpl.commit();
      fail("expected ConflictException");
    } catch (ConflictException ex) {
    }

    // try RR when entry fetched using entrySet
    this.region.put("key1", "value1"); // non-tx
    txMgrImpl.begin();
    this.region.get("key1"); // bootstrap the tx, entrySet does not
    this.region.entrySet(false).iterator().next();
    tx = txMgrImpl.internalSuspend();
    this.region.put("key1", "value2"); // non-tx
    txMgrImpl.resume(tx);

    assertEquals("value1", this.region.get("key1"));
    txMgrImpl.commit();
    
    // try RRW->CONFLICT when entry fetched using entrySet
    this.region.put("key1", "value1"); // non-tx
    txMgrImpl.begin();
    this.region.get("key1"); // bootstrap the tx, entrySet does not
    this.region.entrySet(false).iterator().next();
    tx = txMgrImpl.internalSuspend();
    this.region.put("key1", "value2"); // non-tx
    txMgrImpl.resume(tx);
    assertEquals("value1", this.region.get("key1"));
    this.region.put("key1", "value3");
    try {
      txMgrImpl.commit();
      fail("expected ConflictException");
    } catch (ConflictException ex) {
    }

    // try containsKey
    this.region.put("key1", "value1"); // non-tx
    txMgrImpl.begin();
    assertEquals(true, this.region.containsKey("key1"));
    tx = txMgrImpl.internalSuspend();
    this.region.remove("key1"); // non-tx
    txMgrImpl.resume(tx);
    assertEquals(true, this.region.containsKey("key1"));
    txMgrImpl.commit();
    this.region.put("key1", "value1"); // non-tx
    txMgrImpl.begin();
    assertEquals(true, this.region.containsKey("key1"));
    tx = txMgrImpl.internalSuspend();
    this.region.remove("key1"); // non-tx
    txMgrImpl.resume(tx);
    assertEquals(true, this.region.containsKey("key1"));
    this.region.put("key1", "value3");
    assertEquals(true, this.region.containsKey("key1"));
    try {
      txMgrImpl.commit();
      fail("expected ConflictException");
    } catch (ConflictException ex) {
    }
    // try containsValueForKey
    this.region.put("key1", "value1"); // non-tx
    txMgrImpl.begin();
    assertEquals(true, this.region.containsValueForKey("key1"));
    tx = txMgrImpl.internalSuspend();
    this.region.remove("key1"); // non-tx
    txMgrImpl.resume(tx);
    assertEquals(true, this.region.containsValueForKey("key1"));
    txMgrImpl.commit();
    this.region.put("key1", "value1"); // non-tx
    txMgrImpl.begin();
    assertEquals(true, this.region.containsValueForKey("key1"));
    tx = txMgrImpl.internalSuspend();
    this.region.remove("key1"); // non-tx
    txMgrImpl.resume(tx);
    assertEquals(true, this.region.containsValueForKey("key1"));
    this.region.put("key1", "value3");
    assertEquals(true, this.region.containsValueForKey("key1"));
    try {
      txMgrImpl.commit();
      fail("expected ConflictException");
    } catch (ConflictException ex) {
    }

    // now try the same things but with no entry in committed state at
    // the time of the first read
    // try repeating a get and make sure it doesn't cause a conflict
    this.region.remove("key1"); // non-tx
    txMgrImpl.begin();
    assertEquals(null, this.region.get("key1"));
    tx = txMgrImpl.internalSuspend();
    this.region.put("key1", "value2"); // non-tx
    txMgrImpl.resume(tx);

    assertEquals(null, this.region.get("key1"));
    txMgrImpl.commit();
    
    // try repeating a get and modify the entry and make sure it causes a conflict
    this.region.remove("key1"); // non-tx
    txMgrImpl.begin();
    assertEquals(null, this.region.get("key1"));
    tx = txMgrImpl.internalSuspend();
    this.region.put("key1", "value2"); // non-tx
    txMgrImpl.resume(tx);
    assertEquals(null, this.region.get("key1"));
    this.region.put("key1", "value3");
    assertEquals("value3", this.region.get("key1"));
    try {
      txMgrImpl.commit();
      fail("expected ConflictException");
    } catch (ConflictException ex) {
    }

    // try repeating a getEntry and make sure it doesn't cause a conflict
    this.region.remove("key1"); // non-tx
    txMgrImpl.begin();
    assertEquals(null, this.region.getEntry("key1"));
    tx = txMgrImpl.internalSuspend();
    this.region.put("key1", "value2"); // non-tx
    txMgrImpl.resume(tx);

    assertEquals(null, this.region.getEntry("key1"));
    txMgrImpl.commit();
    
    // try repeating a getEntry and modify the entry and make sure it causes a conflict
    this.region.remove("key1"); // non-tx
    txMgrImpl.begin();
    assertEquals(null, this.region.getEntry("key1"));
    tx = txMgrImpl.internalSuspend();
    this.region.put("key1", "value2"); // non-tx
    txMgrImpl.resume(tx);
    assertEquals(null, this.region.getEntry("key1"));
    this.region.put("key1", "value3");
    try {
      txMgrImpl.commit();
      fail("expected ConflictException");
    } catch (ConflictException ex) {
    }

    // try containsKey
    this.region.remove("key1"); // non-tx
    txMgrImpl.begin();
    assertEquals(false, this.region.containsKey("key1"));
    tx = txMgrImpl.internalSuspend();
    this.region.put("key1", "value2"); // non-tx
    txMgrImpl.resume(tx);
    assertEquals(false, this.region.containsKey("key1"));
    txMgrImpl.commit();
    this.region.remove("key1"); // non-tx
    txMgrImpl.begin();
    assertEquals(false, this.region.containsKey("key1"));
    tx = txMgrImpl.internalSuspend();
    this.region.put("key1", "value2"); // non-tx
    txMgrImpl.resume(tx);
    assertEquals(false, this.region.containsKey("key1"));
    this.region.put("key1", "value3");
    assertEquals(true, this.region.containsKey("key1"));
    try {
      txMgrImpl.commit();
      fail("expected ConflictException");
    } catch (ConflictException ex) {
    }

    // try containsValueForKey
    this.region.remove("key1"); // non-tx
    txMgrImpl.begin();
    assertEquals(false, this.region.containsValueForKey("key1"));
    tx = txMgrImpl.internalSuspend();
    this.region.put("key1", "value2"); // non-tx
    txMgrImpl.resume(tx);
    assertEquals(false, this.region.containsValueForKey("key1"));
    txMgrImpl.commit();
    this.region.remove("key1"); // non-tx
    txMgrImpl.begin();
    assertEquals(false, this.region.containsValueForKey("key1"));
    tx = txMgrImpl.internalSuspend();
    this.region.put("key1", "value2"); // non-tx
    txMgrImpl.resume(tx);
    assertEquals(false, this.region.containsValueForKey("key1"));
    this.region.put("key1", "value3");
    assertEquals(true, this.region.containsValueForKey("key1"));
    try {
      txMgrImpl.commit();
      fail("expected ConflictException");
    } catch (ConflictException ex) {
    }

    // try an invalidate of an already invalid entry
    this.region.remove("key1"); // non-tx
    this.region.create("key1", null); // non-tx
    txMgrImpl.begin();
    this.region.get("key1");
    this.region.localInvalidate("key1"); // should be a noop since it is already invalid
    tx = txMgrImpl.internalSuspend();
    this.region.remove("key1"); // non-tx
    txMgrImpl.resume(tx);
    txMgrImpl.commit();
    assertEquals(false, this.region.containsKey("key1"));

    // make sure a noop invalidate is repeatable read
    this.region.remove("key1"); // non-tx
    this.region.create("key1", null); // non-tx
    txMgrImpl.begin();
    this.region.localInvalidate("key1"); // should be a noop since it is already invalid
    tx = txMgrImpl.internalSuspend();
    this.region.remove("key1"); // non-tx
    txMgrImpl.resume(tx);
    assertEquals(true, this.region.containsKey("key1"));
    assertEquals(false, this.region.containsValueForKey("key1"));
    txMgrImpl.commit();
    assertEquals(false, this.region.containsKey("key1"));

    // make sure a destroy that throws entryNotFound is repeatable read
    this.region.remove("key1"); // non-tx
    txMgrImpl.begin();
    try {
      this.region.localDestroy("key1");
      fail("expected EntryNotFoundException");
    } catch (EntryNotFoundException expected) {
    }
    tx = txMgrImpl.internalSuspend();
    this.region.create("key1", "value1"); // non-tx
    txMgrImpl.resume(tx);
    assertEquals(false, this.region.containsKey("key1"));
    txMgrImpl.commit();
    assertEquals(true, this.region.containsKey("key1"));
    this.region.remove("key1"); // non-tx

    // make sure a create that throws entryExists is repeatable read
    this.region.create("key1", "non-tx-value1"); // non-tx
    txMgrImpl.begin();
    try {
      this.region.create("key1", "value1");
      fail("expected EntryExistsException");
    } catch (EntryExistsException expected) {
    }
    tx = txMgrImpl.internalSuspend();
    this.region.remove("key1"); // non-tx
    txMgrImpl.resume(tx);
    assertEquals(true, this.region.containsKey("key1"));
    txMgrImpl.commit();
    assertEquals(false, this.region.containsKey("key1"));
  }

  /**
   * disabled till non-TX, TX conflict detection is not complete.
   */
  public void DISABLED_TILL_NEW_TX_IMPL_COMPLETE_testConflicts() throws CacheException {
    final TXManagerImpl txMgrImpl = (TXManagerImpl)this.txMgr;
    TXStateInterface tx;
    // try a put with no conflict to show that commit works
    txMgrImpl.begin();
    this.region.put("key1", "value1");
    txMgrImpl.commit();
    assertEquals("value1", this.region.get("key1"));
    this.region.localDestroy("key1");

    // now try a put with a conflict and make sure it is detected
    txMgrImpl.begin();
    this.region.put("key1", "value1");
    tx = txMgrImpl.internalSuspend();
    this.region.put("key1", "value2"); // do a non-tx put to force conflict
    txMgrImpl.resume(tx);
    try {
      txMgrImpl.commit();
      fail("expected ConflictException");
    } catch (ConflictException ex) {
    }
    assertEquals("value2", this.region.get("key1"));
    this.region.localDestroy("key1");

    // slightly difference version where value already exists in cmt state
    this.region.put("key1", "value0");
    txMgrImpl.begin();
    this.region.put("key1", "value1");
    txMgrImpl.commit();
    assertEquals("value1", this.region.get("key1"));
    this.region.localDestroy("key1");

    // now the conflict
    this.region.put("key1", "value0");
    txMgrImpl.begin();
    this.region.put("key1", "value1");
    tx = txMgrImpl.internalSuspend();
    this.region.put("key1", "value2"); // do a non-tx put to force conflict
    txMgrImpl.resume(tx);
    try {
      txMgrImpl.commit();
      fail("expected ConflictException");
    } catch (ConflictException ex) {
    }
    assertEquals("value2", this.region.get("key1"));
    this.region.localDestroy("key1");

    // now test create
    txMgrImpl.begin();
    this.region.create("key1", "value1");
    txMgrImpl.commit();
    assertEquals("value1", this.region.get("key1"));
    this.region.localDestroy("key1");

    // now try a create with a conflict and make sure it is detected
    txMgrImpl.begin();
    this.region.create("key1", "value1");
    tx = txMgrImpl.internalSuspend();
    this.region.put("key1", "value2"); // do a non-tx put to force conflict
    txMgrImpl.resume(tx);
    try {
      txMgrImpl.commit();
      fail("expected ConflictException");
    } catch (ConflictException ex) {
    }
    assertEquals("value2", this.region.get("key1"));
    this.region.localDestroy("key1");

    // test localInvalidate
    this.region.put("key1", "value0");
    txMgrImpl.begin();
    this.region.localInvalidate("key1");
    txMgrImpl.commit();
    assertTrue(this.region.containsKey("key1")
               && !this.region.containsValueForKey("key1"));
    this.region.localDestroy("key1");

    // now the conflict
    this.region.put("key1", "value0");
    txMgrImpl.begin();
    this.region.localInvalidate("key1");
    tx = txMgrImpl.internalSuspend();
    this.region.put("key1", "value2"); // do a non-tx put to force conflict
    txMgrImpl.resume(tx);
    try {
      txMgrImpl.commit();
      fail("expected ConflictException");
    } catch (ConflictException ex) {
    }
    assertEquals("value2", this.region.get("key1"));
    this.region.localDestroy("key1");

    // test invalidate
    this.region.put("key1", "value0");
    txMgrImpl.begin();
    this.region.invalidate("key1");
    txMgrImpl.commit();
    assertTrue(this.region.containsKey("key1")
               && !this.region.containsValueForKey("key1"));
    this.region.localDestroy("key1");

    // now the conflict
    this.region.put("key1", "value0");
    txMgrImpl.begin();
    this.region.invalidate("key1");
    tx = txMgrImpl.internalSuspend();
    this.region.put("key1", "value2"); // do a non-tx put to force conflict
    txMgrImpl.resume(tx);
    try {
      txMgrImpl.commit();
      fail("expected ConflictException");
    } catch (ConflictException ex) {
    }
    assertEquals("value2", this.region.get("key1"));
    this.region.localDestroy("key1");

    // check C + DD is a NOOP that still gets conflict if non-tx entry created */
    this.txMgr.begin();
    this.region.create("newKey", "valueTX");
    tx = txMgrImpl.internalSuspend();
    this.region.create("newKey", "valueNONTX");
    txMgrImpl.resume(tx);
    this.region.destroy("newKey");
    assertTrue(!this.region.containsKey("key1"));
    try {
      txMgrImpl.commit();
      fail("expected ConflictException");
    } catch (ConflictException ex) {
    }
    assertEquals("valueNONTX", this.region.get("newKey"));
    this.region.localDestroy("newKey");

    // check C + LD is a NOOP that still gets conflict if non-tx entry created */
    this.txMgr.begin();
    this.region.create("newKey", "valueTX");
    tx = txMgrImpl.internalSuspend();
    this.region.create("newKey", "valueNONTX");
    txMgrImpl.resume(tx);
    this.region.localDestroy("newKey");
    assertTrue(!this.region.containsKey("key1"));
    try {
      txMgrImpl.commit();
      fail("expected ConflictException");
    } catch (ConflictException ex) {
    }
    assertEquals("valueNONTX", this.region.get("newKey"));
    this.region.localDestroy("newKey");

    // test localDestroy
    this.region.put("key1", "value0");
    txMgrImpl.begin();
    this.region.localDestroy("key1");
    txMgrImpl.commit();
    assertTrue(!this.region.containsKey("key1"));

    // now the conflict
    this.region.put("key1", "value0");
    txMgrImpl.begin();
    this.region.localDestroy("key1");
    tx = txMgrImpl.internalSuspend();
    this.region.put("key1", "value2"); // do a non-tx put to force conflict
    txMgrImpl.resume(tx);
    try {
      txMgrImpl.commit();
      fail("expected ConflictException");
    } catch (ConflictException ex) {
    }
    assertEquals("value2", this.region.get("key1"));
    this.region.localDestroy("key1");

    // test destroy
    this.region.put("key1", "value0");
    txMgrImpl.begin();
    this.region.destroy("key1");
    txMgrImpl.commit();
    assertTrue(!this.region.containsKey("key1"));

    // now the conflict
    this.region.put("key1", "value0");
    txMgrImpl.begin();
    this.region.destroy("key1");
    tx = txMgrImpl.internalSuspend();
    this.region.put("key1", "value2"); // do a non-tx put to force conflict
    txMgrImpl.resume(tx);
    try {
      txMgrImpl.commit();
      fail("expected ConflictException");
    } catch (ConflictException ex) {
    }
    assertEquals("value2", this.region.get("key1"));
    this.region.localDestroy("key1");

    checkUserAttributeConflict(txMgrImpl);

    // make sure non-tx local-invalidate followed by invalidate
    // does not cause conflict
    this.region.create("key1", "val1");
    this.region.localInvalidate("key1");
    txMgrImpl.begin();
    this.region.put("key1", "txVal1");
    tx = txMgrImpl.internalSuspend();
    this.region.invalidate("key1");
    txMgrImpl.resume(tx);
    txMgrImpl.commit();
    assertEquals("txVal1", this.region.getEntry("key1").getValue());
    this.region.destroy("key1");
    
    // now try a put and a region destroy.
    txMgrImpl.begin();
    this.region.create("key1", "value1");
    TXStateInterface tis = txMgrImpl.internalSuspend();
    this.region.localDestroyRegion(); // non-tx
    txMgrImpl.resume(tis);
    
    try {
      txMgrImpl.commit();
      fail("expected ConflictException");
    } catch (ConflictException ex) {
    }
    // this.region is now destroyed

  }

  /**
   * @param txMgrImpl
   */
  protected void checkUserAttributeConflict(final CacheTransactionManager txMgrImpl) {
    { // now check entry user attribute conflict checking
      this.region.put("key1", "value0");
      Region.Entry cmtre = this.region.getEntry("key1");
      assertEquals(null, cmtre.getUserAttribute());
      txMgrImpl.begin();
      Region.Entry txre = this.region.getEntry("key1");
      txre.setUserAttribute("uaValue1");
      txMgrImpl.commit();
      assertEquals("uaValue1", cmtre.getUserAttribute());
      this.region.localDestroy("key1");

      this.region.put("key1", "value0");
      cmtre = this.region.getEntry("key1");
      assertEquals("value0", cmtre.getValue());
      assertEquals(null, cmtre.getUserAttribute());
      this.txMgr.begin();
      txre = this.region.getEntry("key1");
      assertEquals("value0", txre.getValue());
      this.region.put("key1", "valueTX");
      assertEquals("valueTX", txre.getValue());
      assertEquals("value0", cmtre.getValue());
      assertEquals(null, txre.getUserAttribute());
      txre.setUserAttribute("uaValue1");
      assertEquals("uaValue1", txre.getUserAttribute());
      assertEquals(null, cmtre.getUserAttribute());
      cmtre.setUserAttribute("uaValue2");
      assertEquals("uaValue2", cmtre.getUserAttribute());
      assertEquals("uaValue1", txre.getUserAttribute());
      try {
        txMgrImpl.commit();
        fail("expected ConflictException");
      } catch (ConflictException ex) {
      }
      try {
        txre.getValue();
        fail("expected IllegalTransactionStateException");
      } catch (IllegalTransactionStateException ok) {
      }
      try {
        txre.isDestroyed();
        fail("expected IllegalTransactionStateException");
      } catch (IllegalTransactionStateException ok) {
      }
      try {
        txre.getUserAttribute();
        fail("expected IllegalTransactionStateException");
      } catch (IllegalTransactionStateException ok) {
      }
      try {
        txre.setUserAttribute("foo");
        fail("expected IllegalTransactionStateException");
      } catch (IllegalTransactionStateException ok) {
      }
      assertEquals("uaValue2", cmtre.getUserAttribute());
      assertEquals("value0", cmtre.getValue());
      this.region.localDestroy("key1");
    }
  }
  public void testNoopInvalidates() throws CacheException {
    final CachePerfStats stats = this.cache.getCachePerfStats();
    TransactionListener tl = new TransactionListenerAdapter() {
      @Override
      public void afterRollback(TransactionEvent event) {
        te = event;
      }
    };
    this.txMgr.addListener(tl);
    // Make sure invalidates done on invalid entries are noops

    { // distributed invalidate
      // first make sure invalidate is counted as a change
      int txRollbackChanges = stats.getTxRollbackChanges();
      this.region.create("key1", "value1");
      this.txMgr.begin();
      this.region.invalidate("key1");
      this.txMgr.rollback();
      assertEquals(txRollbackChanges+1, stats.getTxRollbackChanges());
      assertEquals(1, te.getEvents().size());
      this.region.destroy("key1");

      this.region.create("key1", "value1");
      this.txMgr.begin();
      this.region.invalidate("key1");
      this.txMgr.commit();
      assertEquals(1, te.getEvents().size());
      this.region.destroy("key1");
      
      // now make sure a committed entry that is invalid is not counted as a change
      txRollbackChanges = stats.getTxRollbackChanges();
      this.region.create("key1", "value1");
      this.region.invalidate("key1");
      this.txMgr.begin();
      this.region.invalidate("key1");
      this.txMgr.rollback();
      assertEquals(txRollbackChanges, stats.getTxRollbackChanges());
      assertEquals(0, te.getEvents().size());
      this.region.destroy("key1");

      this.region.create("key1", "value1");
      this.region.invalidate("key1");
      this.txMgr.begin();
      this.region.invalidate("key1");
      this.txMgr.commit();
      assertEquals(0, te.getEvents().size());
      this.region.destroy("key1");

      
      // now make sure that multiple invalidates of same entry are a single change
      txRollbackChanges = stats.getTxRollbackChanges();
      this.region.create("key1", "value1");
      this.txMgr.begin();
      this.region.invalidate("key1");
      this.region.invalidate("key1");
      this.region.invalidate("key1");
      this.txMgr.rollback();
      assertEquals(txRollbackChanges+1, stats.getTxRollbackChanges());
      assertEquals(1, te.getEvents().size());
      this.region.destroy("key1");
      
      this.region.create("key1", "value1");
      this.txMgr.begin();
      this.region.invalidate("key1");
      this.region.invalidate("key1");
      this.region.invalidate("key1");
      this.txMgr.commit();
      assertEquals(1, te.getEvents().size());
      this.region.destroy("key1");
    }

    { // local invalidate
      // first make sure invalidate is counted as a change
      int txRollbackChanges = stats.getTxRollbackChanges();
      this.region.create("key1", "value1");
      this.txMgr.begin();
      this.region.localInvalidate("key1");
      this.txMgr.rollback();
      assertEquals(txRollbackChanges+1, stats.getTxRollbackChanges());
      this.region.destroy("key1");

      // now make sure a committed entry that is invalid is not counted as a change
      txRollbackChanges = stats.getTxRollbackChanges();
      this.region.create("key1", "value1");
      this.region.localInvalidate("key1");
      this.txMgr.begin();
      this.region.localInvalidate("key1");
      this.txMgr.rollback();
      assertEquals(txRollbackChanges, stats.getTxRollbackChanges());
      this.region.destroy("key1");

      // now make sure that multiple localInvalidates of same entry are a single change
      txRollbackChanges = stats.getTxRollbackChanges();
      this.region.create("key1", "value1");
      this.txMgr.begin();
      this.region.localInvalidate("key1");
      this.region.localInvalidate("key1");
      this.region.localInvalidate("key1");
      this.txMgr.rollback();
      assertEquals(txRollbackChanges+1, stats.getTxRollbackChanges());
      this.region.destroy("key1");
    }
  }

  final static void clearRegion(Region r) throws TimeoutException {
    Iterator kI = r.keySet().iterator();
    try {
      while(kI.hasNext()) {
        r.destroy(kI.next());
      }
    } catch (CacheException ce) {
      fail("clearRegion operation failed");
    } 
  }

  final static int LRUENTRY_NULL = 0;
  final static int LRUENTRY_STRING = 1;
  final static int LRUENTRY_INTEGER = 2;
  final static int LRUENTRY_LONG = 3;
  final static int LRUENTRY_DOUBLE = 4;
  final static void assertLRUEntries(Set entries, int size, String keyPrefix, int instanceId) {
    Iterator entItr = entries.iterator();
    int eSize = 0;
    while(entItr.hasNext()) {
      Region.Entry re = (Region.Entry) entItr.next();
      switch (instanceId) {
      case LRUENTRY_NULL:
          assertNull(re.getValue());
          break;
      case LRUENTRY_STRING:
          assertTrue(re.getValue() instanceof String);
          break;
      case LRUENTRY_INTEGER:
          assertTrue(re.getValue() instanceof Integer);
          break;
      case LRUENTRY_LONG:
          assertTrue(re.getValue() instanceof Long);
          break;
      case LRUENTRY_DOUBLE:
          assertTrue(re.getValue() instanceof Double);
          break;
      default:
        fail("Unknown instance type in assertLRUEntries: " + instanceId);
      }
      String reKey = (String) re.getKey();
      assertTrue(reKey.startsWith(keyPrefix));
      ++eSize;
    }
    assertEquals(size, eSize);
  }

  /**
   * [sumedh] This test need major changes to correct its expectations with new
   * TX model on conflict detection with LRU. Now we detect conflicts eagerly
   * regardless of whether the entry will be eventually evicted/expired or not.
   * Essentially we want the conflict detection to go on regardless of eventual
   * eviction/expiration of new transactional entries, and just ensure that
   * after commit the entries are getting correctly evicted/expired as per the
   * policies.
   */
  public void DISABLED_TILL_NEW_TX_IMPL_COMPLETE_testEviction() throws CacheException {
    final int lruSize = 8;
    AttributesFactory af = new AttributesFactory();
    af.setEvictionAttributes(EvictionAttributes.createLRUEntryAttributes(lruSize, EvictionAction.LOCAL_DESTROY));
    af.setScope(Scope.LOCAL);
    Region lruRegion = this.cache.createRegion("TXLRUTest", af.create());

    // Non-TX LRU verification
    assertEquals(0, lruRegion.entrySet(false).size());
    int numToPut = lruSize+2;
    for(int i=0; i lruSize);
      assertNull(lruRegion.get("non-tx key0"));
      assertLRUEntries(lruRegion.entrySet(false), lruSize, "key", LRUENTRY_INTEGER);

      txMgrImpl.resume(tx1);
      assertLRUEntries(lruRegion.entrySet(false), numToPut, "key", LRUENTRY_LONG);
      // Check to make sure no conflict was caused by non-TX put evictions
      // This should remove all references for each committed entry
      this.txMgr.commit();
      assertLRUEntries(lruRegion.entrySet(false), lruSize, "key", LRUENTRY_LONG);
      txMgrImpl.resume(tx2);
      assertLRUEntries(lruRegion.entrySet(false), numToPut, "key", LRUENTRY_DOUBLE);
      this.txMgr.rollback();
      assertLRUEntries(lruRegion.entrySet(false), lruSize, "key", LRUENTRY_LONG);

      // Test to make sure we can evict something that has been rolled back
      for(int i=0; i 6");
    res = (SelectResults) q.execute();
    assertEquals(0, res.size());

    aIregion.put("qkey0", "qval0");
    aIregion.put("qkey1", "qval01");
    q = qs.newQuery(aIqstr);
    res = (SelectResults) q.execute();
    assertEquals(2, res.size());
    for(Iterator resI = res.iterator(); resI.hasNext();) {
      val = (String) resI.next();
      assertTrue("Value: " + val + " does not start with qval", val.startsWith("qval"));
    }
    q = qs.newQuery(qstr + " where value.length > 6");
    res = (SelectResults) q.execute();
    assertEquals(0, res.size());

    // Test query results in a transaction
    this.txMgr.begin();
    Query q1 = qs.newQuery(qstr);
    this.region.put("noQkey2", "noQval2");
    res = (SelectResults) q1.execute();
    assertEquals(2, res.size());
    for(Iterator resI = res.iterator(); resI.hasNext();) {
      val = (String) resI.next();
      assertTrue("Value: " + val + " does not start with qval", val.startsWith("qval"));
    }
    Query aIq1 = qs.newQuery(aIqstr);
    aIregion.put("noQkey2", "noQval2");
    res = (SelectResults) aIq1.execute();
    assertEquals(2, res.size());
    for(Iterator resI = res.iterator(); resI.hasNext();) {
      val = (String) resI.next();
      assertTrue("Value: " + val + " does not start with qval", val.startsWith("qval"));
    }
    Query q2 = qs.newQuery(qstr + " where value.length > 6");
    res = (SelectResults) q2.execute();
    assertEquals(0, res.size());
    Query aIq2 = qs.newQuery(aIqstr + " where value.length > 6");
    res = (SelectResults) aIq2.execute();
    assertEquals(0, res.size());
    this.txMgr.commit();
    res = (SelectResults) q1.execute();
    assertEquals(3, res.size());
    res = (SelectResults) q2.execute();
    assertEquals(1, res.size());
    res = (SelectResults) aIq1.execute();
    assertEquals(3, res.size());
    res = (SelectResults) aIq2.execute();
    assertEquals(1, res.size());

    this.region.destroy("noQkey2");
    aIregion.destroy("noQkey2");

    // Confirm base functionality for index creation
    Index index0 = qs.createIndex("TXIndex0", IndexType.FUNCTIONAL, "value.length", fromClause);
    assertEquals(2, index0.getStatistics().getNumberOfKeys());
    assertEquals(2, index0.getStatistics().getNumberOfValues());
    assertEquals(2, index0.getStatistics().getNumUpdates()); // Shouldn't this be zero?
    Index aIindex0 = qs.createIndex("aITXIndex0", IndexType.FUNCTIONAL, "value.length", aIfromClause);
    assertEquals(2, aIindex0.getStatistics().getNumberOfKeys());
    assertEquals(2, aIindex0.getStatistics().getNumberOfValues());
    assertEquals(2, aIindex0.getStatistics().getNumUpdates()); // Shouldn't this be zero?
    q = qs.newQuery(qstr);
    res = (SelectResults) q.execute();
    assertEquals(2, res.size());
    assertEquals(0, index0.getStatistics().getTotalUses());
    aIq1 = qs.newQuery(aIqstr);
    res = (SelectResults) aIq1.execute();
    assertEquals(2, res.size());
    assertEquals(0, aIindex0.getStatistics().getTotalUses());

    final String val2 = "qval000002";
    this.region.put("qkey2", val2);
    assertEquals(3, index0.getStatistics().getNumUpdates()); // Shouldn't this be 1?
    assertEquals(3, index0.getStatistics().getNumberOfKeys());
    assertEquals(3, index0.getStatistics().getNumberOfValues());
    aIregion.put("qkey2", val2);
    final IndexManager.IndexUpdaterThread upThread = ((AbstractRegion) aIregion).getIndexManager().getUpdaterThread();
    while(!upThread.isDone()) {
      pause(20);
    }
    // @todo asif: for some reason the value returned by getNumberOfKeys is unstable.
    // Even when the code waits for it to be the expected value it intermittently
    // will fail because it never gets to be the expected value.
    // This stat (in RangeIndex at least) is only updated when we add a new key
    // to the valueToEntriesMap. I do not see a place that we ever remove from
    // this map (even when we do a removeMapping).
    waitForUpdates(aIindex0, 3);
    waitForKeys(aIindex0, 3);
    assertEquals(3, aIindex0.getStatistics().getNumUpdates()); // Shouldn't this be 1?
    assertEquals(3, aIindex0.getStatistics().getNumberOfKeys());
    assertEquals(3, aIindex0.getStatistics().getNumberOfValues());
    q = qs.newQuery("ELEMENT(" + qstr + " where value.length > 6)");
    assertEquals(val2, (String) q.execute());
    assertEquals(1, index0.getStatistics().getTotalUses());
    aIq1 = qs.newQuery("ELEMENT(" + aIqstr + " where value.length > 6)");
    assertEquals(val2, (String) aIq1.execute());

    this.region.destroy("qkey2");
    waitForKeys(index0,2);
    assertEquals(2, index0.getStatistics().getNumberOfKeys()); // Shouldn't this be 1, again?
    assertEquals(2, index0.getStatistics().getNumberOfValues());
    assertEquals(4, index0.getStatistics().getNumUpdates());
    aIregion.destroy("qkey2");
    while(!upThread.isDone()) {
      pause(20);
    }
    waitForUpdates(aIindex0, 4);
//     waitForKeys(aIindex0, 3);
//     assertEquals(3, aIindex0.getStatistics().getNumberOfKeys()); // Shouldn't this be 1, again?
    assertEquals(2, aIindex0.getStatistics().getNumberOfValues());
    assertEquals(4, aIindex0.getStatistics().getNumUpdates());

    // Test index creation
    this.txMgr.begin();
    this.region.destroy("qkey1");
    this.region.put("noQkey3", "noQval3");
    Index index1 = qs.createIndex("TXIndex1", IndexType.FUNCTIONAL, "value", fromClause);
    assertEquals(2, index1.getStatistics().getNumberOfKeys()); 
    assertEquals(2, index1.getStatistics().getNumberOfValues());
    assertEquals(2, index1.getStatistics().getNumUpdates());
    assertEquals(2, index0.getStatistics().getNumberOfKeys()); 
    assertEquals(2, index0.getStatistics().getNumberOfValues());
    assertEquals(4, index0.getStatistics().getNumUpdates());

    aIregion.destroy("qkey1");
    aIregion.put("noQkey3", "noQval3");
    Index aIindex1 = qs.createIndex("aITXIndex1", IndexType.FUNCTIONAL, "value", aIfromClause);
    while(!upThread.isDone()) {
      pause(20);
    }
    waitForUpdates(aIindex0, 4);
    waitForUpdates(aIindex1, 2);
//     waitForKeys(aIindex0, 3);
//     waitForKeys(aIindex1, 2);
    assertEquals(2, aIindex1.getStatistics().getNumberOfKeys()); 
    assertEquals(2, aIindex1.getStatistics().getNumberOfValues());
    assertEquals(2, aIindex1.getStatistics().getNumUpdates());
//     assertEquals(3, aIindex0.getStatistics().getNumberOfKeys()); 
    assertEquals(2, aIindex0.getStatistics().getNumberOfValues());
    assertEquals(4, aIindex0.getStatistics().getNumUpdates());


    q = qs.newQuery(qstr);
    res = (SelectResults) q.execute();
    assertEquals(2, res.size());
    assertEquals(0, index1.getStatistics().getTotalUses());
    assertEquals(1, index0.getStatistics().getTotalUses());

    aIq1 = qs.newQuery(aIqstr);
    res = (SelectResults) aIq1.execute();
    assertEquals(2, res.size());
    assertEquals(0, aIindex1.getStatistics().getTotalUses());
    assertEquals(1, aIindex0.getStatistics().getTotalUses());

    q = qs.newQuery(qstr + " where value < 'q'");
    res = (SelectResults) q.execute();
    assertEquals(1, index1.getStatistics().getTotalUses());
    assertEquals(0, res.size());

    aIq1 = qs.newQuery(aIqstr + " where value < 'q'");
    res = (SelectResults) aIq1.execute();
    assertEquals(1, aIindex1.getStatistics().getTotalUses());
    assertEquals(0, res.size());

    this.region.put("noQkey4", "noQval4");
    assertEquals(2, index1.getStatistics().getNumberOfKeys()); 
    assertEquals(2, index1.getStatistics().getNumberOfValues());
    assertEquals(2, index1.getStatistics().getNumUpdates());
    assertEquals(2, index0.getStatistics().getNumberOfKeys()); 
    assertEquals(2, index0.getStatistics().getNumberOfValues());
    assertEquals(4, index0.getStatistics().getNumUpdates());

    aIregion.put("noQkey4", "noQval4");
    while(!upThread.isDone()) {
      pause(20);
    }
    waitForUpdates(aIindex0, 4);
    waitForUpdates(aIindex1, 2);
     waitForKeys(aIindex0, 2);
//     waitForKeys(aIindex1, 2);
//     assertEquals(2, aIindex1.getStatistics().getNumberOfKeys()); 
    assertEquals(2, aIindex1.getStatistics().getNumberOfValues());
    assertEquals(2, aIindex1.getStatistics().getNumUpdates());
    assertEquals(2, aIindex0.getStatistics().getNumberOfKeys()); 
    assertEquals(2, aIindex0.getStatistics().getNumberOfValues());
    assertEquals(4, aIindex0.getStatistics().getNumUpdates());

    q = qs.newQuery(qstr + " where value < 'q'");
    res = (SelectResults) q.execute();
    assertEquals(2, index1.getStatistics().getTotalUses());
    assertEquals(0, res.size());

    aIq1 = qs.newQuery(aIqstr + " where value <'q'");
    res = (SelectResults) aIq1.execute();
    assertEquals(2, aIindex1.getStatistics().getTotalUses());
    assertEquals(0, res.size());

    q = qs.newQuery(qstr + " where value.length > 6");
    res = (SelectResults) q.execute();
    assertEquals(2, index0.getStatistics().getTotalUses());
    assertEquals(0, res.size());

    aIq1 = qs.newQuery(aIqstr + " where value.length > 6");
    res = (SelectResults) aIq1.execute();
    assertEquals(2, aIindex0.getStatistics().getTotalUses());
    assertEquals(0, res.size());

    this.txMgr.commit();
    assertEquals(3, index1.getStatistics().getNumberOfKeys()); 
    assertEquals(3, index1.getStatistics().getNumberOfValues()); // Shouldn't this be 4?
    assertEquals(5, index1.getStatistics().getNumUpdates()); 
    assertEquals(2, index0.getStatistics().getNumberOfKeys()); 
    assertEquals(3, index0.getStatistics().getNumberOfValues()); // Shouldn't this be 4?
    assertEquals(7, index0.getStatistics().getNumUpdates());

    while(!upThread.isDone()) {
      pause(20);
    }
    waitForUpdates(aIindex0, 7);
    waitForUpdates(aIindex1, 5);
    // waitForKeys(aIindex0, 4); // sometimes 3 sometimes 4
//     waitForKeys(aIindex1, 3);
    assertEquals(3, aIindex1.getStatistics().getNumberOfKeys()); 
    assertEquals(3, aIindex1.getStatistics().getNumberOfValues()); // Shouldn't this be 4?
    assertEquals(5, aIindex1.getStatistics().getNumUpdates()); 
    //assertEquals(4, aIindex0.getStatistics().getNumberOfKeys()); 
    assertEquals(3, aIindex0.getStatistics().getNumberOfValues()); // Shouldn't this be 4?
    assertEquals(7, aIindex0.getStatistics().getNumUpdates());

    q = qs.newQuery(qstr + " where value <'q'");
    res = (SelectResults) q.execute();
    assertEquals(3, index1.getStatistics().getTotalUses());
    assertEquals(2, res.size());

    aIq1 = qs.newQuery(aIqstr + " where value < 'q'");
    res = (SelectResults) aIq1.execute();
    assertEquals(3, aIindex1.getStatistics().getTotalUses());
    assertEquals(2, res.size());

    q = qs.newQuery(qstr + " where value.length > 6");
    res = (SelectResults) q.execute();
    assertEquals(3, index0.getStatistics().getTotalUses());
    assertEquals(2, res.size());

    aIq1 = qs.newQuery(aIqstr + " where value.length > 6");
    res = (SelectResults) aIq1.execute();
    assertEquals(3, aIindex0.getStatistics().getTotalUses());
    assertEquals(2, res.size());
    }finally {
      IndexManager.TEST_RANGEINDEX_ONLY = false;
    }
  }

  /**
   * make sure that we do not expose BucketRegion on transactionListener events
   * @throws Exception
   */
  public void testInternalRegionNotExposed() throws Exception {
    TransactionListenerForRegionTest tl = new TransactionListenerForRegionTest();
    CacheTransactionManager ctm = this.cache.getCacheTransactionManager();
    ctm.addListener(tl);
    CacheListenerForRegionTest cl = new CacheListenerForRegionTest();
    AttributesFactory af = new AttributesFactory();
    PartitionAttributes pa = new PartitionAttributesFactory()
      .setRedundantCopies(0)
      .setTotalNumBuckets(1)
      .create();
    af.setPartitionAttributes(pa);
    af.addCacheListener(cl);
    Region pr = this.cache.createRegion("testTxEventForRegion", af.create());
    pr.put(2, "tw");
    pr.put(3, "three");
    pr.put(4, "four");
    ctm.begin();
    pr.put(1, "one");
    pr.put(2, "two");
    pr.invalidate(3);
    pr.destroy(4);
    ctm.commit();
    assertFalse(tl.exceptionOccurred);
    assertFalse(cl.exceptionOccurred);
  }

  
  
  /**
   * make sure that we throw an UnsupportedOperationInTransactionException
   * @throws Exception
   */
  public void testPutAllSupported() throws Exception {
    TXManagerImpl ctm = this.cache.getTxManager();
    AttributesFactory af = new AttributesFactory();
    Region r = this.cache.createRegion("dRegion", af.create());
    PartitionAttributes pa = new PartitionAttributesFactory()
      .setRedundantCopies(0)
      .setTotalNumBuckets(1)
      .create();
    af.setPartitionAttributes(pa);
    Region pr = this.cache.createRegion("prRegion", af.create());
    Map map = new HashMap();
    map.put("stuff","junk");
    map.put("stuff2","junk2");
    ctm.begin();
    pr.putAll(map);
    r.putAll(map);
    TXStateInterface tx = ctm.internalSuspend();
    assertTrue(!pr.containsKey("stuff"));
    assertTrue(!r.containsKey("stuff"));
    ctm.resume(tx);
    ctm.commit();
    assertTrue(pr.containsKey("stuff"));
    assertTrue(r.containsKey("stuff"));
  }

  /**
   * getAll should now be supported
   */
  public void testGetAllSupported() throws Exception {
    CacheTransactionManager ctm = this.cache.getCacheTransactionManager();
    AttributesFactory af = new AttributesFactory();
    Region r = this.cache.createRegion("dRegion", af.create());
    PartitionAttributes pa = new PartitionAttributesFactory()
      .setRedundantCopies(0)
      .setTotalNumBuckets(1)
      .create();
    af.setPartitionAttributes(pa);
    Region pr = this.cache.createRegion("prRegion", af.create());
    List list = new ArrayList();
    list.add("stuff");
    list.add("stuff2");
    ctm.begin();
    pr.getAll(list);
    r.getAll(list);
    ctm.commit();
    // now we aren't in tx so these shouldn't throw
    pr.getAll(list);
    r.getAll(list);
  }
  
  
  
  /**
   * make sure that we throw an UnsupportedOperationInTransactionException
   * @throws Exception
   */
  public void testDestroyRegionNotSupported() throws Exception {
    CacheTransactionManager ctm = this.cache.getCacheTransactionManager();
    AttributesFactory af = new AttributesFactory();
    Region r = this.cache.createRegion("dRegion", af.create());
    PartitionAttributes pa = new PartitionAttributesFactory()
      .setRedundantCopies(0)
      .setTotalNumBuckets(1)
      .create();
    af.setPartitionAttributes(pa);
    Region pr = this.cache.createRegion("prRegion", af.create());
    List list = new ArrayList();
    list.add("stuff");
    list.add("stuff2");
    ctm.begin();
    try {
      pr.destroyRegion();
      fail("Should have thrown UnsupportedOperationInTransactionException during destroyRegion");
    } catch(UnsupportedOperationInTransactionException ee) {
      // expected
    }
    try {
      pr.localDestroyRegion();
      fail("Should have thrown UnsupportedOperationInTransactionException during localDestroyRegion");
    } catch(UnsupportedOperationInTransactionException ee) {
      // expected
    }
    try {
      r.destroyRegion();
      fail("Should have thrown UnsupportedOperationInTransactionException during destroyRegion");
    } catch(UnsupportedOperationInTransactionException ee) {
      // expected
    }
    
    try {
      r.localDestroyRegion();
      fail("Should have thrown UnsupportedOperationInTransactionException during localDestroyRegion");
    } catch(UnsupportedOperationInTransactionException ee) {
      // expected
    }
    assertTrue(!pr.isDestroyed());
    assertTrue(!r.isDestroyed());
    
    ctm.commit();
    // now we aren't in tx so these shouldn't throw
    pr.destroyRegion();
    r.destroyRegion();
    
  }
  
  /**
   * make sure that we throw an UnsupportedOperationInTransactionException
   * @throws Exception
   */
  public void testInvalidateRegionNotSupported() throws Exception {
    CacheTransactionManager ctm = this.cache.getCacheTransactionManager();
    AttributesFactory af = new AttributesFactory();
    Region r = this.cache.createRegion("dRegion", af.create());
    PartitionAttributes pa = new PartitionAttributesFactory()
      .setRedundantCopies(0)
      .setTotalNumBuckets(1)
      .create();
    af.setPartitionAttributes(pa);
    Region pr = this.cache.createRegion("prRegion", af.create());
    ctm.begin();
    try {
      pr.invalidateRegion();
      fail("Should have thrown UnsupportedOperationInTransactionException during invalidateRegion");
    } catch(UnsupportedOperationInTransactionException ee) {
      // expected
    }
    try {
      pr.localInvalidateRegion();
      fail("Should have thrown UnsupportedOperationInTransactionException during localInvalidateRegion");
    } catch(UnsupportedOperationInTransactionException ee) {
      // expected
    }
    try {
      r.invalidateRegion();
      fail("Should have thrown UnsupportedOperationInTransactionException during invalidateRegion");
    } catch(UnsupportedOperationInTransactionException ee) {
      // expected
    }
    
    try {
      r.localInvalidateRegion();
      fail("Should have thrown UnsupportedOperationInTransactionException during localInvalidateRegion");
    } catch(UnsupportedOperationInTransactionException ee) {
      // expected
    }
    ctm.commit();
    // now we aren't in tx so these shouldn't throw
    pr.invalidateRegion();
    r.invalidateRegion();
    
  }
  
  class TransactionListenerForRegionTest extends TransactionListenerAdapter {
    private boolean exceptionOccurred = false;
    @Override
    public void afterCommit(TransactionEvent event) {
      List> events = event.getEvents();
      for (CacheEvente : events) {
        if (!"/testTxEventForRegion".equals(e.getRegion().getFullPath())) {
          exceptionOccurred = true;
        }
      }
    }
  }

  class CacheListenerForRegionTest extends CacheListenerAdapter {
    private boolean exceptionOccurred = false;
    @Override
    public void afterCreate(EntryEvent event) {
      verifyRegion(event);
    }
    @Override
    public void afterUpdate(EntryEvent event) {
      verifyRegion(event);
    }
    private void verifyRegion(EntryEvent event) {
      if (!"/testTxEventForRegion".equals(event.getRegion().getFullPath())) {
        exceptionOccurred = true;
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy