All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.ejb.containers.EJBContainerTransactionManager Maven / Gradle / Ivy

There is a newer version: 4.1.2.181
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
// Portions Copyright [2016] [Payara Foundation]
package com.sun.ejb.containers;

import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.ejb.EJBException;
import javax.ejb.NoSuchEntityException;
import javax.ejb.NoSuchObjectLocalException;
import javax.ejb.TransactionRolledbackLocalException;
import javax.ejb.TransactionRequiredLocalException;
import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.UserTransaction;

import com.sun.ejb.Container;
import com.sun.ejb.EjbInvocation;
import com.sun.ejb.InvocationInfo;
import com.sun.enterprise.deployment.MethodDescriptor;
import org.glassfish.ejb.deployment.descriptor.ContainerTransaction;
import org.glassfish.ejb.deployment.descriptor.EjbApplicationExceptionInfo;
import org.glassfish.ejb.deployment.descriptor.EjbDescriptor;
import org.glassfish.ejb.deployment.descriptor.runtime.IASEjbExtraDescriptors;

import com.sun.enterprise.transaction.api.JavaEETransaction;
import com.sun.enterprise.transaction.api.JavaEETransactionManager;
import com.sun.enterprise.util.LocalStringManagerImpl;

/**
 * Container support for handling transactions
 *
 * @author mvatkina
 */
public class EJBContainerTransactionManager {

    private static final Logger _logger = EjbContainerUtilImpl.getLogger();

    private static LocalStringManagerImpl localStrings =
            new LocalStringManagerImpl(EJBContainerTransactionManager.class);

    private static final String USER_TX = "java:comp/UserTransaction";

    private EjbContainerUtil ejbContainerUtilImpl = EjbContainerUtilImpl.getInstance();

    private JavaEETransactionManager transactionManager;
    private BaseContainer container;
    private EjbDescriptor ejbDescriptor;
    private int cmtTimeoutInSeconds = 0;

    /**
     * Construct new instance and set basic references
     */
    EJBContainerTransactionManager(Container c, EjbDescriptor ejbDesc) {
        container = (BaseContainer)c;
        ejbDescriptor = ejbDesc;
        transactionManager = ejbContainerUtilImpl.getTransactionManager();

        IASEjbExtraDescriptors iased = ejbDesc.getIASEjbExtraDescriptors();

        if(iased.getCmtTimeoutInSeconds() != 0){
        	cmtTimeoutInSeconds = iased.getCmtTimeoutInSeconds();
        }
    }

    /**
     * Calculate for the transaction attribute for a method.
     * This is only used during container initialization.  After that,
     * tx attributes can be looked up with variations of getTxAttr()
     */
    int findTxAttr(MethodDescriptor md) {
        int txAttr = -1;

        if ( container.isBeanManagedTran ) {
            return Container.TX_BEAN_MANAGED;
        }

        ContainerTransaction ct = ejbDescriptor.getContainerTransactionFor(md);

        if ( ct != null ) {
            String attr = ct.getTransactionAttribute();
            if ( attr.equals(ContainerTransaction.NOT_SUPPORTED) )
                txAttr = Container.TX_NOT_SUPPORTED;
            else if ( attr.equals(ContainerTransaction.SUPPORTS) )
                txAttr = Container.TX_SUPPORTS;
            else if ( attr.equals(ContainerTransaction.REQUIRED) )
                txAttr = Container.TX_REQUIRED;
            else if ( attr.equals(ContainerTransaction.REQUIRES_NEW) )
                txAttr = Container.TX_REQUIRES_NEW;
            else if ( attr.equals(ContainerTransaction.MANDATORY) )
                txAttr = Container.TX_MANDATORY;
            else if ( attr.equals(ContainerTransaction.NEVER) )
                txAttr = Container.TX_NEVER;
        }

        if ( txAttr == -1 ) {
            throw new EJBException("Transaction Attribute not found for method "
                + md.prettyPrint());
        }

        container.validateTxAttr(md, txAttr);

        return txAttr;
    }

