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