org.xerela.zap.hibernate.internal.ZTransaction.java Source code

Java tutorial

Introduction

Here is the source code for org.xerela.zap.hibernate.internal.ZTransaction.java

Source

/*
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * The Original Code is Ziptie Client Framework.
 * 
 * The Initial Developer of the Original Code is AlterPoint.
 * Portions created by AlterPoint are Copyright (C) 2006,
 * AlterPoint, Inc. All Rights Reserved.
 * 
 * Contributor(s):
 */

package org.xerela.zap.hibernate.internal;

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

import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Transaction;
import org.hibernate.TransactionException;
import org.hibernate.jdbc.JDBCContext;
import org.hibernate.transaction.TransactionFactory;

/**
 * ZTransaction
 */
public class ZTransaction implements Transaction {
    private static final String COULD_NOT_DETERMINE_TRANSACTION_STATUS = "Could not determine transaction status"; //$NON-NLS-1$

    private static final String SET_HIBERNATE_TRANSACTION_MANAGER_LOOKUP_CLASS_IF_CACHE_IS_ENABLED = "You should set hibernate.transaction.manager_lookup_class if cache is enabled"; //$NON-NLS-1$

    private static final Logger LOGGER = Logger.getLogger(ZTransaction.class);

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

    private UserTransaction userTransaction;

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

    /**
     * Construct a ZTransaction wrapper around the given UserTransaction.
     *
     * @param ut a UserTransaction instance
     * @param jdbcContext the Hibernate JDBCContext instance
     * @param xactionContext the Hibernate transaction context
     */
    public ZTransaction(UserTransaction ut, JDBCContext jdbcContext, TransactionFactory.Context xactionContext) {
        this.userTransaction = ut;
        this.jdbcContext = jdbcContext;
        this.transactionContext = xactionContext;
    }

    /** {@inheritDoc} */
    public void begin() {
        if (begun) {
            return;
        }

        if (commitFailed) {
            throw new TransactionException("Cannot re-start transaction after failed commit."); //$NON-NLS-1$
        }

        try {
            newTransaction = userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION;
            if (newTransaction) {
                userTransaction.begin();
            }
        } catch (NotSupportedException e) {
            throw new HibernateException(e);
        } catch (SystemException e) {
            throw new HibernateException(e);
        }

        boolean synchronization = jdbcContext.registerSynchronizationIfPossible();

        if (!newTransaction && !synchronization) {
            LOGGER.warn(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);
    }

    /** {@inheritDoc} */
    public void commit() {
        if (!begun) {
            throw new TransactionException("Transaction not successfully started."); //$NON-NLS-1$
        }

        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;
                LOGGER.debug("Committed JTA UserTransaction"); //$NON-NLS-1$
            } catch (Exception e) {
                // so the transaction is already rolled back, by JTA spec
                commitFailed = true;
                throw new TransactionException("ZTransaction commit failed: ", e); //$NON-NLS-1$
            } 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();
        }
    }

    /** {@inheritDoc} */
    public void rollback() {
        if (!begun && !commitFailed) {
            throw new TransactionException("Transaction not successfully started"); //$NON-NLS-1$
        }

        try {
            closeIfRequired();
        } catch (Exception e) {
            LOGGER.error("Could not close session during rollback", e); //$NON-NLS-1$
            //swallow it, and continue to roll back JTA transaction
        }

        try {
            if (newTransaction) {
                if (!commitFailed) {
                    userTransaction.rollback();
                }
            } else {
                userTransaction.setRollbackOnly();
            }
        } catch (Exception e) {
            throw new HibernateException(e);
        } finally {
            afterCommitRollback();
        }
    }

    /** {@inheritDoc} */
    public boolean isActive() {
        if (!begun || commitFailed || commitSucceeded) {
            return false;
        }

        try {
            return userTransaction.getStatus() == Status.STATUS_ACTIVE;
        } catch (SystemException e) {
            throw new HibernateException(e);
        }
    }

    /** {@inheritDoc} */
    public void registerSynchronization(Synchronization synchronization) {
        if (transactionContext.getFactory().getTransactionManager() == null) {
            throw new IllegalStateException("TransactionManager not available"); //$NON-NLS-1$
        } else {
            try {
                transactionContext.getFactory().getTransactionManager().getTransaction()
                        .registerSynchronization(synchronization);
            } catch (Exception e) {
                throw new TransactionException("Could not register synchronization", e); //$NON-NLS-1$
            }
        }
    }

    /** {@inheritDoc} */
    public void setTimeout(int seconds) {
        try {
            userTransaction.setTransactionTimeout(seconds);
        } catch (SystemException e) {
            throw new HibernateException(e);
        }
    }

    /** {@inheritDoc} */
    public boolean wasCommitted() {
        try {
            int status = userTransaction.getStatus();
            if (status == Status.STATUS_UNKNOWN) {
                throw new SystemException();
            }

            return status == Status.STATUS_COMMITTED;
        } catch (SystemException se) {
            throw new TransactionException(COULD_NOT_DETERMINE_TRANSACTION_STATUS);
        }
    }

    /** {@inheritDoc} */
    public boolean wasRolledBack() {
        try {
            int status = userTransaction.getStatus();
            if (status == Status.STATUS_UNKNOWN) {
                throw new SystemException();
            }

            return status == Status.STATUS_ROLLEDBACK || status == Status.STATUS_ROLLING_BACK
                    || status == Status.STATUS_MARKED_ROLLBACK;
        } catch (SystemException se) {
            throw new TransactionException(COULD_NOT_DETERMINE_TRANSACTION_STATUS);
        }
    }

    /**
     * Get the contained UserTransaction.
     *
     * @return a UserTransaction object, or null
     */
    public UserTransaction getUserTransaction() {
        return userTransaction;
    }

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

    /**
     * 
     */
    private void afterCommitRollback() {

        begun = false;

        if (callback) {
            // this method is a noop if there is a Synchronization!
            if (!newTransaction) {
                LOGGER.warn(SET_HIBERNATE_TRANSACTION_MANAGER_LOOKUP_CLASS_IF_CACHE_IS_ENABLED);
            }

            int status = Integer.MIN_VALUE;
            try {
                status = userTransaction.getStatus();
            } catch (Exception e) {
                throw new TransactionException("Could not determine transaction status after commit", e); //$NON-NLS-1$
            } 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);
            }

        }
    }
}