/*
 * Created on 17-Feb-2005
 *
 *
 *
 */
package org.jboss.cache.optimistic;

import org.jboss.cache.GlobalTransaction;
import org.jboss.cache.OptimisticTransactionEntry;
import org.jboss.cache.TransactionTable;
import org.jboss.cache.TreeCache;
import org.jboss.cache.interceptors.*;
import org.jboss.cache.loader.SamplePojo;
import org.jboss.cache.marshall.MethodCallFactory;
import org.jboss.cache.marshall.MethodDeclarations;
import org.jboss.cache.transaction.DummyTransactionManager;
import org.jgroups.Address;
import org.jgroups.blocks.MethodCall;

import javax.transaction.RollbackException;
import javax.transaction.Transaction;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

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


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

    }

    public void testLocalTransaction() throws Exception
    {

        final TreeCache cache = createCache();

        Interceptor txInterceptor = new TxInterceptor();
        txInterceptor.setCache(cache);
        Interceptor replicationInterceptor = new OptimisticReplicationInterceptor();
        replicationInterceptor.setCache(cache);
        Interceptor createInterceptor = new OptimisticCreateIfNotExistsInterceptor();
        createInterceptor.setCache(cache);
        Interceptor nodeInterceptor = new OptimisticNodeInterceptor();
        nodeInterceptor.setCache(cache);
        MockInterceptor dummy = new MockInterceptor();
        dummy.setCache(cache);

        txInterceptor.setNext(replicationInterceptor);
        replicationInterceptor.setNext(createInterceptor);
        createInterceptor.setNext(nodeInterceptor);
        nodeInterceptor.setNext(dummy);

        cache.setInterceptorChain(txInterceptor);

        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());

        //make sure all calls were done in right order

        List calls = dummy.getAllCalled();

        assertEquals(MethodDeclarations.optimisticPrepareMethod, calls.get(0));
        assertEquals(MethodDeclarations.commitMethod, calls.get(1));

        cache.stopService();


    }

    public void testRollbackTransaction() throws Exception
    {

        TreeCache cache = createCache();

        Interceptor txInterceptor = new TxInterceptor();
        txInterceptor.setCache(cache);
        Interceptor replicationInterceptor = new OptimisticReplicationInterceptor();
        replicationInterceptor.setCache(cache);
        Interceptor createInterceptor = new OptimisticCreateIfNotExistsInterceptor();
        createInterceptor.setCache(cache);
        Interceptor nodeInterceptor = new OptimisticNodeInterceptor();
        nodeInterceptor.setCache(cache);
        MockInterceptor dummy = new MockInterceptor();
        dummy.setCache(cache);

        txInterceptor.setNext(replicationInterceptor);
        replicationInterceptor.setNext(createInterceptor);
        createInterceptor.setNext(nodeInterceptor);
        nodeInterceptor.setNext(dummy);
        cache.setInterceptorChain(txInterceptor);

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

        SamplePojo pojo = new SamplePojo(21, "test");
        mgr.begin();
        cache.put("/one/two", "key1", pojo);
        mgr.rollback();
        assertNull(mgr.getTransaction());
        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());

        //make sure all calls were done in right order

        List calls = dummy.getAllCalled();

        assertEquals(1, calls.size());
        assertEquals(MethodDeclarations.rollbackMethod, calls.get(0));

        cache.stopService();


    }

    public void testRemotePrepareTransaction() throws Exception
    {

        TreeCache cache = createCache();

        Interceptor txInterceptor = new TxInterceptor();
        txInterceptor.setCache(cache);
        Interceptor replicationInterceptor = new OptimisticReplicationInterceptor();
        replicationInterceptor.setCache(cache);
        Interceptor createInterceptor = new OptimisticCreateIfNotExistsInterceptor();
        createInterceptor.setCache(cache);
        Interceptor nodeInterceptor = new OptimisticNodeInterceptor();
        nodeInterceptor.setCache(cache);
        MockInterceptor dummy = new MockInterceptor();
        dummy.setCache(cache);

        txInterceptor.setNext(replicationInterceptor);
        replicationInterceptor.setNext(createInterceptor);
        createInterceptor.setNext(nodeInterceptor);
        nodeInterceptor.setNext(dummy);

        cache.setInterceptorChain(txInterceptor);

        DummyTransactionManager mgr = DummyTransactionManager.getInstance();

        //start local transaction
        mgr.begin();
        Transaction tx = mgr.getTransaction();

        //this sets
        cache.getCurrentTransaction(tx);

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

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

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


        GlobalTransaction remoteGtx = new GlobalTransaction();

        remoteGtx.setAddress(new Address()
        {
            public boolean isMulticastAddress()
            {

                return false;
            }

            public void readExternal(ObjectInput arg0) throws IOException,
                    ClassNotFoundException
            {


            }

            public void writeExternal(ObjectOutput arg0) throws IOException
            {


            }

            public int compareTo(Object arg0)
            {

                return 0;
            }

            public int size()
            {

                return 0;
            }

            public void writeTo(DataOutputStream arg0) throws IOException
            {


            }

            public void readFrom(DataInputStream arg0) throws IOException, IllegalAccessException, InstantiationException
            {


            }
        });
        //hack the method call to make it have the remote gtx
        MethodCall meth = (MethodCall) entry.getModifications().get(0);

        meth.getArgs()[0] = remoteGtx;
        //call our remote method
        MethodCall prepareMethod = MethodCallFactory.create(MethodDeclarations.optimisticPrepareMethod, new Object[]{remoteGtx, entry.getModifications(), null, remoteGtx.getAddress(), Boolean.FALSE});
        try
        {
            cache._replicate(prepareMethod);
        }
        catch (Throwable t)
        {
            fail();
        }

        //our thread should be null
        assertNull(mgr.getTransaction());

        //	 there should be a registration for the remote gtx
        assertNotNull(table.get(remoteGtx));
        assertNotNull(table.getLocalTransaction(remoteGtx));
        //assert that this is populated
        assertEquals(1, table.get(remoteGtx).getModifications().size());

        //assert that the remote prepare has populated the local workspace
        OptimisticTransactionEntry opEntry = (OptimisticTransactionEntry) table.get(gtx);

        assertEquals(3, entry.getTransactionWorkSpace().getNodes().size());
        assertEquals(1, entry.getModifications().size());
        List calls = dummy.getAllCalled();
        assertEquals(MethodDeclarations.optimisticPrepareMethod, calls.get(2));


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

        cache.stopService();


    }


    public void testRemoteRollbackTransaction() throws Exception
    {

        TreeCache cache = createCache();

        Interceptor txInterceptor = new TxInterceptor();
        txInterceptor.setCache(cache);
        Interceptor replicationInterceptor = new OptimisticReplicationInterceptor();
        replicationInterceptor.setCache(cache);
        Interceptor createInterceptor = new OptimisticCreateIfNotExistsInterceptor();
        createInterceptor.setCache(cache);
        OptimisticNodeInterceptor nodeInterceptor = new OptimisticNodeInterceptor();
        nodeInterceptor.setCache(cache);
        MockInterceptor dummy = new MockInterceptor();
        dummy.setCache(cache);

        txInterceptor.setNext(replicationInterceptor);
        replicationInterceptor.setNext(createInterceptor);
        createInterceptor.setNext(nodeInterceptor);
        nodeInterceptor.setNext(dummy);

        cache.setInterceptorChain(txInterceptor);

        DummyTransactionManager mgr = DummyTransactionManager.getInstance();

        //start local transaction
        mgr.begin();
        Transaction tx = mgr.getTransaction();

        //this sets
        cache.getCurrentTransaction(tx);

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

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

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


        GlobalTransaction remoteGtx = new GlobalTransaction();

        remoteGtx.setAddress(new Address()
        {
            public boolean isMulticastAddress()
            {

                return false;
            }

            public void readExternal(ObjectInput arg0) throws IOException,
                    ClassNotFoundException
            {


            }

            public void writeExternal(ObjectOutput arg0) throws IOException
            {


            }

            public int compareTo(Object arg0)
            {

                return 0;
            }

            public int size()
            {

                return 0;
            }

            public void writeTo(DataOutputStream arg0) throws IOException
            {


            }

            public void readFrom(DataInputStream arg0) throws IOException, IllegalAccessException, InstantiationException
            {


            }
        });
        //hack the method call to make it have the remote gtx
        MethodCall meth = (MethodCall) entry.getModifications().get(0);

        meth.getArgs()[0] = remoteGtx;
        //call our remote method
        MethodCall prepareMethod = MethodCallFactory.create(MethodDeclarations.optimisticPrepareMethod, new Object[]{remoteGtx, entry.getModifications(), null, remoteGtx.getAddress(), Boolean.FALSE});
        try
        {
            cache._replicate(prepareMethod);
        }
        catch (Throwable t)
        {
            fail();
        }

        //our thread should be null
        assertNull(mgr.getTransaction());

        //	 there should be a registration for the remote gtx
        assertNotNull(table.get(remoteGtx));
        assertNotNull(table.getLocalTransaction(remoteGtx));
        //assert that this is populated
        assertEquals(1, table.get(remoteGtx).getModifications().size());

        //assert that the remote prepare has populated the local workspace
        OptimisticTransactionEntry opEntry = (OptimisticTransactionEntry) table.get(gtx);

        assertEquals(3, entry.getTransactionWorkSpace().getNodes().size());
        assertEquals(1, entry.getModifications().size());
        List calls = dummy.getAllCalled();
        assertEquals(MethodDeclarations.optimisticPrepareMethod, calls.get(2));


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

        //	    call our remote method
        MethodCall rollbackMethod = MethodCallFactory.create(MethodDeclarations.rollbackMethod, new Object[]{remoteGtx});
        try
        {
            cache._replicate(rollbackMethod);
        }
        catch (Throwable t)
        {
            fail();
        }
        //we should have the commit as well now
        assertNull(mgr.getTransaction());
        assertEquals(MethodDeclarations.rollbackMethod, calls.get(3));
        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());

        cache.stopService();


    }


    public void testRemoteCommitNoPrepareTransaction() throws Exception
    {

        TreeCache cache = createCache();

        Interceptor txInterceptor = new TxInterceptor();
        txInterceptor.setCache(cache);
        Interceptor replicationInterceptor = new OptimisticReplicationInterceptor();
        replicationInterceptor.setCache(cache);
        Interceptor createInterceptor = new OptimisticCreateIfNotExistsInterceptor();
        createInterceptor.setCache(cache);
        OptimisticNodeInterceptor nodeInterceptor = new OptimisticNodeInterceptor();
        nodeInterceptor.setCache(cache);
        MockInterceptor dummy = new MockInterceptor();
        dummy.setCache(cache);

        txInterceptor.setNext(replicationInterceptor);
        replicationInterceptor.setNext(createInterceptor);
        createInterceptor.setNext(nodeInterceptor);
        nodeInterceptor.setNext(dummy);

        cache.setInterceptorChain(txInterceptor);

        DummyTransactionManager mgr = DummyTransactionManager.getInstance();

        //start local transaction
        mgr.begin();
        Transaction tx = mgr.getTransaction();

        //this sets
        cache.getCurrentTransaction(tx);

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

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

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


        GlobalTransaction remoteGtx = new GlobalTransaction();

        remoteGtx.setAddress(new Address()
        {
            public boolean isMulticastAddress()
            {

                return false;
            }

            public void readExternal(ObjectInput arg0) throws IOException,
                    ClassNotFoundException
            {


            }

            public void writeExternal(ObjectOutput arg0) throws IOException
            {


            }

            public int compareTo(Object arg0)
            {

                return 0;
            }

            public int size()
            {

                return 0;
            }

            public void writeTo(DataOutputStream arg0) throws IOException
            {


            }

            public void readFrom(DataInputStream arg0) throws IOException, IllegalAccessException, InstantiationException
            {


            }
        });
        //hack the method call to make it have the remote gtx
        MethodCall meth = (MethodCall) entry.getModifications().get(0);

        meth.getArgs()[0] = remoteGtx;


        List calls = dummy.getAllCalled();
        assertEquals(2, calls.size());


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

        //	    call our remote method
        MethodCall commitMethod = MethodCallFactory.create(MethodDeclarations.commitMethod, new Object[]{remoteGtx});
        try
        {
            cache._replicate(commitMethod);
            fail();
        }
        catch (Throwable t)
        {
            assertTrue(t instanceof RuntimeException);
            //t.printStackTrace();
        }
        //we should have the commit as well now
        assertNull(mgr.getTransaction());
        assertEquals(2, calls.size());
        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());

        cache.stopService();


    }


    public void testRemoteRollbackNoPrepareTransaction() throws Throwable
    {

        TreeCache cache = createCache();

        Interceptor txInterceptor = new TxInterceptor();
        txInterceptor.setCache(cache);
        Interceptor replicationInterceptor = new OptimisticReplicationInterceptor();
        replicationInterceptor.setCache(cache);
        Interceptor createInterceptor = new OptimisticCreateIfNotExistsInterceptor();
        createInterceptor.setCache(cache);
        OptimisticNodeInterceptor nodeInterceptor = new OptimisticNodeInterceptor();
        nodeInterceptor.setCache(cache);
        MockInterceptor dummy = new MockInterceptor();
        dummy.setCache(cache);

        txInterceptor.setNext(replicationInterceptor);
        replicationInterceptor.setNext(createInterceptor);
        createInterceptor.setNext(nodeInterceptor);
        nodeInterceptor.setNext(dummy);

        cache.setInterceptorChain(txInterceptor);

        DummyTransactionManager mgr = DummyTransactionManager.getInstance();

        //start local transaction
        mgr.begin();
        Transaction tx = mgr.getTransaction();

        //this sets
        cache.getCurrentTransaction(tx);

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

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

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


        GlobalTransaction remoteGtx = new GlobalTransaction();

        remoteGtx.setAddress(new Address()
        {
            public boolean isMulticastAddress()
            {

                return false;
            }

            public void readExternal(ObjectInput arg0) throws IOException,
                    ClassNotFoundException
            {


            }

            public void writeExternal(ObjectOutput arg0) throws IOException
            {


            }

            public int compareTo(Object arg0)
            {

                return 0;
            }

            public int size()
            {

                return 0;
            }

            public void writeTo(DataOutputStream arg0) throws IOException
            {


            }

            public void readFrom(DataInputStream arg0) throws IOException, IllegalAccessException, InstantiationException
            {


            }
        });
        //hack the method call to make it have the remote gtx
        MethodCall meth = (MethodCall) entry.getModifications().get(0);

        meth.getArgs()[0] = remoteGtx;


        List calls = dummy.getAllCalled();
        assertEquals(2, calls.size());


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

        //	    call our remote method
        MethodCall rollbackMethod = MethodCallFactory.create(MethodDeclarations.rollbackMethod, new Object[]{remoteGtx});

        cache._replicate(rollbackMethod);
        assertTrue("Should be handled on the remote end without barfing, in the event of a rollback without a prepare", true);

        //we should have the commit as well now
        assertNull(mgr.getTransaction());
        assertEquals(2, calls.size());
        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());


        cache.stopService();

    }


    public void testRemoteCommitTransaction() throws Exception
    {

        TreeCache cache = createCache();

        Interceptor txInterceptor = new TxInterceptor();
        txInterceptor.setCache(cache);
        Interceptor replicationInterceptor = new OptimisticReplicationInterceptor();
        replicationInterceptor.setCache(cache);
        Interceptor createInterceptor = new OptimisticCreateIfNotExistsInterceptor();
        createInterceptor.setCache(cache);
        OptimisticNodeInterceptor nodeInterceptor = new OptimisticNodeInterceptor();
        nodeInterceptor.setCache(cache);
        MockInterceptor dummy = new MockInterceptor();
        dummy.setCache(cache);

        txInterceptor.setNext(replicationInterceptor);
        replicationInterceptor.setNext(createInterceptor);
        createInterceptor.setNext(nodeInterceptor);
        nodeInterceptor.setNext(dummy);

        cache.setInterceptorChain(txInterceptor);

        DummyTransactionManager mgr = DummyTransactionManager.getInstance();

        //start local transaction
        mgr.begin();
        Transaction tx = mgr.getTransaction();

        //this sets
        cache.getCurrentTransaction(tx);

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

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

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


        GlobalTransaction remoteGtx = new GlobalTransaction();

        remoteGtx.setAddress(new Address()
        {
            public boolean isMulticastAddress()
            {

                return false;
            }

            public void readExternal(ObjectInput arg0) throws IOException,
                    ClassNotFoundException
            {


            }

            public int size()
            {

                return 0;
            }

            public void writeExternal(ObjectOutput arg0) throws IOException
            {


            }

            public void writeTo(DataOutputStream arg0) throws IOException
            {


            }

            public void readFrom(DataInputStream arg0) throws IOException, IllegalAccessException, InstantiationException
            {


            }

            public int compareTo(Object arg0)
            {

                return 0;
            }
        });
        //hack the method call to make it have the remote gtx
        MethodCall meth = (MethodCall) entry.getModifications().get(0);

        meth.getArgs()[0] = remoteGtx;
        //call our remote method
        MethodCall prepareMethod = MethodCallFactory.create(MethodDeclarations.optimisticPrepareMethod, new Object[]{remoteGtx, entry.getModifications(), null, remoteGtx.getAddress(), Boolean.FALSE});
        try
        {
            cache._replicate(prepareMethod);
        }
        catch (Throwable t)
        {
            fail();
        }

        //our thread should be null
        assertNull(mgr.getTransaction());

        //	 there should be a registration for the remote gtx
        assertNotNull(table.get(remoteGtx));
        assertNotNull(table.getLocalTransaction(remoteGtx));
        //assert that this is populated
        assertEquals(1, table.get(remoteGtx).getModifications().size());

        //assert that the remote prepare has populated the local workspace
        OptimisticTransactionEntry opEntry = (OptimisticTransactionEntry) table.get(gtx);

        assertEquals(3, entry.getTransactionWorkSpace().getNodes().size());
        assertEquals(1, entry.getModifications().size());
        List calls = dummy.getAllCalled();
        assertEquals(MethodDeclarations.optimisticPrepareMethod, calls.get(2));


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

        //	    call our remote method
        MethodCall commitMethod = MethodCallFactory.create(MethodDeclarations.commitMethod, new Object[]{remoteGtx});
        try
        {
            cache._replicate(commitMethod);
        }
        catch (Throwable t)
        {
            fail();
        }
        //we should have the commit as well now
        assertNull(mgr.getTransaction());
        assertEquals(MethodDeclarations.commitMethod, calls.get(3));
        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());


        cache.stopService();

    }


    public void testTwoWayRemoteCacheBroadcast() throws Exception
    {


        TreeCache cache = createReplicatedCache(TreeCache.REPL_SYNC);


        Interceptor txInterceptor = new TxInterceptor();
        txInterceptor.setCache(cache);
        Interceptor replicationInterceptor = new OptimisticReplicationInterceptor();
        replicationInterceptor.setCache(cache);
        Interceptor createInterceptor = new OptimisticCreateIfNotExistsInterceptor();
        createInterceptor.setCache(cache);
        OptimisticNodeInterceptor nodeInterceptor = new OptimisticNodeInterceptor();
        nodeInterceptor.setCache(cache);
        MockInterceptor dummy = new MockInterceptor();
        dummy.setCache(cache);

        txInterceptor.setNext(replicationInterceptor);
        replicationInterceptor.setNext(createInterceptor);
        createInterceptor.setNext(nodeInterceptor);
        nodeInterceptor.setNext(dummy);

        cache.setInterceptorChain(txInterceptor);


        TreeCache cache2 = createReplicatedCache(TreeCache.REPL_SYNC);


        Interceptor txInterceptor2 = new TxInterceptor();
        txInterceptor2.setCache(cache2);
        Interceptor replicationInterceptor2 = new OptimisticReplicationInterceptor();
        replicationInterceptor2.setCache(cache2);
        Interceptor createInterceptor2 = new OptimisticCreateIfNotExistsInterceptor();
        createInterceptor2.setCache(cache2);
        OptimisticNodeInterceptor nodeInterceptor2 = new OptimisticNodeInterceptor();
        nodeInterceptor2.setCache(cache2);
        MockInterceptor dummy2 = new MockInterceptor();
        dummy2.setCache(cache2);

        txInterceptor2.setNext(replicationInterceptor2);
        replicationInterceptor2.setNext(createInterceptor2);
        createInterceptor2.setNext(nodeInterceptor2);
        nodeInterceptor2.setNext(dummy2);

        cache2.setInterceptorChain(txInterceptor2);


        DummyTransactionManager mgr = DummyTransactionManager.getInstance();

        //start local transaction
        mgr.begin();
        Transaction tx = mgr.getTransaction();

        //this sets
        cache.getCurrentTransaction(tx);

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

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

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


        assertNull(mgr.getTransaction());

        //assert that the local cache is in the right state
        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());


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


        List calls = dummy.getAllCalled();
        assertEquals(MethodDeclarations.optimisticPrepareMethod, calls.get(0));
        assertEquals(MethodDeclarations.commitMethod, calls.get(1));

        List calls2 = dummy2.getAllCalled();
        assertEquals(MethodDeclarations.optimisticPrepareMethod, calls2.get(0));
        assertEquals(MethodDeclarations.commitMethod, calls2.get(1));

        cache.stopService();
        cache2.stopService();

    }


    public void testFailurePrepareRemoteCacheBroadcast() throws Exception
    {


        TreeCache cache = createReplicatedCache(TreeCache.REPL_SYNC);


        Interceptor txInterceptor = new TxInterceptor();
        txInterceptor.setCache(cache);
        Interceptor replicationInterceptor = new OptimisticReplicationInterceptor();
        replicationInterceptor.setCache(cache);
        Interceptor createInterceptor = new OptimisticCreateIfNotExistsInterceptor();
        createInterceptor.setCache(cache);
        OptimisticNodeInterceptor nodeInterceptor = new OptimisticNodeInterceptor();
        nodeInterceptor.setCache(cache);
        MockInterceptor dummy = new MockInterceptor();
        dummy.setCache(cache);

        txInterceptor.setNext(replicationInterceptor);
        replicationInterceptor.setNext(createInterceptor);
        createInterceptor.setNext(nodeInterceptor);
        nodeInterceptor.setNext(dummy);

        cache.setInterceptorChain(txInterceptor);


        TreeCache cache2 = createReplicatedCache(TreeCache.REPL_SYNC);


        Interceptor txInterceptor2 = new TxInterceptor();
        txInterceptor2.setCache(cache2);
        Interceptor replicationInterceptor2 = new OptimisticReplicationInterceptor();
        replicationInterceptor2.setCache(cache2);
        Interceptor createInterceptor2 = new OptimisticCreateIfNotExistsInterceptor();
        createInterceptor2.setCache(cache2);
        OptimisticNodeInterceptor nodeInterceptor2 = new OptimisticNodeInterceptor();
        nodeInterceptor2.setCache(cache2);
        MockFailureInterceptor dummy2 = new MockFailureInterceptor();
        List failures = new ArrayList();
        failures.add(MethodDeclarations.optimisticPrepareMethod);
        dummy2.setFailurelist(failures);
        dummy2.setCache(cache2);

        txInterceptor2.setNext(replicationInterceptor2);
        replicationInterceptor2.setNext(createInterceptor2);
        createInterceptor2.setNext(nodeInterceptor2);
        nodeInterceptor2.setNext(dummy2);

        cache2.setInterceptorChain(txInterceptor2);


        DummyTransactionManager mgr = DummyTransactionManager.getInstance();

        //start local transaction
        mgr.begin();
        Transaction tx = mgr.getTransaction();

        //this sets
        cache.getCurrentTransaction(tx);

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

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

        GlobalTransaction gtx = cache.getCurrentTransaction(tx);
        TransactionTable table = cache.getTransactionTable();
        OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx);
        assertNotNull(mgr.getTransaction());
        try
        {
            mgr.commit();
        }
        catch (Exception e)
        {
            assertTrue(e instanceof RollbackException);
        }


        assertNull(mgr.getTransaction());

        //assert that the local cache is in the right state
        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());


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


        List calls = dummy.getAllCalled();
        assertEquals(MethodDeclarations.optimisticPrepareMethod, calls.get(0));
        assertEquals(MethodDeclarations.rollbackMethod, calls.get(1));

        //we have no prepare - as it failed - but we have a commit
        List calls2 = dummy2.getAllCalled();
        assertEquals(MethodDeclarations.rollbackMethod, calls2.get(0));


        cache.stopService();
        cache2.stopService();

    }


    public void testFailurePrepareLocalCacheBroadcast() throws Exception
    {


        TreeCache cache = createReplicatedCache(TreeCache.REPL_SYNC);


        Interceptor txInterceptor = new TxInterceptor();
        txInterceptor.setCache(cache);
        Interceptor replicationInterceptor = new OptimisticReplicationInterceptor();
        replicationInterceptor.setCache(cache);
        Interceptor createInterceptor = new OptimisticCreateIfNotExistsInterceptor();
        createInterceptor.setCache(cache);
        OptimisticNodeInterceptor nodeInterceptor = new OptimisticNodeInterceptor();
        nodeInterceptor.setCache(cache);

        MockFailureInterceptor dummy = new MockFailureInterceptor();
        List failures = new ArrayList();
        failures.add(MethodDeclarations.optimisticPrepareMethod);
        dummy.setFailurelist(failures);
        dummy.setCache(cache);

        txInterceptor.setNext(replicationInterceptor);
        replicationInterceptor.setNext(createInterceptor);
        createInterceptor.setNext(nodeInterceptor);
        nodeInterceptor.setNext(dummy);

        cache.setInterceptorChain(txInterceptor);


        TreeCache cache2 = createReplicatedCache(TreeCache.REPL_SYNC);


        Interceptor txInterceptor2 = new TxInterceptor();
        txInterceptor2.setCache(cache2);
        Interceptor replicationInterceptor2 = new OptimisticReplicationInterceptor();
        replicationInterceptor2.setCache(cache2);
        Interceptor createInterceptor2 = new OptimisticCreateIfNotExistsInterceptor();
        createInterceptor2.setCache(cache2);
        OptimisticNodeInterceptor nodeInterceptor2 = new OptimisticNodeInterceptor();
        nodeInterceptor2.setCache(cache2);

        MockInterceptor dummy2 = new MockInterceptor();
        dummy2.setCache(cache2);


        txInterceptor2.setNext(replicationInterceptor2);
        replicationInterceptor2.setNext(createInterceptor2);
        createInterceptor2.setNext(nodeInterceptor2);
        nodeInterceptor2.setNext(dummy2);

        cache2.setInterceptorChain(txInterceptor2);


        DummyTransactionManager mgr = DummyTransactionManager.getInstance();

        //start local transaction
        mgr.begin();
        Transaction tx = mgr.getTransaction();

        //this sets
        cache.getCurrentTransaction(tx);

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

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

        GlobalTransaction gtx = cache.getCurrentTransaction(tx);
        TransactionTable table = cache.getTransactionTable();
        OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx);
        assertNotNull(mgr.getTransaction());
        try
        {
            mgr.commit();
        }
        catch (Exception e)
        {
            assertTrue(e instanceof RollbackException);
        }


        assertNull(mgr.getTransaction());

        //assert that the local cache is in the right state
        assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions());
        assertEquals(0, cache.getTransactionTable().getNumLocalTransactions());


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


        List calls = dummy.getAllCalled();
        assertEquals(MethodDeclarations.rollbackMethod, calls.get(0));

        //we have no prepare - as it failed - but we have a commit
        List calls2 = dummy2.getAllCalled();
        assertEquals(0, calls2.size());


        cache.stopService();
        cache2.stopService();

    }
}
