package org.jboss.cache.factories;

import org.jboss.cache.Cache;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.DefaultCacheFactory;
import org.jboss.cache.config.BuddyReplicationConfig;
import org.jboss.cache.config.CacheLoaderConfig;
import org.jboss.cache.config.EvictionConfig;
import org.jboss.cache.interceptors.*;
import org.jboss.cache.interceptors.base.CommandInterceptor;
import org.jboss.cache.util.TestingUtil;
import org.jboss.cache.xml.XmlHelper;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertNotNull;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import org.w3c.dom.Element;

import java.util.Iterator;
import java.util.List;

@Test(groups = {"functional"})
public class InterceptorChainFactoryTest extends InterceptorChainTestBase
{
   CacheSPI cache = null;

   @BeforeMethod(alwaysRun = true)
   public void setUp() throws Exception
   {
      cache = (CacheSPI) new DefaultCacheFactory().createCache(false);
      cache.getConfiguration().setCacheMode("LOCAL");
      cache.getConfiguration().setUseLazyDeserialization(false);
   }

   @AfterMethod(alwaysRun = true)
   public void tearDown() throws Exception
   {
      if (cache != null)
      {
         cache.stop();
         cache.destroy();
      }
   }

   public void testBareConfig() throws Exception
   {
      cache.getConfiguration().setExposeManagementStatistics(false);
      InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain();
      List<CommandInterceptor> list = chain.asList();
      Iterator<CommandInterceptor> interceptors = list.iterator();

      System.out.println("testBareConfig interceptors are:\n" + list);
      assertNotNull(list);
      assertEquals(5, list.size());

      assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass());
      assertEquals(TxInterceptor.class, interceptors.next().getClass());
      assertEquals(NotificationInterceptor.class, interceptors.next().getClass());
      assertEquals(PessimisticLockInterceptor.class, interceptors.next().getClass());
      assertEquals(CallInterceptor.class, interceptors.next().getClass());