    /**
     * Handle transaction requirements, if any, before invoking bean method
     */
    final void preInvokeTx(EjbInvocation inv) throws Exception {
        // Get existing Tx status: this tells us if the client
        // started a transaction which was propagated on this invocation.
        Integer preInvokeTxStatus = inv.getPreInvokeTxStatus();
        int status = (preInvokeTxStatus != null) ?
            preInvokeTxStatus.intValue() : transactionManager.getStatus();

        // For MessageDrivenBeans,ejbCreate/ejbRemove must be called without a Tx.
        // For StatelessSessionBeans, ejbCreate/ejbRemove must be called without a Tx.
        // For StatefullSessionBeans ejbCreate/ejbRemove/ejbFind can be called with or without a Tx.
        // For EntityBeans, ejbCreate/ejbRemove/ejbFind must be called with a Tx so no special work needed.
        if ( container.suspendTransaction(inv) ) {
            // EJB2.0 section 7.5.7 says that ejbCreate/ejbRemove etc are called
            // without a Tx. So suspend the client's Tx if any.

            // Note: ejbRemove cannot be called when EJB is associated with
            // a Tx, according to EJB2.0 section 7.6.4. This check is done in
            // the container's implementation of removeBean().

            if ( status != Status.STATUS_NO_TRANSACTION ) {
                // client request is associated with a Tx
                try {
                    inv.clientTx = transactionManager.suspend();
                } catch (SystemException ex) {
                    throw new EJBException(ex);
                }
            }
            return;
        }

        // isNullTx is true if the client sent a null tx context
        // (i.e. a tx context with a null Coordinator objref)
        // or if this server's tx interop mode flag is false.
        // Follow the tables in EJB2.0 sections 19.6.2.2.1 and 19.6.2.2.2.
        boolean isNullTx = false;
        if (inv.isRemote) {
            isNullTx = transactionManager.isNullTransaction();
        }

        int txAttr = container.getTxAttr(inv);

        EJBContextImpl context = (EJBContextImpl)inv.context;

        // Note: in the code below, inv.clientTx is set ONLY if the
        // client's Tx is actually suspended.

        // get the Tx associated with the EJB from previous invocation,
        // if any.
        Transaction prevTx = context.getTransaction();

        switch (txAttr) {
            case Container.TX_BEAN_MANAGED:
                // TX_BEAN_MANAGED rules from EJB2.0 Section 17.6.1, Table 13
                // Note: only MDBs and SessionBeans can be TX_BEAN_MANAGED
                if ( status != Status.STATUS_NO_TRANSACTION ) {
                    // client request associated with a Tx, always suspend
                    inv.clientTx = transactionManager.suspend();
                }
                if ( container.isStatefulSession && prevTx != null
                        && prevTx.getStatus() != Status.STATUS_NO_TRANSACTION ) {
                    // Note: if prevTx != null , then it means
                    // afterCompletion was not called yet for the
                    // previous transaction on the EJB.

                    // The EJB was previously associated with a Tx which was
                    // begun by the EJB itself in a previous invocation.
                    // This is only possible for stateful SessionBeans
                    // not for StatelessSession or Entity.
                    transactionManager.resume(prevTx);

                    // This allows the TM to enlist resources
                    // used by the EJB with the transaction
                    transactionManager.enlistComponentResources();
                }

                break;

            case Container.TX_NOT_SUPPORTED:
                if ( status != Status.STATUS_NO_TRANSACTION ) {
                    inv.clientTx = transactionManager.suspend();
                }
                container.checkUnfinishedTx(prevTx, inv);
                container.preInvokeNoTx(inv);
                break;

            case Container.TX_MANDATORY:
                if ( isNullTx || status == Status.STATUS_NO_TRANSACTION ) {
                    throw new TransactionRequiredLocalException();
                }

                useClientTx(prevTx, inv);
                break;

            case Container.TX_REQUIRED:
                if ( isNullTx ) {
                    throw new TransactionRequiredLocalException();
                }

                if ( status == Status.STATUS_NO_TRANSACTION ) {
                    inv.clientTx = null;
                    startNewTx(prevTx, inv);
                } else { // There is a client Tx
                    inv.clientTx = transactionManager.getTransaction();
                    useClientTx(prevTx, inv);
                }
                break;

            case Container.TX_REQUIRES_NEW:
                if ( status != Status.STATUS_NO_TRANSACTION ) {
                    inv.clientTx = transactionManager.suspend();
                }
                startNewTx(prevTx, inv);
                break;

            case Container.TX_SUPPORTS:
                if ( isNullTx ) {
                    throw new TransactionRequiredLocalException();
                }

                if ( status != Status.STATUS_NO_TRANSACTION ) {
                    useClientTx(prevTx, inv);
                } else { // we need to invoke the EJB with no Tx.
                    container.checkUnfinishedTx(prevTx, inv);
                    container.preInvokeNoTx(inv);
                }
                break;

            case Container.TX_NEVER:
                if ( isNullTx || status != Status.STATUS_NO_TRANSACTION ) {
                    throw new EJBException("EJB cannot be invoked in global transaction");

                } else { // we need to invoke the EJB with no Tx.
                    container.checkUnfinishedTx(prevTx, inv);
                    container.preInvokeNoTx(inv);
                }
                break;

            default:
                throw new EJBException("Bad transaction attribute");
        }
    }

