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 java.util.List;
020:
021: import javax.naming.NamingException;
022:
023: import com.ibm.websphere.uow.UOWSynchronizationRegistry;
024: import com.ibm.wsspi.uow.UOWAction;
025: import com.ibm.wsspi.uow.UOWActionException;
026: import com.ibm.wsspi.uow.UOWException;
027: import com.ibm.wsspi.uow.UOWManager;
028:
029: import org.springframework.transaction.IllegalTransactionStateException;
030: import org.springframework.transaction.InvalidTimeoutException;
031: import org.springframework.transaction.NestedTransactionNotSupportedException;
032: import org.springframework.transaction.TransactionDefinition;
033: import org.springframework.transaction.TransactionException;
034: import org.springframework.transaction.TransactionSystemException;
035: import org.springframework.transaction.support.CallbackPreferringPlatformTransactionManager;
036: import org.springframework.transaction.support.DefaultTransactionDefinition;
037: import org.springframework.transaction.support.DefaultTransactionStatus;
038: import org.springframework.transaction.support.TransactionCallback;
039: import org.springframework.transaction.support.TransactionSynchronizationManager;
040:
041: /**
042: * WebSphere-specific PlatformTransactionManager implementation that delegates
043: * to a {@link com.ibm.wsspi.uow.UOWManager} instance, obtained from WebSphere's
044: * JNDI environment. This allows Spring to leverage the full power of the WebSphere
045: * transaction coordinator, including transaction suspension, in a manner that is
046: * perfectly compliant with officially supported WebSphere API.
047: *
048: * <p>The {@link CallbackPreferringPlatformTransactionManager} interface
049: * implemented by this class indicates that callers should preferably pass in
050: * a {@link TransactionCallback} through the {@link #execute} method, which
051: * will be handled through the callback-based WebSphere UOWManager API instead
052: * of through standard JTA API (UserTransaction / TransactionManager). This avoids
053: * the use of the non-public <code>javax.transaction.TransactionManager</code>
054: * API on WebSphere, staying within supported WebSphere API boundaries.
055: *
056: * <p>This transaction manager implementation derives from Spring's standard
057: * {@link JtaTransactionManager}, inheriting the capability to support programmatic
058: * transaction demarcation via <code>getTransaction</code> / <code>commit</code> /
059: * <code>rollback</code> calls through a JTA UserTransaction handle, for callers
060: * that do not use the TransactionCallback-based {@link #execute} method. However,
061: * transaction suspension is <i>not</i> supported in this <code>getTransaction</code>
062: * style (unless you explicitly specify a {@link #setTransactionManager} reference,
063: * despite the official WebSphere recommendations). Use the {@link #execute} style
064: * for any code that might require transaction suspension.
065: *
066: * <p>This transaction manager is compatible with WebSphere 7.0 as well as recent
067: * WebSphere 6.0.x and 6.1.x versions. Check the documentation for your specific
068: * WebSphere version to find out whether UOWManager support is available. If it
069: * is not available, consider using Spring's standard {@link JtaTransactionManager}
070: * class, if necessary specifying the {@link WebSphereTransactionManagerFactoryBean}
071: * as "transactionManager" through the corresponding bean property. However, note
072: * that transaction suspension is not officially supported in such a scenario
073: * (despite it being known to work properly).
074: *
075: * <p>The default JNDI location for the UOWManager is "java:comp/websphere/UOWManager".
076: * If the location happens to differ according to your WebSphere documentation,
077: * simply specify the actual location through this transaction manager's
078: * "uowManagerName" bean property.
079: *
080: * @author Juergen Hoeller
081: * @since 2.5
082: * @see #setUowManager
083: * @see #setUowManagerName
084: * @see com.ibm.wsspi.uow.UOWManager
085: */
086: public class WebSphereUowTransactionManager extends
087: JtaTransactionManager implements
088: CallbackPreferringPlatformTransactionManager {
089:
090: /**
091: * Default JNDI location for the WebSphere UOWManager.
092: * @see #setUowManagerName
093: */
094: public static final String DEFAULT_UOW_MANAGER_NAME = "java:comp/websphere/UOWManager";
095:
096: private UOWManager uowManager;
097:
098: private String uowManagerName = DEFAULT_UOW_MANAGER_NAME;
099:
100: /**
101: * Create a new WebSphereUowTransactionManager.
102: */
103: public WebSphereUowTransactionManager() {
104: setAutodetectTransactionManager(false);
105: }
106:
107: /**
108: * Create a new WebSphereUowTransactionManager for the given UOWManager.
109: * @param uowManager the WebSphere UOWManager to use as direct reference
110: */
111: public WebSphereUowTransactionManager(UOWManager uowManager) {
112: this ();
113: this .uowManager = uowManager;
114: }
115:
116: /**
117: * Set the WebSphere UOWManager to use as direct reference.
118: * <p>Typically just used for test setups; in a J2EE environment,
119: * the UOWManager will always be fetched from JNDI.
120: * @see #setUserTransactionName
121: */
122: public void setUowManager(UOWManager uowManager) {
123: this .uowManager = uowManager;
124: }
125:
126: /**
127: * Set the JNDI name of the WebSphere UOWManager.
128: * The default "java:comp/websphere/UOWManager" is used if not set.
129: * @see #DEFAULT_USER_TRANSACTION_NAME
130: * @see #setUowManager
131: */
132: public void setUowManagerName(String uowManagerName) {
133: this .uowManagerName = uowManagerName;
134: }
135:
136: public void afterPropertiesSet() throws TransactionSystemException {
137: initUserTransactionAndTransactionManager();
138:
139: // Fetch UOWManager handle from JNDI, if necessary.
140: if (this .uowManager == null) {
141: if (this .uowManagerName != null) {
142: this .uowManager = lookupUowManager(this .uowManagerName);
143: } else {
144: throw new IllegalStateException(
145: "'uowManager' or 'uowManagerName' is required");
146: }
147: }
148: }
149:
150: /**
151: * Look up the WebSphere UOWManager in JNDI via the configured name.
152: * Called by <code>afterPropertiesSet</code> if no direct UOWManager reference was set.
153: * Can be overridden in subclasses to provide a different UOWManager object.
154: * @param uowManagerName the JNDI name of the UOWManager
155: * @return the UOWManager object
156: * @throws TransactionSystemException if the JNDI lookup failed
157: * @see #setJndiTemplate
158: * @see #setUowManagerName
159: */
160: protected UOWManager lookupUowManager(String uowManagerName)
161: throws TransactionSystemException {
162: try {
163: if (logger.isDebugEnabled()) {
164: logger
165: .debug("Retrieving WebSphere UOWManager from JNDI location ["
166: + uowManagerName + "]");
167: }
168: return (UOWManager) getJndiTemplate().lookup(
169: uowManagerName, UOWManager.class);
170: } catch (NamingException ex) {
171: throw new TransactionSystemException(
172: "WebSphere UOWManager is not available at JNDI location ["
173: + uowManagerName + "]", ex);
174: }
175: }
176:
177: /**
178: * Registers the synchronizations as interposed JTA Synchronization on the UOWManager.
179: */
180: protected void doRegisterAfterCompletionWithJtaTransaction(
181: JtaTransactionObject txObject, List synchronizations) {
182: this .uowManager
183: .registerInterposedSynchronization(new JtaAfterCompletionSynchronization(
184: synchronizations));
185: }
186:
187: public Object execute(TransactionDefinition definition,
188: TransactionCallback callback) throws TransactionException {
189: if (definition == null) {
190: // Use defaults if no transaction definition given.
191: definition = new DefaultTransactionDefinition();
192: }
193:
194: if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
195: throw new InvalidTimeoutException(
196: "Invalid transaction timeout", definition
197: .getTimeout());
198: }
199: int pb = definition.getPropagationBehavior();
200: boolean existingTx = (this .uowManager.getUOWStatus() != UOWSynchronizationRegistry.UOW_STATUS_NONE);
201:
202: int uowType = UOWSynchronizationRegistry.UOW_TYPE_GLOBAL_TRANSACTION;
203: boolean joinTx = false;
204: boolean newSynch = false;
205:
206: if (existingTx) {
207: if (pb == TransactionDefinition.PROPAGATION_NEVER) {
208: throw new IllegalTransactionStateException(
209: "Transaction propagation 'never' but existing transaction found");
210: }
211: if (pb == TransactionDefinition.PROPAGATION_NESTED) {
212: throw new NestedTransactionNotSupportedException(
213: "Transaction propagation 'nested' not supported for WebSphere UOW transactions");
214: }
215: if (pb == TransactionDefinition.PROPAGATION_SUPPORTS
216: || pb == TransactionDefinition.PROPAGATION_REQUIRED
217: || pb == TransactionDefinition.PROPAGATION_MANDATORY) {
218: joinTx = true;
219: newSynch = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
220: } else if (pb == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
221: uowType = UOWSynchronizationRegistry.UOW_TYPE_LOCAL_TRANSACTION;
222: newSynch = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
223: } else {
224: newSynch = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
225: }
226: } else {
227: if (pb == TransactionDefinition.PROPAGATION_MANDATORY) {
228: throw new IllegalTransactionStateException(
229: "Transaction propagation 'mandatory' but no existing transaction found");
230: }
231: if (pb == TransactionDefinition.PROPAGATION_SUPPORTS
232: || pb == TransactionDefinition.PROPAGATION_NOT_SUPPORTED
233: || pb == TransactionDefinition.PROPAGATION_NEVER) {
234: uowType = UOWSynchronizationRegistry.UOW_TYPE_LOCAL_TRANSACTION;
235: newSynch = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
236: } else {
237: newSynch = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
238: }
239: }
240:
241: boolean debug = logger.isDebugEnabled();
242: if (debug) {
243: logger.debug("Creating new transaction with name ["
244: + definition.getName() + "]: " + definition);
245: }
246: SuspendedResourcesHolder suspendedResources = (existingTx
247: && !joinTx ? suspend(null) : null);
248: try {
249: if (definition.getTimeout() > TransactionDefinition.TIMEOUT_DEFAULT) {
250: this .uowManager.setUOWTimeout(uowType, definition
251: .getTimeout());
252: }
253: if (debug) {
254: logger.debug("Invoking WebSphere UOW action: type="
255: + uowType + ", join=" + joinTx);
256: }
257: UOWActionAdapter action = new UOWActionAdapter(
258: definition,
259: callback,
260: (uowType == UOWManager.UOW_TYPE_GLOBAL_TRANSACTION),
261: !joinTx, newSynch, debug);
262: this .uowManager.runUnderUOW(uowType, joinTx, action);
263: if (debug) {
264: logger
265: .debug("Returned from WebSphere UOW action: type="
266: + uowType + ", join=" + joinTx);
267: }
268: return action.getResult();
269: } catch (UOWException ex) {
270: throw new TransactionSystemException(
271: "UOWManager transaction processing failed", ex);
272: } catch (UOWActionException ex) {
273: throw new TransactionSystemException(
274: "UOWManager threw unexpected UOWActionException",
275: ex);
276: } finally {
277: if (suspendedResources != null) {
278: resume(null, suspendedResources);
279: }
280: }
281: }
282:
283: /**
284: * Adapter that executes the given Spring transaction within the WebSphere UOWAction shape.
285: */
286: private class UOWActionAdapter implements UOWAction {
287:
288: private final TransactionDefinition definition;
289:
290: private final TransactionCallback callback;
291:
292: private final boolean actualTransaction;
293:
294: private final boolean newTransaction;
295:
296: private final boolean newSynchronization;
297:
298: private boolean debug;
299:
300: private Object result;
301:
302: public UOWActionAdapter(TransactionDefinition definition,
303: TransactionCallback callback,
304: boolean actualTransaction, boolean newTransaction,
305: boolean newSynchronization, boolean debug) {
306: this .definition = definition;
307: this .callback = callback;
308: this .actualTransaction = actualTransaction;
309: this .newTransaction = newTransaction;
310: this .newSynchronization = newSynchronization;
311: this .debug = debug;
312: }
313:
314: public void run() {
315: DefaultTransactionStatus status = newTransactionStatus(
316: this .definition, (this .actualTransaction ? this
317: : null), this .newTransaction,
318: this .newSynchronization, this .debug, null);
319: try {
320: this .result = this .callback.doInTransaction(status);
321: triggerBeforeCommit(status);
322: } finally {
323: if (status.isLocalRollbackOnly()) {
324: if (status.isDebug()) {
325: logger
326: .debug("Transactional code has requested rollback");
327: }
328: uowManager.setRollbackOnly();
329: }
330: triggerBeforeCompletion(status);
331: if (status.isNewSynchronization()) {
332: List synchronizations = TransactionSynchronizationManager
333: .getSynchronizations();
334: TransactionSynchronizationManager.clear();
335: uowManager
336: .registerInterposedSynchronization(new JtaAfterCompletionSynchronization(
337: synchronizations));
338: }
339: }
340: }
341:
342: public Object getResult() {
343: return this.result;
344: }
345: }
346:
347: }
|