package org.jboss.cache.mgmt;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.jboss.cache.Fqn;
import org.jboss.cache.PropertyConfigurator;
import org.jboss.cache.TreeCache;
import org.jboss.cache.interceptors.TxInterceptor;

import javax.transaction.TransactionManager;
import java.util.HashMap;
import java.util.List;

/**
 * Simple functional tests for TxInterceptor statistics
 * @author Jerry Gauthier
 * @version $Id: TxTest.java,v 1.3 2006/05/09 15:46:34 msurtani Exp $
 */
public class TxTest extends TestCase
{
   private static final String CLUSTER_NAME = "TxTestCluster";
   private static final String CAPITAL = "capital";
   private static final String CURRENCY = "currency";
   private static final String POPULATION = "population";
   private static final String AREA = "area";
   
   TreeCache cache1 = null;
   TreeCache cache2 = null;

   protected void setUp() throws Exception
   {
      super.setUp();
      cache1 = createCache(CLUSTER_NAME);
      cache2 = createCache(CLUSTER_NAME);
   }

   protected void tearDown() throws Exception
   {
      super.tearDown();
      if(cache1 != null)
      {
         cache1.stopService();
         cache1.destroyService();
         cache1 = null;
      }
      if(cache2 != null)
      {
         cache2.stopService();
         cache2.destroyService();
         cache2 = null;
      }
   }

