Open Source Repository

Home /hibernate/hibernate-3.2.7.ga | Repository Home


org/hibernate/engine/transaction/Isolater.java
package org.hibernate.engine.transaction;

import java.sql.Connection;
import java.sql.SQLException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.exception.JDBCExceptionHelper;

/**
 * Class which provides the isolation semantics required by
 * an {@link IsolatedWork}.  Processing comes in two flavors:<ul>
 <li>{@link #doIsolatedWork} : makes sure the work to be done is
 * performed in a seperate, distinct transaction</li>
 <li>{@link #doNonTransactedWork} : makes sure the work to be
 * done is performed outside the scope of any transaction</li>
 </ul>
 *
 @author Steve Ebersole
 */
public class Isolater {

  private static final Log log = LogFactory.getLogIsolater.class );

  /**
   * Ensures that all processing actually performed by the given work will
   * occur on a seperate transaction.
   *
   @param work The work to be performed.
   @param session The session from which this request is originating.
   @throws HibernateException
   */
  public static void doIsolatedWork(IsolatedWork work, SessionImplementor sessionthrows HibernateException {
    boolean isJta = session.getFactory().getTransactionManager() != null;
    if isJta ) {
      new JtaDelegatesession ).delegateWorkwork, true );
    }
    else {
      new JdbcDelegatesession ).delegateWorkwork, true );
    }
  }

  /**
   * Ensures that all processing actually performed by the given work will
   * occur outside of a transaction.
   *
   @param work The work to be performed.
   @param session The session from which this request is originating.
   @throws HibernateException
   */
  public static void doNonTransactedWork(IsolatedWork work, SessionImplementor sessionthrows HibernateException {
    boolean isJta = session.getFactory().getTransactionManager() != null;
    if isJta ) {
      new JtaDelegatesession ).delegateWorkwork, false );
    }
    else {
      new JdbcDelegatesession ).delegateWorkwork, false );
    }
  }

  // should be ok performance-wise to generate new delegate instances for each
  // request since these are locally stack-scoped.  Besides, it makes the code
  // much easier to read than the old TransactionHelper stuff...

  private static interface Delegate {
    public void delegateWork(IsolatedWork work, boolean transactedthrows HibernateException;
  }

  /**
   * An isolation delegate for JTA-based transactions.  Essentially susepnds
   * any current transaction, does the work in a new transaction, and then
   * resumes the initial transaction (if there was one).
   */
  public static class JtaDelegate implements Delegate {
    private final SessionImplementor session;

    public JtaDelegate(SessionImplementor session) {
      this.session = session;
    }

    public void delegateWork(IsolatedWork work, boolean transactedthrows HibernateException {
      TransactionManager transactionManager = session.getFactory().getTransactionManager();
      Transaction surroundingTransaction = null;
      Connection connection = null;
      boolean caughtException = false;

      try {
        // First we need to suspend any current JTA transaction and obtain
        // a JDBC connection
        surroundingTransaction = transactionManager.suspend();
        if log.isDebugEnabled() ) {
          log.debug"surrounding JTA transaction suspended [" + surroundingTransaction + "]" );
        }

        if transacted ) {
          transactionManager.begin();
        }

        connection = session.getBatcher().openConnection();

        // perform the actual work
        work.doWorkconnection );

        // if everything went ok, commit the transaction and close the obtained
        // connection handle...
        session.getBatcher().closeConnectionconnection );

        if transacted ) {
          transactionManager.commit();
        }
      }
      catchThrowable t ) {
        // at some point the processing went bad, so we need to:
        //      1) make sure the connection handle gets released
        //      2) try to cleanup the JTA context as much as possible
        caughtException = true;
        try {
          if connection != null && !connection.isClosed() ) {
            session.getBatcher().closeConnectionconnection );
          }
        }
        catchThrowable ignore ) {
          log.trace"unable to release connection on exception [" + ignore + "]" );
        }
        if transacted ) {
          try {
            transactionManager.rollback();
          }
          catchThrowable ignore ) {
            log.trace"unable to rollback new transaction on exception [" + ignore + "]" );
          }
        }
        // finally handle the exception
        if instanceof HibernateException ) {
          throw HibernateException t;
        }
        else {
          throw new HibernateException"error performing isolated work", t );
        }
      }
      finally {
        if surroundingTransaction != null ) {
          try {
            transactionManager.resumesurroundingTransaction );
            if log.isDebugEnabled() ) {
              log.debug"surrounding JTA transaction resumed [" + surroundingTransaction + "]" );
            }
          }
          catchThrowable t ) {
            if !caughtException ) {
              throw new HibernateException"unable to resume previously suspended transaction", t );
            }
          }
        }
      }
    }
  }

  /**
   * An isolation delegate for JDBC-based transactions.  Basically just
   * grabs a new connection and does the work on that.
   */
  public static class JdbcDelegate implements Delegate {
    private final SessionImplementor session;

    public JdbcDelegate(SessionImplementor session) {
      this.session = session;
    }

    public void delegateWork(IsolatedWork work, boolean transactedthrows HibernateException {
      Connection connection = null;
      boolean wasAutoCommit = false;
      try {
        connection = session.getBatcher().openConnection();

        if transacted ) {
          if connection.getAutoCommit() ) {
            wasAutoCommit = true;
            connection.setAutoCommitfalse );
          }
        }

        work.doWorkconnection );

        if transacted ) {
          connection.commit();
        }
      }
      catchThrowable t ) {
        try {
          if transacted && connection != null && !connection.isClosed() ) {
            connection.rollback();
          }
        }
        catchThrowable ignore ) {
          log.trace"unable to release connection on exception [" + ignore + "]" );
        }

        if instanceof HibernateException ) {
          throw HibernateException t;
        }
        else if instanceof SQLException ) {
          throw JDBCExceptionHelper.convert(
              session.getFactory().getSQLExceptionConverter(),
                  SQLException t,
                  "error performing isolated work"
          );
        }
        else {
          throw new HibernateException"error performing isolated work", t );
        }
      }
      finally {
        if connection != null ) {
          if transacted && wasAutoCommit ) {
            try {
              connection.setAutoCommittrue );
            }
            catchThrowable ignore ) {
              log.trace"was unable to reset connection back to auto-commit" );
            }
          }
          session.getBatcher().closeConnectionconnection );
        }
      }
    }
  }
}