package org.jboss.cache.transaction;

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

import javax.transaction.TransactionManager;
import javax.transaction.Transaction;
import javax.transaction.SystemException;
import javax.transaction.NotSupportedException;
import javax.transaction.Status;
import javax.transaction.RollbackException;
import javax.transaction.Synchronization;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Properties;
import java.util.List;
import java.util.LinkedList;
import java.util.Iterator;

/**
 * This test checks how the cache behaves when a JTA STATUS_UNKNOWN is passed in to the cache during afterCompletion().
 *
 * @author <a href="mailto:manik@jboss.org">Manik Surtani</a>
 */
public class StatusUnknownTest extends TestCase
{
   private TreeCache cache;
   private TransactionManager tm;

   protected void setUp() throws Exception
   {
      cache = new TreeCache();
      cache.setTransactionManagerLookupClass(HeuristicFailingDummyTransactionManagerLookup.class.getName());
      cache.startService();
      tm = cache.getTransactionManager();
   }

   protected void tearDown()
   {
      cache.stopService();
   }

   public void testStatusUnknown() throws Exception
   {
      tm.begin();
      cache.put("/a/b/c", "k", "v");
      assertEquals(4, cache.getNumberOfLocksHeld());
      assertTrue(cache.exists("/a/b/c"));
      tm.commit();
      assertEquals(0, cache.getNumberOfLocksHeld());
      assertFalse(cache.exists("/a/b/c"));
   }

   public static class HeuristicFailingDummyTransactionManager extends DummyTransactionManager
   {
      public void begin() throws SystemException, NotSupportedException
      {
         super.begin();

         Transaction tx = new HeuristicFailingDummyTransaction(this);
         setTransaction(tx);
      }

      public static DummyTransactionManager getInstance() 
      {
         if(instance == null)
         {
            instance=new HeuristicFailingDummyTransactionManager();
            try
            {
               Properties p=new Properties();
               p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.cache.transaction.DummyContextFactory");
               Context ctx=new InitialContext(p);
               ctx.bind("java:/TransactionManager", instance);
               ctx.bind("UserTransaction", new DummyUserTransaction(instance));
            }
            catch(NamingException e)
            {
               log.error("binding of DummyTransactionManager failed", e);
            }
         }
         return instance;
      }
   }

   public static class HeuristicFailingDummyTransaction extends DummyTransaction
   {
      public HeuristicFailingDummyTransaction(DummyBaseTransactionManager mgr)
      {
         super(mgr);
      }

      public void commit() throws RollbackException
      {
         status= Status.STATUS_PREPARING;
         try
         {
            notifyBeforeCompletion();
            notifyAfterCompletion(Status.STATUS_UNKNOWN);
         }
         finally
         {
            // Disassociate tx from thread.
            tm_.setTransaction(null);
         }
      }

      protected void notifyAfterCompletion(int status)
      {
         List tmp;

         synchronized(participants)
         {
            tmp=new LinkedList(participants);
         }

         for(Iterator it=tmp.iterator(); it.hasNext();)
         {
            Synchronization s=(Synchronization)it.next();
            try
            {
               s.afterCompletion(status);
            }
            catch(Throwable t)
            {
               throw (RuntimeException) t;
            }
         }

         synchronized(participants)
         {
            participants.clear();
         }
      }
   }

   public static class HeuristicFailingDummyTransactionManagerLookup implements TransactionManagerLookup
   {

      public TransactionManager getTransactionManager() throws Exception
      {
         return HeuristicFailingDummyTransactionManager.getInstance();
      }
   }
}


