Open Source Repository

Home /spring/spring-transaction-3.0.5 | Repository Home



org/springframework/transaction/jta/OC4JJtaTransactionManager.java
/*
 * Copyright 2002-2010 the original author or authors.
 *
 * 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.springframework.transaction.jta;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.transaction.NotSupportedException;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.UserTransaction;

import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionSystemException;
import org.springframework.util.ClassUtils;

/**
 * Special {@link JtaTransactionManager} variant for Oracle OC4J (10.1.3 and higher).
 * Supports the full power of Spring's transaction definitions on OC4J's
 * transaction coordinator, <i>beyond standard JTA</i>: transaction names
 * and per-transaction isolation levels.
 *
 <p>Uses OC4J's special <code>begin(name)</code> method to start a JTA transaction,
 * in orderto make <b>Spring-driven transactions visible in OC4J's transaction
 * monitor</b>. In case of Spring's declarative transactions, the exposed name will
 * (by default) be the fully-qualified class name + "." + method name.
 *
 <p>Supports a <b>per-transaction isolation level</b> through OC4J's corresponding
 <code>OC4JTransaction.setTransactionIsolation(int)</code> method. This will
 * apply the specified isolation level (e.g. ISOLATION_SERIALIZABLE) to all
 * JDBC Connections that participate in the given transaction.
 *
 <p>Automatically detects the available OC4J server version and adapts accordingly.
 * Supports the "com.evermind.server" package in OC4J 10.1.3.2 as well as the
 * "oracle.j2ee.transaction" package in later OC4J versions.
 *
 <p>By default, the JTA UserTransaction and TransactionManager handles are
 * fetched directly from OC4J's <code>TransactionUtility</code> in 10.1.3.2+.
 * This can be overridden by specifying "userTransaction"/"userTransactionName"
 * and "transactionManager"/"transactionManagerName", passing in existing handles
 * or specifying corresponding JNDI locations to look up.
 *
 <p>Thanks to Oracle for donating the original version of this extended OC4J
 * integration code to the Spring project!
 *
 @author Paul Parkinson
 @author Juergen Hoeller
 @since 2.0.3
 @see org.springframework.transaction.TransactionDefinition#getName
 @see org.springframework.transaction.TransactionDefinition#getIsolationLevel
 @see oracle.j2ee.transaction.OC4JTransactionManager#begin(String)
 @see oracle.j2ee.transaction.OC4JTransaction#setTransactionIsolation
 @see oracle.j2ee.transaction.TransactionUtility
 */
public class OC4JJtaTransactionManager extends JtaTransactionManager {

  private static final String TRANSACTION_UTILITY_CLASS_NAME =
      "oracle.j2ee.transaction.TransactionUtility";

  private static final String TRANSACTION_MANAGER_CLASS_NAME =
      "oracle.j2ee.transaction.OC4JTransactionManager";

  private static final String TRANSACTION_CLASS_NAME =
      "oracle.j2ee.transaction.OC4JTransaction";

  private static final String FALLBACK_TRANSACTION_MANAGER_CLASS_NAME =
      "com.evermind.server.ApplicationServerTransactionManager";

  private static final String FALLBACK_TRANSACTION_CLASS_NAME =
      "com.evermind.server.ApplicationServerTransaction";


  private Method beginWithNameMethod;

  private Method setTransactionIsolationMethod;


  @Override
  public void afterPropertiesSet() throws TransactionSystemException {
    super.afterPropertiesSet();
    loadOC4JTransactionClasses();
  }

