001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.transaction.jta; 018 019import javax.transaction.Status; 020import javax.transaction.Synchronization; 021import javax.transaction.TransactionManager; 022import javax.transaction.UserTransaction; 023 024import org.apache.commons.logging.Log; 025import org.apache.commons.logging.LogFactory; 026 027import org.springframework.lang.Nullable; 028import org.springframework.transaction.support.TransactionSynchronization; 029import org.springframework.transaction.support.TransactionSynchronizationManager; 030import org.springframework.util.Assert; 031 032/** 033 * Adapter that implements the JTA {@link javax.transaction.Synchronization} 034 * interface delegating to an underlying Spring 035 * {@link org.springframework.transaction.support.TransactionSynchronization}. 036 * 037 * <p>Useful for synchronizing Spring resource management code with plain 038 * JTA / EJB CMT transactions, despite the original code being built for 039 * Spring transaction synchronization. 040 * 041 * @author Juergen Hoeller 042 * @since 2.0 043 * @see javax.transaction.Transaction#registerSynchronization 044 * @see org.springframework.transaction.support.TransactionSynchronization 045 */ 046public class SpringJtaSynchronizationAdapter implements Synchronization { 047 048 protected static final Log logger = LogFactory.getLog(SpringJtaSynchronizationAdapter.class); 049 050 private final TransactionSynchronization springSynchronization; 051 052 @Nullable 053 private UserTransaction jtaTransaction; 054 055 private boolean beforeCompletionCalled = false; 056 057 058 /** 059 * Create a new SpringJtaSynchronizationAdapter for the given Spring 060 * TransactionSynchronization and JTA TransactionManager. 061 * @param springSynchronization the Spring TransactionSynchronization to delegate to 062 */ 063 public SpringJtaSynchronizationAdapter(TransactionSynchronization springSynchronization) { 064 Assert.notNull(springSynchronization, "TransactionSynchronization must not be null"); 065 this.springSynchronization = springSynchronization; 066 } 067 068 /** 069 * Create a new SpringJtaSynchronizationAdapter for the given Spring 070 * TransactionSynchronization and JTA TransactionManager. 071 * <p>Note that this adapter will never perform a rollback-only call on WebLogic, 072 * since WebLogic Server is known to automatically mark the transaction as 073 * rollback-only in case of a {@code beforeCompletion} exception. Hence, 074 * on WLS, this constructor is equivalent to the single-arg constructor. 075 * @param springSynchronization the Spring TransactionSynchronization to delegate to 076 * @param jtaUserTransaction the JTA UserTransaction to use for rollback-only 077 * setting in case of an exception thrown in {@code beforeCompletion} 078 * (can be omitted if the JTA provider itself marks the transaction rollback-only 079 * in such a scenario, which is required by the JTA specification as of JTA 1.1). 080 */ 081 public SpringJtaSynchronizationAdapter(TransactionSynchronization springSynchronization, 082 @Nullable UserTransaction jtaUserTransaction) { 083 084 this(springSynchronization); 085 if (jtaUserTransaction != null && !jtaUserTransaction.getClass().getName().startsWith("weblogic.")) { 086 this.jtaTransaction = jtaUserTransaction; 087 } 088 } 089 090 /** 091 * Create a new SpringJtaSynchronizationAdapter for the given Spring 092 * TransactionSynchronization and JTA TransactionManager. 093 * <p>Note that this adapter will never perform a rollback-only call on WebLogic, 094 * since WebLogic Server is known to automatically mark the transaction as 095 * rollback-only in case of a {@code beforeCompletion} exception. Hence, 096 * on WLS, this constructor is equivalent to the single-arg constructor. 097 * @param springSynchronization the Spring TransactionSynchronization to delegate to 098 * @param jtaTransactionManager the JTA TransactionManager to use for rollback-only 099 * setting in case of an exception thrown in {@code beforeCompletion} 100 * (can be omitted if the JTA provider itself marks the transaction rollback-only 101 * in such a scenario, which is required by the JTA specification as of JTA 1.1) 102 */ 103 public SpringJtaSynchronizationAdapter( 104 TransactionSynchronization springSynchronization, @Nullable TransactionManager jtaTransactionManager) { 105 106 this(springSynchronization); 107 if (jtaTransactionManager != null && !jtaTransactionManager.getClass().getName().startsWith("weblogic.")) { 108 this.jtaTransaction = new UserTransactionAdapter(jtaTransactionManager); 109 } 110 } 111 112 113 /** 114 * JTA {@code beforeCompletion} callback: just invoked before commit. 115 * <p>In case of an exception, the JTA transaction will be marked as rollback-only. 116 * @see org.springframework.transaction.support.TransactionSynchronization#beforeCommit 117 */ 118 @Override 119 public void beforeCompletion() { 120 try { 121 boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly(); 122 this.springSynchronization.beforeCommit(readOnly); 123 } 124 catch (RuntimeException | Error ex) { 125 setRollbackOnlyIfPossible(); 126 throw ex; 127 } 128 finally { 129 // Process Spring's beforeCompletion early, in order to avoid issues 130 // with strict JTA implementations that issue warnings when doing JDBC 131 // operations after transaction completion (e.g. Connection.getWarnings). 132 this.beforeCompletionCalled = true; 133 this.springSynchronization.beforeCompletion(); 134 } 135 } 136 137 /** 138 * Set the underlying JTA transaction to rollback-only. 139 */ 140 private void setRollbackOnlyIfPossible() { 141 if (this.jtaTransaction != null) { 142 try { 143 this.jtaTransaction.setRollbackOnly(); 144 } 145 catch (UnsupportedOperationException ex) { 146 // Probably Hibernate's WebSphereExtendedJTATransactionLookup pseudo JTA stuff... 147 logger.debug("JTA transaction handle does not support setRollbackOnly method - " + 148 "relying on JTA provider to mark the transaction as rollback-only based on " + 149 "the exception thrown from beforeCompletion", ex); 150 } 151 catch (Throwable ex) { 152 logger.error("Could not set JTA transaction rollback-only", ex); 153 } 154 } 155 else { 156 logger.debug("No JTA transaction handle available and/or running on WebLogic - " + 157 "relying on JTA provider to mark the transaction as rollback-only based on " + 158 "the exception thrown from beforeCompletion"); 159 } 160 } 161 162 /** 163 * JTA {@code afterCompletion} callback: invoked after commit/rollback. 164 * <p>Needs to invoke the Spring synchronization's {@code beforeCompletion} 165 * at this late stage in case of a rollback, since there is no corresponding 166 * callback with JTA. 167 * @see org.springframework.transaction.support.TransactionSynchronization#beforeCompletion 168 * @see org.springframework.transaction.support.TransactionSynchronization#afterCompletion 169 */ 170 @Override 171 public void afterCompletion(int status) { 172 if (!this.beforeCompletionCalled) { 173 // beforeCompletion not called before (probably because of JTA rollback). 174 // Perform the cleanup here. 175 this.springSynchronization.beforeCompletion(); 176 } 177 // Call afterCompletion with the appropriate status indication. 178 switch (status) { 179 case Status.STATUS_COMMITTED: 180 this.springSynchronization.afterCompletion(TransactionSynchronization.STATUS_COMMITTED); 181 break; 182 case Status.STATUS_ROLLEDBACK: 183 this.springSynchronization.afterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK); 184 break; 185 default: 186 this.springSynchronization.afterCompletion(TransactionSynchronization.STATUS_UNKNOWN); 187 } 188 } 189 190}