/*
 *
 * JBoss, the OpenSource J2EE webOS
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.cache.replicated;

import org.jboss.cache.CacheFactory;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.DefaultCacheFactory;
import org.jboss.cache.config.Configuration;
import org.jboss.cache.lock.IsolationLevel;
import org.jboss.cache.transaction.DummyTransactionManager;
import static org.testng.AssertJUnit.assertNotNull;
import static org.testng.AssertJUnit.fail;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import javax.naming.Context;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.TransactionManager;
import java.io.NotSerializableException;
import java.io.Serializable;

/**
 * Teting of replication exception for a Nonerislizable object
 *
 * @author Ben Wang
 * @version $Revision: 5189 $
 */
@Test(groups = {"functional"})
public class ReplicationExceptionTest
{
   private CacheSPI<String, ContainerData> cache1, cache2;

   String old_factory = null;
   final String FACTORY = "org.jboss.cache.transaction.DummyContextFactory";

   @BeforeMethod(alwaysRun = true)
   public void setUp() throws Exception
   {
      old_factory = System.getProperty(Context.INITIAL_CONTEXT_FACTORY);
      System.setProperty(Context.INITIAL_CONTEXT_FACTORY, FACTORY);
   }

   @AfterMethod(alwaysRun = true)
   public void tearDown() throws Exception
   {
      DummyTransactionManager.destroy();
      destroyCaches();
      if (old_factory != null)
      {
         System.setProperty(Context.INITIAL_CONTEXT_FACTORY, old_factory);
         old_factory = null;
      }
   }

   private TransactionManager beginTransaction() throws SystemException, NotSupportedException
   {
      TransactionManager mgr = cache1.getConfiguration().getRuntimeConfig().getTransactionManager();
      mgr.begin();
      return mgr;
   }

   private void initCaches(Configuration.CacheMode caching_mode)
   {
      CacheFactory<String, ContainerData> instance = new DefaultCacheFactory();
      cache1 = (CacheSPI<String, ContainerData>) instance.createCache(false);
      cache2 = (CacheSPI<String, ContainerData>) instance.createCache(false);
      cache1.getConfiguration().setCacheMode(caching_mode);
      cache2.getConfiguration().setCacheMode(caching_mode);
      cache1.getConfiguration().setIsolationLevel(IsolationLevel.SERIALIZABLE);
      cache2.getConfiguration().setIsolationLevel(IsolationLevel.SERIALIZABLE);

      cache1.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup");
      cache2.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup");
      /*
       cache1.setTransactionManagerLookupClass("org.jboss.cache.transaction.GenericTransactionManagerLookup");
       cache2.setTransactionManagerLookupClass("org.jboss.cache.transaction.GenericTransactionManagerLookup");
       */
      cache1.getConfiguration().setLockAcquisitionTimeout(5000);
      cache2.getConfiguration().setLockAcquisitionTimeout(5000);
      cache1.start();
      cache2.start();
   }

   void destroyCaches() throws Exception
   {
      if (cache1 != null)
      {
         cache1.stop();
      }
      if (cache2 != null)
      {
         cache2.stop();
      }
      cache1 = null;
      cache2 = null;
   }

   public void testNonSerializableRepl() throws Exception
   {
      try
      {
         initCaches(Configuration.CacheMode.REPL_SYNC);

         cache1.put("/a/b/c", "test", new ContainerData());

         // We should not come here.
         assertNotNull("NonSerializableData should not be null on cache2", cache2.get("/a/b/c", "test"));
      }
      catch (RuntimeException runtime)
      {
         Throwable t = runtime.getCause();
         if (t instanceof NotSerializableException)
         {
            System.out.println("received NotSerializableException - as expected");
         }
         else
         {
            throw runtime;
         }
      }
   }

   public void testNonSerializableReplWithTx() throws Exception
   {
      TransactionManager tm;

      try
      {
         initCaches(Configuration.CacheMode.REPL_SYNC);

         tm = beginTransaction();
         cache1.put("/a/b/c", "test", new ContainerData());
         tm.commit();

         // We should not come here.
         assertNotNull("NonSerializableData should not be null on cache2", cache2.get("/a/b/c", "test"));
      }
      catch (RollbackException rollback)
      {
         System.out.println("received RollbackException - as expected");
      }
      catch (Exception e)
      {
         // We should also examine that it is indeed throwing a NonSerilaizable exception.
         fail(e.toString());
      }
   }

   static class NonSerializabeData
   {
      int i;
   }

   static class ContainerData implements Serializable
   {
      int i;
      NonSerializabeData non_serializable_data;
      private static final long serialVersionUID = -8322197791060897247L;

      public ContainerData()
      {
         i = 99;
         non_serializable_data = new NonSerializabeData();
      }
   }
}
