org.hibernate.transaction.JTATransaction.java Source code

Java tutorial

Introduction

Here is the source code for org.hibernate.transaction.JTATransaction.java

Source

/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
 * indicated by the @author tags or express copyright attribution
 * statements applied by the authors.  All third-party contributions are
 * distributed under license by Red Hat Middleware LLC.
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 *
 */
package org.hibernate.transaction;

import javax.transaction.Status;
import javax.transaction.Synchronization;
import javax.transaction.SystemException;
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.hibernate.HibernateException;
import org.hibernate.Transaction;
import org.hibernate.TransactionException;
import org.hibernate.jdbc.JDBCContext;
import org.hibernate.util.JTAHelper;

/**
 * {@link Transaction} implementation based on transaction management through
 * a JTA {@link UserTransaction}.  Similar to {@link CMTTransaction}, except
 * here we are actually managing the transactions through the Hibernate
 * transaction mechanism.
 *
 * @author Gavin King
 * @author Steve Ebersole
 * @author Les Hazlewood
 */
public class JTATransaction implements Transaction {

    private static final Log log = LogFactory.getLog(JTATransaction.class);

    private final JDBCContext jdbcContext;
    private final TransactionFactory.Context transactionContext;

    private UserTransaction userTransaction;
    private boolean newTransaction;
    private boolean begun;
    private boolean commitFailed;
    private boolean commitSucceeded;
    private boolean callback;

    public JTATransaction(UserTransaction userTransaction, JDBCContext jdbcContext,
            TransactionFactory.Context transactionContext) {
        this.jdbcContext = jdbcContext;
        this.transactionContext = transactionContext;
        this.userTransaction = userTransaction;
    }

    public void begin() throws HibernateException {
        if (begun) {
            return;
        }
        if (commitFailed) {
            throw new TransactionException("cannot re-start transaction after failed commit");
        }

        log.debug("begin");

        try {
            newTransaction = userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION;
            if (newTransaction) {
                userTransaction.begin();
                log.debug("Began a new JTA transaction");
            }
        } catch (Exception e) {
            log.error("JTA transaction begin failed", e);
            throw new TransactionException("JTA transaction begin failed", e);
        }

        /*if (newTransaction) {
           // don't need a synchronization since we are committing
           // or rolling back the transaction ourselves - assuming
           // that we do no work in beforeTransactionCompletion()
           synchronization = false;
        }*/

        boolean synchronization = jdbcContext.registerSynchronizationIfPossible();

        if (!newTransaction && !synchronization) {
            log.warn("You should set hibernate.transaction.manager_lookup_class if cache is enabled");
        }

        if (!synchronization) {
            //if we could not register a synchronization,
            //do the before/after completion callbacks
            //ourself (but we need to let jdbcContext
            //know that this is what we are going to
            //do, so it doesn't keep trying to register
            //synchronizations)
            callback = jdbcContext.registerCallbackIfNecessary();
        }

        begun = true;
        commitSucceeded = false;

        jdbcContext.afterTransactionBegin(this);
    }

    public void commit() throws HibernateException {
        if (!begun) {
            throw new TransactionException("Transaction not successfully started");
        }

        log.debug("commit");

        boolean flush = !transactionContext.isFlushModeNever()
                && (callback || !transactionContext.isFlushBeforeCompletionEnabled());

        if (flush) {
            transactionContext.managedFlush(); //if an exception occurs during flush, user must call rollback()
        }

        if (callback && newTransaction) {
            jdbcContext.beforeTransactionCompletion(this);
        }

        closeIfRequired();

        if (newTransaction) {
            try {
                userTransaction.commit();
                commitSucceeded = true;
                log.debug("Committed JTA UserTransaction");
            } catch (Exception e) {
                commitFailed = true; // so the transaction is already rolled back, by JTA spec
                log.error("JTA commit failed", e);
                throw new TransactionException("JTA commit failed: ", e);
            } finally {
                afterCommitRollback();
            }
        } else {
            // this one only really needed for badly-behaved applications!
            // (if the TransactionManager has a Sychronization registered,
            // its a noop)
            // (actually we do need it for downgrading locks)
            afterCommitRollback();
        }

    }