    /**
     * Start a CMT transaction, enlist resources, and call afterBegin, which is a
     * no-op in those containers that do not need this callback
     */
    private void startNewTx(Transaction prevTx, EjbInvocation inv) throws Exception {

        container.checkUnfinishedTx(prevTx, inv);

        if (cmtTimeoutInSeconds > 0) {
            transactionManager.begin(cmtTimeoutInSeconds);
        } else {
            transactionManager.begin();
        }

        EJBContextImpl context = (EJBContextImpl)inv.context;
        Transaction tx = transactionManager.getTransaction();
        if (! container.isSingleton) {
            context.setTransaction(tx);
        }

        // This allows the TM to enlist resources used by the EJB
        // with the transaction
        transactionManager.enlistComponentResources();

        // register synchronization for methods other than finders/home methods
        if ( !inv.invocationInfo.isHomeFinder ) {
            // Register for Synchronization notification
            ejbContainerUtilImpl.getContainerSync(tx).addBean(context);
        }

        // Call afterBegin/ejbLoad. If ejbLoad throws exceptions,
        // the completeNewTx machinery called by postInvokeTx
        // will rollback the tx. Since we have already registered
        // a Synchronization object with the TM, the afterCompletion
        // will get called.
        container.afterBegin(context);
    }

    /**
     * Use caller transaction to execute a bean method
     */
    protected void useClientTx(Transaction prevTx, EjbInvocation inv) {
        Transaction clientTx;
        int status=-1;
        int prevStatus=-1;
        try {
            // Note: inv.clientTx will not be set at this point.
            clientTx = transactionManager.getTransaction();
            status = clientTx.getStatus();  // clientTx cant be null
            if ( prevTx != null ) {
                prevStatus = prevTx.getStatus();
            }
        } catch (Exception ex) {
            try {
                transactionManager.setRollbackOnly();
            } catch ( Exception e ) {
		//FIXME: Use LogStrings.properties
		_logger.log(Level.FINEST, "", e);
	    }
            throw new TransactionRolledbackLocalException("", ex);
        }

        // If the client's tx is going to rollback, it is fruitless
        // to invoke the EJB, so throw an exception back to client.
        if ( status == Status.STATUS_MARKED_ROLLBACK
                || status == Status.STATUS_ROLLEDBACK
                || status == Status.STATUS_ROLLING_BACK ) {
            throw new TransactionRolledbackLocalException("Client's transaction aborted");
        }

        container.validateEMForClientTx(inv, (JavaEETransaction) clientTx);

        if ( prevTx == null || prevStatus == Status.STATUS_NO_TRANSACTION ) {
            // First time the bean is running in this new client Tx
            EJBContextImpl context = (EJBContextImpl)inv.context;

            //Must change this for singleton
            if (! container.isSingleton) {
                context.setTransaction(clientTx);
            }
            try {
                transactionManager.enlistComponentResources();

                if ( !container.isStatelessSession && !container.isMessageDriven && !container.isSingleton) {
                    // Create a Synchronization object.

                    // Not needed for stateless beans or message-driven beans
                    // or singletons because they cant have Synchronization callbacks,
                    // and they cant be associated with a tx across
                    // invocations.
                    // Register sync for methods other than finders/home methods
                    if ( !inv.invocationInfo.isHomeFinder ) {
                        ejbContainerUtilImpl.getContainerSync(clientTx).addBean(
                        context);
                    }

                    container.afterBegin(context);
                }
            } catch (Exception ex) {
                try {
                    transactionManager.setRollbackOnly();
                } catch ( Exception e ) {
					//FIXME: Use LogStrings.properties
					_logger.log(Level.FINEST, "", e);
				}
                throw new TransactionRolledbackLocalException("", ex);
            }
        } else { // Bean already has a transaction associated with it.
            if ( !prevTx.equals(clientTx) ) {
                // There is already a different Tx in progress !!
                // Note: this can only happen for stateful SessionBeans.
                // EntityBeans will get a different context for every Tx.
                if ( container.isSession ) {
                    // Row 2 in Table E
                    throw new IllegalStateException(
                    "EJB is already associated with an incomplete transaction");
                }
            } else { // Bean was invoked again with the same transaction
                // This allows the TM to enlist resources used by the EJB
                // with the transaction
                try {
                    transactionManager.enlistComponentResources();
                } catch (Exception ex) {
                    try {
                        transactionManager.setRollbackOnly();
                    } catch ( Exception e ) {
                        //FIXME: Use LogStrings.properties
                        _logger.log(Level.FINEST, "", e);
					}
                    throw new TransactionRolledbackLocalException("", ex);
                }
            }
        }
     }

