package org.jboss.cache.interop;

import EDU.oswego.cs.dl.util.concurrent.Latch;
import junit.framework.TestCase;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.ConfigureException;
import org.jboss.cache.PropertyConfigurator;
import org.jboss.cache.TreeCache;
import org.jboss.cache.TreeCacheMBean;
import org.jboss.cache.lock.TimeoutException;
import org.jboss.cache.misc.TestingUtil;

import javax.transaction.Synchronization;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class InteropTest extends TestCase
{
   private static Log log = LogFactory.getLog(InteropTest.class);
   
   private String configFile;
   private Map caches;
   
   protected void setUp() throws Exception
   {
      super.setUp();
      
      configFile = System.getProperty("interop.test.config");
      
      if (configFile == null)
         throw new IllegalStateException("No config file property set");
      
      caches = new HashMap();
   }

   public void testInterop() throws Exception
   {      
      TreeCacheMBean cache = createCache("driver");
      
      TestingUtil.blockUntilViewReceived(cache, 2, 10000);
      
      cache.put("/put", "key", "value");
      
      Map map = new HashMap();
      map.put("0", new Integer(0));
      map.put("1", new Integer(1));
      cache.put("/put/map", map);
      
      cache.put("/remove", "key", "value");
      cache.remove("/remove");
      
      cache.put("/txremove", "key", "value");
      
      TransactionManager tm = cache.getTransactionManager();      
      tm.begin();
      Transaction tx = tm.getTransaction(); 
      cache.put("/txput", "key", "value");
      cache.remove("/txremove");
      tx.commit();
      
      cache.put("/rbremove", "key", "value");
      
      tm.begin();
      tx = tm.getTransaction();
      
      cache.put("/rbput", "key", "value");
      cache.remove("/rbremove");
      

      tx.registerSynchronization(new RollbackSync(tx));
      try
      {
         tx.commit();
      }
      catch (Exception ignored) {}
      
      stopCache("driver");
      
      cache = createCache("recipient");
      
      TestingUtil.blockUntilViewReceived(cache, 2, 10000);
      
      assertEquals("value", cache.get("/put", "key"));
      assertEquals(new Integer(0), cache.get("/put/map", "0"));
      assertEquals(new Integer(1), cache.get("/put/map", "1"));
      assertNull(cache.get("/remove", "key"));
      assertEquals("value", cache.get("/txput", "key"));
      assertNull(cache.get("/txremove", "key"));
      assertNull(cache.get("/rbput", "key"));
      assertEquals("value", cache.get("/rbremove", "key"));
   }
   
   /**
    * JBCACHE-443.  Confirms TimeoutException and ConfigureException
    * are interoperable after they were refactored to not extend
    * NestedException
    * 
    * TODO Remove this after a while once we've totally proven the point
    * 
    * @throws Exception
    */
   public void testPutTE() throws Exception
   {     
      
      TreeCacheMBean cache = createCache("driver");
      
      TestingUtil.blockUntilViewReceived(cache, 2, 10000);
    
      HashMap hm = new HashMap();
      hm.put("te", new TimeoutException("test", new RuntimeException()));
      ConfigureException ce = null;
      try
      {
         ce = new ConfigureException("test", new RuntimeException());
      }
      catch (Throwable t)
      {
         // Above signature not valid if cache is 123
         ce = new ConfigureException("123");
      }
      hm.put("ce", ce);
      cache.put("/TestTE", "TE", hm);
      
      stopCache("driver");
      
      cache = createCache("recipient");
      
      TestingUtil.blockUntilViewReceived(cache, 2, 10000);
      
      hm = (HashMap) cache.get("/TestTE", "TE");
      assertTrue("not null", hm != null);
      assertTrue("correct type", hm.get("te") instanceof TimeoutException);
      assertTrue("correct type", hm.get("ce") instanceof ConfigureException);
   }
   
   protected TreeCacheMBean createCache(String cacheID) throws Exception
   {
      if (caches.get(cacheID) != null)
      throw new IllegalStateException(cacheID + " already created");
      
      TreeCacheMBean tree=new TreeCache();
      PropertyConfigurator config=new PropertyConfigurator();
      config.configure(tree, configFile);
      
      tree.createService();
      tree.startService();
      
      caches.put(cacheID, tree);
      
      return tree;
   }
   
   protected void tearDown() throws Exception
   {
      super.tearDown();
      
      Set keys = caches.keySet();
      String[] cacheIDs = new String[keys.size()];
      cacheIDs = (String[]) keys.toArray(cacheIDs);
      for (int i = 0; i < cacheIDs.length; i++)
      {
         stopCache(cacheIDs[i]);
      }
   }
   
   protected void stopCache(String id)
   {
      TreeCache cache = (TreeCache) caches.get(id);
      if (cache != null)
      {
         try {
            cache.stopService();
            cache.destroyService();
            caches.remove(id);
         }
         catch (Exception e) {
            System.out.println("Exception stopping cache " + e.getMessage());
            e.printStackTrace(System.out);
         }
      }
   }
   
   class RollbackSync implements Synchronization
   {
      Transaction tx;
      
      RollbackSync(Transaction tx)
      {
         this.tx = tx;
      }

      public void afterCompletion(int arg0)
      {
              
      }

      public void beforeCompletion()
      {
         try
         {
            tx.setRollbackOnly();
         }
         catch (Exception e)
         {
            e.printStackTrace();
         }    
         
      }
      
   }
   
   class HangSync implements Synchronization
   {
      private Latch latch;
      
      HangSync(Latch latch)
      {
         this.latch = latch;
      }
      
      public void afterCompletion(int arg0)
      {
              
      }

      public void beforeCompletion()
      {
         try
         {
            latch.release();
            Thread.sleep(30000);
         }
         catch (Exception e)
         {
            e.printStackTrace();
         }    
         
      }
      
   }
   
}
