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 java.io.IOException;
020import java.io.ObjectInputStream;
021import java.io.Serializable;
022import java.util.List;
023import java.util.Properties;
024import javax.naming.NamingException;
025import javax.transaction.HeuristicMixedException;
026import javax.transaction.HeuristicRollbackException;
027import javax.transaction.InvalidTransactionException;
028import javax.transaction.NotSupportedException;
029import javax.transaction.RollbackException;
030import javax.transaction.Status;
031import javax.transaction.SystemException;
032import javax.transaction.Transaction;
033import javax.transaction.TransactionManager;
034import javax.transaction.TransactionSynchronizationRegistry;
035import javax.transaction.UserTransaction;
036
037import org.springframework.beans.factory.InitializingBean;
038import org.springframework.jndi.JndiTemplate;
039import org.springframework.transaction.CannotCreateTransactionException;
040import org.springframework.transaction.HeuristicCompletionException;
041import org.springframework.transaction.IllegalTransactionStateException;
042import org.springframework.transaction.InvalidIsolationLevelException;
043import org.springframework.transaction.NestedTransactionNotSupportedException;
044import org.springframework.transaction.TransactionDefinition;
045import org.springframework.transaction.TransactionSuspensionNotSupportedException;
046import org.springframework.transaction.TransactionSystemException;
047import org.springframework.transaction.UnexpectedRollbackException;
048import org.springframework.transaction.support.AbstractPlatformTransactionManager;
049import org.springframework.transaction.support.DefaultTransactionStatus;
050import org.springframework.transaction.support.TransactionSynchronization;
051import org.springframework.util.Assert;
052import org.springframework.util.StringUtils;
053
054/**
055 * {@link org.springframework.transaction.PlatformTransactionManager} implementation
056 * for JTA, delegating to a backend JTA provider. This is typically used to delegate
057 * to a Java EE server's transaction coordinator, but may also be configured with a
058 * local JTA provider which is embedded within the application.
059 *
060 * <p>This transaction manager is appropriate for handling distributed transactions,
061 * i.e. transactions that span multiple resources, and for controlling transactions on
062 * application server resources (e.g. JDBC DataSources available in JNDI) in general.
063 * For a single JDBC DataSource, DataSourceTransactionManager is perfectly sufficient,
064 * and for accessing a single resource with Hibernate (including transactional cache),
065 * HibernateTransactionManager is appropriate, for example.
066 *
067 * <p><b>For typical JTA transactions (REQUIRED, SUPPORTS, MANDATORY, NEVER), a plain
068 * JtaTransactionManager definition is all you need, portable across all Java EE servers.</b>
069 * This corresponds to the functionality of the JTA UserTransaction, for which Java EE
070 * specifies a standard JNDI name ("java:comp/UserTransaction"). There is no need to
071 * configure a server-specific TransactionManager lookup for this kind of JTA usage.
072 *
073 * <p><b>Transaction suspension (REQUIRES_NEW, NOT_SUPPORTED) is just available with a
074 * JTA TransactionManager being registered.</b> Common TransactionManager locations are
075 * autodetected by JtaTransactionManager, provided that the "autodetectTransactionManager"
076 * flag is set to "true" (which it is by default).
077 *
078 * <p>Note: Support for the JTA TransactionManager interface is not required by Java EE.
079 * Almost all Java EE servers expose it, but do so as extension to EE. There might be some
080 * issues with compatibility, despite the TransactionManager interface being part of JTA.
081 * As a consequence, Spring provides various vendor-specific PlatformTransactionManagers,
082 * which are recommended to be used if appropriate: {@link WebLogicJtaTransactionManager}
083 * and {@link WebSphereUowTransactionManager}. For all other Java EE servers, the
084 * standard JtaTransactionManager is sufficient.
085 *
086 * <p>This pure JtaTransactionManager class supports timeouts but not per-transaction
087 * isolation levels. Custom subclasses may override the {@link #doJtaBegin} method for
088 * specific JTA extensions in order to provide this functionality; Spring includes a
089 * corresponding {@link WebLogicJtaTransactionManager} class for WebLogic Server. Such
090 * adapters for specific Java EE transaction coordinators may also expose transaction
091 * names for monitoring; with standard JTA, transaction names will simply be ignored.
092 *
093 * <p><b>Consider using Spring's {@code tx:jta-transaction-manager} configuration
094 * element for automatically picking the appropriate JTA platform transaction manager
095 * (automatically detecting WebLogic and WebSphere).</b>
096 *
097 * <p>JTA 1.1 adds the TransactionSynchronizationRegistry facility, as public Java EE 5
098 * API in addition to the standard JTA UserTransaction handle. As of Spring 2.5, this
099 * JtaTransactionManager autodetects the TransactionSynchronizationRegistry and uses
100 * it for registering Spring-managed synchronizations when participating in an existing
101 * JTA transaction (e.g. controlled by EJB CMT). If no TransactionSynchronizationRegistry
102 * is available, then such synchronizations will be registered via the (non-EE) JTA
103 * TransactionManager handle.
104 *
105 * <p>This class is serializable. However, active synchronizations do not survive serialization.
106 *
107 * @author Juergen Hoeller
108 * @since 24.03.2003
109 * @see javax.transaction.UserTransaction
110 * @see javax.transaction.TransactionManager
111 * @see javax.transaction.TransactionSynchronizationRegistry
112 * @see #setUserTransactionName
113 * @see #setUserTransaction
114 * @see #setTransactionManagerName
115 * @see #setTransactionManager
116 * @see WebLogicJtaTransactionManager
117 */
118@SuppressWarnings("serial")
119public class JtaTransactionManager extends AbstractPlatformTransactionManager
120                implements TransactionFactory, InitializingBean, Serializable {
121
122        /**
123         * Default JNDI location for the JTA UserTransaction. Many Java EE servers
124         * also provide support for the JTA TransactionManager interface there.
125         * @see #setUserTransactionName
126         * @see #setAutodetectTransactionManager
127         */
128        public static final String DEFAULT_USER_TRANSACTION_NAME = "java:comp/UserTransaction";
129
130        /**
131         * Fallback JNDI locations for the JTA TransactionManager. Applied if
132         * the JTA UserTransaction does not implement the JTA TransactionManager
133         * interface, provided that the "autodetectTransactionManager" flag is "true".
134         * @see #setTransactionManagerName
135         * @see #setAutodetectTransactionManager
136         */
137        public static final String[] FALLBACK_TRANSACTION_MANAGER_NAMES =
138                        new String[] {"java:comp/TransactionManager", "java:appserver/TransactionManager",
139                                        "java:pm/TransactionManager", "java:/TransactionManager"};
140
141        /**
142         * Standard Java EE 5 JNDI location for the JTA TransactionSynchronizationRegistry.
143         * Autodetected when available.
144         */
145        public static final String DEFAULT_TRANSACTION_SYNCHRONIZATION_REGISTRY_NAME =
146                        "java:comp/TransactionSynchronizationRegistry";
147
148
149        private transient JndiTemplate jndiTemplate = new JndiTemplate();
150
151        private transient UserTransaction userTransaction;
152
153        private String userTransactionName;
154
155        private boolean autodetectUserTransaction = true;
156
157        private boolean cacheUserTransaction = true;
158
159        private boolean userTransactionObtainedFromJndi = false;
160
161        private transient TransactionManager transactionManager;
162
163        private String transactionManagerName;
164
165        private boolean autodetectTransactionManager = true;
166
167        private transient TransactionSynchronizationRegistry transactionSynchronizationRegistry;
168
169        private String transactionSynchronizationRegistryName;
170
171        private boolean autodetectTransactionSynchronizationRegistry = true;
172
173        private boolean allowCustomIsolationLevels = false;
174
175
176        /**
177         * Create a new JtaTransactionManager instance, to be configured as bean.
178         * Invoke {@code afterPropertiesSet} to activate the configuration.
179         * @see #setUserTransactionName
180         * @see #setUserTransaction
181         * @see #setTransactionManagerName
182         * @see #setTransactionManager
183         * @see #afterPropertiesSet()
184         */
185        public JtaTransactionManager() {
186                setNestedTransactionAllowed(true);
187        }
188
189        /**
190         * Create a new JtaTransactionManager instance.
191         * @param userTransaction the JTA UserTransaction to use as direct reference
192         */
193        public JtaTransactionManager(UserTransaction userTransaction) {
194                this();
195                Assert.notNull(userTransaction, "UserTransaction must not be null");
196                this.userTransaction = userTransaction;
197        }
198
199        /**
200         * Create a new JtaTransactionManager instance.
201         * @param userTransaction the JTA UserTransaction to use as direct reference
202         * @param transactionManager the JTA TransactionManager to use as direct reference
203         */
204        public JtaTransactionManager(UserTransaction userTransaction, TransactionManager transactionManager) {
205                this();
206                Assert.notNull(userTransaction, "UserTransaction must not be null");
207                Assert.notNull(transactionManager, "TransactionManager must not be null");
208                this.userTransaction = userTransaction;
209                this.transactionManager = transactionManager;
210        }
211
212        /**
213         * Create a new JtaTransactionManager instance.
214         * @param transactionManager the JTA TransactionManager to use as direct reference
215         */
216        public JtaTransactionManager(TransactionManager transactionManager) {
217                this();
218                Assert.notNull(transactionManager, "TransactionManager must not be null");
219                this.transactionManager = transactionManager;
220                this.userTransaction = buildUserTransaction(transactionManager);
221        }
222
223
224        /**
225         * Set the JndiTemplate to use for JNDI lookups.
226         * A default one is used if not set.
227         */
228        public void setJndiTemplate(JndiTemplate jndiTemplate) {
229                Assert.notNull(jndiTemplate, "JndiTemplate must not be null");
230                this.jndiTemplate = jndiTemplate;
231        }
232
233        /**
234         * Return the JndiTemplate used for JNDI lookups.
235         */
236        public JndiTemplate getJndiTemplate() {
237                return this.jndiTemplate;
238        }
239
240        /**
241         * Set the JNDI environment to use for JNDI lookups.
242         * Creates a JndiTemplate with the given environment settings.
243         * @see #setJndiTemplate
244         */
245        public void setJndiEnvironment(Properties jndiEnvironment) {
246                this.jndiTemplate = new JndiTemplate(jndiEnvironment);
247        }
248
249        /**
250         * Return the JNDI environment to use for JNDI lookups.
251         */
252        public Properties getJndiEnvironment() {
253                return this.jndiTemplate.getEnvironment();
254        }
255
256
257        /**
258         * Set the JTA UserTransaction to use as direct reference.
259         * <p>Typically just used for local JTA setups; in a Java EE environment,
260         * the UserTransaction will always be fetched from JNDI.
261         * @see #setUserTransactionName
262         * @see #setAutodetectUserTransaction
263         */
264        public void setUserTransaction(UserTransaction userTransaction) {
265                this.userTransaction = userTransaction;
266        }
267
268        /**
269         * Return the JTA UserTransaction that this transaction manager uses.
270         */
271        public UserTransaction getUserTransaction() {
272                return this.userTransaction;
273        }
274
275        /**
276         * Set the JNDI name of the JTA UserTransaction.
277         * <p>Note that the UserTransaction will be autodetected at the Java EE
278         * default location "java:comp/UserTransaction" if not specified explicitly.
279         * @see #DEFAULT_USER_TRANSACTION_NAME
280         * @see #setUserTransaction
281         * @see #setAutodetectUserTransaction
282         */
283        public void setUserTransactionName(String userTransactionName) {
284                this.userTransactionName = userTransactionName;
285        }
286
287        /**
288         * Set whether to autodetect the JTA UserTransaction at its default
289         * JNDI location "java:comp/UserTransaction", as specified by Java EE.
290         * Will proceed without UserTransaction if none found.
291         * <p>Default is "true", autodetecting the UserTransaction unless
292         * it has been specified explicitly. Turn this flag off to allow for
293         * JtaTransactionManager operating against the TransactionManager only,
294         * despite a default UserTransaction being available.
295         * @see #DEFAULT_USER_TRANSACTION_NAME
296         */
297        public void setAutodetectUserTransaction(boolean autodetectUserTransaction) {
298                this.autodetectUserTransaction = autodetectUserTransaction;
299        }
300
301        /**
302         * Set whether to cache the JTA UserTransaction object fetched from JNDI.
303         * <p>Default is "true": UserTransaction lookup will only happen at startup,
304         * reusing the same UserTransaction handle for all transactions of all threads.
305         * This is the most efficient choice for all application servers that provide
306         * a shared UserTransaction object (the typical case).
307         * <p>Turn this flag off to enforce a fresh lookup of the UserTransaction
308         * for every transaction. This is only necessary for application servers
309         * that return a new UserTransaction for every transaction, keeping state
310         * tied to the UserTransaction object itself rather than the current thread.
311         * @see #setUserTransactionName
312         */
313        public void setCacheUserTransaction(boolean cacheUserTransaction) {
314                this.cacheUserTransaction = cacheUserTransaction;
315        }
316
317        /**
318         * Set the JTA TransactionManager to use as direct reference.
319         * <p>A TransactionManager is necessary for suspending and resuming transactions,
320         * as this not supported by the UserTransaction interface.
321         * <p>Note that the TransactionManager will be autodetected if the JTA
322         * UserTransaction object implements the JTA TransactionManager interface too,
323         * as well as autodetected at various well-known fallback JNDI locations.
324         * @see #setTransactionManagerName
325         * @see #setAutodetectTransactionManager
326         */
327        public void setTransactionManager(TransactionManager transactionManager) {
328                this.transactionManager = transactionManager;
329        }
330
331        /**
332         * Return the JTA TransactionManager that this transaction manager uses, if any.
333         */
334        public TransactionManager getTransactionManager() {
335                return this.transactionManager;
336        }
337
338        /**
339         * Set the JNDI name of the JTA TransactionManager.
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 #setTransactionManager
346         * @see #setAutodetectTransactionManager
347         */
348        public void setTransactionManagerName(String transactionManagerName) {
349                this.transactionManagerName = transactionManagerName;
350        }
351
352        /**
353         * Set whether to autodetect a JTA UserTransaction object that implements
354         * the JTA TransactionManager interface too (i.e. the JNDI location for the
355         * TransactionManager is "java:comp/UserTransaction", same as for the UserTransaction).
356         * Also checks the fallback JNDI locations "java:comp/TransactionManager" and
357         * "java:/TransactionManager". Will proceed without TransactionManager if none found.
358         * <p>Default is "true", autodetecting the TransactionManager unless it has been
359         * specified explicitly. Can be turned off to deliberately ignore an available
360         * TransactionManager, for example when there are known issues with suspend/resume
361         * and any attempt to use REQUIRES_NEW or NOT_SUPPORTED should fail fast.
362         * @see #FALLBACK_TRANSACTION_MANAGER_NAMES
363         */
364        public void setAutodetectTransactionManager(boolean autodetectTransactionManager) {
365                this.autodetectTransactionManager = autodetectTransactionManager;
366        }
367
368        /**
369         * Set the JTA 1.1 TransactionSynchronizationRegistry to use as direct reference.
370         * <p>A TransactionSynchronizationRegistry allows for interposed registration
371         * of transaction synchronizations, as an alternative to the regular registration
372         * methods on the JTA TransactionManager API. Also, it is an official part of the
373         * Java EE 5 platform, in contrast to the JTA TransactionManager itself.
374         * <p>Note that the TransactionSynchronizationRegistry will be autodetected in JNDI and
375         * also from the UserTransaction/TransactionManager object if implemented there as well.
376         * @see #setTransactionSynchronizationRegistryName
377         * @see #setAutodetectTransactionSynchronizationRegistry
378         */
379        public void setTransactionSynchronizationRegistry(TransactionSynchronizationRegistry transactionSynchronizationRegistry) {
380                this.transactionSynchronizationRegistry = transactionSynchronizationRegistry;
381        }
382
383        /**
384         * Return the JTA 1.1 TransactionSynchronizationRegistry that this transaction manager uses, if any.
385         */
386        public TransactionSynchronizationRegistry getTransactionSynchronizationRegistry() {
387                return this.transactionSynchronizationRegistry;
388        }
389
390        /**
391         * Set the JNDI name of the JTA 1.1 TransactionSynchronizationRegistry.
392         * <p>Note that the TransactionSynchronizationRegistry will be autodetected
393         * at the Java EE 5 default location "java:comp/TransactionSynchronizationRegistry"
394         * if not specified explicitly.
395         * @see #DEFAULT_TRANSACTION_SYNCHRONIZATION_REGISTRY_NAME
396         */
397        public void setTransactionSynchronizationRegistryName(String transactionSynchronizationRegistryName) {
398                this.transactionSynchronizationRegistryName = transactionSynchronizationRegistryName;
399        }
400
401        /**
402         * Set whether to autodetect a JTA 1.1 TransactionSynchronizationRegistry object
403         * at its default JDNI location ("java:comp/TransactionSynchronizationRegistry")
404         * if the UserTransaction has also been obtained from JNDI, and also whether
405         * to fall back to checking whether the JTA UserTransaction/TransactionManager
406         * object implements the JTA TransactionSynchronizationRegistry interface too.
407         * <p>Default is "true", autodetecting the TransactionSynchronizationRegistry
408         * unless it has been specified explicitly. Can be turned off to delegate
409         * synchronization registration to the regular JTA TransactionManager API.
410         */
411        public void setAutodetectTransactionSynchronizationRegistry(boolean autodetectTransactionSynchronizationRegistry) {
412                this.autodetectTransactionSynchronizationRegistry = autodetectTransactionSynchronizationRegistry;
413        }
414
415        /**
416         * Set whether to allow custom isolation levels to be specified.
417         * <p>Default is "false", throwing an exception if a non-default isolation level
418         * is specified for a transaction. Turn this flag on if affected resource adapters
419         * check the thread-bound transaction context and apply the specified isolation
420         * levels individually (e.g. through an IsolationLevelDataSourceAdapter).
421         * @see org.springframework.jdbc.datasource.IsolationLevelDataSourceAdapter
422         * @see org.springframework.jdbc.datasource.lookup.IsolationLevelDataSourceRouter
423         */
424        public void setAllowCustomIsolationLevels(boolean allowCustomIsolationLevels) {
425                this.allowCustomIsolationLevels = allowCustomIsolationLevels;
426        }
427
428
429        /**
430         * Initialize the UserTransaction as well as the TransactionManager handle.
431         * @see #initUserTransactionAndTransactionManager()
432         */
433        @Override
434        public void afterPropertiesSet() throws TransactionSystemException {
435                initUserTransactionAndTransactionManager();
436                checkUserTransactionAndTransactionManager();
437                initTransactionSynchronizationRegistry();
438        }
439
440        /**
441         * Initialize the UserTransaction as well as the TransactionManager handle.
442         * @throws TransactionSystemException if initialization failed
443         */
444        protected void initUserTransactionAndTransactionManager() throws TransactionSystemException {
445                if (this.userTransaction == null) {
446                        // Fetch JTA UserTransaction from JNDI, if necessary.
447                        if (StringUtils.hasLength(this.userTransactionName)) {
448                                this.userTransaction = lookupUserTransaction(this.userTransactionName);
449                                this.userTransactionObtainedFromJndi = true;
450                        }
451                        else {
452                                this.userTransaction = retrieveUserTransaction();
453                                if (this.userTransaction == null && this.autodetectUserTransaction) {
454                                        // Autodetect UserTransaction at its default JNDI location.
455                                        this.userTransaction = findUserTransaction();
456                                }
457                        }
458                }
459
460                if (this.transactionManager == null) {
461                        // Fetch JTA TransactionManager from JNDI, if necessary.
462                        if (StringUtils.hasLength(this.transactionManagerName)) {
463                                this.transactionManager = lookupTransactionManager(this.transactionManagerName);
464                        }
465                        else {
466                                this.transactionManager = retrieveTransactionManager();
467                                if (this.transactionManager == null && this.autodetectTransactionManager) {
468                                        // Autodetect UserTransaction object that implements TransactionManager,
469                                        // and check fallback JNDI locations otherwise.
470                                        this.transactionManager = findTransactionManager(this.userTransaction);
471                                }
472                        }
473                }
474
475                // If only JTA TransactionManager specified, create UserTransaction handle for it.
476                if (this.userTransaction == null && this.transactionManager != null) {
477                        this.userTransaction = buildUserTransaction(this.transactionManager);
478                }
479        }
480
481        /**
482         * Check the UserTransaction as well as the TransactionManager handle,
483         * assuming standard JTA requirements.
484         * @throws IllegalStateException if no sufficient handles are available
485         */
486        protected void checkUserTransactionAndTransactionManager() throws IllegalStateException {
487                // We at least need the JTA UserTransaction.
488                if (this.userTransaction != null) {
489                        if (logger.isInfoEnabled()) {
490                                logger.info("Using JTA UserTransaction: " + this.userTransaction);
491                        }
492                }
493                else {
494                        throw new IllegalStateException("No JTA UserTransaction available - specify either " +
495                                        "'userTransaction' or 'userTransactionName' or 'transactionManager' or 'transactionManagerName'");
496                }
497
498                // For transaction suspension, the JTA TransactionManager is necessary too.
499                if (this.transactionManager != null) {
500                        if (logger.isInfoEnabled()) {
501                                logger.info("Using JTA TransactionManager: " + this.transactionManager);
502                        }
503                }
504                else {
505                        logger.warn("No JTA TransactionManager found: transaction suspension not available");
506                }
507        }
508
509        /**
510         * Initialize the JTA 1.1 TransactionSynchronizationRegistry, if available.
511         * <p>To be called after {@link #initUserTransactionAndTransactionManager()},
512         * since it may check the UserTransaction and TransactionManager handles.
513         * @throws TransactionSystemException if initialization failed
514         */
515        protected void initTransactionSynchronizationRegistry() {
516                if (this.transactionSynchronizationRegistry == null) {
517                        // Fetch JTA TransactionSynchronizationRegistry from JNDI, if necessary.
518                        if (StringUtils.hasLength(this.transactionSynchronizationRegistryName)) {
519                                this.transactionSynchronizationRegistry =
520                                                lookupTransactionSynchronizationRegistry(this.transactionSynchronizationRegistryName);
521                        }
522                        else {
523                                this.transactionSynchronizationRegistry = retrieveTransactionSynchronizationRegistry();
524                                if (this.transactionSynchronizationRegistry == null && this.autodetectTransactionSynchronizationRegistry) {
525                                        // Autodetect in JNDI if applicable, and check UserTransaction/TransactionManager
526                                        // object that implements TransactionSynchronizationRegistry otherwise.
527                                        this.transactionSynchronizationRegistry =
528                                                        findTransactionSynchronizationRegistry(this.userTransaction, this.transactionManager);
529                                }
530                        }
531                }
532
533                if (this.transactionSynchronizationRegistry != null) {
534                        if (logger.isInfoEnabled()) {
535                                logger.info("Using JTA TransactionSynchronizationRegistry: " + this.transactionSynchronizationRegistry);
536                        }
537                }
538        }
539
540
541        /**
542         * Build a UserTransaction handle based on the given TransactionManager.
543         * @param transactionManager the TransactionManager
544         * @return a corresponding UserTransaction handle
545         */
546        protected UserTransaction buildUserTransaction(TransactionManager transactionManager) {
547                if (transactionManager instanceof UserTransaction) {
548                        return (UserTransaction) transactionManager;
549                }
550                else {
551                        return new UserTransactionAdapter(transactionManager);
552                }
553        }
554
555        /**
556         * Look up the JTA UserTransaction in JNDI via the configured name.
557         * <p>Called by {@code afterPropertiesSet} if no direct UserTransaction reference was set.
558         * Can be overridden in subclasses to provide a different UserTransaction object.
559         * @param userTransactionName the JNDI name of the UserTransaction
560         * @return the UserTransaction object
561         * @throws TransactionSystemException if the JNDI lookup failed
562         * @see #setJndiTemplate
563         * @see #setUserTransactionName
564         */
565        protected UserTransaction lookupUserTransaction(String userTransactionName)
566                        throws TransactionSystemException {
567                try {
568                        if (logger.isDebugEnabled()) {
569                                logger.debug("Retrieving JTA UserTransaction from JNDI location [" + userTransactionName + "]");
570                        }
571                        return getJndiTemplate().lookup(userTransactionName, UserTransaction.class);
572                }
573                catch (NamingException ex) {
574                        throw new TransactionSystemException(
575                                        "JTA UserTransaction is not available at JNDI location [" + userTransactionName + "]", ex);
576                }
577        }
578
579        /**
580         * Look up the JTA TransactionManager in JNDI via the configured name.
581         * <p>Called by {@code afterPropertiesSet} if no direct TransactionManager reference was set.
582         * Can be overridden in subclasses to provide a different TransactionManager object.
583         * @param transactionManagerName the JNDI name of the TransactionManager
584         * @return the UserTransaction object
585         * @throws TransactionSystemException if the JNDI lookup failed
586         * @see #setJndiTemplate
587         * @see #setTransactionManagerName
588         */
589        protected TransactionManager lookupTransactionManager(String transactionManagerName)
590                        throws TransactionSystemException {
591                try {
592                        if (logger.isDebugEnabled()) {
593                                logger.debug("Retrieving JTA TransactionManager from JNDI location [" + transactionManagerName + "]");
594                        }
595                        return getJndiTemplate().lookup(transactionManagerName, TransactionManager.class);
596                }
597                catch (NamingException ex) {
598                        throw new TransactionSystemException(
599                                        "JTA TransactionManager is not available at JNDI location [" + transactionManagerName + "]", ex);
600                }
601        }
602
603        /**
604         * Look up the JTA 1.1 TransactionSynchronizationRegistry in JNDI via the configured name.
605         * <p>Can be overridden in subclasses to provide a different TransactionManager object.
606         * @param registryName the JNDI name of the
607         * TransactionSynchronizationRegistry
608         * @return the TransactionSynchronizationRegistry object
609         * @throws TransactionSystemException if the JNDI lookup failed
610         * @see #setJndiTemplate
611         * @see #setTransactionSynchronizationRegistryName
612         */
613        protected TransactionSynchronizationRegistry lookupTransactionSynchronizationRegistry(String registryName) throws TransactionSystemException {
614                try {
615                        if (logger.isDebugEnabled()) {
616                                logger.debug("Retrieving JTA TransactionSynchronizationRegistry from JNDI location [" + registryName + "]");
617                        }
618                        return getJndiTemplate().lookup(registryName, TransactionSynchronizationRegistry.class);
619                }
620                catch (NamingException ex) {
621                        throw new TransactionSystemException(
622                                        "JTA TransactionSynchronizationRegistry is not available at JNDI location [" + registryName + "]", ex);
623                }
624        }
625
626        /**
627         * Allows subclasses to retrieve the JTA UserTransaction in a vendor-specific manner.
628         * Only called if no "userTransaction" or "userTransactionName" specified.
629         * <p>The default implementation simply returns {@code null}.
630         * @return the JTA UserTransaction handle to use, or {@code null} if none found
631         * @throws TransactionSystemException in case of errors
632         * @see #setUserTransaction
633         * @see #setUserTransactionName
634         */
635        protected UserTransaction retrieveUserTransaction() throws TransactionSystemException {
636                return null;
637        }
638
639        /**
640         * Allows subclasses to retrieve the JTA TransactionManager in a vendor-specific manner.
641         * Only called if no "transactionManager" or "transactionManagerName" specified.
642         * <p>The default implementation simply returns {@code null}.
643         * @return the JTA TransactionManager handle to use, or {@code null} if none found
644         * @throws TransactionSystemException in case of errors
645         * @see #setTransactionManager
646         * @see #setTransactionManagerName
647         */
648        protected TransactionManager retrieveTransactionManager() throws TransactionSystemException {
649                return null;
650        }
651
652        /**
653         * Allows subclasses to retrieve the JTA 1.1 TransactionSynchronizationRegistry
654         * in a vendor-specific manner.
655         * <p>The default implementation simply returns {@code null}.
656         * @return the JTA TransactionSynchronizationRegistry handle to use,
657         * or {@code null} if none found
658         * @throws TransactionSystemException in case of errors
659         */
660        protected TransactionSynchronizationRegistry retrieveTransactionSynchronizationRegistry() throws TransactionSystemException {
661                return null;
662        }
663
664        /**
665         * Find the JTA UserTransaction through a default JNDI lookup:
666         * "java:comp/UserTransaction".
667         * @return the JTA UserTransaction reference, or {@code null} if not found
668         * @see #DEFAULT_USER_TRANSACTION_NAME
669         */
670        protected UserTransaction findUserTransaction() {
671                String jndiName = DEFAULT_USER_TRANSACTION_NAME;
672                try {
673                        UserTransaction ut = getJndiTemplate().lookup(jndiName, UserTransaction.class);
674                        if (logger.isDebugEnabled()) {
675                                logger.debug("JTA UserTransaction found at default JNDI location [" + jndiName + "]");
676                        }
677                        this.userTransactionObtainedFromJndi = true;
678                        return ut;
679                }
680                catch (NamingException ex) {
681                        if (logger.isDebugEnabled()) {
682                                logger.debug("No JTA UserTransaction found at default JNDI location [" + jndiName + "]", ex);
683                        }
684                        return null;
685                }
686        }
687
688        /**
689         * Find the JTA TransactionManager through autodetection: checking whether the
690         * UserTransaction object implements the TransactionManager, and checking the
691         * fallback JNDI locations.
692         * @param ut the JTA UserTransaction object
693         * @return the JTA TransactionManager reference, or {@code null} if not found
694         * @see #FALLBACK_TRANSACTION_MANAGER_NAMES
695         */
696        protected TransactionManager findTransactionManager(UserTransaction ut) {
697                if (ut instanceof TransactionManager) {
698                        if (logger.isDebugEnabled()) {
699                                logger.debug("JTA UserTransaction object [" + ut + "] implements TransactionManager");
700                        }
701                        return (TransactionManager) ut;
702                }
703
704                // Check fallback JNDI locations.
705                for (String jndiName : FALLBACK_TRANSACTION_MANAGER_NAMES) {
706                        try {
707                                TransactionManager tm = getJndiTemplate().lookup(jndiName, TransactionManager.class);
708                                if (logger.isDebugEnabled()) {
709                                        logger.debug("JTA TransactionManager found at fallback JNDI location [" + jndiName + "]");
710                                }
711                                return tm;
712                        }
713                        catch (NamingException ex) {
714                                if (logger.isDebugEnabled()) {
715                                        logger.debug("No JTA TransactionManager found at fallback JNDI location [" + jndiName + "]", ex);
716                                }
717                        }
718                }
719
720                // OK, so no JTA TransactionManager is available...
721                return null;
722        }
723
724        /**
725         * Find the JTA 1.1 TransactionSynchronizationRegistry through autodetection:
726         * checking whether the UserTransaction object or TransactionManager object
727         * implements it, and checking Java EE 5's standard JNDI location.
728         * <p>The default implementation simply returns {@code null}.
729         * @param ut the JTA UserTransaction object
730         * @param tm the JTA TransactionManager object
731         * @return the JTA TransactionSynchronizationRegistry handle to use,
732         * or {@code null} if none found
733         * @throws TransactionSystemException in case of errors
734         */
735        protected TransactionSynchronizationRegistry findTransactionSynchronizationRegistry(UserTransaction ut, TransactionManager tm)
736                        throws TransactionSystemException {
737
738                if (this.userTransactionObtainedFromJndi) {
739                        // UserTransaction has already been obtained from JNDI, so the
740                        // TransactionSynchronizationRegistry probably sits there as well.
741                        String jndiName = DEFAULT_TRANSACTION_SYNCHRONIZATION_REGISTRY_NAME;
742                        try {
743                                TransactionSynchronizationRegistry tsr = getJndiTemplate().lookup(jndiName, TransactionSynchronizationRegistry.class);
744                                if (logger.isDebugEnabled()) {
745                                        logger.debug("JTA TransactionSynchronizationRegistry found at default JNDI location [" + jndiName + "]");
746                                }
747                                return tsr;
748                        }
749                        catch (NamingException ex) {
750                                if (logger.isDebugEnabled()) {
751                                        logger.debug("No JTA TransactionSynchronizationRegistry found at default JNDI location [" + jndiName + "]", ex);
752                                }
753                        }
754                }
755                // Check whether the UserTransaction or TransactionManager implements it...
756                if (ut instanceof TransactionSynchronizationRegistry) {
757                        return (TransactionSynchronizationRegistry) ut;
758                }
759                if (tm instanceof TransactionSynchronizationRegistry) {
760                        return (TransactionSynchronizationRegistry) tm;
761                }
762                // OK, so no JTA 1.1 TransactionSynchronizationRegistry is available...
763                return null;
764        }
765
766
767        /**
768         * This implementation returns a JtaTransactionObject instance for the
769         * JTA UserTransaction.
770         * <p>The UserTransaction object will either be looked up freshly for the
771         * current transaction, or the cached one looked up at startup will be used.
772         * The latter is the default: Most application servers use a shared singleton
773         * UserTransaction that can be cached. Turn off the "cacheUserTransaction"
774         * flag to enforce a fresh lookup for every transaction.
775         * @see #setCacheUserTransaction
776         */
777        @Override
778        protected Object doGetTransaction() {
779                UserTransaction ut = getUserTransaction();
780                if (ut == null) {
781                        throw new CannotCreateTransactionException("No JTA UserTransaction available - " +
782                                        "programmatic PlatformTransactionManager.getTransaction usage not supported");
783                }
784                if (!this.cacheUserTransaction) {
785                        ut = lookupUserTransaction(
786                                        this.userTransactionName != null ? this.userTransactionName : DEFAULT_USER_TRANSACTION_NAME);
787                }
788                return doGetJtaTransaction(ut);
789        }
790
791        /**
792         * Get a JTA transaction object for the given current UserTransaction.
793         * <p>Subclasses can override this to provide a JtaTransactionObject
794         * subclass, for example holding some additional JTA handle needed.
795         * @param ut the UserTransaction handle to use for the current transaction
796         * @return the JtaTransactionObject holding the UserTransaction
797         */
798        protected JtaTransactionObject doGetJtaTransaction(UserTransaction ut) {
799                return new JtaTransactionObject(ut);
800        }
801
802        @Override
803        protected boolean isExistingTransaction(Object transaction) {
804                JtaTransactionObject txObject = (JtaTransactionObject) transaction;
805                try {
806                        return (txObject.getUserTransaction().getStatus() != Status.STATUS_NO_TRANSACTION);
807                }
808                catch (SystemException ex) {
809                        throw new TransactionSystemException("JTA failure on getStatus", ex);
810                }
811        }
812
813        /**
814         * This implementation returns false to cause a further invocation
815         * of doBegin despite an already existing transaction.
816         * <p>JTA implementations might support nested transactions via further
817         * {@code UserTransaction.begin()} invocations, but never support savepoints.
818         * @see #doBegin
819         * @see javax.transaction.UserTransaction#begin()
820         */
821        @Override
822        protected boolean useSavepointForNestedTransaction() {
823                return false;
824        }
825
826
827        @Override
828        protected void doBegin(Object transaction, TransactionDefinition definition) {
829                JtaTransactionObject txObject = (JtaTransactionObject) transaction;
830                try {
831                        doJtaBegin(txObject, definition);
832                }
833                catch (NotSupportedException ex) {
834                        throw new NestedTransactionNotSupportedException(
835                                        "JTA implementation does not support nested transactions", ex);
836                }
837                catch (UnsupportedOperationException ex) {
838                        throw new NestedTransactionNotSupportedException(
839                                        "JTA implementation does not support nested transactions", ex);
840                }
841                catch (SystemException ex) {
842                        throw new CannotCreateTransactionException("JTA failure on begin", ex);
843                }
844        }
845
846        /**
847         * Perform a JTA begin on the JTA UserTransaction or TransactionManager.
848         * <p>This implementation only supports standard JTA functionality:
849         * that is, no per-transaction isolation levels and no transaction names.
850         * Can be overridden in subclasses, for specific JTA implementations.
851         * <p>Calls {@code applyIsolationLevel} and {@code applyTimeout}
852         * before invoking the UserTransaction's {@code begin} method.
853         * @param txObject the JtaTransactionObject containing the UserTransaction
854         * @param definition TransactionDefinition instance, describing propagation
855         * behavior, isolation level, read-only flag, timeout, and transaction name
856         * @throws NotSupportedException if thrown by JTA methods
857         * @throws SystemException if thrown by JTA methods
858         * @see #getUserTransaction
859         * @see #getTransactionManager
860         * @see #applyIsolationLevel
861         * @see #applyTimeout
862         * @see JtaTransactionObject#getUserTransaction()
863         * @see javax.transaction.UserTransaction#setTransactionTimeout
864         * @see javax.transaction.UserTransaction#begin
865         */
866        protected void doJtaBegin(JtaTransactionObject txObject, TransactionDefinition definition)
867                        throws NotSupportedException, SystemException {
868
869                applyIsolationLevel(txObject, definition.getIsolationLevel());
870                int timeout = determineTimeout(definition);
871                applyTimeout(txObject, timeout);
872                txObject.getUserTransaction().begin();
873        }
874
875        /**
876         * Apply the given transaction isolation level. The default implementation
877         * will throw an exception for any level other than ISOLATION_DEFAULT.
878         * <p>To be overridden in subclasses for specific JTA implementations,
879         * as alternative to overriding the full {@link #doJtaBegin} method.
880         * @param txObject the JtaTransactionObject containing the UserTransaction
881         * @param isolationLevel isolation level taken from transaction definition
882         * @throws InvalidIsolationLevelException if the given isolation level
883         * cannot be applied
884         * @throws SystemException if thrown by the JTA implementation
885         * @see #doJtaBegin
886         * @see JtaTransactionObject#getUserTransaction()
887         * @see #getTransactionManager()
888         */
889        protected void applyIsolationLevel(JtaTransactionObject txObject, int isolationLevel)
890                        throws InvalidIsolationLevelException, SystemException {
891
892                if (!this.allowCustomIsolationLevels && isolationLevel != TransactionDefinition.ISOLATION_DEFAULT) {
893                        throw new InvalidIsolationLevelException(
894                                        "JtaTransactionManager does not support custom isolation levels by default - " +
895                                        "switch 'allowCustomIsolationLevels' to 'true'");
896                }
897        }
898
899        /**
900         * Apply the given transaction timeout. The default implementation will call
901         * {@code UserTransaction.setTransactionTimeout} for a non-default timeout value.
902         * @param txObject the JtaTransactionObject containing the UserTransaction
903         * @param timeout timeout value taken from transaction definition
904         * @throws SystemException if thrown by the JTA implementation
905         * @see #doJtaBegin
906         * @see JtaTransactionObject#getUserTransaction()
907         * @see javax.transaction.UserTransaction#setTransactionTimeout(int)
908         */
909        protected void applyTimeout(JtaTransactionObject txObject, int timeout) throws SystemException {
910                if (timeout > TransactionDefinition.TIMEOUT_DEFAULT) {
911                        txObject.getUserTransaction().setTransactionTimeout(timeout);
912                        if (timeout > 0) {
913                                txObject.resetTransactionTimeout = true;
914                        }
915                }
916        }
917
918
919        @Override
920        protected Object doSuspend(Object transaction) {
921                JtaTransactionObject txObject = (JtaTransactionObject) transaction;
922                try {
923                        return doJtaSuspend(txObject);
924                }
925                catch (SystemException ex) {
926                        throw new TransactionSystemException("JTA failure on suspend", ex);
927                }
928        }
929
930        /**
931         * Perform a JTA suspend on the JTA TransactionManager.
932         * <p>Can be overridden in subclasses, for specific JTA implementations.
933         * @param txObject the JtaTransactionObject containing the UserTransaction
934         * @return the suspended JTA Transaction object
935         * @throws SystemException if thrown by JTA methods
936         * @see #getTransactionManager()
937         * @see javax.transaction.TransactionManager#suspend()
938         */
939        protected Object doJtaSuspend(JtaTransactionObject txObject) throws SystemException {
940                if (getTransactionManager() == null) {
941                        throw new TransactionSuspensionNotSupportedException(
942                                        "JtaTransactionManager needs a JTA TransactionManager for suspending a transaction: " +
943                                        "specify the 'transactionManager' or 'transactionManagerName' property");
944                }
945                return getTransactionManager().suspend();
946        }
947
948        @Override
949        protected void doResume(Object transaction, Object suspendedResources) {
950                JtaTransactionObject txObject = (JtaTransactionObject) transaction;
951                try {
952                        doJtaResume(txObject, suspendedResources);
953                }
954                catch (InvalidTransactionException ex) {
955                        throw new IllegalTransactionStateException("Tried to resume invalid JTA transaction", ex);
956                }
957                catch (IllegalStateException ex) {
958                        throw new TransactionSystemException("Unexpected internal transaction state", ex);
959                }
960                catch (SystemException ex) {
961                        throw new TransactionSystemException("JTA failure on resume", ex);
962                }
963        }
964
965        /**
966         * Perform a JTA resume on the JTA TransactionManager.
967         * <p>Can be overridden in subclasses, for specific JTA implementations.
968         * @param txObject the JtaTransactionObject containing the UserTransaction
969         * @param suspendedTransaction the suspended JTA Transaction object
970         * @throws InvalidTransactionException if thrown by JTA methods
971         * @throws SystemException if thrown by JTA methods
972         * @see #getTransactionManager()
973         * @see javax.transaction.TransactionManager#resume(javax.transaction.Transaction)
974         */
975        protected void doJtaResume(JtaTransactionObject txObject, Object suspendedTransaction)
976                throws InvalidTransactionException, SystemException {
977
978                if (getTransactionManager() == null) {
979                        throw new TransactionSuspensionNotSupportedException(
980                                        "JtaTransactionManager needs a JTA TransactionManager for suspending a transaction: " +
981                                        "specify the 'transactionManager' or 'transactionManagerName' property");
982                }
983                getTransactionManager().resume((Transaction) suspendedTransaction);
984        }
985
986
987        /**
988         * This implementation returns "true": a JTA commit will properly handle
989         * transactions that have been marked rollback-only at a global level.
990         */
991        @Override
992        protected boolean shouldCommitOnGlobalRollbackOnly() {
993                return true;
994        }
995
996        @Override
997        protected void doCommit(DefaultTransactionStatus status) {
998                JtaTransactionObject txObject = (JtaTransactionObject) status.getTransaction();
999                try {
1000                        int jtaStatus = txObject.getUserTransaction().getStatus();
1001                        if (jtaStatus == Status.STATUS_NO_TRANSACTION) {
1002                                // Should never happen... would have thrown an exception before
1003                                // and as a consequence led to a rollback, not to a commit call.
1004                                // In any case, the transaction is already fully cleaned up.
1005                                throw new UnexpectedRollbackException("JTA transaction already completed - probably rolled back");
1006                        }
1007                        if (jtaStatus == Status.STATUS_ROLLEDBACK) {
1008                                // Only really happens on JBoss 4.2 in case of an early timeout...
1009                                // Explicit rollback call necessary to clean up the transaction.
1010                                // IllegalStateException expected on JBoss; call still necessary.
1011                                try {
1012                                        txObject.getUserTransaction().rollback();
1013                                }
1014                                catch (IllegalStateException ex) {
1015                                        if (logger.isDebugEnabled()) {
1016                                                logger.debug("Rollback failure with transaction already marked as rolled back: " + ex);
1017                                        }
1018                                }
1019                                throw new UnexpectedRollbackException("JTA transaction already rolled back (probably due to a timeout)");
1020                        }
1021                        txObject.getUserTransaction().commit();
1022                }
1023                catch (RollbackException ex) {
1024                        throw new UnexpectedRollbackException(
1025                                        "JTA transaction unexpectedly rolled back (maybe due to a timeout)", ex);
1026                }
1027                catch (HeuristicMixedException ex) {
1028                        throw new HeuristicCompletionException(HeuristicCompletionException.STATE_MIXED, ex);
1029                }
1030                catch (HeuristicRollbackException ex) {
1031                        throw new HeuristicCompletionException(HeuristicCompletionException.STATE_ROLLED_BACK, ex);
1032                }
1033                catch (IllegalStateException ex) {
1034                        throw new TransactionSystemException("Unexpected internal transaction state", ex);
1035                }
1036                catch (SystemException ex) {
1037                        throw new TransactionSystemException("JTA failure on commit", ex);
1038                }
1039        }
1040
1041        @Override
1042        protected void doRollback(DefaultTransactionStatus status) {
1043                JtaTransactionObject txObject = (JtaTransactionObject) status.getTransaction();
1044                try {
1045                        int jtaStatus = txObject.getUserTransaction().getStatus();
1046                        if (jtaStatus != Status.STATUS_NO_TRANSACTION) {
1047                                try {
1048                                        txObject.getUserTransaction().rollback();
1049                                }
1050                                catch (IllegalStateException ex) {
1051                                        if (jtaStatus == Status.STATUS_ROLLEDBACK) {
1052                                                // Only really happens on JBoss 4.2 in case of an early timeout...
1053                                                if (logger.isDebugEnabled()) {
1054                                                        logger.debug("Rollback failure with transaction already marked as rolled back: " + ex);
1055                                                }
1056                                        }
1057                                        else {
1058                                                throw new TransactionSystemException("Unexpected internal transaction state", ex);
1059                                        }
1060                                }
1061                        }
1062                }
1063                catch (SystemException ex) {
1064                        throw new TransactionSystemException("JTA failure on rollback", ex);
1065                }
1066        }
1067
1068        @Override
1069        protected void doSetRollbackOnly(DefaultTransactionStatus status) {
1070                JtaTransactionObject txObject = (JtaTransactionObject) status.getTransaction();
1071                if (status.isDebug()) {
1072                        logger.debug("Setting JTA transaction rollback-only");
1073                }
1074                try {
1075                        int jtaStatus = txObject.getUserTransaction().getStatus();
1076                        if (jtaStatus != Status.STATUS_NO_TRANSACTION && jtaStatus != Status.STATUS_ROLLEDBACK) {
1077                                txObject.getUserTransaction().setRollbackOnly();
1078                        }
1079                }
1080                catch (IllegalStateException ex) {
1081                        throw new TransactionSystemException("Unexpected internal transaction state", ex);
1082                }
1083                catch (SystemException ex) {
1084                        throw new TransactionSystemException("JTA failure on setRollbackOnly", ex);
1085                }
1086        }
1087
1088
1089        @Override
1090        protected void registerAfterCompletionWithExistingTransaction(
1091                        Object transaction, List<TransactionSynchronization> synchronizations) {
1092
1093                JtaTransactionObject txObject = (JtaTransactionObject) transaction;
1094                logger.debug("Registering after-completion synchronization with existing JTA transaction");
1095                try {
1096                        doRegisterAfterCompletionWithJtaTransaction(txObject, synchronizations);
1097                }
1098                catch (SystemException ex) {
1099                        throw new TransactionSystemException("JTA failure on registerSynchronization", ex);
1100                }
1101                catch (Exception ex) {
1102                        // Note: JBoss throws plain RuntimeException with RollbackException as cause.
1103                        if (ex instanceof RollbackException || ex.getCause() instanceof RollbackException) {
1104                                logger.debug("Participating in existing JTA transaction that has been marked for rollback: " +
1105                                                "cannot register Spring after-completion callbacks with outer JTA transaction - " +
1106                                                "immediately performing Spring after-completion callbacks with outcome status 'rollback'. " +
1107                                                "Original exception: " + ex);
1108                                invokeAfterCompletion(synchronizations, TransactionSynchronization.STATUS_ROLLED_BACK);
1109                        }
1110                        else {
1111                                logger.debug("Participating in existing JTA transaction, but unexpected internal transaction " +
1112                                                "state encountered: cannot register Spring after-completion callbacks with outer JTA " +
1113                                                "transaction - processing Spring after-completion callbacks with outcome status 'unknown'" +
1114                                                "Original exception: " + ex);
1115                                invokeAfterCompletion(synchronizations, TransactionSynchronization.STATUS_UNKNOWN);
1116                        }
1117                }
1118        }
1119
1120        /**
1121         * Register a JTA synchronization on the JTA TransactionManager, for calling
1122         * {@code afterCompletion} on the given Spring TransactionSynchronizations.
1123         * <p>The default implementation registers the synchronizations on the
1124         * JTA 1.1 TransactionSynchronizationRegistry, if available, or on the
1125         * JTA TransactionManager's current Transaction - again, if available.
1126         * If none of the two is available, a warning will be logged.
1127         * <p>Can be overridden in subclasses, for specific JTA implementations.
1128         * @param txObject the current transaction object
1129         * @param synchronizations List of TransactionSynchronization objects
1130         * @throws RollbackException if thrown by JTA methods
1131         * @throws SystemException if thrown by JTA methods
1132         * @see #getTransactionManager()
1133         * @see javax.transaction.Transaction#registerSynchronization
1134         * @see javax.transaction.TransactionSynchronizationRegistry#registerInterposedSynchronization
1135         */
1136        protected void doRegisterAfterCompletionWithJtaTransaction(
1137                        JtaTransactionObject txObject, List<TransactionSynchronization> synchronizations)
1138                        throws RollbackException, SystemException {
1139
1140                int jtaStatus = txObject.getUserTransaction().getStatus();
1141                if (jtaStatus == Status.STATUS_NO_TRANSACTION) {
1142                        throw new RollbackException("JTA transaction already completed - probably rolled back");
1143                }
1144                if (jtaStatus == Status.STATUS_ROLLEDBACK) {
1145                        throw new RollbackException("JTA transaction already rolled back (probably due to a timeout)");
1146                }
1147
1148                if (this.transactionSynchronizationRegistry != null) {
1149                        // JTA 1.1 TransactionSynchronizationRegistry available - use it.
1150                        this.transactionSynchronizationRegistry.registerInterposedSynchronization(
1151                                        new JtaAfterCompletionSynchronization(synchronizations));
1152                }
1153
1154                else if (getTransactionManager() != null) {
1155                        // At least the JTA TransactionManager available - use that one.
1156                        Transaction transaction = getTransactionManager().getTransaction();
1157                        if (transaction == null) {
1158                                throw new IllegalStateException("No JTA Transaction available");
1159                        }
1160                        transaction.registerSynchronization(new JtaAfterCompletionSynchronization(synchronizations));
1161                }
1162
1163                else {
1164                        // No JTA TransactionManager available - log a warning.
1165                        logger.warn("Participating in existing JTA transaction, but no JTA TransactionManager available: " +
1166                                        "cannot register Spring after-completion callbacks with outer JTA transaction - " +
1167                                        "processing Spring after-completion callbacks with outcome status 'unknown'");
1168                        invokeAfterCompletion(synchronizations, TransactionSynchronization.STATUS_UNKNOWN);
1169                }
1170        }
1171
1172        @Override
1173        protected void doCleanupAfterCompletion(Object transaction) {
1174                JtaTransactionObject txObject = (JtaTransactionObject) transaction;
1175                if (txObject.resetTransactionTimeout) {
1176                        try {
1177                                txObject.getUserTransaction().setTransactionTimeout(0);
1178                        }
1179                        catch (SystemException ex) {
1180                                logger.debug("Failed to reset transaction timeout after JTA completion", ex);
1181                        }
1182                }
1183        }
1184
1185
1186        //---------------------------------------------------------------------
1187        // Implementation of TransactionFactory interface
1188        //---------------------------------------------------------------------
1189
1190        @Override
1191        public Transaction createTransaction(String name, int timeout) throws NotSupportedException, SystemException {
1192                TransactionManager tm = getTransactionManager();
1193                Assert.state(tm != null, "No JTA TransactionManager available");
1194                if (timeout >= 0) {
1195                        tm.setTransactionTimeout(timeout);
1196                }
1197                tm.begin();
1198                return new ManagedTransactionAdapter(tm);
1199        }
1200
1201        @Override
1202        public boolean supportsResourceAdapterManagedTransactions() {
1203                return false;
1204        }
1205
1206
1207        //---------------------------------------------------------------------
1208        // Serialization support
1209        //---------------------------------------------------------------------
1210
1211        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
1212                // Rely on default serialization; just initialize state after deserialization.
1213                ois.defaultReadObject();
1214
1215                // Create template for client-side JNDI lookup.
1216                this.jndiTemplate = new JndiTemplate();
1217
1218                // Perform a fresh lookup for JTA handles.
1219                initUserTransactionAndTransactionManager();
1220                initTransactionSynchronizationRegistry();
1221        }
1222
1223}