package org.jboss.cache.optimistic;

import junit.framework.Assert;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.*;
import org.jboss.cache.loader.SamplePojo;
import org.jboss.cache.transaction.DummyTransactionManager;

import javax.transaction.RollbackException;
import javax.transaction.Transaction;
import java.util.Set;

/**
 * @author xenephon
 */
public class FullStackInterceptorTest extends AbstractOptimisticTestCase
{

    private Log log = LogFactory.getLog(FullStackInterceptorTest.class);

    /**
     * @param name
     */
    public FullStackInterceptorTest(String name)
    {
        super(name);
    }

    private int groupIncreaser = 0;

    public void testLocalTransaction() throws Exception
    {

        TreeCache cache = createCacheWithListener();

        DummyTransactionManager mgr = DummyTransactionManager.getInstance();
        assertNull(mgr.getTransaction());

        mgr.begin();

        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());

        SamplePojo pojo = new SamplePojo(21, "test");

        cache.put("/one/two", "key1", pojo);

        mgr.commit();

        assertNull(mgr.getTransaction());
        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());

        assertTrue(cache.exists(Fqn.fromString("/one/two")));
        assertNotNull(cache.get(Fqn.fromString("/")).getChild("one"));
        assertNotNull(cache.get(Fqn.fromString("/one/two"), "key1"));
        // flesh this out a bit more

        destroyCache(cache);

    }

    public void testNoLocalTransaction() throws Exception
    {
        TestListener listener = new TestListener();

        TreeCache cache = createCacheWithListener(listener);

        DummyTransactionManager mgr = DummyTransactionManager.getInstance();
        assertNull(mgr.getTransaction());

        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());

        SamplePojo pojo = new SamplePojo(21, "test");

        cache.put("/one/two", "key1", pojo);

        assertNull(mgr.getTransaction());
        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());

        assertTrue(cache.exists(Fqn.fromString("/one/two")));
        assertNotNull(cache.get(Fqn.fromString("/")).getChild("one"));
        assertEquals(false, cache.get(Fqn.fromString("/")).getLock()
                .isLocked());
        assertEquals(false, cache.get(Fqn.fromString("/one")).getLock()
                .isLocked());
        assertEquals(false, cache.get(Fqn.fromString("/one/two"))
                .getLock().isLocked());
        assertNotNull(cache.get(Fqn.fromString("/one")).getChild("two"));
        assertNotNull(cache.get(Fqn.fromString("/one/two"), "key1"));

        assertEquals(2, listener.getNodesAdded());

        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        destroyCache(cache);

    }

    public void testSingleInstanceCommit() throws Exception
    {
        groupIncreaser++;
        TreeCache cache = createCacheWithListener();

        DummyTransactionManager mgr = DummyTransactionManager.getInstance();
        assertNull(mgr.getTransaction());

        mgr.begin();

        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());

        SamplePojo pojo = new SamplePojo(21, "test");

        cache.put("/one/two", "key1", pojo);

        mgr.commit();

        assertNull(mgr.getTransaction());
        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());

        assertTrue(cache.exists(Fqn.fromString("/one/two")));
        assertNotNull(cache.get(Fqn.fromString("/")).getChild("one"));
        assertNotNull(cache.get(Fqn.fromString("/one/two"), "key1"));

        assertTrue(cache.exists(Fqn.fromString("/one/two")));
        assertNotNull(cache.get(Fqn.fromString("/")).getChild("one"));
        assertEquals(false, cache.get(Fqn.fromString("/")).getLock()
                .isLocked());
        assertEquals(false, cache.get(Fqn.fromString("/one")).getLock()
                .isLocked());
        assertEquals(false, cache.get(Fqn.fromString("/one/two"))
                .getLock().isLocked());
        assertNotNull(cache.get(Fqn.fromString("/one")).getChild("two"));
        assertNotNull(cache.get(Fqn.fromString("/one/two"), "key1"));
        destroyCache(cache);

    }

    public void testSingleInstanceRollback() throws Exception
    {
        groupIncreaser++;
        TreeCache cache = createSyncReplicatedCache();

        DummyTransactionManager mgr = DummyTransactionManager.getInstance();
        assertNull(mgr.getTransaction());

        mgr.begin();

        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());

        SamplePojo pojo = new SamplePojo(21, "test");

        cache.put("/one/two", "key1", pojo);

        mgr.rollback();

        assertNull(mgr.getTransaction());
        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());

        assertEquals(false, cache.exists(Fqn.fromString("/one/two")));
        assertNull(cache.get(Fqn.fromString("/")).getChild("one"));

        destroyCache(cache);

    }

    public void testSingleInstanceDuplicateCommit() throws Exception
    {
        groupIncreaser++;
        TreeCache cache = createSyncReplicatedCache();

        DummyTransactionManager mgr = DummyTransactionManager.getInstance();
        assertNull(mgr.getTransaction());

        mgr.begin();

        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());

        SamplePojo pojo = new SamplePojo(21, "test");

        cache.put("/one/two", "key1", pojo);

        mgr.commit();

        assertNull(mgr.getTransaction());

        boolean fail = false;
        try
        {
            mgr.commit();
        }
        catch (Exception e)
        {
            fail = true;

        }

        assertEquals(true, fail);
        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());

        assertTrue(cache.exists(Fqn.fromString("/one/two")));
        assertNotNull(cache.get(Fqn.fromString("/")).getChild("one"));
        assertEquals(false, cache.get(Fqn.fromString("/")).getLock()
                .isLocked());
        assertEquals(false, cache.get(Fqn.fromString("/one")).getLock()
                .isLocked());
        assertEquals(false, cache.get(Fqn.fromString("/one/two"))
                .getLock().isLocked());
        assertNotNull(cache.get(Fqn.fromString("/one")).getChild("two"));
        assertNotNull(cache.get(Fqn.fromString("/one/two"), "key1"));

        destroyCache(cache);

    }

    public void testValidationFailCommit() throws Exception
    {
        groupIncreaser++;
        TreeCache cache = createSyncReplicatedCache();

        DummyTransactionManager mgr = DummyTransactionManager.getInstance();
        assertNull(mgr.getTransaction());

        mgr.begin();
        Transaction tx = mgr.getTransaction();
        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());

        SamplePojo pojo = new SamplePojo(21, "test");

        cache.put("/one/two", "key1", pojo);

        mgr.suspend();

        assertEquals(1, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(1, cache.getTransactionTable().getNumLocalTransactions());

        assertNull(mgr.getTransaction());

        mgr.begin();

        SamplePojo pojo2 = new SamplePojo(22, "test2");

        cache.put("/one/two", "key1", pojo2);

        mgr.commit();

        assertEquals(1, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(1, cache.getTransactionTable().getNumLocalTransactions());

        mgr.resume(tx);

        boolean fail = false;
        try
        {
            mgr.commit();
        }
        catch (Exception e)
        {
            fail = true;

        }
        assertNull(mgr.getTransaction());
        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());

        assertEquals(true, fail);

        assertTrue(cache.exists(Fqn.fromString("/one/two")));
        assertNotNull(cache.get(Fqn.fromString("/")).getChild("one"));
        assertEquals(false, cache.get(Fqn.fromString("/")).getLock()
                .isLocked());
        assertEquals(false, cache.get(Fqn.fromString("/one")).getLock()
                .isLocked());
        assertEquals(false, cache.get(Fqn.fromString("/one/two")).getLock().isLocked());
        assertNotNull(cache.get(Fqn.fromString("/one")).getChild("two"));
        assertEquals(pojo2, cache.get(Fqn.fromString("/one/two"), "key1"));

        destroyCache(cache);

    }

    public void test2InstanceCommit() throws Exception
    {
        groupIncreaser++;
        TreeCache cache = createSyncReplicatedCache();
        TreeCache cache2 = createSyncReplicatedCache();

        DummyTransactionManager mgr = DummyTransactionManager.getInstance();
        assertNull(mgr.getTransaction());

        mgr.begin();

        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());

        SamplePojo pojo = new SamplePojo(21, "test");

        cache.put("/one/two", "key1", pojo);

        mgr.commit();

        // cache asserts
        assertNull(mgr.getTransaction());
        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());

        assertTrue(cache.exists(Fqn.fromString("/one/two")));
        assertNotNull(cache.get(Fqn.fromString("/")).getChild("one"));
        assertNotNull(cache.get(Fqn.fromString("/one/two"), "key1"));

        assertTrue(cache.exists(Fqn.fromString("/one/two")));
        assertNotNull(cache.get(Fqn.fromString("/")).getChild("one"));
        assertEquals(false, cache.get(Fqn.fromString("/")).getLock()
                .isLocked());
        assertEquals(false, cache.get(Fqn.fromString("/one")).getLock()
                .isLocked());
        assertEquals(false, cache.get(Fqn.fromString("/one/two")).getLock().isLocked());
        assertNotNull(cache.get(Fqn.fromString("/one")).getChild("two"));
        assertNotNull(cache.get(Fqn.fromString("/one/two"), "key1"));

        // cache2 asserts
        assertEquals(0, cache2.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache2.getTransactionTable().getNumLocalTransactions());

        assertTrue(cache2.exists(Fqn.fromString("/one/two")));
        assertNotNull(cache2.get(Fqn.fromString("/")).getChild("one"));
        assertNotNull(cache2.get(Fqn.fromString("/one/two"), "key1"));

        assertTrue(cache2.exists(Fqn.fromString("/one/two")));
        assertNotNull(cache2.get(Fqn.fromString("/")).getChild("one"));
        assertEquals(false, cache2.get(Fqn.fromString("/")).getLock().isLocked());
        assertEquals(false, cache2.get(Fqn.fromString("/one")).getLock().isLocked());
        assertEquals(false, cache2.get(Fqn.fromString("/one/two")).getLock().isLocked());
        assertNotNull(cache2.get(Fqn.fromString("/one")).getChild("two"));
        assertNotNull(cache2.get(Fqn.fromString("/one/two"), "key1"));

        destroyCache(cache);
        destroyCache(cache2);
    }

    public void test2InstanceRemove() throws Exception
    {
        groupIncreaser++;
        TreeCache cache = createSyncReplicatedCache();
        TreeCache cache2 = createSyncReplicatedCache();

        DummyTransactionManager mgr = DummyTransactionManager.getInstance();
        assertNull(mgr.getTransaction());

        mgr.begin();

        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());

        SamplePojo pojo = new SamplePojo(21, "test");

        cache.put("/one/two", "key1", pojo);

        mgr.commit();

        // cache asserts
        assertNull(mgr.getTransaction());
        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());

        assertTrue(cache.exists(Fqn.fromString("/one/two")));
        assertNotNull(cache.get(Fqn.fromString("/")).getChild("one"));
        assertNotNull(cache.get(Fqn.fromString("/one/two"), "key1"));

        assertTrue(cache.exists(Fqn.fromString("/one/two")));
        assertNotNull(cache.get(Fqn.fromString("/")).getChild("one"));
        assertEquals(false, cache.get(Fqn.fromString("/")).getLock()
                .isLocked());
        assertEquals(false, cache.get(Fqn.fromString("/one")).getLock()
                .isLocked());
        assertEquals(false, cache.get(Fqn.fromString("/one/two"))
                .getLock().isLocked());
        assertNotNull(cache.get(Fqn.fromString("/one")).getChild("two"));
        assertNotNull(cache.get(Fqn.fromString("/one/two"), "key1"));

        // cache2 asserts
        assertEquals(0, cache2.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache2.getTransactionTable().getNumLocalTransactions());

        assertTrue(cache2.exists(Fqn.fromString("/one/two")));
        assertNotNull(cache2.get(Fqn.fromString("/")).getChild("one"));
        assertNotNull(cache2.get(Fqn.fromString("/one/two"), "key1"));

        assertTrue(cache2.exists(Fqn.fromString("/one/two")));
        assertNotNull(cache2.get(Fqn.fromString("/")).getChild("one"));
        assertEquals(false, cache2.get(Fqn.fromString("/")).getLock().isLocked());
        assertEquals(false, cache2.get(Fqn.fromString("/one")).getLock().isLocked());
        assertEquals(false, cache2.get(Fqn.fromString("/one/two")).getLock().isLocked());
        assertNotNull(cache2.get(Fqn.fromString("/one")).getChild("two"));
        assertNotNull(cache2.get(Fqn.fromString("/one/two"), "key1"));

        cache.remove("/one/two");
        log.debug("  C1 " + cache.get("/one/two"));
        log.debug("  C2 " + cache2.get("/one/two"));

        assertEquals(false, cache.exists("/one/two"));
        assertEquals(false, cache2.exists("/one/two"));

        assertEquals(null, cache.get("/one/two", "key1"));
        assertEquals(null, cache2.get("/one/two", "key1"));
        destroyCache(cache);
        destroyCache(cache2);
    }

    public void testValidationFailCommit2Instances() throws Exception
    {
        groupIncreaser++;
        TreeCache cache = createSyncReplicatedCache();
        TreeCache cache2 = createSyncReplicatedCache();

        DummyTransactionManager mgr = DummyTransactionManager.getInstance();
        assertNull(mgr.getTransaction());

        mgr.begin();
        Transaction tx = mgr.getTransaction();
        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());

        SamplePojo pojo = new SamplePojo(21, "test");

        cache.put("/one/two", "key1", pojo);

        mgr.suspend();

        assertEquals(1, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(1, cache.getTransactionTable().getNumLocalTransactions());

        GlobalTransaction gtx = cache.getCurrentTransaction(tx);
        TransactionTable table = cache.getTransactionTable();
        OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table
                .get(gtx);


        assertEquals(3, entry.getTransactionWorkSpace().getNodes().size());
        assertNull(mgr.getTransaction());

        mgr.begin();

        SamplePojo pojo2 = new SamplePojo(22, "test2");

        cache2.put("/one/two", "key1", pojo2);

        mgr.commit();

        assertEquals(1, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(1, cache.getTransactionTable().getNumLocalTransactions());

        mgr.resume(tx);

        boolean fail = false;
        try
        {
            mgr.commit();
        }
        catch (Exception e)
        {
            fail = true;

        }

        assertEquals(true, fail);
        assertNull(mgr.getTransaction());
        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());

        assertEquals(0, entry.getTransactionWorkSpace().getNodes().size());

        assertTrue(cache.exists(Fqn.fromString("/one/two")));
        assertNotNull(cache.get(Fqn.fromString("/")).getChild("one"));
        assertEquals(false, cache.get(Fqn.fromString("/")).getLock().isLocked());
        assertEquals(false, cache.get(Fqn.fromString("/one")).getLock().isLocked());
        assertEquals(false, cache.get(Fqn.fromString("/one/two")).getLock().isLocked());
        assertNotNull(cache.get(Fqn.fromString("/one")).getChild("two"));
        assertEquals(pojo2, cache.get(Fqn.fromString("/one/two"), "key1"));

        destroyCache(cache);
        destroyCache(cache2);

    }

    public void testGetKeyValIsolationTransaction() throws Exception
    {
        SamplePojo pojo1 = new SamplePojo(21, "test-1");
        SamplePojo pojo2 = new SamplePojo(21, "test-2");

        TreeCache cache = createCacheWithListener();

        DummyTransactionManager mgr = DummyTransactionManager.getInstance();
        assertNull(mgr.getTransaction());

        // first put in a value
        mgr.begin();

        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());

        cache.put("/one/two", "key1", pojo1);

        mgr.commit();

        mgr.begin();
        Transaction tx = mgr.getTransaction();
        System.out.println("Current TX " + mgr.getTransaction());
        assertEquals(pojo1, cache.get("/one/two", "key1"));

        // start another
        mgr.suspend();

        mgr.begin();
        System.out.println("Current TX " + mgr.getTransaction());
        cache.put("/one/two", "key2", pojo2);

        // assert we can see this INSIDE the existing tx
        //assertEquals(pojo2, cache.get("/one/two", "key2"));

        mgr.commit();

        // assert we can see this outside the existing tx
        assertEquals(pojo2, cache.get("/one/two", "key2"));
        System.out.println("Current TX " + mgr.getTransaction());
        // resume the suspended one
        mgr.resume(tx);
        System.out.println("Current TX " + mgr.getTransaction());
        // assert we can't see the change from tx2 as we already touched the node
        assertEquals(null, cache.get("/one/two", "key2"));
        mgr.commit();
        destroyCache(cache);
    }

    public void testGetKeysIsolationTransaction() throws Exception
    {

        TreeCache cache = createCacheWithListener();

        DummyTransactionManager mgr = DummyTransactionManager.getInstance();
        if (mgr.getTransaction() != null) mgr.rollback();
        assertNull(mgr.getTransaction());

        // first put in a value
        mgr.begin();

        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());

        SamplePojo pojo = new SamplePojo(21, "test");

        cache.put("/one/two", "key1", pojo);

        mgr.commit();

        mgr.begin();
        Transaction tx = mgr.getTransaction();
        assertEquals(1, cache.getKeys("/one/two").size());
        // start another
        mgr.suspend();

        mgr.begin();
        cache.put("/one/two", "key2", pojo);

        mgr.commit();

        // assert we can see this outsode the existing tx
        assertEquals(2, cache.getKeys("/one/two").size());

        // resume the suspended one
        mgr.resume(tx);
        // assert we can't see thge change from tx2 as we already touched the node
        assertEquals(1, cache.getKeys("/one/two").size());
        mgr.commit();
        destroyCache(cache);

    }


    public void testTxRollbackThroughConcurrentWrite() throws Exception
    {
        TreeCache cache = createCacheWithListener();
        Set keys;

        DummyTransactionManager mgr = DummyTransactionManager.getInstance();
        if (mgr.getTransaction() != null) mgr.rollback();
        assertNull(mgr.getTransaction());

        // first put in a value
        mgr.begin();
        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());
        cache.put("/one/two", "key1", "val1");
        mgr.commit();
        keys = cache.getKeys("/one/two");
        System.out.println("keys after TX #1: " + keys);
        assertEquals(1, keys.size());

        // First TX
        mgr.begin();
        Transaction tx = mgr.getTransaction();
        cache.put("/one/two", "key2", "val2"); // version for this is 1

        // start another
        mgr.suspend();

        // Second TX
        mgr.begin();
        cache.put("/one/two", "key3", "val3");
        mgr.commit(); // now version is 2, attrs are key1 and key3

        // assert we can see this outside the existing tx
        keys = cache.getKeys("/one/two");
        System.out.println("keys after TX #3 committed: " + keys);
        assertEquals(2, keys.size());

        // resume the suspended one
        mgr.resume(tx);
        // assert we can't see the change from tx2 as we already touched the node
        keys = cache.getKeys("/one/two");
        System.out.println("keys after TX #2 resumed (in private workspace of TX #2): " + keys);
        assertEquals(2, keys.size()); // we will see key1 and key2, but *not* key3

        // this will fail as our workspace has version 1, whereas cache has 2; TX will be rolled back
        try
        {
            mgr.commit();
            fail("TX should fail as other TX incremented version number");
        }
        catch (RollbackException rollback_ex)
        {
            System.out.println("TX was rolled back because the other TX committed first and incremented version ID." +
                    " This is the expected behavior");
        }

        keys = cache.getKeys("/one/two");
        System.out.println("keys after TX #2 was rolled back: " + keys);
        assertEquals(2, keys.size()); // key1 and key2
        destroyCache(cache);
    }


    protected TreeCache createSyncReplicatedCache() throws Exception
    {
        return createReplicatedCache("temp" + groupIncreaser, TreeCache.REPL_SYNC);
    }

    public void testPuts() throws Exception
    {
        TreeCache cache = createCache();
        Transaction tx;

        DummyTransactionManager mgr = DummyTransactionManager.getInstance();
        Assert.assertNull(cache.get(fqn));

        mgr.begin();
        cache.put(fqn, key, value);
        Assert.assertEquals(value, cache.get(fqn, key));
        tx = mgr.getTransaction();
        mgr.suspend();

        mgr.begin();
        Assert.assertNull(cache.get(fqn, key));
        mgr.commit();

        mgr.resume(tx);
        Assert.assertEquals(value, cache.get(fqn, key));
        mgr.commit();

        Assert.assertEquals(value, cache.get(fqn, key));

    }

    public void testRemoves() throws Exception
    {
        TreeCache cache = createCache();
        Transaction tx;

        DummyTransactionManager mgr = DummyTransactionManager.getInstance();
        Assert.assertNull(cache.get(fqn));
        cache.put(fqn, key, value);
        Assert.assertEquals(value, cache.get(fqn, key));

        mgr.begin();
        Assert.assertEquals(value, cache.get(fqn, key));
        cache.remove(fqn);
        Assert.assertNull(cache.get(fqn));
        tx = mgr.getTransaction();
        mgr.suspend();

        mgr.begin();
        Assert.assertEquals(value, cache.get(fqn, key));
        mgr.commit();

        mgr.resume(tx);
        Assert.assertNull(cache.get(fqn));
        mgr.commit();

        Assert.assertNull(cache.get(fqn));
    }


    public void testRemovesBeforeGet() throws Exception
    {
        TreeCache cache = createCache();
        Transaction tx;

        DummyTransactionManager mgr = DummyTransactionManager.getInstance();
        Assert.assertNull(cache.get(fqn));
        cache.put(fqn, key, value);
        Assert.assertEquals(value, cache.get(fqn, key));

        mgr.begin();
        cache.remove(fqn);
        Assert.assertNull(cache.get(fqn));
        tx = mgr.getTransaction();
        mgr.suspend();

        mgr.begin();
        Assert.assertEquals(value, cache.get(fqn, key));
        mgr.commit();

        mgr.resume(tx);
        Assert.assertNull(cache.get(fqn));
        mgr.commit();

        Assert.assertNull(cache.get(fqn));
    }

    public void testLoopedPutAndGet() throws Exception
    {
        try
        {
            log.debug("Starting test");
            TreeCache cache1 = createSyncReplicatedCache();
            TreeCache cache2 = createSyncReplicatedCache();
            log.debug("Created caches");
            DummyTransactionManager mgr = DummyTransactionManager.getInstance();

            int numLoops = 5, numPuts = 5;


            log.debug("Starting " + numLoops + " loops");
            for (int i = 0; i < numLoops; i++)
            {
                log.debug(" *** in loop " + i);
                mgr.begin();
                for (int j = 0; j < numPuts; j++)
                {
                    cache1.put(Fqn.fromString("/profiler/node" + i), "key" + j, "value" + j);
                }
                log.debug("*** >> Out of put loop");
                mgr.commit();
                //cache2.get(Fqn.fromString("/profiler/node" + i));
            }

            destroyCache(cache1);
            destroyCache(cache2);
        }
        catch (Exception e)
        {
            log.debug("Error: ", e);
            Assert.assertFalse("Threw exception!", true);
            throw e;
        }
    }

}
