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

import org.jboss.cache.*;
import org.jboss.cache.interceptors.Interceptor;
import org.jboss.cache.interceptors.OptimisticCreateIfNotExistsInterceptor;
import org.jboss.cache.interceptors.OptimisticNodeInterceptor;
import org.jboss.cache.interceptors.OptimisticValidatorInterceptor;
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.blocks.MethodCall;

import javax.transaction.Transaction;
import java.util.HashMap;
import java.util.Map;

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


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

    public void testTransactionvalidateMethod() throws Exception
    {
        TreeCache cache = createCacheWithListener();

        Interceptor validateInterceptor = new OptimisticValidatorInterceptor();
        validateInterceptor.setCache(cache);
        Interceptor interceptor = new OptimisticCreateIfNotExistsInterceptor();
        interceptor.setCache(cache);
        Interceptor nodeInterceptor = new OptimisticNodeInterceptor();
        nodeInterceptor.setCache(cache);
        MockInterceptor dummy = new MockInterceptor();
        dummy.setCache(cache);
        validateInterceptor.setNext(interceptor);
        interceptor.setNext(nodeInterceptor);
        nodeInterceptor.setNext(dummy);

        cache.setInterceptorChain(validateInterceptor);

//		 first set up a node with a pojo
        DummyTransactionManager mgr = DummyTransactionManager.getInstance();
        mgr.begin();
        Transaction tx = mgr.getTransaction();

        // inject InvocationContext
        cache.getInvocationContext().setTransaction(tx);
        cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction(tx));

        SamplePojo pojo = new SamplePojo(21, "test");
        Map temp = new HashMap();
        temp.put("key1", pojo);
        cache.put("/one/two", temp);

        assertEquals(null, dummy.getCalled());
        TransactionTable table = cache.getTransactionTable();

        GlobalTransaction gtx = table.get(tx);

        OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx);

        TransactionWorkspace workspace = entry.getTransactionWorkSpace();

        /*GlobalTransaction.class,
        List.class,
        Address.class,
        boolean.class*/

        assertEquals(3, workspace.getNodes().size());
        assertNotNull(workspace.getNode(Fqn.fromString("/one/two")));
        assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1"));
        assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size());
        assertTrue(entry.getLocks().isEmpty());
        assertEquals(1, entry.getModifications().size());
        assertTrue(!cache.exists("/one/two"));
        assertEquals(null, dummy.getCalled());

        //now let us do a prepare
        MethodCall prepareMethod = MethodCallFactory.create(MethodDeclarations.optimisticPrepareMethod, new Object[]{gtx, entry.getModifications(), gtx.getAddress(), Boolean.FALSE});
        try
        {
            cache._replicate(prepareMethod);
        }
        catch (Throwable t)
        {

        }


        assertEquals(3, workspace.getNodes().size());
        assertNotNull(workspace.getNode(Fqn.fromString("/one/two")));
        assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1"));
        assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size());
        assertTrue(entry.getLocks().isEmpty());
        assertEquals(1, entry.getModifications().size());
        assertTrue(!cache.exists("/one/two"));
        assertEquals(null, dummy.getCalled());


        mgr.commit();

        destroyCache(cache);
    }

    public void testTransactionValidateFailureMethod() throws Exception
    {

        TreeCache cache = createCacheWithListener();

        Interceptor validateInterceptor = new OptimisticValidatorInterceptor();
        validateInterceptor.setCache(cache);
        Interceptor interceptor = new OptimisticCreateIfNotExistsInterceptor();
        interceptor.setCache(cache);
        Interceptor nodeInterceptor = new OptimisticNodeInterceptor();
        nodeInterceptor.setCache(cache);
        MockInterceptor dummy = new MockInterceptor();
        dummy.setCache(cache);
        validateInterceptor.setNext(interceptor);
        interceptor.setNext(nodeInterceptor);
        nodeInterceptor.setNext(dummy);

        cache.setInterceptorChain(validateInterceptor);

//		 first set up a node with a pojo
        DummyTransactionManager mgr = DummyTransactionManager.getInstance();
        mgr.begin();
        Transaction tx = mgr.getTransaction();

        // inject InvocationContext
        cache.getInvocationContext().setTransaction(tx);
        cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction(tx));

        SamplePojo pojo = new SamplePojo(21, "test");
        Map temp = new HashMap();
        temp.put("key1", pojo);
        cache.put("/one/two", temp);

        assertEquals(null, dummy.getCalled());
        TransactionTable table = cache.getTransactionTable();

        GlobalTransaction gtx = table.get(tx);

        OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx);

        TransactionWorkspace workspace = entry.getTransactionWorkSpace();

        /*GlobalTransaction.class,
List.class,
Address.class,
boolean.class*/

        assertEquals(3, workspace.getNodes().size());
        assertNotNull(workspace.getNode(Fqn.fromString("/one/two")));
        assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1"));
        assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size());
        assertTrue(entry.getLocks().isEmpty());
        assertEquals(1, entry.getModifications().size());
        assertTrue(!cache.exists("/one/two"));
        assertEquals(null, dummy.getCalled());

        //lets change one of the underlying version numbers
        ((OptimisticTreeNode) workspace.getNode(Fqn.fromString("/one/two")).getNode()).setVersion(new DefaultDataVersion(2));
        //now let us do a prepare
        MethodCall prepareMethod = MethodCallFactory.create(MethodDeclarations.optimisticPrepareMethod, new Object[]{gtx, entry.getModifications(), gtx.getAddress(), Boolean.FALSE});
        try
        {
            cache._replicate(prepareMethod);
            fail();
        }
        catch (Throwable t)
        {
            assertTrue(true);
        }


        mgr.commit();

        destroyCache(cache);
    }

    public void testTransactionValidateCommitMethod() throws Exception
    {

        TreeCache cache = createCacheWithListener();

        Interceptor validateInterceptor = new OptimisticValidatorInterceptor();
        validateInterceptor.setCache(cache);
        Interceptor interceptor = new OptimisticCreateIfNotExistsInterceptor();
        interceptor.setCache(cache);
        Interceptor nodeInterceptor = new OptimisticNodeInterceptor();
        nodeInterceptor.setCache(cache);
        MockInterceptor dummy = new MockInterceptor();
        dummy.setCache(cache);
        validateInterceptor.setNext(interceptor);
        interceptor.setNext(nodeInterceptor);
        nodeInterceptor.setNext(dummy);

        cache.setInterceptorChain(validateInterceptor);

//		 first set up a node with a pojo
        DummyTransactionManager mgr = DummyTransactionManager.getInstance();
        mgr.begin();
        Transaction tx = mgr.getTransaction();

        // inject InvocationContext
        cache.getInvocationContext().setTransaction(tx);
        cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction(tx));

        SamplePojo pojo = new SamplePojo(21, "test");
        Map temp = new HashMap();
        temp.put("key1", pojo);
        cache.put("/one/two", temp);

        assertEquals(null, dummy.getCalled());
        TransactionTable table = cache.getTransactionTable();

        GlobalTransaction gtx = table.get(tx);

        OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx);

        TransactionWorkspace workspace = entry.getTransactionWorkSpace();

        /*GlobalTransaction.class,
List.class,
Address.class,
boolean.class*/

        assertEquals(3, workspace.getNodes().size());
        assertNotNull(workspace.getNode(Fqn.fromString("/one/two")));
        assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1"));
        assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size());
        assertTrue(entry.getLocks().isEmpty());
        assertEquals(1, entry.getModifications().size());
        assertTrue(!cache.exists("/one/two"));
        assertEquals(null, dummy.getCalled());

        //lets change one of the underlying version numbers
        //now let us do a prepare
        MethodCall prepareMethod = MethodCallFactory.create(MethodDeclarations.optimisticPrepareMethod, new Object[]{gtx, entry.getModifications(), gtx.getAddress(), Boolean.FALSE});
        try
        {
            cache._replicate(prepareMethod);
            fail();
        }
        catch (Throwable t)
        {
            assertTrue(true);
        }

        MethodCall commitMethod = MethodCallFactory.create(MethodDeclarations.commitMethod, new Object[]{gtx});
        try
        {
            cache._replicate(commitMethod);
        }
        catch (Throwable t)
        {
            fail();
        }


        assertEquals(3, workspace.getNodes().size());
        assertNotNull(workspace.getNode(Fqn.fromString("/one/two")));
        assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1"));
        assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size());
        assertTrue(entry.getLocks().isEmpty());
        assertEquals(1, entry.getModifications().size());


        assertEquals(null, dummy.getCalled());
        OptimisticTreeNode node = (OptimisticTreeNode) workspace.getNode(Fqn.fromString("/")).getNode();
        //assert we can navigate

        assertNotNull(node);
        node = (OptimisticTreeNode) node.getChild("one");
        assertEquals(new DefaultDataVersion(1), node.getVersion());
        assertNotNull(node);
        assertTrue(cache.exists(node.getFqn()));
        assertEquals(new DefaultDataVersion(1), node.getVersion());
        node = (OptimisticTreeNode) node.getChild("two");
        assertNotNull(node);
        assertTrue(cache.exists(node.getFqn()));
        assertEquals(new DefaultDataVersion(1), node.getVersion());

        assertEquals(pojo, node.get("key1"));

        mgr.commit();

        destroyCache(cache);
    }


    public void testTransactionValidateFailRemoteCommitMethod() throws Exception
    {

        TreeCache cache = createCacheWithListener();

        Interceptor validateInterceptor = new OptimisticValidatorInterceptor();
        validateInterceptor.setCache(cache);
        Interceptor interceptor = new OptimisticCreateIfNotExistsInterceptor();
        interceptor.setCache(cache);
        Interceptor nodeInterceptor = new OptimisticNodeInterceptor();
        nodeInterceptor.setCache(cache);
        MockInterceptor dummy = new MockInterceptor();
        dummy.setCache(cache);
        validateInterceptor.setNext(interceptor);
        interceptor.setNext(nodeInterceptor);
        nodeInterceptor.setNext(dummy);

        cache.setInterceptorChain(validateInterceptor);

//		 first set up a node with a pojo
        DummyTransactionManager mgr = DummyTransactionManager.getInstance();
        mgr.begin();
        Transaction tx = mgr.getTransaction();

        // inject InvocationContext
        cache.getInvocationContext().setTransaction(tx);
        cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction(tx));

        SamplePojo pojo = new SamplePojo(21, "test");
        Map temp = new HashMap();
        temp.put("key1", pojo);
        cache.put("/one/two", temp);

        assertEquals(null, dummy.getCalled());
        TransactionTable table = cache.getTransactionTable();

        GlobalTransaction gtx = table.get(tx);

        OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx);

        TransactionWorkspace workspace = entry.getTransactionWorkSpace();

        assertEquals(3, workspace.getNodes().size());
        assertNotNull(workspace.getNode(Fqn.fromString("/one/two")));
        assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1"));
        assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size());
        assertTrue(entry.getLocks().isEmpty());
        assertEquals(1, entry.getModifications().size());
        assertTrue(!cache.exists("/one/two"));
        assertEquals(null, dummy.getCalled());

        //lets change one of the underlying version numbers
        //now let us do a prepare
        MethodCall prepareMethod = MethodCallFactory.create(MethodDeclarations.optimisticPrepareMethod, new Object[]{gtx, entry.getModifications(), gtx.getAddress(), Boolean.FALSE});
        try
        {
            cache._replicate(prepareMethod);
            fail();
        }
        catch (Throwable t)
        {
            assertTrue(true);
        }


        MethodCall commitMethod = MethodCallFactory.create(MethodDeclarations.commitMethod, new Object[]{gtx});
        try
        {
            cache._replicate(commitMethod);
        }
        catch (Throwable t)
        {
            fail();
        }


        assertEquals(3, workspace.getNodes().size());
        assertNotNull(workspace.getNode(Fqn.fromString("/one/two")));
        assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1"));
        assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size());
        assertTrue(entry.getLocks().isEmpty());
        assertEquals(1, entry.getModifications().size());


        assertEquals(null, dummy.getCalled());
        OptimisticTreeNode node = (OptimisticTreeNode) workspace.getNode(Fqn.fromString("/")).getNode();
        //assert we can navigate

        assertNotNull(node);
        node = (OptimisticTreeNode) node.getChild("one");
        assertEquals(new DefaultDataVersion(1), node.getVersion());
        assertNotNull(node);
        assertTrue(cache.exists(node.getFqn()));
        assertEquals(new DefaultDataVersion(1), node.getVersion());
        node = (OptimisticTreeNode) node.getChild("two");
        assertNotNull(node);
        assertTrue(cache.exists(node.getFqn()));
        assertEquals(new DefaultDataVersion(1), node.getVersion());

        assertEquals(pojo, node.get("key1"));

        mgr.commit();

        destroyCache(cache);
    }

    public void testTransactionValidateRollbackMethod() throws Exception
    {

        TreeCache cache = createCacheWithListener();
        Interceptor validateInterceptor = new OptimisticValidatorInterceptor();
        validateInterceptor.setCache(cache);
        Interceptor interceptor = new OptimisticCreateIfNotExistsInterceptor();
        interceptor.setCache(cache);
        Interceptor nodeInterceptor = new OptimisticNodeInterceptor();
        nodeInterceptor.setCache(cache);
        MockInterceptor dummy = new MockInterceptor();
        dummy.setCache(cache);
        validateInterceptor.setNext(interceptor);
        interceptor.setNext(nodeInterceptor);
        nodeInterceptor.setNext(dummy);

        cache.setInterceptorChain(validateInterceptor);

//		 first set up a node with a pojo
        DummyTransactionManager mgr = DummyTransactionManager.getInstance();
        mgr.begin();
        Transaction tx = mgr.getTransaction();

        // inject InvocationContext
        cache.getInvocationContext().setTransaction(tx);
        cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction(tx));

        SamplePojo pojo = new SamplePojo(21, "test");
        Map temp = new HashMap();
        temp.put("key1", pojo);
        cache.put("/one/two", temp);

        assertEquals(null, dummy.getCalled());
        TransactionTable table = cache.getTransactionTable();

        GlobalTransaction gtx = table.get(tx);

        OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx);

        TransactionWorkspace workspace = entry.getTransactionWorkSpace();

        /*GlobalTransaction.class,
List.class,
Address.class,
boolean.class*/

        assertEquals(3, workspace.getNodes().size());
        assertNotNull(workspace.getNode(Fqn.fromString("/one/two")));
        assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1"));
        assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size());
        assertTrue(entry.getLocks().isEmpty());
        assertEquals(1, entry.getModifications().size());
        assertTrue(!cache.exists("/one/two"));
        assertEquals(null, dummy.getCalled());

        //lets change one of the underlying version numbers
        //now let us do a prepare
        MethodCall prepareMethod = MethodCallFactory.create(MethodDeclarations.optimisticPrepareMethod, new Object[]{gtx, entry.getModifications(), gtx.getAddress(), Boolean.FALSE});
        try
        {
            cache._replicate(prepareMethod);
            fail();
        }
        catch (Throwable t)
        {
            assertTrue(true);
        }

        MethodCall rollbackMethod = MethodCallFactory.create(MethodDeclarations.rollbackMethod, new Object[]{gtx});
        try
        {
            cache._replicate(rollbackMethod);
        }
        catch (Throwable t)
        {
            fail();
        }


        assertEquals(0, workspace.getNodes().size());
        assertNull(workspace.getNode(Fqn.fromString("/one/two")));
        assertNull(workspace.getNode(Fqn.fromString("/one")));
        assertTrue(entry.getLocks().isEmpty());
        assertEquals(1, entry.getModifications().size());

        mgr.commit();

        destroyCache(cache);
    }

}
