Package org.apache.geronimo.transaction.context

Source Code of org.apache.geronimo.transaction.context.InheritableTransactionContext

/**
*
* Copyright 2003-2004 The Apache Software Foundation
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/

package org.apache.geronimo.transaction.context;

import java.util.Iterator;
import java.util.Map;
import java.util.ArrayList;
import javax.transaction.SystemException;
import javax.transaction.Status;
import javax.transaction.Transaction;
import javax.transaction.InvalidTransactionException;
import javax.transaction.NotSupportedException;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.RollbackException;
import javax.transaction.Synchronization;
import javax.transaction.xa.XAResource;

import org.apache.geronimo.transaction.ExtendedTransactionManager;
import org.apache.geronimo.transaction.ConnectionReleaser;
import org.apache.geronimo.transaction.InstanceContext;

/**
* @version $Rev: 355877 $ $Date: 2005-12-11 03:48:27 +0100 (Sun, 11 Dec 2005) $
*/
abstract class InheritableTransactionContext extends AbstractTransactionContext {
    private final ExtendedTransactionManager txnManager;
    private Transaction transaction;
    private boolean threadAssociated = false;

    protected InheritableTransactionContext(ExtendedTransactionManager txnManager) {
        this.txnManager = txnManager;
    }

    protected InheritableTransactionContext(ExtendedTransactionManager txnManager, Transaction transaction) {
        this.txnManager = txnManager;
        this.transaction = transaction;
    }

    void begin(long transactionTimeoutMilliseconds) throws SystemException, NotSupportedException {
        if (transaction != null) {
            throw new SystemException("Context is already associated with a transaction");
        }
        transaction = txnManager.begin(transactionTimeoutMilliseconds);
        threadAssociated = true;
    }

    boolean isThreadAssociated() {
        return threadAssociated;
    }

    public Transaction getTransaction() {
        return transaction;
    }

    public boolean isInheritable() {
        return true;
    }

    public boolean isActive() {
        if (transaction == null) {
            return false;
        }
        try {
            int status = transaction.getStatus();
            return status == Status.STATUS_ACTIVE || status == Status.STATUS_MARKED_ROLLBACK;
        } catch (SystemException e) {
            return false;
        }
    }

    public boolean enlistResource(XAResource xaResource) throws RollbackException, SystemException {
        if (transaction == null) {
            throw new IllegalStateException("There is no transaction in progress.");
        }

        return transaction.enlistResource(xaResource);
    }

    public boolean delistResource(XAResource xaResource, int flag) throws SystemException {
        if (transaction == null) {
            throw new IllegalStateException("There is no transaction in progress.");
        }
        boolean success = transaction.delistResource(xaResource, flag);
        if (!success) {
            transaction.setRollbackOnly();
        }
        return success;
    }

    public void registerSynchronization(Synchronization synchronization) throws RollbackException, SystemException {
        if (transaction == null) {
            throw new IllegalStateException("There is no transaction in progress.");
        }

        transaction.registerSynchronization(synchronization);
    }

    public boolean getRollbackOnly() throws SystemException {
        if (transaction == null) {
            throw new IllegalStateException("There is no transaction in progress.");
        }

        int status = transaction.getStatus();
        return (status == Status.STATUS_MARKED_ROLLBACK ||
                status == Status.STATUS_ROLLEDBACK ||
                status == Status.STATUS_ROLLING_BACK);
    }

    public void setRollbackOnly() throws IllegalStateException, SystemException {
        if (transaction == null) {
            throw new IllegalStateException("There is no transaction in progress.");
        }
        transaction.setRollbackOnly();
    }

    public void suspend() throws SystemException {
        Transaction suspendedTransaction = txnManager.suspend();
        if (transaction != suspendedTransaction) {
            throw new SystemException("Suspend did not return our transaction: expectedTx=" + transaction + ", suspendedTx=" + suspendedTransaction);
        }
        threadAssociated = false;
    }

    public void resume() throws SystemException, InvalidTransactionException {
        txnManager.resume(transaction);
        threadAssociated = true;
    }

    public boolean commit() throws HeuristicMixedException, HeuristicRollbackException, SystemException, RollbackException {
        return complete();
    }

    public void rollback() throws SystemException {
        setRollbackOnly();
        try {
            complete();
        } catch (SystemException e) {
            throw e;
        } catch (RuntimeException e) {
            throw e;
        } catch (Error e) {
            throw e;
        } catch (Exception e) {
            throw (SystemException) new SystemException("After commit of container transaction failed").initCause(e);
        }
    }

