package org.jboss.cache.lock;

import junit.framework.TestCase;
import org.jboss.cache.TreeCache;

import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import java.util.Collections;

public class WriteLockOnParentTest extends TestCase
{
   private TreeCache cache;
   private TransactionManager tm;

   protected void setUp() throws Exception
   {
      cache = new TreeCache();
      cache.setTransactionManagerLookupClass("org.jboss.cache.DummyTransactionManagerLookup");
      // reduce LAT so the test runs faster
      cache.setLockAcquisitionTimeout(500);

      cache.startService();
      tm = cache.getTransactionManager();
   }

   protected void tearDown() throws Exception
   {
      if (tm.getTransaction() != null)
      {
         try
         {
            tm.rollback();
         }
         catch (Exception e)
         {
            // do sweet F.A.
         }
      }
      cache.stopService();
   }

   public void testDefaultCfg()
   {
      assertFalse("Locking of parent nodes for child inserts and removes should be false by default", cache.getLockParentForChildInsertRemove());
   }

   public void testDefaultChildInsert() throws Exception
   {
      cache.put("/a", Collections.EMPTY_MAP);

      assertTrue("/a should exist", cache.exists("/a"));

      // concurrent insert of /a/b and /a/c
      tm.begin();
      cache.put("/a/b", Collections.EMPTY_MAP);
      Transaction t1 = tm.suspend();

      tm.begin();
      cache.put("/a/c", Collections.EMPTY_MAP);
      tm.commit();

      tm.resume(t1);
      tm.commit();

      assertTrue("/a/b should exist", cache.exists("/a/b"));
      assertTrue("/a/c should exist", cache.exists("/a/c"));
   }

   public void testLockParentChildInsert() throws Exception
   {
      cache.stop();
      cache.setLockParentForChildInsertRemove(true);
      cache.start();
      cache.put("/a", Collections.EMPTY_MAP);

      assertTrue("/a should exist", cache.exists("/a"));

      // concurrent insert of /a/b and /a/c
      tm.begin();
      cache.put("/a/b", Collections.EMPTY_MAP);
      Transaction t1 = tm.suspend();

      tm.begin();
      try
      {
         cache.put("/a/c", Collections.EMPTY_MAP);
         fail("Should not get here.");
      }
      catch (TimeoutException e)
      {
         // expected
      }
      tm.commit();

      tm.resume(t1);
      tm.commit();

      assertTrue("/a/b should exist", cache.exists("/a/b"));
      assertTrue("/a/c should not exist", !cache.exists("/a/c"));
   }

   public void testDefaultChildRemove() throws Exception
   {
      cache.put("/a", Collections.EMPTY_MAP);
      cache.put("/a/b", Collections.EMPTY_MAP);
      cache.put("/a/c", Collections.EMPTY_MAP);

      assertTrue("/a should exist", cache.exists("/a"));
      assertTrue("/a/b should exist", cache.exists("/a/b"));
      assertTrue("/a/c should exist", cache.exists("/a/c"));

      // concurrent remove of /a/b and /a/c
      tm.begin();
      cache.remove("/a/b");
      Transaction t1 = tm.suspend();

      tm.begin();
      cache.remove("/a/c");
      tm.commit();

      tm.resume(t1);
      tm.commit();

      assertTrue("/a should exist", cache.exists("/a"));
      assertTrue("/a/b should not exist", !cache.exists("/a/b"));
      assertTrue("/a/c should not exist", !cache.exists("/a/c"));
   }
   
   public void testLockParentChildRemove() throws Exception
   {
      cache.stop();
      cache.setLockParentForChildInsertRemove(true);
      cache.start();
      
      cache.put("/a", Collections.EMPTY_MAP);
      cache.put("/a/b", Collections.EMPTY_MAP);
      cache.put("/a/c", Collections.EMPTY_MAP);

      assertTrue("/a should exist", cache.exists("/a"));
      assertTrue("/a/b should exist", cache.exists("/a/b"));
      assertTrue("/a/c should exist", cache.exists("/a/c"));

      // concurrent remove of /a/b and /a/c
      tm.begin();
      cache.remove("/a/b");
      Transaction t1 = tm.suspend();

      tm.begin();
      try
      {
         cache.remove("/a/c");
         fail("Should not get here.");
      }
      catch (TimeoutException e)
      {
         // expected
      }
      tm.commit();

      tm.resume(t1);
      tm.commit();

      assertTrue("/a should exist", cache.exists("/a"));
      assertTrue("/a/b should not exist", !cache.exists("/a/b"));
      assertTrue("/a/c should exist", cache.exists("/a/c"));
   }

}