    /**
     * Handle transaction requirements, if any, after invoking bean method
     */
    protected void postInvokeTx(EjbInvocation inv) throws Exception {

        Throwable exception = inv.exception;

        // For StatelessSessionBeans, ejbCreate/ejbRemove was called without a Tx,
        // so resume client's Tx if needed.
        // For StatefulSessionBeans ejbCreate/ejbRemove was called with or without a Tx,
        // so resume client's Tx if needed.
        // For EntityBeans, ejbCreate/ejbRemove/ejbFind must be called with a Tx
        // so no special processing needed.
        if ( container.resumeTransaction(inv) ) {
           // check if there was a suspended client Tx
            if ( inv.clientTx != null ) {
                transactionManager.resume(inv.clientTx);
            }

            if ( inv.exception != null
                     && inv.exception instanceof BaseContainer.PreInvokeException ) {
                inv.exception = ((BaseContainer.PreInvokeException)exception).exception;
            }

            return;
        }

        EJBContextImpl context = (EJBContextImpl)inv.context;

        int status = transactionManager.getStatus();
        int txAttr = inv.invocationInfo.txAttr;

        Throwable newException = exception; // default

        // Note: inv.exception may have been thrown by the container
        // during preInvoke (i.e. bean may never have been invoked).

        // Exception and Tx handling rules. See EJB2.0 Sections 17.6, 18.3.
        switch (txAttr) {
            case Container.TX_BEAN_MANAGED:
                // EJB2.0 section 18.3.1, Table 16
                // Note: only SessionBeans can be TX_BEAN_MANAGED
                newException = checkExceptionBeanMgTx(context, exception, status);
                if ( inv.clientTx != null ) {
                    // there was a client Tx which was suspended
                    transactionManager.resume(inv.clientTx);
                }
                break;

            case Container.TX_NOT_SUPPORTED:
            case Container.TX_NEVER:
                // NotSupported and Never are handled in the same way
                // EJB2.0 sections 17.6.2.1, 17.6.2.6.
                // EJB executed in no Tx
                if ( exception != null ) {
                    newException = checkExceptionNoTx(context, exception);
                }
                container.postInvokeNoTx(inv);

                if ( inv.clientTx != null ) {
                    // there was a client Tx which was suspended
                    transactionManager.resume(inv.clientTx);
                }

                break;

            case Container.TX_MANDATORY:
                // EJB2.0 section 18.3.1, Table 15
                // EJB executed in client's Tx
                if ( exception != null ) {
                    newException = checkExceptionClientTx(context, exception);
                }
                break;

            case Container.TX_REQUIRED:
                // EJB2.0 section 18.3.1, Table 15
                if ( inv.clientTx == null ) {
                    // EJB executed in new Tx started in preInvokeTx
                    newException = completeNewTx(context, exception, status);
                } else {
                    // EJB executed in client's tx
                    if ( exception != null ) {
                        newException = checkExceptionClientTx(context, exception);
                    }
                }
                break;

            case Container.TX_REQUIRES_NEW:
                // EJB2.0 section 18.3.1, Table 15
                // EJB executed in new Tx started in preInvokeTx
                newException = completeNewTx(context, exception, status);

                if ( inv.clientTx != null ) {
                    // there was a client Tx which was suspended
                    transactionManager.resume(inv.clientTx);
                }
                break;

            case Container.TX_SUPPORTS:
                // EJB2.0 section 18.3.1, Table 15
                if ( status != Status.STATUS_NO_TRANSACTION ) {
                    // EJB executed in client's tx
                    if ( exception != null ) {
                        newException = checkExceptionClientTx(context, exception);
                    }
                } else {
                    // EJB executed in no Tx
                    if ( exception != null ) {
                        newException = checkExceptionNoTx(context, exception);
                    }
                    container.postInvokeNoTx(inv);
                }
                break;

            default:
        }

        inv.exception = newException;

        // XXX If any of the TM commit/rollback/suspend calls throws an
        // exception, should the transaction be rolled back if not already so ?
     }