      assertInterceptorLinkage(list);
   }


   public void testTxConfig() throws Exception
   {
      cache.getConfiguration().setExposeManagementStatistics(false);
      cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup");

      InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain();
      List<CommandInterceptor> list = chain.asList();
      Iterator<CommandInterceptor> interceptors = list.iterator();

      System.out.println("testTxConfig interceptors are:\n" + list);
      assertNotNull(list);
      assertEquals(5, list.size());

      assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass());
      assertEquals(TxInterceptor.class, interceptors.next().getClass());
      assertEquals(NotificationInterceptor.class, interceptors.next().getClass());
      assertEquals(PessimisticLockInterceptor.class, interceptors.next().getClass());
      assertEquals(CallInterceptor.class, interceptors.next().getClass());

      assertInterceptorLinkage(list);
   }

   protected CacheLoaderConfig getCacheLoaderConfig(boolean pasv, boolean fetchPersistentState) throws Exception
   {
      String xml = "            <config>\n" +
            "                \n" +
            "                <passivation>" + pasv + "</passivation>\n" +
            "                <preload></preload>\n" +
            "\n" +
            "                <cacheloader>\n" +
            "                    <class>org.jboss.cache.loader.FileCacheLoader</class>\n" +
            "                    <properties>\n" +
            "                        location=/tmp\n" +
            "                    </properties>\n" +
            "                    <async>false</async>\n" +
            "                    <fetchPersistentState>" + fetchPersistentState + "</fetchPersistentState>\n" +
            "                    <ignoreModifications>false</ignoreModifications>\n" +
            "                </cacheloader>\n" +
            "                \n" +
            "            </config>";
      Element element = XmlHelper.stringToElement(xml);
      return XmlConfigurationParser.parseCacheLoaderConfig(element);
   }

   public void testSharedCacheLoaderConfig() throws Exception
   {
      cache.getConfiguration().setExposeManagementStatistics(false);
      cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup");
      cache.getConfiguration().setCacheLoaderConfig(getCacheLoaderConfig(false, false));
      cache.getConfiguration().setCacheMode("REPL_ASYNC");
      cache.getConfiguration().setFetchInMemoryState(false);
      cache.create();
      InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain();
      List<CommandInterceptor> list = chain.asList();
      Iterator<CommandInterceptor> interceptors = list.iterator();

      System.out.println("testSharedCacheLoaderConfig interceptors are:\n" + list);
      assertNotNull(list);

      assertEquals(8, list.size());

      assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass());
      assertEquals(TxInterceptor.class, interceptors.next().getClass());
      assertEquals(NotificationInterceptor.class, interceptors.next().getClass());
      assertEquals(ReplicationInterceptor.class, interceptors.next().getClass());
      assertEquals(PessimisticLockInterceptor.class, interceptors.next().getClass());
      assertEquals(CacheLoaderInterceptor.class, interceptors.next().getClass());
      assertEquals(CacheStoreInterceptor.class, interceptors.next().getClass());
      assertEquals(CallInterceptor.class, interceptors.next().getClass());

      assertInterceptorLinkage(list);
   }

   public void testUnsharedCacheLoaderConfig() throws Exception
   {
      cache.getConfiguration().setExposeManagementStatistics(false);
      cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup");
      cache.getConfiguration().setCacheLoaderConfig(getCacheLoaderConfig(false, true));
      cache.getConfiguration().setCacheMode("REPL_ASYNC");
      cache.getConfiguration().setFetchInMemoryState(false);
      cache.create();
      InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain();
      List<CommandInterceptor> list = chain.asList();
      Iterator<CommandInterceptor> interceptors = list.iterator();

      System.out.println("testUnsharedCacheLoaderConfig interceptors are:\n" + list);
      assertNotNull(list);

      assertEquals(8, list.size());

      assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass());
      assertEquals(TxInterceptor.class, interceptors.next().getClass());
      assertEquals(NotificationInterceptor.class, interceptors.next().getClass());
      assertEquals(ReplicationInterceptor.class, interceptors.next().getClass());
      assertEquals(PessimisticLockInterceptor.class, interceptors.next().getClass());
      assertEquals(CacheLoaderInterceptor.class, interceptors.next().getClass());
      assertEquals(CacheStoreInterceptor.class, interceptors.next().getClass());
      assertEquals(CallInterceptor.class, interceptors.next().getClass());

      assertInterceptorLinkage(list);
   }

   public void testTxAndRepl() throws Exception
   {
      cache.getConfiguration().setExposeManagementStatistics(false);
      cache.getConfiguration().setCacheMode("repl_sync");
      cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup");
      InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain();
      List<CommandInterceptor> list = chain.asList();
      Iterator<CommandInterceptor> interceptors = list.iterator();

      System.out.println("testTxAndRepl interceptors are:\n" + list);
      assertNotNull(list);

      assertEquals(6, list.size());

      assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass());
      assertEquals(TxInterceptor.class, interceptors.next().getClass());
      assertEquals(NotificationInterceptor.class, interceptors.next().getClass());
      assertEquals(ReplicationInterceptor.class, interceptors.next().getClass());
      assertEquals(PessimisticLockInterceptor.class, interceptors.next().getClass());
      assertEquals(CallInterceptor.class, interceptors.next().getClass());

      assertInterceptorLinkage(list);
   }


   public void testOptimisticChain() throws Exception
   {
      cache.getConfiguration().setExposeManagementStatistics(false);
      cache.getConfiguration().setNodeLockingOptimistic(true);

      InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain();
      List<CommandInterceptor> list = chain.asList();
      Iterator<CommandInterceptor> interceptors = list.iterator();

      assertEquals(8, list.size());

      assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticTxInterceptor.class, interceptors.next().getClass());
      assertEquals(NotificationInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticLockingInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticValidatorInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticCreateIfNotExistsInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticNodeInterceptor.class, interceptors.next().getClass());
      assertEquals(CallInterceptor.class, interceptors.next().getClass());

      assertInterceptorLinkage(list);
   }

   public void testOptimisticReplicatedChain() throws Exception
   {
      cache.getConfiguration().setExposeManagementStatistics(false);
      cache.getConfiguration().setNodeLockingOptimistic(true);
      cache.getConfiguration().setCacheMode("REPL_SYNC");

      InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain();
      List<CommandInterceptor> list = chain.asList();
      Iterator<CommandInterceptor> interceptors = list.iterator();

      assertEquals(9, list.size());

      assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticTxInterceptor.class, interceptors.next().getClass());
      assertEquals(NotificationInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticReplicationInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticLockingInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticValidatorInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticCreateIfNotExistsInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticNodeInterceptor.class, interceptors.next().getClass());
      assertEquals(CallInterceptor.class, interceptors.next().getClass());

      assertInterceptorLinkage(list);
   }

   public void testOptimisticCacheLoaderChain() throws Exception
   {
      cache.getConfiguration().setExposeManagementStatistics(false);
      cache.getConfiguration().setNodeLockingOptimistic(true);
      cache.getConfiguration().setCacheLoaderConfig(getCacheLoaderConfig(false, false));
      cache.create();
      InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain();
      List<CommandInterceptor> list = chain.asList();
      Iterator<CommandInterceptor> interceptors = list.iterator();

      assertEquals(10, list.size());

      assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticTxInterceptor.class, interceptors.next().getClass());
      assertEquals(NotificationInterceptor.class, interceptors.next().getClass());
      assertEquals(CacheLoaderInterceptor.class, interceptors.next().getClass());
      assertEquals(CacheStoreInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticLockingInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticValidatorInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticCreateIfNotExistsInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticNodeInterceptor.class, interceptors.next().getClass());
      assertEquals(CallInterceptor.class, interceptors.next().getClass());

      assertInterceptorLinkage(list);
   }

   public void testOptimisticPassivationCacheLoaderChain() throws Exception
   {
      cache.getConfiguration().setExposeManagementStatistics(false);
      cache.getConfiguration().setNodeLockingOptimistic(true);
      cache.getConfiguration().setCacheLoaderConfig(getCacheLoaderConfig(true, false));
      cache.create();
      InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain();
      List<CommandInterceptor> list = chain.asList();
      Iterator<CommandInterceptor> interceptors = list.iterator();

      assertEquals(10, list.size());

      assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticTxInterceptor.class, interceptors.next().getClass());
      assertEquals(NotificationInterceptor.class, interceptors.next().getClass());
      assertEquals(ActivationInterceptor.class, interceptors.next().getClass());
      assertEquals(PassivationInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticLockingInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticValidatorInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticCreateIfNotExistsInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticNodeInterceptor.class, interceptors.next().getClass());
      assertEquals(CallInterceptor.class, interceptors.next().getClass());

      assertInterceptorLinkage(list);
   }

   public void testInvalidationInterceptorChain() throws Exception
   {
      cache.getConfiguration().setExposeManagementStatistics(false);
      cache.getConfiguration().setCacheMode("REPL_ASYNC");

      InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain();
      List<CommandInterceptor> list = chain.asList();
      Iterator<CommandInterceptor> interceptors = list.iterator();

      assertEquals(6, list.size());

      assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass());
      assertEquals(TxInterceptor.class, interceptors.next().getClass());
      assertEquals(NotificationInterceptor.class, interceptors.next().getClass());
      assertEquals(ReplicationInterceptor.class, interceptors.next().getClass());
      assertEquals(PessimisticLockInterceptor.class, interceptors.next().getClass());
      assertEquals(CallInterceptor.class, interceptors.next().getClass());

      // ok, my replication chain looks good.

      // now for my invalidation chain.
      cache.getConfiguration().setExposeManagementStatistics(false);
      cache.getConfiguration().setCacheMode("INVALIDATION_ASYNC");
      chain = getInterceptorChainFactory(cache).buildInterceptorChain();
      list = chain.asList();
      interceptors = list.iterator();

      assertEquals(6, list.size());

      assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass());
      assertEquals(TxInterceptor.class, interceptors.next().getClass());
      assertEquals(NotificationInterceptor.class, interceptors.next().getClass());
      assertEquals(InvalidationInterceptor.class, interceptors.next().getClass());
      assertEquals(PessimisticLockInterceptor.class, interceptors.next().getClass());
      assertEquals(CallInterceptor.class, interceptors.next().getClass());

      assertInterceptorLinkage(list);
   }

   public void testCacheMgmtConfig() throws Exception
   {
      cache.getConfiguration().setExposeManagementStatistics(true);
      InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain();
      List<CommandInterceptor> list = chain.asList();
      Iterator<CommandInterceptor> interceptors = list.iterator();

      System.out.println("testCacheMgmtConfig interceptors are:\n" + list);
      assertNotNull(list);
      assertEquals(6, list.size());

      assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass());
      assertEquals(CacheMgmtInterceptor.class, interceptors.next().getClass());
      assertEquals(TxInterceptor.class, interceptors.next().getClass());
      assertEquals(NotificationInterceptor.class, interceptors.next().getClass());
      assertEquals(PessimisticLockInterceptor.class, interceptors.next().getClass());
      assertEquals(CallInterceptor.class, interceptors.next().getClass());

      assertInterceptorLinkage(list);

   }

   public void testEvictionInterceptorConfig() throws Exception
   {
      cache.getConfiguration().setEvictionConfig(new EvictionConfig()
      {
         private static final long serialVersionUID = -6644183636899605065L;

         public boolean isValidConfig()
         {
            return true;
         }
      }
      );
      InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain();
      List<CommandInterceptor> list = chain.asList();
      Iterator<CommandInterceptor> interceptors = list.iterator();

      System.out.println("testEvictionInterceptorConfig interceptors are:\n" + list);
      assertNotNull(list);
      assertEquals(7, list.size());

      assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass());
      assertEquals(CacheMgmtInterceptor.class, interceptors.next().getClass());
      assertEquals(TxInterceptor.class, interceptors.next().getClass());
      assertEquals(NotificationInterceptor.class, interceptors.next().getClass());
      assertEquals(PessimisticLockInterceptor.class, interceptors.next().getClass());
      assertEquals(EvictionInterceptor.class, interceptors.next().getClass());
      assertEquals(CallInterceptor.class, interceptors.next().getClass());

      assertInterceptorLinkage(list);
   }

   public void testBuddyReplicationOptLocking() throws Exception
   {
      String xmlString = "<config><buddyReplicationEnabled>true</buddyReplicationEnabled>\n" +
            "<buddyCommunicationTimeout>600000</buddyCommunicationTimeout>\n" +
            "          <buddyLocatorClass>org.jboss.cache.buddyreplication.NextMemberBuddyLocator</buddyLocatorClass>\n" +
            "          <buddyLocatorProperties>numBuddies = 1</buddyLocatorProperties>\n";

      xmlString += "<buddyPoolName>buddyPoolName</buddyPoolName>";
      xmlString += "</config>";
      Element element = XmlHelper.stringToElement(xmlString);
      BuddyReplicationConfig brc = XmlConfigurationParser.parseBuddyReplicationConfig(element);
      cache.getConfiguration().setBuddyReplicationConfig(brc);

      cache.getConfiguration().setCacheMode("REPL_SYNC");
      cache.getConfiguration().setNodeLockingScheme("OPTIMISTIC");
      cache.create();// initialise various subsystems such as BRManager
      InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain();
      List<CommandInterceptor> list = chain.asList();
      Iterator<CommandInterceptor> interceptors = list.iterator();

      System.out.println("testEvictionInterceptorConfig interceptors are:\n" + list);
      assertNotNull(list);
      assertEquals(11, list.size());

      assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass());
      assertEquals(CacheMgmtInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticTxInterceptor.class, interceptors.next().getClass());
      assertEquals(NotificationInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticReplicationInterceptor.class, interceptors.next().getClass());
      assertEquals(DataGravitatorInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticLockingInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticValidatorInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticCreateIfNotExistsInterceptor.class, interceptors.next().getClass());
      assertEquals(OptimisticNodeInterceptor.class, interceptors.next().getClass());
      assertEquals(CallInterceptor.class, interceptors.next().getClass());

      assertInterceptorLinkage(list);
   }

   public void testBuddyReplicationPessLocking() throws Exception
   {
      String xmlString = "<config><buddyReplicationEnabled>true</buddyReplicationEnabled>\n" +
            "<buddyCommunicationTimeout>600000</buddyCommunicationTimeout>\n" +
            "          <buddyLocatorClass>org.jboss.cache.buddyreplication.NextMemberBuddyLocator</buddyLocatorClass>\n" +
            "          <buddyLocatorProperties>numBuddies = 1</buddyLocatorProperties>\n";

      xmlString += "<buddyPoolName>buddyPoolName</buddyPoolName>";
      xmlString += "</config>";
      Element element = XmlHelper.stringToElement(xmlString);
      BuddyReplicationConfig brc = XmlConfigurationParser.parseBuddyReplicationConfig(element);
      cache.getConfiguration().setBuddyReplicationConfig(brc);

      cache.getConfiguration().setCacheMode("REPL_SYNC");
      cache.create();// initialise various subsystems such as BRManager
      InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain();
      List<CommandInterceptor> list = chain.asList();
      Iterator<CommandInterceptor> interceptors = list.iterator();

      System.out.println("testEvictionInterceptorConfig interceptors are:\n" + list);
      assertNotNull(list);
      assertEquals(8, list.size());

      assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass());
      assertEquals(CacheMgmtInterceptor.class, interceptors.next().getClass());
      assertEquals(TxInterceptor.class, interceptors.next().getClass());
      assertEquals(NotificationInterceptor.class, interceptors.next().getClass());
      assertEquals(ReplicationInterceptor.class, interceptors.next().getClass());
      assertEquals(PessimisticLockInterceptor.class, interceptors.next().getClass());
      assertEquals(DataGravitatorInterceptor.class, interceptors.next().getClass());
      assertEquals(CallInterceptor.class, interceptors.next().getClass());

      assertInterceptorLinkage(list);
   }

   private InterceptorChainFactory getInterceptorChainFactory(Cache cache)
   {
      return InterceptorChainFactory.getInstance(TestingUtil.extractComponentRegistry(cache), cache.getConfiguration());
   }
}
