package org.jboss.cache;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.jboss.cache.lock.IsolationLevel;
import org.jboss.cache.transaction.DummyTransactionManager;

import javax.transaction.NotSupportedException;
import javax.transaction.Transaction;

/**
 * Tests whether modifications within callbacks (TreeCacheListener) are handled correctly
 * @author Bela Ban
 * @version $Id: CallbackTest.java,v 1.7.2.1 2006/07/10 12:17:36 msurtani Exp $
 */
public class CallbackTest extends TestCase {
   TreeCache cache=null, cache2;
   Transaction tx=null;
   final Fqn FQN_A=Fqn.fromString("/a");
   final Fqn FQN_B=Fqn.fromString("/b");
   final String KEY="key";
   final String VALUE="value";





   protected void setUp() throws Exception {
      super.setUp();
   }

   protected void tearDown() throws Exception {
      super.tearDown();
      if(cache != null) {
         cache.stopService();
         cache.destroyService();
         cache=null;
      }
      if(tx != null) {
         tx.commit();
         tx=null;
      }
   }





   public void testLocalPutCallbackWithoutTransaction() throws Exception, NotSupportedException {
      cache=createCache(TreeCache.LOCAL, IsolationLevel.SERIALIZABLE);
      cache.addTreeCacheListener(new PutListener(cache));

      cache.put(FQN_A, null);
      assertTrue(cache.exists(FQN_A));
      assertTrue(cache.exists(FQN_B)); //created by callback
      assertEquals(cache.getLockTable().size(), 0);
      System.out.println("cache locks:\n" + cache.printLockInfo());
      assertEquals(0, cache.getNumberOfLocksHeld());
   }

   public void testLocalGetCallbackSameFqnWithoutTransaction() throws Exception, NotSupportedException {
      cache=createCache(TreeCache.LOCAL, IsolationLevel.SERIALIZABLE);
      cache.addTreeCacheListener(new GetListener(cache, FQN_A));

      cache.put(FQN_A, null);
      assertTrue(cache.exists(FQN_A));
      assertEquals(cache.getLockTable().size(), 0);
      System.out.println("cache locks:\n" + cache.printLockInfo());
      assertEquals(0, cache.getNumberOfLocksHeld());
   }

   public void testLocalGetCallbackDifferentFqnWithoutTransaction() throws Exception, NotSupportedException {
      cache=createCache(TreeCache.LOCAL, IsolationLevel.SERIALIZABLE);
      cache.put(FQN_B, null);
      cache.addTreeCacheListener(new GetListener(cache, FQN_B));

      cache.put("/a", null);
      assertTrue(cache.exists(FQN_A));
      assertTrue(cache.exists(FQN_B));
      assertEquals(cache.getLockTable().size(), 0);
      System.out.println("cache locks:\n" + cache.printLockInfo());
      assertEquals(0, cache.getNumberOfLocksHeld());
   }



   public void testLocalCallbackWithTransaction() throws Exception, NotSupportedException {
      cache=createCache(TreeCache.LOCAL, IsolationLevel.SERIALIZABLE);
      cache.addTreeCacheListener(new PutListener(cache));
      tx=startTransaction();
      cache.put(FQN_A, null);
      tx.commit();
      assertTrue(cache.exists(FQN_A));
      assertEquals(0, cache.getNumberOfLocksHeld());
   }


   public void testLocalCallbackWithException() throws Exception, NotSupportedException {
      cache=createCache(TreeCache.LOCAL, IsolationLevel.SERIALIZABLE);
      cache.addTreeCacheListener(new ExceptionListener());
      tx=startTransaction();
      try {
         cache.put(FQN_A, null);
         tx.rollback();
      }
      catch(RuntimeException ex) {
        tx.rollback();
      }
      assertFalse(cache.exists(FQN_A));
      assertEquals(0, cache.getNumberOfLocksHeld());
      // assertFalse(cache.exists("/a"));  -- fix: reason is that CreateIfNotExists interceptor is *before*
      // PessimisticLockInterceptor, which registers for TX completion, we don't even get that far
   }



   TreeCache createCache(int mode, IsolationLevel level) throws Exception {
      TreeCache c=new TreeCache();
      c.setCacheMode(mode);
      c.setIsolationLevel(level);
      c.setTransactionManagerLookupClass("org.jboss.cache.DummyTransactionManagerLookup");
      c.createService();
      c.startService();
      return c;
   }

   Transaction startTransaction() {
      DummyTransactionManager mgr=DummyTransactionManager.getInstance();
      try {
         mgr.begin();
         return mgr.getTransaction();
      }
      catch(Throwable t) {
         return null;
      }
   }



   class ExceptionListener extends AbstractTreeCacheListener {
      public void nodeCreated(Fqn fqn) {
         throw new RuntimeException("this will cause the TX to rollback");
      }
   }


   class GetListener extends AbstractTreeCacheListener {
      TreeCache c;
      Fqn my_fqn;

      public GetListener(TreeCache c, Fqn my_fqn) {
         this.c=c;
         this.my_fqn=my_fqn;
      }

      public void nodeCreated(Fqn fqn) {
         try {
            TreeNode n=c.get(this.my_fqn);
            assertNotNull(n);
         }
         catch(CacheException e) {
            fail("listener was unable to do a get(" + my_fqn + ") during callback: " + e);
         }
      }

   }

   class PutListener extends AbstractTreeCacheListener {
      TreeCache c;

      public PutListener(TreeCache c) {
         this.c=c;
      }

      public void nodeCreated(Fqn fqn) {
         try {
            if(!c.exists(FQN_B)) {
               System.out.println("PutListener: creating node " + FQN_B);
               c.put(FQN_B, KEY, VALUE);
               System.out.println("PutListener: created node " + FQN_B);
            }
         }
         catch(CacheException e) {
            fail("listener was unable to update cache during callback: " + e);
         }
      }

   }




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

   //public static void main(String[] args) {
     // junit.textui.TestRunner.run(suite());
   //}

}