    final UserTransaction getUserTransaction() {
        // Only session beans with bean-managed transactions
        // or message-driven beans with bean-managed transactions
        // can programmatically demarcate transactions.
        if ( (container.isSession || container.isMessageDriven) && container.isBeanManagedTran ) {
            try {
                UserTransaction utx = (UserTransaction)
                        container.namingManager.getInitialContext().lookup(USER_TX);
                return utx;
            } catch ( Exception ex ) {
                _logger.log(Level.FINE, "ejb.user_transaction_exception", ex);
                throw new EJBException(_logger.getResourceBundle().
                        getString("ejb.user_transaction_exception"), ex);
            }
        }
        else {
            throw new IllegalStateException(localStrings.getLocalString(
                "ejb.ut_only_for_bmt",
                "Only session beans with bean-managed transactions can obtain UserTransaction"));
        }
    }

    private EJBException destroyBeanAndRollback(EJBContextImpl context, String type) throws Exception {
        try {
            container.forceDestroyBean(context);
        } finally {
            transactionManager.rollback();
        }

        EJBException ex = null;
        if (type != null) {
            ex = new EJBException(type + " method returned without completing transaction");
            _logger.log(Level.FINE, "ejb.incomplete_sessionbean_txn_exception");
            _logger.log(Level.FINE,"",ex);
        }
        return ex;
    }

    private Throwable checkExceptionBeanMgTx(EJBContextImpl context,
            Throwable exception, int status) throws Exception {
        Throwable newException = exception;
        // EJB2.0 section 18.3.1, Table 16
        if ( exception != null && exception instanceof BaseContainer.PreInvokeException ) {
            // A PreInvokeException was thrown, so bean was not invoked
            newException= ((BaseContainer.PreInvokeException)exception).exception;

        } else if ( status == Status.STATUS_NO_TRANSACTION ) {
            // EJB was invoked, EJB's Tx is complete.
            if ( exception != null ) {
                newException = checkExceptionNoTx(context, exception);
            }
        } else {
            // EJB was invoked, EJB's Tx is incomplete.
            // See EJB2.0 Section 17.6.1
            if ( container.isStatefulSession ) {
                if ( !container.isSystemUncheckedException(exception) ) {
                    if( isAppExceptionRequiringRollback(exception) ) {
                        transactionManager.rollback();
                    } else {
                        transactionManager.suspend();
                    }
                } else {
                    // system/unchecked exception was thrown by EJB
                    destroyBeanAndRollback(context, null);
                    newException = processSystemException(exception);
                }
            } else if( container.isStatelessSession  ) { // stateless SessionBean
                newException = destroyBeanAndRollback(context, "Stateless SessionBean");

            } else if( container.isSingleton ) {
                newException = destroyBeanAndRollback(context, "Singleton SessionBean");

            } else { // MessageDrivenBean
                newException = destroyBeanAndRollback(context, "MessageDrivenBean");
            }
        }
        return newException;
    }

    private Throwable checkExceptionNoTx(EJBContextImpl context, Throwable exception)
            throws Exception {
        if ( exception instanceof BaseContainer.PreInvokeException ) {
            // A PreInvokeException was thrown, so bean was not invoked
            return ((BaseContainer.PreInvokeException)exception).exception;
        }

        // If PreInvokeException was not thrown, EJB was invoked with no Tx
        Throwable newException = exception;
        if ( container.isSystemUncheckedException(exception) ) {
            // Table 15, EJB2.0
            newException = processSystemException(exception);
            container.forceDestroyBean(context);
        }
        return newException;
    }

