/*
* 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 javax.transaction.Status;
import javax.transaction.Synchronization;
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
/**
* Adapter that implements the JTA {@link javax.transaction.Synchronization}
* interface delegating to an underlying Spring
* {@link org.springframework.transaction.support.TransactionSynchronization}.
*
* <p>Useful for synchronizing Spring resource management code with plain
* JTA / EJB CMT transactions, despite the original code being built for
* Spring transaction synchronization.
*
* @author Juergen Hoeller
* @since 2.0
* @see javax.transaction.Transaction#registerSynchronization
* @see org.springframework.transaction.support.TransactionSynchronization
*/
public class SpringJtaSynchronizationAdapter implements Synchronization {
protected static final Log logger = LogFactory.getLog(SpringJtaSynchronizationAdapter.class);
private final TransactionSynchronization springSynchronization;
private UserTransaction jtaTransaction;
private boolean beforeCompletionCalled = false;
/**
* Create a new SpringJtaSynchronizationAdapter for the given Spring
* TransactionSynchronization and JTA TransactionManager.
* @param springSynchronization the Spring TransactionSynchronization to delegate to
*/
public SpringJtaSynchronizationAdapter(TransactionSynchronization springSynchronization) {
Assert.notNull(springSynchronization, "TransactionSynchronization must not be null");
this.springSynchronization = springSynchronization;
}
/**
* Create a new SpringJtaSynchronizationAdapter for the given Spring
* TransactionSynchronization and JTA TransactionManager.
* <p>Note that this adapter will never perform a rollback-only call on WebLogic,
* since WebLogic Server is known to automatically mark the transaction as
* rollback-only in case of a <code>beforeCompletion</code> exception. Hence,
* on WLS, this constructor is equivalent to the single-arg constructor.
* @param springSynchronization the Spring TransactionSynchronization to delegate to
* @param jtaUserTransaction the JTA UserTransaction to use for rollback-only
* setting in case of an exception thrown in <code>beforeCompletion</code>
* (can be omitted if the JTA provider itself marks the transaction rollback-only
* in such a scenario, which is required by the JTA specification as of JTA 1.1).
*/
public SpringJtaSynchronizationAdapter(
TransactionSynchronization springSynchronization, UserTransaction jtaUserTransaction) {
this(springSynchronization);
if (jtaUserTransaction != null && !jtaUserTransaction.getClass().getName().startsWith("weblogic.")) {
this.jtaTransaction = jtaUserTransaction;
}
}
/**
* Create a new SpringJtaSynchronizationAdapter for the given Spring
* TransactionSynchronization and JTA TransactionManager.
* <p>Note that this adapter will never perform a rollback-only call on WebLogic,
* since WebLogic Server is known to automatically mark the transaction as
* rollback-only in case of a <code>beforeCompletion</code> exception. Hence,
* on WLS, this constructor is equivalent to the single-arg constructor.
* @param springSynchronization the Spring TransactionSynchronization to delegate to
* @param jtaTransactionManager the JTA TransactionManager to use for rollback-only
* setting in case of an exception thrown in <code>beforeCompletion</code>
* (can be omitted if the JTA provider itself marks the transaction rollback-only
* in such a scenario, which is required by the JTA specification as of JTA 1.1)
*/
public SpringJtaSynchronizationAdapter(
TransactionSynchronization springSynchronization, TransactionManager jtaTransactionManager) {
this(springSynchronization);
if (jtaTransactionManager != null && !jtaTransactionManager.getClass().getName().startsWith("weblogic.")) {
this.jtaTransaction = new UserTransactionAdapter(jtaTransactionManager);
}
}
/**
* JTA <code>beforeCompletion</code> callback: just invoked before commit.
* <p>In case of an exception, the JTA transaction will be marked as rollback-only.
* @see org.springframework.transaction.support.TransactionSynchronization#beforeCommit
*/
public void beforeCompletion() {
try {
boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
this.springSynchronization.beforeCommit(readOnly);
}
catch (RuntimeException ex) {
setRollbackOnlyIfPossible();
throw ex;
}
catch (Error err) {
setRollbackOnlyIfPossible();
throw err;
}
finally {
// Process Spring's beforeCompletion early, in order to avoid issues
// with strict JTA implementations that issue warnings when doing JDBC
// operations after transaction completion (e.g. Connection.getWarnings).
this.beforeCompletionCalled = true;
this.springSynchronization.beforeCompletion();
}
}
/**
* Set the underlying JTA transaction to rollback-only.
*/
private void setRollbackOnlyIfPossible() {
if (this.jtaTransaction != null) {
try {
this.jtaTransaction.setRollbackOnly();
}
catch (UnsupportedOperationException ex) {
// Probably Hibernate's WebSphereExtendedJTATransactionLookup pseudo JTA stuff...
logger.debug("JTA transaction handle does not support setRollbackOnly method - " +
"relying on JTA provider to mark the transaction as rollback-only based on " +
"the exception thrown from beforeCompletion", ex);
}
catch (Throwable ex) {
logger.error("Could not set JTA transaction rollback-only", ex);
}
}
else {
logger.debug("No JTA transaction handle available and/or running on WebLogic - " +
"relying on JTA provider to mark the transaction as rollback-only based on " +
"the exception thrown from beforeCompletion");
}
}
/**
* JTA <code>afterCompletion</code> callback: invoked after commit/rollback.
* <p>Needs to invoke the Spring synchronization's <code>beforeCompletion</code>
* at this late stage in case of a rollback, since there is no corresponding
* callback with JTA.
* @see org.springframework.transaction.support.TransactionSynchronization#beforeCompletion
* @see org.springframework.transaction.support.TransactionSynchronization#afterCompletion
*/
public void afterCompletion(int status) {
if (!this.beforeCompletionCalled) {
// beforeCompletion not called before (probably because of JTA rollback).
// Perform the cleanup here.
this.springSynchronization.beforeCompletion();
}
// Call afterCompletion with the appropriate status indication.
switch (status) {
case Status.STATUS_COMMITTED:
this.springSynchronization.afterCompletion(TransactionSynchronization.STATUS_COMMITTED);
break;
case Status.STATUS_ROLLEDBACK:
this.springSynchronization.afterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK);
break;
default:
this.springSynchronization.afterCompletion(TransactionSynchronization.STATUS_UNKNOWN);
}
}
}
|