Save This Page
Home » Hibernate-3.3.2.GA » org.hibernate » transaction » [javadoc | source]
    1   /*
    2    * Hibernate, Relational Persistence for Idiomatic Java
    3    *
    4    * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
    5    * indicated by the @author tags or express copyright attribution
    6    * statements applied by the authors.  All third-party contributions are
    7    * distributed under license by Red Hat Middleware LLC.
    8    *
    9    * This copyrighted material is made available to anyone wishing to use, modify,
   10    * copy, or redistribute it subject to the terms and conditions of the GNU
   11    * Lesser General Public License, as published by the Free Software Foundation.
   12    *
   13    * This program is distributed in the hope that it will be useful,
   14    * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   15    * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
   16    * for more details.
   17    *
   18    * You should have received a copy of the GNU Lesser General Public License
   19    * along with this distribution; if not, write to:
   20    * Free Software Foundation, Inc.
   21    * 51 Franklin Street, Fifth Floor
   22    * Boston, MA  02110-1301  USA
   23    *
   24    */
   25   package org.hibernate.transaction;
   26   
   27   import javax.transaction.Status;
   28   import javax.transaction.Synchronization;
   29   import javax.transaction.SystemException;
   30   import javax.transaction.TransactionManager;
   31   import javax.transaction.UserTransaction;
   32   
   33   import org.slf4j.Logger;
   34   import org.slf4j.LoggerFactory;
   35   
   36   import org.hibernate.HibernateException;
   37   import org.hibernate.Transaction;
   38   import org.hibernate.TransactionException;
   39   import org.hibernate.jdbc.JDBCContext;
   40   import org.hibernate.util.JTAHelper;
   41   
   42   /**
   43    * {@link Transaction} implementation based on transaction management through
   44    * a JTA {@link UserTransaction}.  Similar to {@link CMTTransaction}, except
   45    * here we are actually managing the transactions through the Hibernate
   46    * transaction mechanism.
   47    *
   48    * @author Gavin King
   49    * @author Steve Ebersole
   50    * @author Les Hazlewood
   51    */
   52   public class JTATransaction implements Transaction {
   53   
   54   	private static final Logger log = LoggerFactory.getLogger( JTATransaction.class );
   55   
   56   	private final JDBCContext jdbcContext;
   57   	private final TransactionFactory.Context transactionContext;
   58   
   59   	private UserTransaction userTransaction;
   60   	private boolean newTransaction;
   61   	private boolean begun;
   62   	private boolean commitFailed;
   63   	private boolean commitSucceeded;
   64   	private boolean callback;
   65   
   66   	public JTATransaction(
   67   			UserTransaction userTransaction,
   68   			JDBCContext jdbcContext,
   69   			TransactionFactory.Context transactionContext) {
   70   		this.jdbcContext = jdbcContext;
   71   		this.transactionContext = transactionContext;
   72   		this.userTransaction = userTransaction;
   73   	}
   74   
   75   	/**
   76   	 * {@inheritDoc}
   77   	 */
   78   	public void begin() throws HibernateException {
   79   		if ( begun ) {
   80   			return;
   81   		}
   82   		if ( commitFailed ) {
   83   			throw new TransactionException( "cannot re-start transaction after failed commit" );
   84   		}
   85   
   86   		log.debug( "begin" );
   87   
   88   		try {
   89   			newTransaction = userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION;
   90   			if ( newTransaction ) {
   91   				userTransaction.begin();
   92   				log.debug( "Began a new JTA transaction" );
   93   			}
   94   		}
   95   		catch ( Exception e ) {
   96   			log.error( "JTA transaction begin failed", e );
   97   			throw new TransactionException( "JTA transaction begin failed", e );
   98   		}
   99   
  100   		/*if (newTransaction) {
  101   			// don't need a synchronization since we are committing
  102   			// or rolling back the transaction ourselves - assuming
  103   			// that we do no work in beforeTransactionCompletion()
  104   			synchronization = false;
  105   		}*/
  106   
  107   		boolean synchronization = jdbcContext.registerSynchronizationIfPossible();
  108   
  109   		if ( !newTransaction && !synchronization ) {
  110   			log.warn( "You should set hibernate.transaction.manager_lookup_class if cache is enabled" );
  111   		}
  112   
  113   		if ( !synchronization ) {
  114   			//if we could not register a synchronization,
  115   			//do the before/after completion callbacks
  116   			//ourself (but we need to let jdbcContext
  117   			//know that this is what we are going to
  118   			//do, so it doesn't keep trying to register
  119   			//synchronizations)
  120   			callback = jdbcContext.registerCallbackIfNecessary();
  121   		}
  122   
  123   		begun = true;
  124   		commitSucceeded = false;
  125   
  126   		jdbcContext.afterTransactionBegin( this );
  127   	}
  128   
  129   	/**
  130   	 * {@inheritDoc}
  131   	 */
  132   	public void commit() throws HibernateException {
  133   		if ( !begun ) {
  134   			throw new TransactionException( "Transaction not successfully started" );
  135   		}
  136   
  137   		log.debug( "commit" );
  138   
  139   		boolean flush = !transactionContext.isFlushModeNever()
  140   				&& ( callback || !transactionContext.isFlushBeforeCompletionEnabled() );
  141   
  142   		if ( flush ) {
  143   			transactionContext.managedFlush(); //if an exception occurs during flush, user must call rollback()
  144   		}
  145   
  146   		if ( callback && newTransaction ) {
  147   			jdbcContext.beforeTransactionCompletion( this );
  148   		}
  149   
  150   		closeIfRequired();
  151   
  152   		if ( newTransaction ) {
  153   			try {
  154   				userTransaction.commit();
  155   				commitSucceeded = true;
  156   				log.debug( "Committed JTA UserTransaction" );
  157   			}
  158   			catch ( Exception e ) {
  159   				commitFailed = true; // so the transaction is already rolled back, by JTA spec
  160   				log.error( "JTA commit failed", e );
  161   				throw new TransactionException( "JTA commit failed: ", e );
  162   			}
  163   			finally {
  164   				afterCommitRollback();
  165   			}
  166   		}
  167   		else {
  168   			// this one only really needed for badly-behaved applications!
  169   			// (if the TransactionManager has a Sychronization registered,
  170   			// its a noop)
  171   			// (actually we do need it for downgrading locks)
  172   			afterCommitRollback();
  173   		}
  174   
  175   	}
  176   
  177   	/**
  178   	 * {@inheritDoc}
  179   	 */
  180   	public void rollback() throws HibernateException {
  181   		if ( !begun && !commitFailed ) {
  182   			throw new TransactionException( "Transaction not successfully started" );
  183   		}
  184   
  185   		log.debug( "rollback" );
  186   
  187   		try {
  188   			closeIfRequired();
  189   		}
  190   		catch ( Exception e ) {
  191   			// swallow it, and continue to roll back JTA transaction
  192   			log.error( "could not close session during rollback", e );
  193   		}
  194   
  195   		try {
  196   			if ( newTransaction ) {
  197   				if ( !commitFailed ) {
  198   					userTransaction.rollback();
  199   					log.debug( "Rolled back JTA UserTransaction" );
  200   				}
  201   			}
  202   			else {
  203   				userTransaction.setRollbackOnly();
  204   				log.debug( "set JTA UserTransaction to rollback only" );
  205   			}
  206   		}
  207   		catch ( Exception e ) {
  208   			log.error( "JTA rollback failed", e );
  209   			throw new TransactionException( "JTA rollback failed", e );
  210   		}
  211   		finally {
  212   			afterCommitRollback();
  213   		}
  214   	}
  215   
  216   	private static final int NULL = Integer.MIN_VALUE;
  217   
  218   	private void afterCommitRollback() throws TransactionException {
  219   
  220   		begun = false;
  221   		// this method is a noop if there is a Synchronization!
  222   		if ( callback ) {
  223   			if ( !newTransaction ) {
  224   				log.warn( "You should set hibernate.transaction.manager_lookup_class if cache is enabled" );
  225   			}
  226   			int status = NULL;
  227   			try {
  228   				status = userTransaction.getStatus();
  229   			}
  230   			catch ( Exception e ) {
  231   				log.error( "Could not determine transaction status after commit", e );
  232   				throw new TransactionException( "Could not determine transaction status after commit", e );
  233   			}
  234   			finally {
  235   				jdbcContext.afterTransactionCompletion( status == Status.STATUS_COMMITTED, this );
  236   			}
  237   		}
  238   	}
  239   
  240   	/**
  241   	 * {@inheritDoc}
  242   	 */
  243   	public boolean wasRolledBack() throws TransactionException {
  244   		final int status;
  245   		try {
  246   			status = userTransaction.getStatus();
  247   		}
  248   		catch ( SystemException se ) {
  249   			log.error( "Could not determine transaction status", se );
  250   			throw new TransactionException( "Could not determine transaction status", se );
  251   		}
  252   		if ( status == Status.STATUS_UNKNOWN ) {
  253   			throw new TransactionException( "Could not determine transaction status" );
  254   		}
  255   		else {
  256   			return JTAHelper.isRollback( status );
  257   		}
  258   	}
  259   
  260   	/**
  261   	 * {@inheritDoc}
  262   	 */
  263   	public boolean wasCommitted() throws TransactionException {
  264   		final int status;
  265   		try {
  266   			status = userTransaction.getStatus();
  267   		}
  268   		catch ( SystemException se ) {
  269   			log.error( "Could not determine transaction status", se );
  270   			throw new TransactionException( "Could not determine transaction status: ", se );
  271   		}
  272   		if ( status == Status.STATUS_UNKNOWN ) {
  273   			throw new TransactionException( "Could not determine transaction status" );
  274   		}
  275   		else {
  276   			return status == Status.STATUS_COMMITTED;
  277   		}
  278   	}
  279   
  280   	/**
  281   	 * {@inheritDoc}
  282   	 */
  283   	public boolean isActive() throws TransactionException {
  284   		if ( !begun || commitFailed || commitSucceeded ) {
  285   			return false;
  286   		}
  287   
  288   		final int status;
  289   		try {
  290   			status = userTransaction.getStatus();
  291   		}
  292   		catch ( SystemException se ) {
  293   			log.error( "Could not determine transaction status", se );
  294   			throw new TransactionException( "Could not determine transaction status: ", se );
  295   		}
  296   		if ( status == Status.STATUS_UNKNOWN ) {
  297   			throw new TransactionException( "Could not determine transaction status" );
  298   		}
  299   		else {
  300   			return status == Status.STATUS_ACTIVE;
  301   		}
  302   	}
  303   
  304   	/**
  305   	 * {@inheritDoc}
  306   	 */
  307   	public void registerSynchronization(Synchronization sync) throws HibernateException {
  308   		if ( getTransactionManager() == null ) {
  309   			throw new IllegalStateException( "JTA TransactionManager not available" );
  310   		}
  311   		else {
  312   			try {
  313   				getTransactionManager().getTransaction().registerSynchronization( sync );
  314   			}
  315   			catch ( Exception e ) {
  316   				throw new TransactionException( "could not register synchronization", e );
  317   			}
  318   		}
  319   	}
  320   
  321   	/**
  322   	 * Getter for property 'transactionManager'.
  323   	 *
  324   	 * @return Value for property 'transactionManager'.
  325   	 */
  326   	private TransactionManager getTransactionManager() {
  327   		return transactionContext.getFactory().getTransactionManager();
  328   	}
  329   
  330   	private void closeIfRequired() throws HibernateException {
  331   		boolean close = callback &&
  332   				transactionContext.shouldAutoClose() &&
  333   				!transactionContext.isClosed();
  334   		if ( close ) {
  335   			transactionContext.managedClose();
  336   		}
  337   	}
  338   
  339   	/**
  340   	 * {@inheritDoc}
  341   	 */
  342   	public void setTimeout(int seconds) {
  343   		try {
  344   			userTransaction.setTransactionTimeout( seconds );
  345   		}
  346   		catch ( SystemException se ) {
  347   			throw new TransactionException( "could not set transaction timeout", se );
  348   		}
  349   	}
  350   
  351   	/**
  352   	 * Getter for property 'userTransaction'.
  353   	 *
  354   	 * @return Value for property 'userTransaction'.
  355   	 */
  356   	protected UserTransaction getUserTransaction() {
  357   		return userTransaction;
  358   	}
  359   }

Save This Page
Home » Hibernate-3.3.2.GA » org.hibernate » transaction » [javadoc | source]