001: /*
002: * Copyright 2002-2007 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: * http://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:
017: package org.springframework.transaction.jta;
018:
019: import javax.transaction.Status;
020: import javax.transaction.Synchronization;
021: import javax.transaction.TransactionManager;
022: import javax.transaction.UserTransaction;
023:
024: import org.apache.commons.logging.Log;
025: import org.apache.commons.logging.LogFactory;
026:
027: import org.springframework.transaction.support.TransactionSynchronization;
028: import org.springframework.transaction.support.TransactionSynchronizationManager;
029: import 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: */
045: public class SpringJtaSynchronizationAdapter implements Synchronization {
046:
047: protected static final Log logger = LogFactory
048: .getLog(SpringJtaSynchronizationAdapter.class);
049:
050: private final TransactionSynchronization springSynchronization;
051:
052: private UserTransaction jtaTransaction;
053:
054: private boolean beforeCompletionCalled = false;
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(
062: TransactionSynchronization springSynchronization) {
063: Assert.notNull(springSynchronization,
064: "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</code> 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</code>
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(
082: TransactionSynchronization springSynchronization,
083: UserTransaction jtaUserTransaction) {
084:
085: this (springSynchronization);
086: if (jtaUserTransaction != null
087: && !jtaUserTransaction.getClass().getName().startsWith(
088: "weblogic.")) {
089: this .jtaTransaction = jtaUserTransaction;
090: }
091: }
092:
093: /**
094: * Create a new SpringJtaSynchronizationAdapter for the given Spring
095: * TransactionSynchronization and JTA TransactionManager.
096: * <p>Note that this adapter will never perform a rollback-only call on WebLogic,
097: * since WebLogic Server is known to automatically mark the transaction as
098: * rollback-only in case of a <code>beforeCompletion</code> exception. Hence,
099: * on WLS, this constructor is equivalent to the single-arg constructor.
100: * @param springSynchronization the Spring TransactionSynchronization to delegate to
101: * @param jtaTransactionManager the JTA TransactionManager to use for rollback-only
102: * setting in case of an exception thrown in <code>beforeCompletion</code>
103: * (can be omitted if the JTA provider itself marks the transaction rollback-only
104: * in such a scenario, which is required by the JTA specification as of JTA 1.1)
105: */
106: public SpringJtaSynchronizationAdapter(
107: TransactionSynchronization springSynchronization,
108: TransactionManager jtaTransactionManager) {
109:
110: this (springSynchronization);
111: if (jtaTransactionManager != null
112: && !jtaTransactionManager.getClass().getName()
113: .startsWith("weblogic.")) {
114: this .jtaTransaction = new UserTransactionAdapter(
115: jtaTransactionManager);
116: }
117: }
118:
119: /**
120: * JTA <code>beforeCompletion</code> callback: just invoked before commit.
121: * <p>In case of an exception, the JTA transaction will be marked as rollback-only.
122: * @see org.springframework.transaction.support.TransactionSynchronization#beforeCommit
123: */
124: public void beforeCompletion() {
125: try {
126: boolean readOnly = TransactionSynchronizationManager
127: .isCurrentTransactionReadOnly();
128: this .springSynchronization.beforeCommit(readOnly);
129: } catch (RuntimeException ex) {
130: setRollbackOnlyIfPossible();
131: throw ex;
132: } catch (Error err) {
133: setRollbackOnlyIfPossible();
134: throw err;
135: } finally {
136: // Process Spring's beforeCompletion early, in order to avoid issues
137: // with strict JTA implementations that issue warnings when doing JDBC
138: // operations after transaction completion (e.g. Connection.getWarnings).
139: this .beforeCompletionCalled = true;
140: this .springSynchronization.beforeCompletion();
141: }
142: }
143:
144: /**
145: * Set the underlying JTA transaction to rollback-only.
146: */
147: private void setRollbackOnlyIfPossible() {
148: if (this .jtaTransaction != null) {
149: try {
150: this .jtaTransaction.setRollbackOnly();
151: } catch (UnsupportedOperationException ex) {
152: // Probably Hibernate's WebSphereExtendedJTATransactionLookup pseudo JTA stuff...
153: logger
154: .debug(
155: "JTA transaction handle does not support setRollbackOnly method - "
156: + "relying on JTA provider to mark the transaction as rollback-only based on "
157: + "the exception thrown from beforeCompletion",
158: ex);
159: } catch (Throwable ex) {
160: logger.error(
161: "Could not set JTA transaction rollback-only",
162: ex);
163: }
164: } else {
165: logger
166: .debug("No JTA transaction handle available and/or running on WebLogic - "
167: + "relying on JTA provider to mark the transaction as rollback-only based on "
168: + "the exception thrown from beforeCompletion");
169: }
170: }
171:
172: /**
173: * JTA <code>afterCompletion</code> callback: invoked after commit/rollback.
174: * <p>Needs to invoke the Spring synchronization's <code>beforeCompletion</code>
175: * at this late stage in case of a rollback, since there is no corresponding
176: * callback with JTA.
177: * @see org.springframework.transaction.support.TransactionSynchronization#beforeCompletion
178: * @see org.springframework.transaction.support.TransactionSynchronization#afterCompletion
179: */
180: public void afterCompletion(int status) {
181: if (!this .beforeCompletionCalled) {
182: // beforeCompletion not called before (probably because of JTA rollback).
183: // Perform the cleanup here.
184: this .springSynchronization.beforeCompletion();
185: }
186: // Call afterCompletion with the appropriate status indication.
187: switch (status) {
188: case Status.STATUS_COMMITTED:
189: this.springSynchronization
190: .afterCompletion(TransactionSynchronization.STATUS_COMMITTED);
191: break;
192: case Status.STATUS_ROLLEDBACK:
193: this.springSynchronization
194: .afterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK);
195: break;
196: default:
197: this.springSynchronization
198: .afterCompletion(TransactionSynchronization.STATUS_UNKNOWN);
199: }
200: }
201:
202: }
|