    // Can be called by the container - do not make it private
    Throwable checkExceptionClientTx(EJBContextImpl context, Throwable exception)
             throws Exception {
        if ( exception instanceof BaseContainer.PreInvokeException ) {
            // A PreInvokeException was thrown, so bean was not invoked
            return ((BaseContainer.PreInvokeException)exception).exception;
        }

        // If PreInvokeException wasn't thrown, EJB was invoked with client's Tx
        Throwable newException = exception;
        if ( container.isSystemUncheckedException(exception) ) {
            // Table 15, EJB2.0
            try {
                container.forceDestroyBean(context);
            } finally {
                transactionManager.setRollbackOnly();
            }
            if ( exception instanceof Exception ) {
                newException = new TransactionRolledbackLocalException(
                	"Exception thrown from bean", (Exception)exception);
            } else {
                newException = new TransactionRolledbackLocalException(
                	"Exception thrown from bean: "+exception.toString());
                newException.initCause(exception);
            }
        } else if( isAppExceptionRequiringRollback(exception ) ) {
            transactionManager.setRollbackOnly();
        }

        return newException;
     }

    // this is the counterpart of startNewTx
    private Throwable completeNewTx(EJBContextImpl context, Throwable exception,
            int status) throws Exception {
        Throwable newException = exception;
        if ( exception instanceof BaseContainer.PreInvokeException )
            newException = ((BaseContainer.PreInvokeException)exception).exception;

        if ( status == Status.STATUS_NO_TRANSACTION ) {
            // no tx was started, probably an exception was thrown
            // before tm.begin() was called
            return newException;
        }

        if ( container.isStatefulSession && (context instanceof SessionContextImpl)) {
            ((SessionContextImpl) context).setTxCompleting(true);
        }

        // A new tx was started, so we must commit/rollback
        if ( newException != null && container.isSystemUncheckedException(newException) ) {
            // EJB2.0 section 18.3.1, Table 15
            // Rollback the Tx we started
            destroyBeanAndRollback(context, null);
            newException = processSystemException(newException);
        }
        else {
            try {
                if ( status == Status.STATUS_MARKED_ROLLBACK ) {
                    // EJB2.0 section 18.3.1, Table 15, and 18.3.6:
                    // rollback tx, no exception
                    if (transactionManager.isTimedOut()) {
                        _logger.log(Level.WARNING, "ejb.tx_timeout", new Object[] {
                            transactionManager.getTransaction(), ejbDescriptor.getName()});
                    }
                    transactionManager.rollback();
                }
                else {
                    if( (newException != null) &&
                            isAppExceptionRequiringRollback(newException) ) {
                        transactionManager.rollback();
                    } else {
                        // Note: if exception is an application exception
                        // we do a commit as in EJB2.0 Section 18.3.1,
                        // Table 15. Commit the Tx we started
                        transactionManager.commit();
                    }
                }
            } catch (RollbackException ex) {
                _logger.log(Level.FINE, "ejb.transaction_abort_exception", ex);
                // EJB2.0 section 18.3.6
                newException = new EJBException("Transaction aborted", ex);
            } catch ( Exception ex ) {
                _logger.log(Level.FINE, "ejb.cmt_exception", ex);
                // Commit or rollback failed.
                // EJB2.0 section 18.3.6
                newException = new EJBException("Unable to complete" +
                    " container-managed transaction.", ex);
            }
        }
        return newException;
    }

    private Throwable processSystemException(Throwable sysEx) {
        Throwable newException;
        if ( sysEx instanceof EJBException)
            return sysEx;

        // EJB2.0 section 18.3.4
        if ( sysEx instanceof NoSuchEntityException ) { // for EntityBeans only
            newException = new NoSuchObjectLocalException
                ("NoSuchEntityException thrown by EJB method.");
            newException.initCause(sysEx);
        } else {
            newException = new EJBException();
            newException.initCause(sysEx);
        }

        return newException;
    }


    /**
     * Returns true if this exception is an Application Exception and
     * it requires rollback of the transaction in which it was thrown.
     */
    private boolean isAppExceptionRequiringRollback(Throwable exception) {
        boolean appExceptionRequiringRollback = false;

        if ( exception != null ) {

            Class clazz = exception.getClass();
            String exceptionClassName = clazz.getName();
            Map appExceptions =
                    ejbDescriptor.getEjbBundleDescriptor().getApplicationExceptions();
            while (clazz != null) {
                String eClassName = clazz.getName();
                if (appExceptions.containsKey(eClassName)) {
                    if( exceptionClassName.equals(eClassName) ||
                            appExceptions.get(eClassName).getInherited() == true) {
                        // Exact exception is specified as an ApplicationException
                        // or superclass exception is inherited
                        appExceptionRequiringRollback = appExceptions.get(eClassName).getRollback();
                    }
                    break;
                }
                clazz = clazz.getSuperclass();
            }
        }

        return appExceptionRequiringRollback;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy