KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > transaction > jta > JtaTransactionManager


1 /*
2  * Copyright 2002-2007 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.transaction.jta;
18
19 import java.io.IOException JavaDoc;
20 import java.io.ObjectInputStream JavaDoc;
21 import java.io.Serializable JavaDoc;
22 import java.util.List JavaDoc;
23 import java.util.Properties JavaDoc;
24
25 import javax.naming.NamingException JavaDoc;
26 import javax.transaction.HeuristicMixedException JavaDoc;
27 import javax.transaction.HeuristicRollbackException JavaDoc;
28 import javax.transaction.InvalidTransactionException JavaDoc;
29 import javax.transaction.NotSupportedException JavaDoc;
30 import javax.transaction.RollbackException JavaDoc;
31 import javax.transaction.Status JavaDoc;
32 import javax.transaction.SystemException JavaDoc;
33 import javax.transaction.Transaction JavaDoc;
34 import javax.transaction.TransactionManager JavaDoc;
35 import javax.transaction.UserTransaction JavaDoc;
36
37 import org.springframework.beans.factory.InitializingBean;
38 import org.springframework.jndi.JndiTemplate;
39 import org.springframework.transaction.CannotCreateTransactionException;
40 import org.springframework.transaction.HeuristicCompletionException;
41 import org.springframework.transaction.IllegalTransactionStateException;
42 import org.springframework.transaction.InvalidIsolationLevelException;
43 import org.springframework.transaction.NestedTransactionNotSupportedException;
44 import org.springframework.transaction.NoTransactionException;
45 import org.springframework.transaction.TransactionDefinition;
46 import org.springframework.transaction.TransactionSuspensionNotSupportedException;
47 import org.springframework.transaction.TransactionSystemException;
48 import org.springframework.transaction.UnexpectedRollbackException;
49 import org.springframework.transaction.support.AbstractPlatformTransactionManager;
50 import org.springframework.transaction.support.DefaultTransactionStatus;
51 import org.springframework.transaction.support.TransactionSynchronization;
52 import org.springframework.util.Assert;
53
54 /**
55  * {@link org.springframework.transaction.PlatformTransactionManager} implementation
56  * for JTA, delegating to a backend JTA provider. This is typically used to delegate
57  * to a J2EE server's transaction coordinator, but may also be configured with a
58  * local JTA provider which is embedded within the application.
59  *
60  * <p>This transaction manager is appropriate for handling distributed transactions,
61  * i.e. transactions that span multiple resources, and for controlling transactions on
62  * application server resources (e.g. JDBC DataSources available in JNDI) in general.
63  * For a single JDBC DataSource, DataSourceTransactionManager is perfectly sufficient,
64  * and for accessing a single resource with Hibernate (including transactional cache),
65  * HibernateTransactionManager is appropriate, for example.
66  *
67  * <p>Transaction synchronization is active by default, to allow data access support
68  * classes to register resources that are opened within the transaction for closing at
69  * transaction completion time. Spring's support classes for JDBC, Hibernate, JDO etc
70  * all perform such registration, allowing for reuse of the same Hibernate Session etc
71  * within the transaction. Standard JTA does not even guarantee that for Connections
72  * from a transactional JDBC DataSource: Spring's synchronization solves those issues.
73  *
74  * <p><b>For typical JTA transactions (REQUIRED, SUPPORTS, MANDATORY, NEVER), a plain
75  * JtaTransactionManager definition is all you need, completely portable across all
76  * J2EE servers.</b> This corresponds to the functionality of the JTA UserTransaction,
77  * for which J2EE specifies a standard JNDI name ("java:comp/UserTransaction").
78  * There is no need to configure a server-specific TransactionManager lookup for this
79  * kind of JTA usage.
80  *
81  * <p><b>Note: Advanced JTA usage below. Dealing with these mechanisms is not
82  * necessary for typical usage scenarios.</b>
83  *
84  * <p>Transaction suspension (REQUIRES_NEW, NOT_SUPPORTED) is just available with
85  * a JTA TransactionManager being registered, via the "transactionManagerName" or
86  * "transactionManager" property. The location of this well-defined JTA object is
87  * <i>not</i> specified by J2EE; it is specific to each J2EE server, often kept
88  * in JNDI like the JTA UserTransaction. Some well-known JNDI locations are:
89  * <ul>
90  * <li>"java:comp/UserTransaction" for Resin 2.x, Oracle OC4J (Orion),
91  * JOnAS (JOTM), BEA WebLogic
92  * <li>"java:comp/TransactionManager" for Resin 3.x
93  * <li>"java:pm/TransactionManager" for Borland Enterprise Server and
94  * Sun Application Server (Sun ONE 7 and later)
95  * <li>"java:/TransactionManager" for JBoss Application Server
96  * </ul>
97  *
98  * <p>All of these cases are autodetected by JtaTransactionManager (since Spring 1.2),
99  * provided that the "autodetectTransactionManager" flag is set to "true" (which it is
100  * by default). Consequently, JtaTransactionManager will support transaction suspension
101  * out-of-the-box on many J2EE servers.
102  *
103  * <p>A JNDI lookup can also be factored out into a corresponding
104  * {@link org.springframework.jndi.JndiObjectFactoryBean}, passed into
105  * JtaTransactionManager's "transactionManager" property. Such a bean definition
106  * can then be reused by other objects, for example Spring's LocalSessionFactoryBean
107  * for Hibernate.
108  *
109  * <p>For IBM WebSphere and standalone JOTM, static accessor methods are required to
110  * obtain the JTA TransactionManager: Therefore, WebSphere and JOTM have their own
111  * FactoryBean implementations, to be wired with the "transactionManager" property.
112  * In case of {@link JotmFactoryBean}, the same JTA object implements UserTransaction
113  * too: Therefore, passing the object to the "userTransaction" property is sufficient.
114  *
115  * <p>It is also possible to specify a JTA TransactionManager only, either through
116  * the corresponding constructor or through the "transactionManager" property.
117  * In the latter case, the "userTransactionName" property needs to be set to <code>null</code>,
118  * to suppress the default "java:comp/UserTransaction" JNDI lookup and thus enforce
119  * use of the given JTA TransactionManager even for begin, commit and rollback.
120  *
121  * <p><b>Note: Support for the JTA TransactionManager interface is not required by J2EE.
122  * Almost all J2EE servers expose it, but do so as extension to J2EE. There might be some
123  * issues with compatibility, despite the TransactionManager interface being part of JTA.</b>
124  * The only currently known problem is resuming a transaction on WebLogic, which by default
125  * fails if the suspended transaction was marked rollback-only; for other usages, it works
126  * properly. Use Spring's {@link WebLogicJtaTransactionManager} to address this issue.
127  *
128  * <p><b>This standard JtaTransactionManager supports timeouts but not per-transaction
129  * isolation levels.</b> Custom subclasses may override {@link #doJtaBegin} for
130  * specific JTA extensions in order to provide this functionality; Spring includes
131  * corresponding {@link WebLogicJtaTransactionManager} and {@link OC4JJtaTransactionManager}
132  * classes, for BEA's WebLogic Server and Oracle's OC4J, respectively. Such adapters
133  * for specific J2EE transaction coordinators may also expose transaction names for
134  * monitoring (both of the above do); with standard JTA, transaction names will simply
135  * be ignored.
136  *
137  * <p>This class is serializable. However, active synchronizations do not survive
138  * serialization.
139  *
140  * @author Juergen Hoeller
141  * @since 24.03.2003
142  * @see #setUserTransactionName
143  * @see #setUserTransaction
144  * @see #setTransactionManagerName
145  * @see #setTransactionManager
146  * @see #doJtaBegin
147  * @see JotmFactoryBean
148  * @see WebSphereTransactionManagerFactoryBean
149  * @see WebLogicJtaTransactionManager
150  * @see org.springframework.jndi.JndiObjectFactoryBean
151  * @see org.springframework.orm.hibernate.LocalSessionFactoryBean#setJtaTransactionManager
152  */

153 public class JtaTransactionManager extends AbstractPlatformTransactionManager
154         implements InitializingBean, Serializable JavaDoc {
155
156     /**
157      * Default JNDI location for the JTA UserTransaction. Many J2EE servers
158      * also provide support for the JTA TransactionManager interface there.
159      * @see #setUserTransactionName
160      * @see #setAutodetectTransactionManager
161      */

162     public static final String JavaDoc DEFAULT_USER_TRANSACTION_NAME = "java:comp/UserTransaction";
163
164     /**
165      * Fallback JNDI locations for the JTA TransactionManager. Applied if
166      * the JTA UserTransaction does not implement the JTA TransactionManager
167      * interface, provided that the "autodetectTransactionManager" flag is "true".
168      * @see #setTransactionManagerName
169      * @see #setAutodetectTransactionManager
170      */

171     public static final String JavaDoc[] FALLBACK_TRANSACTION_MANAGER_NAMES =
172             new String JavaDoc[] {"java:comp/TransactionManager", "java:pm/TransactionManager", "java:/TransactionManager"};
173
174
175     private transient JndiTemplate jndiTemplate = new JndiTemplate();
176
177     private transient UserTransaction JavaDoc userTransaction;
178
179     private String JavaDoc userTransactionName;
180
181     private boolean autodetectUserTransaction = true;
182
183     private boolean cacheUserTransaction = true;
184
185     private transient TransactionManager JavaDoc transactionManager;
186
187     private String JavaDoc transactionManagerName;
188
189     private boolean autodetectTransactionManager = true;
190
191     private boolean allowCustomIsolationLevels = false;
192
193
194     /**
195      * Create a new JtaTransactionManager instance, to be configured as bean.
196      * Invoke <code>afterPropertiesSet</code> to activate the configuration.
197      * @see #setUserTransactionName
198      * @see #setUserTransaction
199      * @see #setTransactionManagerName
200      * @see #setTransactionManager
201      * @see #afterPropertiesSet()
202      */

203     public JtaTransactionManager() {
204         setNestedTransactionAllowed(true);
205     }
206
207     /**
208      * Create a new JtaTransactionManager instance.
209      * @param userTransaction the JTA UserTransaction to use as direct reference
210      */

211     public JtaTransactionManager(UserTransaction JavaDoc userTransaction) {
212         this();
213         Assert.notNull(userTransaction, "UserTransaction must not be null");
214         this.userTransaction = userTransaction;
215     }
216
217     /**
218      * Create a new JtaTransactionManager instance.
219      * @param userTransaction the JTA UserTransaction to use as direct reference
220      * @param transactionManager the JTA TransactionManager to use as direct reference
221      */

222     public JtaTransactionManager(UserTransaction JavaDoc userTransaction, TransactionManager JavaDoc transactionManager) {
223         this();
224         Assert.notNull(userTransaction, "UserTransaction must not be null");
225         Assert.notNull(transactionManager, "TransactionManager must not be null");
226         this.userTransaction = userTransaction;
227         this.transactionManager = transactionManager;
228     }
229
230     /**
231      * Create a new JtaTransactionManager instance.
232      * @param transactionManager the JTA TransactionManager to use as direct reference
233      */

234     public JtaTransactionManager(TransactionManager JavaDoc transactionManager) {
235         this();
236         Assert.notNull(transactionManager, "TransactionManager must not be null");
237         this.transactionManager = transactionManager;
238         this.userTransaction = buildUserTransaction(transactionManager);
239     }
240
241
242     /**
243      * Set the JndiTemplate to use for JNDI lookups.
244      * A default one is used if not set.
245      */

246     public void setJndiTemplate(JndiTemplate jndiTemplate) {
247         if (jndiTemplate == null) {
248             throw new IllegalArgumentException JavaDoc("jndiTemplate must not be null");
249         }
250         this.jndiTemplate = jndiTemplate;
251     }
252
253     /**
254      * Return the JndiTemplate used for JNDI lookups.
255      */

256     public JndiTemplate getJndiTemplate() {
257         return this.jndiTemplate;
258     }
259
260     /**
261      * Set the JNDI environment to use for JNDI lookups.
262      * Creates a JndiTemplate with the given environment settings.
263      * @see #setJndiTemplate
264      */

265     public void setJndiEnvironment(Properties JavaDoc jndiEnvironment) {
266         this.jndiTemplate = new JndiTemplate(jndiEnvironment);
267     }
268
269     /**
270      * Return the JNDI environment to use for JNDI lookups.
271      */

272     public Properties JavaDoc getJndiEnvironment() {
273         return this.jndiTemplate.getEnvironment();
274     }
275
276
277     /**
278      * Set the JTA UserTransaction to use as direct reference.
279      * <p>Typically just used for local JTA setups; in a J2EE environment,
280      * the UserTransaction will always be fetched from JNDI.
281      * @see #setUserTransactionName
282      * @see #setAutodetectUserTransaction
283      */

284     public void setUserTransaction(UserTransaction JavaDoc userTransaction) {
285         this.userTransaction = userTransaction;
286     }
287
288     /**
289      * Return the JTA UserTransaction that this transaction manager uses.
290      */

291     public UserTransaction JavaDoc getUserTransaction() {
292         return this.userTransaction;
293     }
294
295     /**
296      * Set the JNDI name of the JTA UserTransaction.
297      * <p>Note that the UserTransaction will be autodetected at the J2EE default
298      * location "java:comp/UserTransaction" if not specified explicitly.
299      * @see #DEFAULT_USER_TRANSACTION_NAME
300      * @see #setUserTransaction
301      * @see #setAutodetectUserTransaction
302      */

303     public void setUserTransactionName(String JavaDoc userTransactionName) {
304         this.userTransactionName = userTransactionName;
305     }
306
307     /**
308      * Set whether to autodetect the JTA UserTransaction at its default
309      * JNDI location "java:comp/UserTransaction", as specified by J2EE.
310      * Will proceed without UserTransaction if none found.
311      * <p>Default is "true", autodetecting the UserTransaction unless
312      * it has been specified explicitly. Turn this flag off to allow for
313      * JtaTransactionManager operating against the TransactionManager only,
314      * despite a default UserTransaction being available.
315      * @see #DEFAULT_USER_TRANSACTION_NAME
316      */

317     public void setAutodetectUserTransaction(boolean autodetectUserTransaction) {
318         this.autodetectUserTransaction = autodetectUserTransaction;
319     }
320
321     /**
322      * Set whether to cache the JTA UserTransaction object fetched from JNDI.
323      * <p>Default is "true": UserTransaction lookup will only happen at startup,
324      * reusing the same UserTransaction handle for all transactions of all threads.
325      * This is the most efficient choice for all application servers that provide
326      * a shared UserTransaction object (the typical case).
327      * <p>Turn this flag off to enforce a fresh lookup of the UserTransaction
328      * for every transaction. This is only necessary for application servers
329      * that return a new UserTransaction for every transaction, keeping state
330      * tied to the UserTransaction object itself rather than the current thread.
331      * @see #setUserTransactionName
332      */

333     public void setCacheUserTransaction(boolean cacheUserTransaction) {
334         this.cacheUserTransaction = cacheUserTransaction;
335     }
336
337
338     /**
339      * Set the JTA TransactionManager to use as direct reference.
340      * <p>A TransactionManager is necessary for suspending and resuming transactions,
341      * as this not supported by the UserTransaction interface.
342      * <p>Note that the TransactionManager will be autodetected if the JTA
343      * UserTransaction object implements the JTA TransactionManager interface too,
344      * as well as autodetected at various well-known fallback JNDI locations.
345      * @see #setTransactionManagerName
346      * @see #setAutodetectTransactionManager
347      */

348     public void setTransactionManager(TransactionManager JavaDoc transactionManager) {
349         this.transactionManager = transactionManager;
350     }
351
352     /**
353      * Return the JTA TransactionManager that this transaction manager uses.
354      */

355     public TransactionManager JavaDoc getTransactionManager() {
356         return this.transactionManager;
357     }
358
359     /**
360      * Set the JNDI name of the JTA TransactionManager.
361      * <p>A TransactionManager is necessary for suspending and resuming transactions,
362      * as this not supported by the UserTransaction interface.
363      * <p>Note that the TransactionManager will be autodetected if the JTA
364      * UserTransaction object implements the JTA TransactionManager interface too,
365      * as well as autodetected at various well-known fallback JNDI locations.
366      * @see #setTransactionManager
367      * @see #setAutodetectTransactionManager
368      */

369     public void setTransactionManagerName(String JavaDoc transactionManagerName) {
370         this.transactionManagerName = transactionManagerName;
371     }
372
373     /**
374      * Set whether to autodetect a JTA UserTransaction object that implements
375      * the JTA TransactionManager interface too (i.e. the JNDI location for the
376      * TransactionManager is "java:comp/UserTransaction", same as for the UserTransaction).
377      * Also checks the fallback JNDI locations "java:comp/TransactionManager" and
378      * "java:/TransactionManager". Will proceed without TransactionManager if none found.
379      * <p>Default is "true", autodetecting the TransactionManager unless it has been
380      * specified explicitly. Can be turned off to deliberately ignore an available
381      * TransactionManager, for example when there are known issues with suspend/resume
382      * and any attempt to use REQUIRES_NEW or NOT_SUPPORTED should fail fast.
383      * @see #FALLBACK_TRANSACTION_MANAGER_NAMES
384      */

385     public void setAutodetectTransactionManager(boolean autodetectTransactionManager) {
386         this.autodetectTransactionManager = autodetectTransactionManager;
387     }
388
389     /**
390      * Set whether to allow custom isolation levels to be specified.
391      * <p>Default is "false", throwing an exception if a non-default isolation level
392      * is specified for a transaction. Turn this flag on if affected resource adapters
393      * check the thread-bound transaction context and apply the specified isolation
394      * levels individually (e.g. through a IsolationLevelDataSourceRouter).
395      * @see org.springframework.jdbc.datasource.lookup.IsolationLevelDataSourceRouter
396      */

397     public void setAllowCustomIsolationLevels(boolean allowCustomIsolationLevels) {
398         this.allowCustomIsolationLevels = allowCustomIsolationLevels;
399     }
400
401
402     /**
403      * Initialize the UserTransaction as well as the TransactionManager handle.
404      * @see #initUserTransactionAndTransactionManager()
405      */

406     public void afterPropertiesSet() throws TransactionSystemException {
407         initUserTransactionAndTransactionManager();
408     }
409
410     /**
411      * Initialize the UserTransaction as well as the TransactionManager handle.
412      * @throws TransactionSystemException if initialization failed
413      */

414     protected void initUserTransactionAndTransactionManager() throws TransactionSystemException {
415         // Fetch JTA UserTransaction from JNDI, if necessary.
416
if (this.userTransaction == null) {
417             if (this.userTransactionName != null) {
418                 this.userTransaction = lookupUserTransaction(this.userTransactionName);
419             }
420             else {
421                 this.userTransaction = retrieveUserTransaction();
422             }
423         }
424
425         // Fetch JTA TransactionManager from JNDI, if necessary.
426
if (this.transactionManager == null) {
427             if (this.transactionManagerName != null) {
428                 this.transactionManager = lookupTransactionManager(this.transactionManagerName);
429             }
430             else {
431                 this.transactionManager = retrieveTransactionManager();
432             }
433         }
434
435         // Autodetect UserTransaction at its default JNDI location.
436
if (this.userTransaction == null && this.autodetectUserTransaction) {
437             this.userTransaction = findUserTransaction();
438         }
439
440         // Autodetect UserTransaction object that implements TransactionManager,
441
// and check fallback JNDI locations else.
442
if (this.transactionManager == null && this.autodetectTransactionManager) {
443             this.transactionManager = findTransactionManager(this.userTransaction);
444         }
445
446         // If only JTA TransactionManager specified, create UserTransaction handle for it.
447
if (this.userTransaction == null && this.transactionManager != null) {
448             this.userTransaction = buildUserTransaction(this.transactionManager);
449         }
450
451         // We at least need the JTA UserTransaction.
452
if (this.userTransaction != null) {
453             if (logger.isInfoEnabled()) {
454                 logger.info("Using JTA UserTransaction: " + this.userTransaction);
455             }
456         }
457         else {
458             throw new IllegalStateException JavaDoc(
459                     "Either 'userTransaction' or 'userTransactionName' or 'transactionManager' " +
460                     "or 'transactionManagerName' must be specified");
461         }
462
463         // For transaction suspension, the JTA TransactionManager is necessary too.
464
if (this.transactionManager != null) {
465             if (logger.isInfoEnabled()) {
466                 logger.info("Using JTA TransactionManager: " + this.transactionManager);
467             }
468         }
469         else {
470             logger.warn("No JTA TransactionManager found: " +
471                     "transaction suspension and synchronization with existing JTA transactions not available");
472         }
473     }
474
475
476     /**
477      * Look up the JTA UserTransaction in JNDI via the configured name.
478      * Called by <code>afterPropertiesSet</code> if no direct UserTransaction reference was set.
479      * Can be overridden in subclasses to provide a different UserTransaction object.
480      * @param userTransactionName the JNDI name of the UserTransaction
481      * @return the UserTransaction object
482      * @throws TransactionSystemException if the JNDI lookup failed
483      * @see #setJndiTemplate
484      * @see #setUserTransactionName
485      */

486     protected UserTransaction JavaDoc lookupUserTransaction(String JavaDoc userTransactionName)
487             throws TransactionSystemException {
488         try {
489             if (logger.isDebugEnabled()) {
490                 logger.debug("Retrieving JTA UserTransaction from JNDI location [" + userTransactionName + "]");
491             }
492             return (UserTransaction JavaDoc) getJndiTemplate().lookup(userTransactionName, UserTransaction JavaDoc.class);
493         }
494         catch (NamingException JavaDoc ex) {
495             throw new TransactionSystemException(
496                     "JTA UserTransaction is not available at JNDI location [" + userTransactionName + "]", ex);
497         }
498     }
499
500     /**
501      * Look up the JTA TransactionManager in JNDI via the configured name.
502      * Called by <code>afterPropertiesSet</code> if no direct TransactionManager reference was set.
503      * Can be overridden in subclasses to provide a different TransactionManager object.
504      * @param transactionManagerName the JNDI name of the TransactionManager
505      * @return the UserTransaction object
506      * @throws TransactionSystemException if the JNDI lookup failed
507      * @see #setJndiTemplate
508      * @see #setTransactionManagerName
509      */

510     protected TransactionManager JavaDoc lookupTransactionManager(String JavaDoc transactionManagerName)
511             throws TransactionSystemException {
512         try {
513             if (logger.isDebugEnabled()) {
514                 logger.debug("Retrieving JTA TransactionManager from JNDI location [" + transactionManagerName + "]");
515             }
516             return (TransactionManager JavaDoc) getJndiTemplate().lookup(transactionManagerName, TransactionManager JavaDoc.class);
517         }
518         catch (NamingException JavaDoc ex) {
519             throw new TransactionSystemException(
520                     "JTA TransactionManager is not available at JNDI location [" + transactionManagerName + "]", ex);
521         }
522     }
523
524     /**
525      * Allows subclasses to retrieve the JTA UserTransaction in a vendor-specific manner.
526      * Only called if no "userTransaction" or "userTransactionName" specified.
527      * <p>The default implementation simply returns <code>null</code>.
528      * @return the JTA UserTransaction handle to use, or <code>null</code> if none found
529      * @throws TransactionSystemException in case of errors
530      * @see #setUserTransaction
531      * @see #setUserTransactionName
532      */

533     protected UserTransaction JavaDoc retrieveUserTransaction() throws TransactionSystemException {
534         return null;
535     }
536
537     /**
538      * Allows subclasses to retrieve the JTA TransactionManager in a vendor-specific manner.
539      * Only called if no "transactionManager" or "transactionManagerName" specified.
540      * <p>The default implementation simply returns <code>null</code>.
541      * @return the JTA TransactionManager handle to use, or <code>null</code> if none found
542      * @throws TransactionSystemException in case of errors
543      * @see #setTransactionManager
544      * @see #setTransactionManagerName
545      */

546     protected TransactionManager JavaDoc retrieveTransactionManager() throws TransactionSystemException {
547         return null;
548     }
549
550     /**
551      * Find the JTA UserTransaction through a default JNDI lookup:
552      * "java:comp/UserTransaction".
553      * @return the JTA UserTransaction reference, or <code>null</code> if not found
554      * @see #DEFAULT_USER_TRANSACTION_NAME
555      */

556     protected UserTransaction JavaDoc findUserTransaction() {
557         String JavaDoc jndiName = DEFAULT_USER_TRANSACTION_NAME;
558         try {
559             UserTransaction JavaDoc ut = (UserTransaction JavaDoc) getJndiTemplate().lookup(jndiName, UserTransaction JavaDoc.class);
560             if (logger.isDebugEnabled()) {
561                 logger.debug("JTA UserTransaction found at default JNDI location [" + jndiName + "]");
562             }
563             return ut;
564         }
565         catch (NamingException JavaDoc ex) {
566             if (logger.isDebugEnabled()) {
567                 logger.debug("No JTA UserTransaction found at default JNDI location [" + jndiName + "]", ex);
568             }
569             return null;
570         }
571     }
572
573     /**
574      * Find the JTA TransactionManager through autodetection: checking whether the
575      * UserTransaction object implements the TransactionManager, and checking the
576      * fallback JNDI locations.
577      * @param ut the JTA UserTransaction object
578      * @return the JTA TransactionManager reference, or <code>null</code> if not found
579      * @see #FALLBACK_TRANSACTION_MANAGER_NAMES
580      */

581     protected TransactionManager JavaDoc findTransactionManager(UserTransaction JavaDoc ut) {
582         if (ut instanceof TransactionManager JavaDoc) {
583             if (logger.isDebugEnabled()) {
584                 logger.debug("JTA UserTransaction object [" + ut + "] implements TransactionManager");
585             }
586             return (TransactionManager JavaDoc) ut;
587         }
588
589         // Check fallback JNDI locations.
590
for (int i = 0; i < FALLBACK_TRANSACTION_MANAGER_NAMES.length; i++) {
591             String JavaDoc jndiName = FALLBACK_TRANSACTION_MANAGER_NAMES[i];
592             try {
593                 TransactionManager JavaDoc tm = (TransactionManager JavaDoc) getJndiTemplate().lookup(jndiName, TransactionManager JavaDoc.class);
594                 if (logger.isDebugEnabled()) {
595                     logger.debug("JTA TransactionManager found at fallback JNDI location [" + jndiName + "]");
596                 }
597                 return tm;
598             }
599             catch (NamingException JavaDoc ex) {
600                 if (logger.isDebugEnabled()) {
601                     logger.debug("No JTA TransactionManager found at fallback JNDI location [" + jndiName + "]", ex);
602                 }
603             }
604         }
605
606         // OK, so no JTA TransactionManager is available...
607
return null;
608     }
609
610     /**
611      * Build a UserTransaction handle based on the given TransactionManager.
612      * @param transactionManager the TransactionManager
613      * @return a corresponding UserTransaction handle
614      */

615     protected UserTransaction JavaDoc buildUserTransaction(TransactionManager JavaDoc transactionManager) {
616         if (transactionManager instanceof UserTransaction JavaDoc) {
617             return (UserTransaction JavaDoc) transactionManager;
618         }
619         else {
620             return new UserTransactionAdapter(transactionManager);
621         }
622     }
623
624
625     /**
626      * This implementation returns a JtaTransactionObject instance for the
627      * JTA UserTransaction.
628      * <p>The UserTransaction object will either be looked up freshly for the
629      * current transaction, or the cached one looked up at startup will be used.
630      * The latter is the default: Most application servers use a shared singleton
631      * UserTransaction that can be cached. Turn off the "cacheUserTransaction"
632      * flag to enforce a fresh lookup for every transaction.
633      * @see #setCacheUserTransaction
634      */

635     protected Object JavaDoc doGetTransaction() {
636         UserTransaction JavaDoc ut = getUserTransaction();
637         if (!this.cacheUserTransaction) {
638             ut = lookupUserTransaction(
639                     this.userTransactionName != null ? this.userTransactionName : DEFAULT_USER_TRANSACTION_NAME);
640         }
641         return doGetJtaTransaction(ut);
642     }
643
644     /**
645      * Get a JTA transaction object for the given current UserTransaction.
646      * <p>Subclasses can override this to provide a JtaTransactionObject
647      * subclass, for example holding some additional JTA handle needed.
648      * @param ut the UserTransaction handle to use for the current transaction
649      * @return the JtaTransactionObject holding the UserTransaction
650      */

651     protected JtaTransactionObject doGetJtaTransaction(UserTransaction JavaDoc ut) {
652         return new JtaTransactionObject(ut);
653     }
654
655     protected boolean isExistingTransaction(Object JavaDoc transaction) {
656         JtaTransactionObject txObject = (JtaTransactionObject) transaction;
657         try {
658             return (txObject.getUserTransaction().getStatus() != Status.STATUS_NO_TRANSACTION);
659         }
660         catch (SystemException JavaDoc ex) {
661             throw new TransactionSystemException("JTA failure on getStatus", ex);
662         }
663     }
664
665     /**
666      * This implementation returns false to cause a further invocation
667      * of doBegin despite an already existing transaction.
668      * <p>JTA implementations might support nested transactions via further
669      * <code>UserTransaction.begin()</code> invocations, but never support savepoints.
670      * @see #doBegin
671      * @see javax.transaction.UserTransaction#begin()
672      */

673     protected boolean useSavepointForNestedTransaction() {
674         return false;
675     }
676
677
678     protected void doBegin(Object JavaDoc transaction, TransactionDefinition definition) {
679         JtaTransactionObject txObject = (JtaTransactionObject) transaction;
680         try {
681             doJtaBegin(txObject, definition);
682         }
683         catch (NotSupportedException JavaDoc ex) {
684             // assume nested transaction not supported
685
throw new NestedTransactionNotSupportedException(
686                 "JTA implementation does not support nested transactions", ex);
687         }
688         catch (UnsupportedOperationException JavaDoc ex) {
689             // assume nested transaction not supported
690
throw new NestedTransactionNotSupportedException(
691                 "JTA implementation does not support nested transactions", ex);
692         }
693         catch (SystemException JavaDoc ex) {
694             throw new CannotCreateTransactionException("JTA failure on begin", ex);
695         }
696     }
697
698     /**
699      * Perform a JTA begin on the JTA UserTransaction or TransactionManager.
700      * <p>This implementation only supports standard JTA functionality:
701      * that is, no per-transaction isolation levels and no transaction names.
702      * Can be overridden in subclasses, for specific JTA implementations.
703      * <p>Calls <code>applyIsolationLevel</code> and <code>applyTimeout</code>
704      * before invoking the UserTransaction's <code>begin</code> method.
705      * @param txObject the JtaTransactionObject containing the UserTransaction
706      * @param definition TransactionDefinition instance, describing propagation
707      * behavior, isolation level, read-only flag, timeout, and transaction name
708      * @throws NotSupportedException if thrown by JTA methods
709      * @throws SystemException if thrown by JTA methods
710      * @see #getUserTransaction
711      * @see #getTransactionManager
712      * @see #applyIsolationLevel
713      * @see #applyTimeout
714      * @see JtaTransactionObject#getUserTransaction()
715      * @see javax.transaction.UserTransaction#setTransactionTimeout
716      * @see javax.transaction.UserTransaction#begin
717      */

718     protected void doJtaBegin(JtaTransactionObject txObject, TransactionDefinition definition)
719             throws NotSupportedException JavaDoc, SystemException JavaDoc {
720
721         applyIsolationLevel(txObject, definition.getIsolationLevel());
722         int timeout = determineTimeout(definition);
723         applyTimeout(txObject, timeout);
724         txObject.getUserTransaction().begin();
725     }
726
727     /**
728      * Apply the given transaction isolation level. The default implementation
729      * will throw an exception for any level other than ISOLATION_DEFAULT.
730      * <p>To be overridden in subclasses for specific JTA implementations,
731      * as alternative to overriding the full {@link #doJtaBegin} method.
732      * @param txObject the JtaTransactionObject containing the UserTransaction
733      * @param isolationLevel isolation level taken from transaction definition
734      * @throws InvalidIsolationLevelException if the given isolation level
735      * cannot be applied
736      * @throws SystemException if thrown by the JTA implementation
737      * @see #doJtaBegin
738      * @see JtaTransactionObject#getUserTransaction()
739      * @see #getTransactionManager()
740      */

741     protected void applyIsolationLevel(JtaTransactionObject txObject, int isolationLevel)
742         throws InvalidIsolationLevelException, SystemException JavaDoc {
743
744         if (!this.allowCustomIsolationLevels && isolationLevel != TransactionDefinition.ISOLATION_DEFAULT) {
745             throw new InvalidIsolationLevelException(
746                 "JtaTransactionManager does not support custom isolation levels by default - " +
747                     "switch 'allowCustomIsolationLevels' to 'true'");
748         }
749     }
750
751     /**
752      * Apply the given transaction timeout. The default implementation will call
753      * <code>UserTransaction.setTransactionTimeout</code> for a non-default timeout value.
754      * @param txObject the JtaTransactionObject containing the UserTransaction
755      * @param timeout timeout value taken from transaction definition
756      * @throws SystemException if thrown by the JTA implementation
757      * @see #doJtaBegin
758      * @see JtaTransactionObject#getUserTransaction()
759      * @see javax.transaction.UserTransaction#setTransactionTimeout(int)
760      */

761     protected void applyTimeout(JtaTransactionObject txObject, int timeout) throws SystemException JavaDoc {
762         if (timeout > TransactionDefinition.TIMEOUT_DEFAULT) {
763             txObject.getUserTransaction().setTransactionTimeout(timeout);
764         }
765     }
766
767
768     protected Object JavaDoc doSuspend(Object JavaDoc transaction) {
769         JtaTransactionObject txObject = (JtaTransactionObject) transaction;
770         try {
771             return doJtaSuspend(txObject);
772         }
773         catch (SystemException JavaDoc ex) {
774             throw new TransactionSystemException("JTA failure on suspend", ex);
775         }
776     }
777
778     /**
779      * Perform a JTA suspend on the JTA TransactionManager.
780      * <p>Can be overridden in subclasses, for specific JTA implementations.
781      * @param txObject the JtaTransactionObject containing the UserTransaction
782      * @return the suspended JTA Transaction object
783      * @throws SystemException if thrown by JTA methods
784      * @see #getTransactionManager()
785      * @see javax.transaction.TransactionManager#suspend()
786      */

787     protected Object JavaDoc doJtaSuspend(JtaTransactionObject txObject) throws SystemException JavaDoc {
788         if (getTransactionManager() == null) {
789             throw new TransactionSuspensionNotSupportedException(
790                     "JtaTransactionManager needs a JTA TransactionManager for suspending a transaction: " +
791                     "specify the 'transactionManager' or 'transactionManagerName' property");
792         }
793         return getTransactionManager().suspend();
794     }
795
796     protected void doResume(Object JavaDoc transaction, Object JavaDoc suspendedResources) {
797         JtaTransactionObject txObject = (JtaTransactionObject) transaction;
798         try {
799             doJtaResume(txObject, suspendedResources);
800         }
801         catch (InvalidTransactionException JavaDoc ex) {
802             throw new IllegalTransactionStateException("Tried to resume invalid JTA transaction", ex);
803         }
804         catch (SystemException JavaDoc ex) {
805             throw new TransactionSystemException("JTA failure on resume", ex);
806         }
807     }
808
809     /**
810      * Perform a JTA resume on the JTA TransactionManager.
811      * <p>Can be overridden in subclasses, for specific JTA implementations.
812      * @param txObject the JtaTransactionObject containing the UserTransaction
813      * @param suspendedTransaction the suspended JTA Transaction object
814      * @throws InvalidTransactionException if thrown by JTA methods
815      * @throws SystemException if thrown by JTA methods
816      * @see #getTransactionManager()
817      * @see javax.transaction.TransactionManager#resume(javax.transaction.Transaction)
818      */

819     protected void doJtaResume(JtaTransactionObject txObject, Object JavaDoc suspendedTransaction)
820         throws InvalidTransactionException JavaDoc, SystemException JavaDoc {
821
822         if (getTransactionManager() == null) {
823             throw new TransactionSuspensionNotSupportedException(
824                     "JtaTransactionManager needs a JTA TransactionManager for suspending a transaction: " +
825                     "specify the 'transactionManager' or 'transactionManagerName' property");
826         }
827         getTransactionManager().resume((Transaction JavaDoc) suspendedTransaction);
828     }
829
830
831     /**
832      * This implementation returns "true": a JTA commit will properly handle
833      * transactions that have been marked rollback-only at a global level.
834      */

835     protected boolean shouldCommitOnGlobalRollbackOnly() {
836         return true;
837     }
838
839     protected void doCommit(DefaultTransactionStatus status) {
840         JtaTransactionObject txObject = (JtaTransactionObject) status.getTransaction();
841         try {
842             txObject.getUserTransaction().commit();
843         }
844         catch (RollbackException JavaDoc ex) {
845             throw new UnexpectedRollbackException(
846                     "JTA transaction unexpectedly rolled back (maybe due to a timeout)", ex);
847         }
848         catch (HeuristicMixedException JavaDoc ex) {
849             throw new HeuristicCompletionException(HeuristicCompletionException.STATE_MIXED, ex);
850         }
851         catch (HeuristicRollbackException JavaDoc ex) {
852             throw new HeuristicCompletionException(HeuristicCompletionException.STATE_ROLLED_BACK, ex);
853         }
854         catch (SystemException JavaDoc ex) {
855             throw new TransactionSystemException("JTA failure on commit", ex);
856         }
857     }
858
859     protected void doRollback(DefaultTransactionStatus status) {
860         JtaTransactionObject txObject = (JtaTransactionObject) status.getTransaction();
861         try {
862             if (txObject.getUserTransaction().getStatus() != Status.STATUS_NO_TRANSACTION) {
863                 txObject.getUserTransaction().rollback();
864             }
865         }
866         catch (SystemException JavaDoc ex) {
867             throw new TransactionSystemException("JTA failure on rollback", ex);
868         }
869     }
870
871     protected void doSetRollbackOnly(DefaultTransactionStatus status) {
872         JtaTransactionObject txObject = (JtaTransactionObject) status.getTransaction();
873         if (status.isDebug()) {
874             logger.debug("Setting JTA transaction rollback-only");
875         }
876         try {
877             if (txObject.getUserTransaction().getStatus() != Status.STATUS_NO_TRANSACTION) {
878                 txObject.getUserTransaction().setRollbackOnly();
879             }
880         }
881         catch (IllegalStateException JavaDoc ex) {
882             throw new NoTransactionException("No active JTA transaction");
883         }
884         catch (SystemException JavaDoc ex) {
885             throw new TransactionSystemException("JTA failure on setRollbackOnly", ex);
886         }
887     }
888
889
890     protected void registerAfterCompletionWithExistingTransaction(Object JavaDoc transaction, List JavaDoc synchronizations) {
891         JtaTransactionObject txObject = (JtaTransactionObject) transaction;
892         logger.debug("Registering after-completion synchronization with existing JTA transaction");
893         try {
894             doRegisterAfterCompletionWithJtaTransaction(txObject, synchronizations);
895         }
896         catch (RollbackException JavaDoc ex) {
897             logger.debug("Participating in existing JTA transaction that has been marked rollback-only: " +
898                     "cannot register Spring after-completion callbacks with outer JTA transaction - " +
899                     "immediately performing Spring after-completion callbacks with outcome status 'rollback'");
900             invokeAfterCompletion(synchronizations, TransactionSynchronization.STATUS_ROLLED_BACK);
901         }
902         catch (IllegalStateException JavaDoc ex) {
903             throw new NoTransactionException("No active JTA transaction");
904         }
905         catch (SystemException JavaDoc ex) {
906             throw new TransactionSystemException("JTA failure on registerSynchronization", ex);
907         }
908     }
909
910     /**
911      * Register a JTA synchronization on the JTA TransactionManager, for calling
912      * <code>afterCompletion</code> on the given Spring TransactionSynchronizations.
913      * <p>Can be overridden in subclasses, for specific JTA implementations.
914      * @param synchronizations List of TransactionSynchronization objects
915      * @throws RollbackException if thrown by JTA methods
916      * @throws SystemException if thrown by JTA methods
917      * @see #getTransactionManager()
918      * @see javax.transaction.Transaction#registerSynchronization
919      * @see #invokeAfterCompletion(java.util.List, int)
920      */

921     protected void doRegisterAfterCompletionWithJtaTransaction(
922             JtaTransactionObject txObject, List JavaDoc synchronizations)
923             throws RollbackException JavaDoc, SystemException JavaDoc {
924
925         if (getTransactionManager() != null) {
926             Transaction JavaDoc transaction = getTransactionManager().getTransaction();
927             if (transaction != null) {
928                 transaction.registerSynchronization(new JtaAfterCompletionSynchronization(synchronizations));
929             }
930             else {
931                 // No current JTA Transaction available.
932
logger.debug("Participating in existing JTA transaction, but no current JTA Transaction available: " +
933                         "cannot register Spring after-completion callbacks with outer JTA transaction - " +
934                         "processing Spring after-completion callbacks with outcome status 'unknown'");
935                 invokeAfterCompletion(synchronizations, TransactionSynchronization.STATUS_UNKNOWN);
936             }
937         }
938         else {
939             // No JTA TransactionManager available.
940
logger.warn("Participating in existing JTA transaction, but no JTA TransactionManager available: " +
941                     "cannot register Spring after-completion callbacks with outer JTA transaction - " +
942                     "processing Spring after-completion callbacks with outcome status 'unknown'");
943             invokeAfterCompletion(synchronizations, TransactionSynchronization.STATUS_UNKNOWN);
944         }
945     }
946
947
948     //---------------------------------------------------------------------
949
// Serialization support
950
//---------------------------------------------------------------------
951

952     private void readObject(ObjectInputStream JavaDoc ois) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
953         // Rely on default serialization; just initialize state after deserialization.
954
ois.defaultReadObject();
955
956         // Create template for client-side JNDI lookup.
957
this.jndiTemplate = new JndiTemplate();
958
959         // Perform lookup for JTA UserTransaction and TransactionManager.
960
initUserTransactionAndTransactionManager();
961     }
962
963 }
964
Popular Tags