/*
 * Copyright 2002-2018 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.lang.Nullable;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;

Adapter that implements the JTA Synchronization interface delegating to an underlying Spring TransactionSynchronization.

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
See Also:
Since:2.0
/** * 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; @Nullable private UserTransaction jtaTransaction; private boolean beforeCompletionCalled = false;
Create a new SpringJtaSynchronizationAdapter for the given Spring TransactionSynchronization and JTA TransactionManager.
Params:
  • springSynchronization – the Spring TransactionSynchronization to delegate to
/** * 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.

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 beforeCompletion exception. Hence, on WLS, this constructor is equivalent to the single-arg constructor.

Params:
  • springSynchronization – the Spring TransactionSynchronization to delegate to
  • jtaUserTransaction – the JTA UserTransaction to use for rollback-only setting in case of an exception thrown in beforeCompletion (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).
/** * 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} 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} * (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, @Nullable 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.

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 beforeCompletion exception. Hence, on WLS, this constructor is equivalent to the single-arg constructor.

Params:
  • springSynchronization – the Spring TransactionSynchronization to delegate to
  • jtaTransactionManager – the JTA TransactionManager to use for rollback-only setting in case of an exception thrown in beforeCompletion (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)
/** * 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} 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} * (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, @Nullable TransactionManager jtaTransactionManager) { this(springSynchronization); if (jtaTransactionManager != null && !jtaTransactionManager.getClass().getName().startsWith("weblogic.")) { this.jtaTransaction = new UserTransactionAdapter(jtaTransactionManager); } }
JTA beforeCompletion callback: just invoked before commit.

In case of an exception, the JTA transaction will be marked as rollback-only.

See Also:
/** * JTA {@code beforeCompletion} 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 */
@Override public void beforeCompletion() { try { boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly(); this.springSynchronization.beforeCommit(readOnly); } catch (RuntimeException | Error ex) { setRollbackOnlyIfPossible(); throw ex; } 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.
/** * 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 afterCompletion callback: invoked after commit/rollback.

Needs to invoke the Spring synchronization's beforeCompletion at this late stage in case of a rollback, since there is no corresponding callback with JTA.

See Also:
/** * JTA {@code afterCompletion} callback: invoked after commit/rollback. * <p>Needs to invoke the Spring synchronization's {@code beforeCompletion} * 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 */
@Override 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); } } }