/*
 * JBoss, Home of Professional Open Source
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */

package org.jboss.cache.pojo.interceptors;

import org.jboss.aop.joinpoint.Invocation;
import org.jboss.aop.joinpoint.MethodInvocation;
import org.jboss.cache.Fqn;
import org.jboss.cache.pojo.PojoCacheException;
import org.jboss.cache.transaction.BatchModeTransactionManager;

import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;

/**
 * Interceptor (done via aop advice) for transaction
 *
 * @author Ben Wang
 * @version $Id: PojoTxInterceptor.java 3892 2007-05-23 10:28:59Z msurtani $
 */
public class PojoTxInterceptor extends AbstractInterceptor
{
   private TransactionManager localTm_ = null;
   private TransactionManager txManager_;
   public static final String TAG = "PC";
   public static final String TX = "TX";


   public Object invoke(Invocation in) throws Throwable
   {
      if (!(in instanceof MethodInvocation))
      {
         throw new IllegalArgumentException("TxInterceptor.invoke(): invocation not MethodInvocation");
      }
      MethodInvocation invocation = (MethodInvocation) in;

      if (txManager_ == null)
      {
         txManager_ = getCache(invocation).getTransactionManager();
      }

      Transaction tx = null;
      if (txManager_ != null)
      {
         // Use the configured one if so setup.
         localTm_ = txManager_;
      }
      else
      {
         localTm_ = BatchModeTransactionManager.getInstance();
      }

      tx = localTm_.getTransaction();

      boolean needTx = false;
      if (tx == null) needTx = true;
      Fqn id = null;
      try
      {
         if (needTx)
         {
            id = (Fqn) invocation.getArguments()[0];
            log.debug("Initiating a local transaction for batch processing with id: " + id.toString());
            // Start one of our own as batch processing.
            try
            {
               localTm_.begin();
               tx = localTm_.getTransaction();
               // Stuff tx context into the metadata
               invocation.getMetaData().addMetaData(TAG, TX, tx);
               return invocation.invokeNext(); // proceed to next advice or actual call
            }
            catch (Exception e)
            {
               log.warn(invocation.getMethod().getName() + ": exception occurred: " + e);
               try
               {
                  localTm_.setRollbackOnly();
               }
               catch (Exception e2)
               {
                  log.error("setRollbackOnly", e2);
               }

               throw new PojoCacheException("PojoCache operation will be rollback. id: " + id
                                            + ". Operation: " + invocation.getMethod().getName(), e);
            }
         }
         else
         {
            invocation.getMetaData().addMetaData(TAG, TX, tx);
            return invocation.invokeNext(); // proceed to next advice or actual call
         }
      }
      finally
      {
         if (needTx)
         {
            endTransaction(id);
         }
      }
   }

   private void endTransaction(Fqn id)
   {
      if (localTm_ == null)
      {
         log.warn("endTransaction(): tm is null for id: " + id);
         return;
      }


      try
      {
         if (localTm_.getTransaction().getStatus() != Status.STATUS_MARKED_ROLLBACK)
         {
            localTm_.commit();
         }
         else if (localTm_.getTransaction().getStatus() == Status.STATUS_ROLLEDBACK)
         {
            log.info("endTransaction(): has been rolled back for id: " + id);
         }
         else
         {
            log.info("endTransaction(): rolling back tx for id: " + id);
            localTm_.rollback();
         }
      }
      catch (RollbackException re)
      {
         // Do nothing here since cache may rollback automatically.
         log.warn("endTransaction(): rolling back transaction with exception: " + re);
      }
      catch (Exception e)
      {
         log.warn("endTransaction(): Failed with exception: " + e);
      }
   }
}
