/* JFox, the OpenSource J2EE Application Server
 *
 * Distributable under GNU LGPL license by gun.org
 * more details please visit http://www.huihoo.org/jfox
 */

package org.jfox.ejb.handler;

import java.util.Iterator;
import javax.ejb.EJBException;
import javax.ejb.EnterpriseBean;
import javax.ejb.SessionSynchronization;
import javax.transaction.NotSupportedException;
import javax.transaction.Status;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import javax.transaction.TransactionRequiredException;

import org.jfox.ejb.Bucket;
import org.jfox.ejb.ContainerImpl;
import org.jfox.ejb.EJBSynchronization;
import org.jfox.ejb.Invocation;
import org.jfox.ejb.meta.SessionBeanDescriptor;
import org.jfox.ejb.meta.TransactionAttribute;

/**
 * @author <a href="mailto:young_yy@hotmail.com">Young Yang</a>
 */

public class T5_TransactionHandler extends AbstractHandler {
    private static TransactionManager tm = ContainerImpl.getInstance().getTransactionManager();

    public Object invokeBean(Bucket bucket, Invocation invocation, Iterator iter) throws Exception {
        logger.debug("invokeBean " + bucket + ", " + invocation);
        Transaction suspendedTrans = null;
        SessionBeanDescriptor meta = (SessionBeanDescriptor) bucket.getEJBDescriptor();

        // 事务同步
        EnterpriseBean bean = invocation.getBean();
        boolean isSync = bean instanceof SessionSynchronization ? true : false;
        if(isSync & meta.isStateless()) {
            throw new EJBException("only stateful session bean can implement javax.ejb.SessionSynchronization.");
        }

//    byte type = TransactionAttribute.getTransactionAttribute(meta.getTransactionType());
        byte type = meta.getMethodTransactionAttribute(invocation.getMethod());
        int status = tm.getStatus();

        /**
         * 由这个方法创建的事务，因为该方法可能集成其调用方法的事务，
         * 但是该方法不能 commit 该事务，而应该由其调用方法来 commit
         */
        boolean created = false;
        boolean toCommit = true;
        try {
            switch(type) {
                case TransactionAttribute.TX_NOT_SUPPORTED:
                    {
                        if(status != Status.STATUS_NO_TRANSACTION) {
                            suspendedTrans = tm.suspend();
                        }
                        break;
                    }
                case TransactionAttribute.TX_SUPPORTS:
                    {
                        break;
                    }
                case TransactionAttribute.TX_REQUIRED:
                    {
                        if(status == Status.STATUS_NO_TRANSACTION) {
                            tm.begin();
                            // tx synchronize
                            if(isSync) {
                                EJBSynchronization ejbSync = new EJBSynchronization((SessionSynchronization) bean, tm.getTransaction());
                                ejbSync.afterBegin();
                            }
                            created = true;
                        }
                        break;
                    }
                case TransactionAttribute.TX_REQUIRES_NEW:
                    {
                        if(status != Status.STATUS_NO_TRANSACTION) {
                            suspendedTrans = tm.suspend();
                        }
                        tm.begin();
                        // tx synchronize
                        if(isSync) {
                            EJBSynchronization ejbSync = new EJBSynchronization((SessionSynchronization) bean, tm.getTransaction());
                            tm.getTransaction().registerSynchronization(ejbSync);
                            ejbSync.afterBegin();
                        }
                        created = true;
                        break;
                    }
                case TransactionAttribute.TX_MANDATORY:
                    {
                        if(status == Status.STATUS_NO_TRANSACTION) {
                            throw new TransactionRequiredException("Transaction Required");
                        }
                        break;
                    }
                case TransactionAttribute.TX_NEVER:
                    {
                        if(status != Status.STATUS_NO_TRANSACTION) {
                            throw new NotSupportedException("Transaction Not Supported");
                        }
                        break;
                    }
            }
            return super.invokeBean(bucket, invocation, iter);
        }
                // only catch EJBException, rollback the transaction
                // Application Exception maybe right logic
        catch(EJBException e) {
            toCommit = false;
            throw e;
        }
        finally {
            if(created == true) {
                if(toCommit) {
                    tm.commit();
                }
                else {
                    tm.rollback();
                }
            }
            else { // 如果没有新建事务，但是继承了事务并且当前线程抛出了异常，要标记当前事务为 rollback only
                if(toCommit == false && tm.getStatus() != Status.STATUS_NO_TRANSACTION) {
                    tm.getTransaction().setRollbackOnly();
                }
            }
            if(suspendedTrans != null) {
                tm.resume(suspendedTrans);
            }
        }
    }

    public static void main(String[] args) {

    }
}