   public void testTxMgmt() throws Exception
   {
      assertNotNull("Cache1 is null.", cache1);
      assertNotNull("Cache2 is null.", cache2);
      
      // Note: because these tests are normally executed without a server, the interceptor
      // MBeans are usually not available for use in the tests.  Consequently it's necessary 
      // to obtain a reference to the interceptor and work with it directly.
      TxInterceptor tx1 = getTxInterceptor(cache1);
      assertNotNull("Cache1 InvalidationInterceptor not found.", tx1);
      TxInterceptor tx2 = getTxInterceptor(cache2);
      assertNotNull("Cache2 InvalidationInterceptor not found.", tx2);  
      
      TransactionManager tm1 = cache1.getTransactionManager();
      assertNotNull("TransactionManager is null.", tm1);
      
      // populate cache1 with test data - no transaction
      loadCacheNoTx(cache1);
      
      // confirm that data is in cache1 and in cache2
      Fqn key = Fqn.fromString("Europe/Austria");
      assertNotNull("Cache1 retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache1.get(key, CAPITAL));
      assertNotNull("Cache2 retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache2.get(key, CAPITAL));
      key = Fqn.fromString("Europe/Albania");
      assertNotNull("Cache1 retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache1.get(key, CAPITAL));
      assertNotNull("Cache2 retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache2.get(key, CAPITAL));
      
      // verify basic statistics for entries loaded into cache
      assertEquals("Cache1 Tx Prepares error after reset: ", new Long(0), new Long(tx1.getPrepares()));
      assertEquals("Cache1 Tx Commits error after reset: ", new Long(0), new Long(tx1.getCommits()));
      assertEquals("Cache1 Tx Rollbacks error after reset: ", new Long(0), new Long(tx1.getRollbacks()));
      assertEquals("Cache2 Tx Prepares error after reset: ", new Long(0), new Long(tx2.getPrepares()));
      assertEquals("Cache2 Tx Commits error after reset: ", new Long(0), new Long(tx2.getCommits()));
      assertEquals("Cache2 Tx Rollbacks error after reset: ", new Long(0), new Long(tx2.getRollbacks()));
      
      // populate cache1 with test data - then transaction commit
      loadCacheTxCommit(cache1, tm1);
      loadCacheTxCommit2(cache1, tm1);
      
      // confirm that committed data is in cache1 and in cache2
      key = Fqn.fromString("Europe/England");
      assertNotNull("Cache1 retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache1.get(key, CAPITAL));
      assertNotNull("Cache2 retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache2.get(key, CAPITAL));
      key = Fqn.fromString("Europe/Hungary");
      assertNotNull("Cache1 retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache1.get(key, CAPITAL));
      assertNotNull("Cache2 retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache2.get(key, CAPITAL));
 
      // populate cache1 with test data - then transaction rollback
      loadCacheTxRollback(cache1, tm1);
      
      // confirm that rolled back data is not in cache1 or cache2
      key = Fqn.fromString("Europe/France");
      assertNull("Cache1 retrieval error: did not expect to retrieve " + CAPITAL + " for " + key, cache1.get(key, CAPITAL));
      assertNull("Cache2 retrieval error: did not expect to retrieve " + CAPITAL + " for " + key, cache2.get(key, CAPITAL));
      key = Fqn.fromString("Europe/Germany");
      assertNull("Cache1 retrieval error: did not expect to retrieve " + CAPITAL + " for " + key, cache1.get(key, CAPITAL));
      assertNull("Cache2 retrieval error: did not expect to retrieve " + CAPITAL + " for " + key, cache2.get(key, CAPITAL));
      
      // check the statistics - transactions are only handled by JBoss Cache on the remote node (i.e., node2)
      assertEquals("Cache1 Tx Prepares error after reset: ", new Long(0), new Long(tx1.getPrepares()));
      assertEquals("Cache1 Tx Commits error after reset: ", new Long(0), new Long(tx1.getCommits()));
      assertEquals("Cache1 Tx Rollbacks error after reset: ", new Long(0), new Long(tx1.getRollbacks()));
      assertEquals("Cache2 Tx Prepares error after reset: ", new Long(2), new Long(tx2.getPrepares()));
      assertEquals("Cache2 Tx Commits error after reset: ", new Long(2), new Long(tx2.getCommits()));
      // rollbacks don't currently get propagated so the counter will be 0, not 1
      assertEquals("Cache2 Tx Rollbacks error after reset: ", new Long(0), new Long(tx2.getRollbacks()));
      
      // reset statistics
      tx1.resetStatistics();
      tx2.resetStatistics();
     
      // check the statistics again
      assertEquals("Cache1 Tx Prepares error after reset: ", new Long(0), new Long(tx1.getPrepares()));
      assertEquals("Cache1 Tx Commits error after reset: ", new Long(0), new Long(tx1.getCommits()));
      assertEquals("Cache1 Tx Rollbacks error after reset: ", new Long(0), new Long(tx1.getRollbacks()));
      assertEquals("Cache2 Tx Prepares error after reset: ", new Long(0), new Long(tx2.getPrepares()));
      assertEquals("Cache2 Tx Commits error after reset: ", new Long(0), new Long(tx2.getCommits()));
      assertEquals("Cache2 Tx Rollbacks error after reset: ", new Long(0), new Long(tx2.getRollbacks()));
   }
   
   private void loadCacheNoTx(TreeCache cache) throws Exception
   {
      cache.put("Europe", new HashMap());
      cache.put("Europe/Austria", new HashMap());      
      cache.put("Europe/Austria", CAPITAL, "Vienna");      
      cache.put("Europe/Austria", CURRENCY, "Euro");      
      cache.put("Europe/Austria", POPULATION, new Integer(8184691));
      
      HashMap albania = new HashMap(4);
      albania.put(CAPITAL, "Tirana");
      albania.put(CURRENCY, "Lek");
      albania.put(POPULATION, new Integer(3563112));
      albania.put(AREA, new Integer(28748));
      cache.put("Europe/Albania", albania);      
   }
   
   private void loadCacheTxCommit(TreeCache cache, TransactionManager tm) throws Exception
   {
      tm.begin();
      
      cache.put("Europe/Czech Republic", new HashMap());
      cache.put("Europe/Czech Republic", CAPITAL, "Prague");
      cache.put("Europe/Czech Republic", CURRENCY, "Czech Koruna");
      cache.put("Europe/Czech Republic", POPULATION, new Integer(10241138));
      
      cache.put("Europe/England", new HashMap());
      cache.put("Europe/England", CAPITAL, "London");
      cache.put("Europe/England", CURRENCY, "British Pound");
      cache.put("Europe/England", POPULATION, new Integer(60441457));
      
      tm.commit();      
   }
   
   private void loadCacheTxCommit2(TreeCache cache, TransactionManager tm) throws Exception
   {     
      tm.begin();
      
      HashMap hungary = new HashMap(4);
      hungary.put(CAPITAL, "Budapest");
      hungary.put(CURRENCY, "Forint");
      hungary.put(POPULATION, new Integer(10006835));
      hungary.put(AREA, new Integer(93030));
      cache.put("Europe/Hungary", hungary);
      
      HashMap romania = new HashMap(4);
      romania.put(CAPITAL, "Bucharest");
      romania.put(CURRENCY, "Leu");
      romania.put(POPULATION, new Integer(22329977));
      romania.put(AREA, new Integer(237500));
      cache.put("Europe/Romania", romania);
      
      tm.commit();
   }
   
   private void loadCacheTxRollback(TreeCache cache, TransactionManager tm) throws Exception
   {
      tm.begin();
      
      cache.put("Europe/France", new HashMap());
      cache.put("Europe/France", CAPITAL, "Paris");
      cache.put("Europe/France", CURRENCY, "Euro");
      cache.put("Europe/France", POPULATION, new Integer(60656178));
      
      cache.put("Europe/Germany", new HashMap());
      cache.put("Europe/Germany", CAPITAL, "Berlin");
      cache.put("Europe/Germany", CURRENCY, "Euro");
      cache.put("Europe/Germany", POPULATION, new Integer(82431390));
      
      tm.rollback();
   }
   
   private TreeCache createCache(String clusterName) throws Exception
   {
      TreeCache cache = new TreeCache();
      PropertyConfigurator config=new PropertyConfigurator();
      config.configure(cache, "META-INF/replSync-service.xml");
      cache.setUseRegionBasedMarshalling(false); 
      cache.setCacheMode(TreeCache.REPL_SYNC);
      cache.setUseInterceptorMbeans(true);
      cache.setClusterName(clusterName);
      cache.createService();
      cache.startService();
      return cache;
   }
   
   private TxInterceptor getTxInterceptor(TreeCache cache)
   {
      List interceptors = cache.getInterceptors();
      if (interceptors.isEmpty())
         return null;
     
     for (int i = 0; i < interceptors.size(); i++)
     {
        Object o = interceptors.get(i);
        if (o instanceof TxInterceptor)
           return (TxInterceptor)o;
     }
     return null;
   }

   public static Test suite()
   {
      return new TestSuite(TxTest.class);
   }

}
