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