org.springframework.transaction.jta.OC4JJtaTransactionManager Maven / Gradle / Ivy
/*
* Copyright 2002-2007 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, beyond standard JTA: transaction names
* and per-transaction isolation levels.
*
* Uses OC4J's special begin(name)
method to start a JTA transaction,
* in orderto make Spring-driven transactions visible in OC4J's transaction
* monitor. In case of Spring's declarative transactions, the exposed name will
* (by default) be the fully-qualified class name + "." + method name.
*
*
Supports a per-transaction isolation level through OC4J's corresponding
* OC4JTransaction.setTransactionIsolation(int)
method. This will
* apply the specified isolation level (e.g. ISOLATION_SERIALIZABLE) to all
* JDBC Connections that participate in the given transaction.
*
*
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.
*
*
By default, the JTA UserTransaction and TransactionManager handles are
* fetched directly from OC4J's TransactionUtility
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.
*
*
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;
public void afterPropertiesSet() throws TransactionSystemException {
super.afterPropertiesSet();
loadOC4JTransactionClasses();
}
protected UserTransaction retrieveUserTransaction() throws TransactionSystemException {
try {
Class transactionUtilityClass = getClass().getClassLoader().loadClass(TRANSACTION_UTILITY_CLASS_NAME);
Method getInstanceMethod = transactionUtilityClass.getMethod("getInstance", new Class[0]);
Object transactionUtility = getInstanceMethod.invoke(null, new Object[0]);
logger.debug("Retrieving JTA UserTransaction from OC4J TransactionUtility");
Method getUserTransactionMethod =
transactionUtility.getClass().getMethod("getOC4JUserTransaction", new Class[0]);
return (UserTransaction) getUserTransactionMethod.invoke(transactionUtility, new Object[0]);
}
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", new Class[] {String.class});
this.setTransactionIsolationMethod = ClassUtils.getMethodIfAvailable(
transactionClass, "setTransactionIsolation", new Class[] {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");
}
}
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(), new Object[] {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());
*/
Integer isolationLevel = new Integer(definition.getIsolationLevel());
this.setTransactionIsolationMethod.invoke(tx, new Object[] {isolationLevel});
}
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());
}
}
}