    public void rollback() throws HibernateException {
        if (!begun && !commitFailed) {
            throw new TransactionException("Transaction not successfully started");
        }

        log.debug("rollback");

        /*if (!synchronization && newTransaction && !commitFailed) {
           jdbcContext.beforeTransactionCompletion(this);
        }*/

        try {
            closeIfRequired();
        } catch (Exception e) {
            log.error("could not close session during rollback", e);
            //swallow it, and continue to roll back JTA transaction
        }

        try {
            if (newTransaction) {
                if (!commitFailed) {
                    userTransaction.rollback();
                    log.debug("Rolled back JTA UserTransaction");
                }
            } else {
                userTransaction.setRollbackOnly();
                log.debug("set JTA UserTransaction to rollback only");
            }
        } catch (Exception e) {
            log.error("JTA rollback failed", e);
            throw new TransactionException("JTA rollback failed", e);
        } finally {
            afterCommitRollback();
        }
    }

    private static final int NULL = Integer.MIN_VALUE;

    private void afterCommitRollback() throws TransactionException {

        begun = false;

        if (callback) { // this method is a noop if there is a Synchronization!

            if (!newTransaction) {
                log.warn("You should set hibernate.transaction.manager_lookup_class if cache is enabled");
            }
            int status = NULL;
            try {
                status = userTransaction.getStatus();
            } catch (Exception e) {
                log.error("Could not determine transaction status after commit", e);
                throw new TransactionException("Could not determine transaction status after commit", e);
            } finally {
                /*if (status!=Status.STATUS_COMMITTED && status!=Status.STATUS_ROLLEDBACK) {
                   log.warn("Transaction not complete - you should set hibernate.transaction.manager_lookup_class if cache is enabled");
                   //throw exception??
                }*/
                jdbcContext.afterTransactionCompletion(status == Status.STATUS_COMMITTED, this);
            }

        }
    }

    public boolean wasRolledBack() throws TransactionException {

        //if (!begun) return false;
        //if (commitFailed) return true;

        final int status;
        try {
            status = userTransaction.getStatus();
        } catch (SystemException se) {
            log.error("Could not determine transaction status", se);
            throw new TransactionException("Could not determine transaction status", se);
        }
        if (status == Status.STATUS_UNKNOWN) {
            throw new TransactionException("Could not determine transaction status");
        } else {
            return JTAHelper.isRollback(status);
        }
    }

    public boolean wasCommitted() throws TransactionException {

        //if (!begun || commitFailed) return false;

        final int status;
        try {
            status = userTransaction.getStatus();
        } catch (SystemException se) {
            log.error("Could not determine transaction status", se);
            throw new TransactionException("Could not determine transaction status: ", se);
        }
        if (status == Status.STATUS_UNKNOWN) {
            throw new TransactionException("Could not determine transaction status");
        } else {
            return status == Status.STATUS_COMMITTED;
        }
    }

    public boolean isActive() throws TransactionException {

        if (!begun || commitFailed || commitSucceeded) {
            return false;
        }

        final int status;
        try {
            status = userTransaction.getStatus();
        } catch (SystemException se) {
            log.error("Could not determine transaction status", se);
            throw new TransactionException("Could not determine transaction status: ", se);
        }
        if (status == Status.STATUS_UNKNOWN) {
            throw new TransactionException("Could not determine transaction status");
        } else {
            return status == Status.STATUS_ACTIVE;
        }
    }

    public void registerSynchronization(Synchronization sync) throws HibernateException {
        if (getTransactionManager() == null) {
            throw new IllegalStateException("JTA TransactionManager not available");
        } else {
            try {
                getTransactionManager().getTransaction().registerSynchronization(sync);
            } catch (Exception e) {
                throw new TransactionException("could not register synchronization", e);
            }
        }
    }

    private TransactionManager getTransactionManager() {
        return transactionContext.getFactory().getTransactionManager();
    }

    private void closeIfRequired() throws HibernateException {
        boolean close = callback && transactionContext.shouldAutoClose() && !transactionContext.isClosed();
        if (close) {
            transactionContext.managedClose();
        }
    }

    public void setTimeout(int seconds) {
        try {
            userTransaction.setTransactionTimeout(seconds);
        } catch (SystemException se) {
            throw new TransactionException("could not set transaction timeout", se);
        }
    }

    protected UserTransaction getUserTransaction() {
        return userTransaction;
    }
}