    private boolean complete() throws HeuristicMixedException, HeuristicRollbackException, SystemException, RollbackException {
        if (transaction == null) {
            throw new IllegalStateException("There is no transaction in progress.");
        }

        boolean wasCommitted = false;
        try {
            if (isRolledback()) {
                return false;
            }

            flushState();

            if (isRolledback()) {
                return false;
            }

            // todo we need to flush anyone enrolled during before and then call before on any flushed...
            beforeCommit();

            if (isRolledback()) {
                return false;
            }

            // verify our tx is the current tx associated with the thread
            // this is really only an error case and should never happen, but just to be sure double check
            // immedately before committing using the transaction manager
            Transaction currentTransaction = txnManager.getTransaction();
            if (currentTransaction != transaction) {
                throw new SystemException("An unknown transaction is currently associated with the thread: expectedTx=" + transaction + ", currentTx=" + currentTransaction);
            }

            txnManager.commit();
            wasCommitted = true;
        } catch (Throwable t) {
            rollbackAndThrow("Unable to commit container transaction", t);
        } finally {
            transaction = null;
            try {
                afterCommit(wasCommitted);
            } catch (Throwable e) {
                rollbackAndThrow("After commit of container transaction failed", e);
            } finally {
                unassociateAll();
                connectorAfterCommit();
                threadAssociated = false;
            }
        }
        return wasCommitted;
    }

    private void beforeCommit() throws Throwable {
        // @todo allow for enrollment during pre-commit
        ArrayList toFlush = getAssociatedContexts();
        for (Iterator i = toFlush.iterator(); i.hasNext();) {
            InstanceContext context = (InstanceContext) i.next();
            if (!context.isDead()) {
                context.beforeCommit();
            }
        }
    }

    private void afterCommit(boolean status) throws Throwable {
        Throwable firstThrowable = null;
        ArrayList toFlush = getAssociatedContexts();
        for (Iterator i = toFlush.iterator(); i.hasNext();) {
            InstanceContext context = (InstanceContext) i.next();
            if (!context.isDead()) {
                try {
                    context.afterCommit(status);
                } catch (Throwable e) {
                    if (firstThrowable == null) {
                        firstThrowable = e;
                    }
                }
            }
        }

        if (firstThrowable instanceof Error) {
            throw (Error) firstThrowable;
        } else if (firstThrowable instanceof Exception) {
            throw (Exception) firstThrowable;
        } else if (firstThrowable != null) {
            throw (SystemException) new SystemException().initCause(firstThrowable);
        }
    }

    private void connectorAfterCommit() {
        if (managedConnections != null) {
            for (Iterator entries = managedConnections.entrySet().iterator(); entries.hasNext();) {
                Map.Entry entry = (Map.Entry) entries.next();
                ConnectionReleaser key = (ConnectionReleaser) entry.getKey();
                key.afterCompletion(entry.getValue());
            }
            //If BeanTransactionContext never reuses the same instance for sequential BMT, this
            //clearing is unnecessary.
            managedConnections.clear();
        }
    }

    private boolean isRolledback() throws SystemException {
        int status;
        try {
            status = transaction.getStatus();
        } catch (SystemException e) {
            transaction.rollback();
            throw e;
        }

        if (status == Status.STATUS_MARKED_ROLLBACK) {
            // verify our tx is the current tx associated with the thread
            // this is really only an error case and should never happen, but just to be sure double check
            // immedately before committing using the transaction manager
            Transaction currentTransaction = txnManager.getTransaction();
            if (currentTransaction != transaction) {
                throw new SystemException("An unknown transaction is currently associated with the thread: expectedTx=" + transaction + ", currentTx=" + currentTransaction);
            }

            // we need to rollback
            txnManager.rollback();
            return true;
        } else if (status == Status.STATUS_ROLLEDBACK ||
                status == Status.STATUS_ROLLING_BACK) {
            // already rolled back
            return true;
        }
        return false;
    }

    private void rollbackAndThrow(String message, Throwable throwable) throws HeuristicMixedException, HeuristicRollbackException, SystemException, RollbackException {
        try {
            if (txnManager.getStatus() != Status.STATUS_NO_TRANSACTION) {
                txnManager.rollback();
            }
        } catch (Throwable t) {
            log.error("Unable to roll back transaction", t);
        }

        try {
            // make doubly sure our transaction was rolled back
            // this can happen when there was a junk transaction on the thread
            int status = transaction.getStatus();
            if (status != Status.STATUS_ROLLEDBACK &&
                    status != Status.STATUS_ROLLING_BACK) {
                transaction.rollback();
            }
        } catch (Throwable t) {
            log.error("Unable to roll back transaction", t);
        }

        if (throwable instanceof HeuristicMixedException) {
            throw (HeuristicMixedException) throwable;
        } else if (throwable instanceof HeuristicRollbackException) {
            throw (HeuristicRollbackException) throwable;
        } else if (throwable instanceof RollbackException) {
            throw (RollbackException) throwable;
        } else if (throwable instanceof SystemException) {
            throw (SystemException) throwable;
        } else if (throwable instanceof Error) {
            throw (Error) throwable;
        } else if (throwable instanceof RuntimeException) {
            throw (RuntimeException) throwable;
        } else {
            throw (SystemException) new SystemException(message).initCause(throwable);
        }
    }
}
TOP

Related Classes of org.apache.geronimo.transaction.context.InheritableTransactionContext

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.