  @Override
  protected UserTransaction retrieveUserTransaction() throws TransactionSystemException {
    try {
      Class transactionUtilityClass = getClass().getClassLoader().loadClass(TRANSACTION_UTILITY_CLASS_NAME);
      Method getInstanceMethod = transactionUtilityClass.getMethod("getInstance");
      Object transactionUtility = getInstanceMethod.invoke(null);
      logger.debug("Retrieving JTA UserTransaction from OC4J TransactionUtility");
      Method getUserTransactionMethod =
          transactionUtility.getClass().getMethod("getOC4JUserTransaction");
      return (UserTransactiongetUserTransactionMethod.invoke(transactionUtility);
    }
    catch (ClassNotFoundException ex) {
      logger.debug("Could not find OC4J 10.1.3.2 TransactionUtility: " + ex);
      // Return null to make the superclass perform its standard J2EE lookup,
      // which will work on earlier OC4J versions.
      return null;
    }
    catch (InvocationTargetException ex) {
      throw new TransactionSystemException(
          "OC4J's TransactionUtility.getOC4JUserTransaction() method failed", ex.getTargetException());
    }
    catch (Exception ex) {
      throw new TransactionSystemException(
          "Could not invoke OC4J's TransactionUtility.getOC4JUserTransaction() method", ex);
    }
  }

  private void loadOC4JTransactionClasses() throws TransactionSystemException {
    // Find available OC4J API (in "oracle.j2ee.transaction" or "com.evermind.server")
    Class transactionManagerClass = null;
    Class transactionClass = null;
    try {
      transactionManagerClass = getClass().getClassLoader().loadClass(TRANSACTION_MANAGER_CLASS_NAME);
      transactionClass = getClass().getClassLoader().loadClass(TRANSACTION_CLASS_NAME);
    }
    catch (ClassNotFoundException ex) {
      try {
        transactionManagerClass = getClass().getClassLoader().loadClass(FALLBACK_TRANSACTION_MANAGER_CLASS_NAME);
        transactionClass = getClass().getClassLoader().loadClass(FALLBACK_TRANSACTION_CLASS_NAME);
      }
      catch (ClassNotFoundException ex2) {
        throw new TransactionSystemException(
            "Could not initialize OC4JJtaTransactionManager because OC4J API classes are not available", ex);
      }
    }

    // Cache reflective Method references for later use.
    if (transactionManagerClass.isInstance(getUserTransaction())) {
      this.beginWithNameMethod = ClassUtils.getMethodIfAvailable(
          transactionManagerClass, "begin", String.class);
      this.setTransactionIsolationMethod = ClassUtils.getMethodIfAvailable(
          transactionClass, "setTransactionIsolation"int.class);
      logger.info("Support for OC4J transaction names and isolation levels available");
    }
    else {
      logger.info("Support for OC4J transaction names and isolation levels not available");
    }
  }


  @Override
  protected void doJtaBegin(JtaTransactionObject txObject, TransactionDefinition definition)
      throws NotSupportedException, SystemException {

    int timeout = determineTimeout(definition);
    applyTimeout(txObject, timeout);

    // Apply transaction name, if any, through the extended OC4J transaction begin method.
    if (this.beginWithNameMethod != null && definition.getName() != null) {
      /*
      oracle.j2ee.transaction.OC4JTransactionManager otm = (oracle.j2ee.transaction.OC4JTransactionManager) ut;
      otm.begin(definition.getName());
      */
      try {
        this.beginWithNameMethod.invoke(txObject.getUserTransaction(), definition.getName());
      }
      catch (InvocationTargetException ex) {
        throw new TransactionSystemException(
            "OC4J's UserTransaction.begin(String) method failed", ex.getTargetException());
      }
      catch (Exception ex) {
        throw new TransactionSystemException(
            "Could not invoke OC4J's UserTransaction.begin(String) method", ex);
      }
    }
    else {
      // No OC4J UserTransaction available or no transaction name specified
      // -> standard JTA begin call.
      txObject.getUserTransaction().begin();
    }

    // Specify isolation level, if any, through the corresponding OC4J transaction method.
    if (this.setTransactionIsolationMethod != null) {
      if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
        try {
          Transaction tx = getTransactionManager().getTransaction();
          /*
          oracle.j2ee.transaction.OC4JTransaction otx = (oracle.j2ee.transaction.OC4JTransaction) tx;
          otx.setTransactionIsolation(definition.getIsolationLevel());
          */
          this.setTransactionIsolationMethod.invoke(tx, definition.getIsolationLevel());
        }
        catch (InvocationTargetException ex) {
          throw new TransactionSystemException(
              "OC4J's Transaction.setTransactionIsolation(int) method failed", ex.getTargetException());
        }
        catch (Exception ex) {
          throw new TransactionSystemException(
              "Could not invoke OC4J's Transaction.setTransactionIsolation(int) method", ex);
        }
      }
    }
    else {
      applyIsolationLevel(txObject, definition.getIsolationLevel());
    }
  }


  @Override
  public Transaction createTransaction(String name, int timeoutthrows NotSupportedException, SystemException {
    if (this.beginWithNameMethod != null && name != null) {
      UserTransaction ut = getUserTransaction();
      if (timeout >= 0) {
        ut.setTransactionTimeout(timeout);
      }
      try {
        this.beginWithNameMethod.invoke(ut, name);
      }
      catch (InvocationTargetException ex) {
        if (ex.getTargetException() instanceof NotSupportedException) {
          throw (NotSupportedExceptionex.getTargetException();
        }
        else if (ex.getTargetException() instanceof SystemException) {
          throw (SystemExceptionex.getTargetException();
        }
        else if (ex.getTargetException() instanceof RuntimeException) {
          throw (RuntimeExceptionex.getTargetException();
        }
        else {
          throw new SystemException(
              "OC4J's begin(String) method failed with an unexpected error: " + ex.getTargetException());
        }
      }
      catch (Exception ex) {
        throw new SystemException("Could not invoke OC4J's UserTransaction.begin(String) method: " + ex);
      }
      return new ManagedTransactionAdapter(getTransactionManager());
    }

    else {
      // No name specified - standard JTA is sufficient.
      return super.createTransaction(name, timeout);
    }
  }

}