All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.transaction.jta.JtaTransactionManager Maven / Gradle / Ivy

There is a newer version: 5.3.34
Show newest version
/*
 * Copyright 2002-2007 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.transaction.jta;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.List;
import java.util.Properties;

import javax.naming.NamingException;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.InvalidTransactionException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.jndi.JndiTemplate;
import org.springframework.transaction.CannotCreateTransactionException;
import org.springframework.transaction.HeuristicCompletionException;
import org.springframework.transaction.IllegalTransactionStateException;
import org.springframework.transaction.InvalidIsolationLevelException;
import org.springframework.transaction.NestedTransactionNotSupportedException;
import org.springframework.transaction.NoTransactionException;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionSuspensionNotSupportedException;
import org.springframework.transaction.TransactionSystemException;
import org.springframework.transaction.UnexpectedRollbackException;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
import org.springframework.transaction.support.DefaultTransactionStatus;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.util.Assert;

/**
 * {@link org.springframework.transaction.PlatformTransactionManager} implementation
 * for JTA, delegating to a backend JTA provider. This is typically used to delegate
 * to a J2EE server's transaction coordinator, but may also be configured with a
 * local JTA provider which is embedded within the application.
 *
 * 

This transaction manager is appropriate for handling distributed transactions, * i.e. transactions that span multiple resources, and for controlling transactions on * application server resources (e.g. JDBC DataSources available in JNDI) in general. * For a single JDBC DataSource, DataSourceTransactionManager is perfectly sufficient, * and for accessing a single resource with Hibernate (including transactional cache), * HibernateTransactionManager is appropriate, for example. * *

Transaction synchronization is active by default, to allow data access support * classes to register resources that are opened within the transaction for closing at * transaction completion time. Spring's support classes for JDBC, Hibernate, JDO etc * all perform such registration, allowing for reuse of the same Hibernate Session etc * within the transaction. Standard JTA does not even guarantee that for Connections * from a transactional JDBC DataSource: Spring's synchronization solves those issues. * *

For typical JTA transactions (REQUIRED, SUPPORTS, MANDATORY, NEVER), a plain * JtaTransactionManager definition is all you need, completely portable across all * J2EE servers. This corresponds to the functionality of the JTA UserTransaction, * for which J2EE specifies a standard JNDI name ("java:comp/UserTransaction"). * There is no need to configure a server-specific TransactionManager lookup for this * kind of JTA usage. * *

Note: Advanced JTA usage below. Dealing with these mechanisms is not * necessary for typical usage scenarios. * *

Transaction suspension (REQUIRES_NEW, NOT_SUPPORTED) is just available with * a JTA TransactionManager being registered, via the "transactionManagerName" or * "transactionManager" property. The location of this well-defined JTA object is * not specified by J2EE; it is specific to each J2EE server, often kept * in JNDI like the JTA UserTransaction. Some well-known JNDI locations are: *

    *
  • "java:comp/UserTransaction" for Resin 2.x, Oracle OC4J (Orion), * JOnAS (JOTM), BEA WebLogic *
  • "java:comp/TransactionManager" for Resin 3.x *
  • "java:pm/TransactionManager" for Borland Enterprise Server and * Sun Application Server (Sun ONE 7 and later) *
  • "java:/TransactionManager" for JBoss Application Server *
* *

All of these cases are autodetected by JtaTransactionManager (since Spring 1.2), * provided that the "autodetectTransactionManager" flag is set to "true" (which it is * by default). Consequently, JtaTransactionManager will support transaction suspension * out-of-the-box on many J2EE servers. * *

A JNDI lookup can also be factored out into a corresponding * {@link org.springframework.jndi.JndiObjectFactoryBean}, passed into * JtaTransactionManager's "transactionManager" property. Such a bean definition * can then be reused by other objects, for example Spring's LocalSessionFactoryBean * for Hibernate. * *

For IBM WebSphere and standalone JOTM, static accessor methods are required to * obtain the JTA TransactionManager: Therefore, WebSphere and JOTM have their own * FactoryBean implementations, to be wired with the "transactionManager" property. * In case of {@link JotmFactoryBean}, the same JTA object implements UserTransaction * too: Therefore, passing the object to the "userTransaction" property is sufficient. * *

It is also possible to specify a JTA TransactionManager only, either through * the corresponding constructor or through the "transactionManager" property. * In the latter case, the "userTransactionName" property needs to be set to null, * to suppress the default "java:comp/UserTransaction" JNDI lookup and thus enforce * use of the given JTA TransactionManager even for begin, commit and rollback. * *

Note: Support for the JTA TransactionManager interface is not required by J2EE. * Almost all J2EE servers expose it, but do so as extension to J2EE. There might be some * issues with compatibility, despite the TransactionManager interface being part of JTA. * The only currently known problem is resuming a transaction on WebLogic, which by default * fails if the suspended transaction was marked rollback-only; for other usages, it works * properly. Use Spring's {@link WebLogicJtaTransactionManager} to address this issue. * *

This standard JtaTransactionManager supports timeouts but not per-transaction * isolation levels. Custom subclasses may override {@link #doJtaBegin} for * specific JTA extensions in order to provide this functionality; Spring includes * corresponding {@link WebLogicJtaTransactionManager} and {@link OC4JJtaTransactionManager} * classes, for BEA's WebLogic Server and Oracle's OC4J, respectively. Such adapters * for specific J2EE transaction coordinators may also expose transaction names for * monitoring (both of the above do); with standard JTA, transaction names will simply * be ignored. * *

This class is serializable. However, active synchronizations do not survive * serialization. * * @author Juergen Hoeller * @since 24.03.2003 * @see #setUserTransactionName * @see #setUserTransaction * @see #setTransactionManagerName * @see #setTransactionManager * @see #doJtaBegin * @see JotmFactoryBean * @see WebSphereTransactionManagerFactoryBean * @see WebLogicJtaTransactionManager * @see org.springframework.jndi.JndiObjectFactoryBean * @see org.springframework.orm.hibernate.LocalSessionFactoryBean#setJtaTransactionManager */ public class JtaTransactionManager extends AbstractPlatformTransactionManager implements InitializingBean, Serializable { /** * Default JNDI location for the JTA UserTransaction. Many J2EE servers * also provide support for the JTA TransactionManager interface there. * @see #setUserTransactionName * @see #setAutodetectTransactionManager */ public static final String DEFAULT_USER_TRANSACTION_NAME = "java:comp/UserTransaction"; /** * Fallback JNDI locations for the JTA TransactionManager. Applied if * the JTA UserTransaction does not implement the JTA TransactionManager * interface, provided that the "autodetectTransactionManager" flag is "true". * @see #setTransactionManagerName * @see #setAutodetectTransactionManager */ public static final String[] FALLBACK_TRANSACTION_MANAGER_NAMES = new String[] {"java:comp/TransactionManager", "java:pm/TransactionManager", "java:/TransactionManager"}; private transient JndiTemplate jndiTemplate = new JndiTemplate(); private transient UserTransaction userTransaction; private String userTransactionName; private boolean autodetectUserTransaction = true; private boolean cacheUserTransaction = true; private transient TransactionManager transactionManager; private String transactionManagerName; private boolean autodetectTransactionManager = true; private boolean allowCustomIsolationLevels = false; /** * Create a new JtaTransactionManager instance, to be configured as bean. * Invoke afterPropertiesSet to activate the configuration. * @see #setUserTransactionName * @see #setUserTransaction * @see #setTransactionManagerName * @see #setTransactionManager * @see #afterPropertiesSet() */ public JtaTransactionManager() { setNestedTransactionAllowed(true); } /** * Create a new JtaTransactionManager instance. * @param userTransaction the JTA UserTransaction to use as direct reference */ public JtaTransactionManager(UserTransaction userTransaction) { this(); Assert.notNull(userTransaction, "UserTransaction must not be null"); this.userTransaction = userTransaction; } /** * Create a new JtaTransactionManager instance. * @param userTransaction the JTA UserTransaction to use as direct reference * @param transactionManager the JTA TransactionManager to use as direct reference */ public JtaTransactionManager(UserTransaction userTransaction, TransactionManager transactionManager) { this(); Assert.notNull(userTransaction, "UserTransaction must not be null"); Assert.notNull(transactionManager, "TransactionManager must not be null"); this.userTransaction = userTransaction; this.transactionManager = transactionManager; } /** * Create a new JtaTransactionManager instance. * @param transactionManager the JTA TransactionManager to use as direct reference */ public JtaTransactionManager(TransactionManager transactionManager) { this(); Assert.notNull(transactionManager, "TransactionManager must not be null"); this.transactionManager = transactionManager; this.userTransaction = buildUserTransaction(transactionManager); } /** * Set the JndiTemplate to use for JNDI lookups. * A default one is used if not set. */ public void setJndiTemplate(JndiTemplate jndiTemplate) { if (jndiTemplate == null) { throw new IllegalArgumentException("jndiTemplate must not be null"); } this.jndiTemplate = jndiTemplate; } /** * Return the JndiTemplate used for JNDI lookups. */ public JndiTemplate getJndiTemplate() { return this.jndiTemplate; } /** * Set the JNDI environment to use for JNDI lookups. * Creates a JndiTemplate with the given environment settings. * @see #setJndiTemplate */ public void setJndiEnvironment(Properties jndiEnvironment) { this.jndiTemplate = new JndiTemplate(jndiEnvironment); } /** * Return the JNDI environment to use for JNDI lookups. */ public Properties getJndiEnvironment() { return this.jndiTemplate.getEnvironment(); } /** * Set the JTA UserTransaction to use as direct reference. *

Typically just used for local JTA setups; in a J2EE environment, * the UserTransaction will always be fetched from JNDI. * @see #setUserTransactionName * @see #setAutodetectUserTransaction */ public void setUserTransaction(UserTransaction userTransaction) { this.userTransaction = userTransaction; } /** * Return the JTA UserTransaction that this transaction manager uses. */ public UserTransaction getUserTransaction() { return this.userTransaction; } /** * Set the JNDI name of the JTA UserTransaction. *

Note that the UserTransaction will be autodetected at the J2EE default * location "java:comp/UserTransaction" if not specified explicitly. * @see #DEFAULT_USER_TRANSACTION_NAME * @see #setUserTransaction * @see #setAutodetectUserTransaction */ public void setUserTransactionName(String userTransactionName) { this.userTransactionName = userTransactionName; } /** * Set whether to autodetect the JTA UserTransaction at its default * JNDI location "java:comp/UserTransaction", as specified by J2EE. * Will proceed without UserTransaction if none found. *

Default is "true", autodetecting the UserTransaction unless * it has been specified explicitly. Turn this flag off to allow for * JtaTransactionManager operating against the TransactionManager only, * despite a default UserTransaction being available. * @see #DEFAULT_USER_TRANSACTION_NAME */ public void setAutodetectUserTransaction(boolean autodetectUserTransaction) { this.autodetectUserTransaction = autodetectUserTransaction; } /** * Set whether to cache the JTA UserTransaction object fetched from JNDI. *

Default is "true": UserTransaction lookup will only happen at startup, * reusing the same UserTransaction handle for all transactions of all threads. * This is the most efficient choice for all application servers that provide * a shared UserTransaction object (the typical case). *

Turn this flag off to enforce a fresh lookup of the UserTransaction * for every transaction. This is only necessary for application servers * that return a new UserTransaction for every transaction, keeping state * tied to the UserTransaction object itself rather than the current thread. * @see #setUserTransactionName */ public void setCacheUserTransaction(boolean cacheUserTransaction) { this.cacheUserTransaction = cacheUserTransaction; } /** * Set the JTA TransactionManager to use as direct reference. *

A TransactionManager is necessary for suspending and resuming transactions, * as this not supported by the UserTransaction interface. *

Note that the TransactionManager will be autodetected if the JTA * UserTransaction object implements the JTA TransactionManager interface too, * as well as autodetected at various well-known fallback JNDI locations. * @see #setTransactionManagerName * @see #setAutodetectTransactionManager */ public void setTransactionManager(TransactionManager transactionManager) { this.transactionManager = transactionManager; } /** * Return the JTA TransactionManager that this transaction manager uses. */ public TransactionManager getTransactionManager() { return this.transactionManager; } /** * Set the JNDI name of the JTA TransactionManager. *

A TransactionManager is necessary for suspending and resuming transactions, * as this not supported by the UserTransaction interface. *

Note that the TransactionManager will be autodetected if the JTA * UserTransaction object implements the JTA TransactionManager interface too, * as well as autodetected at various well-known fallback JNDI locations. * @see #setTransactionManager * @see #setAutodetectTransactionManager */ public void setTransactionManagerName(String transactionManagerName) { this.transactionManagerName = transactionManagerName; } /** * Set whether to autodetect a JTA UserTransaction object that implements * the JTA TransactionManager interface too (i.e. the JNDI location for the * TransactionManager is "java:comp/UserTransaction", same as for the UserTransaction). * Also checks the fallback JNDI locations "java:comp/TransactionManager" and * "java:/TransactionManager". Will proceed without TransactionManager if none found. *

Default is "true", autodetecting the TransactionManager unless it has been * specified explicitly. Can be turned off to deliberately ignore an available * TransactionManager, for example when there are known issues with suspend/resume * and any attempt to use REQUIRES_NEW or NOT_SUPPORTED should fail fast. * @see #FALLBACK_TRANSACTION_MANAGER_NAMES */ public void setAutodetectTransactionManager(boolean autodetectTransactionManager) { this.autodetectTransactionManager = autodetectTransactionManager; } /** * Set whether to allow custom isolation levels to be specified. *

Default is "false", throwing an exception if a non-default isolation level * is specified for a transaction. Turn this flag on if affected resource adapters * check the thread-bound transaction context and apply the specified isolation * levels individually (e.g. through a IsolationLevelDataSourceRouter). * @see org.springframework.jdbc.datasource.lookup.IsolationLevelDataSourceRouter */ public void setAllowCustomIsolationLevels(boolean allowCustomIsolationLevels) { this.allowCustomIsolationLevels = allowCustomIsolationLevels; } /** * Initialize the UserTransaction as well as the TransactionManager handle. * @see #initUserTransactionAndTransactionManager() */ public void afterPropertiesSet() throws TransactionSystemException { initUserTransactionAndTransactionManager(); } /** * Initialize the UserTransaction as well as the TransactionManager handle. * @throws TransactionSystemException if initialization failed */ protected void initUserTransactionAndTransactionManager() throws TransactionSystemException { // Fetch JTA UserTransaction from JNDI, if necessary. if (this.userTransaction == null) { if (this.userTransactionName != null) { this.userTransaction = lookupUserTransaction(this.userTransactionName); } else { this.userTransaction = retrieveUserTransaction(); } } // Fetch JTA TransactionManager from JNDI, if necessary. if (this.transactionManager == null) { if (this.transactionManagerName != null) { this.transactionManager = lookupTransactionManager(this.transactionManagerName); } else { this.transactionManager = retrieveTransactionManager(); } } // Autodetect UserTransaction at its default JNDI location. if (this.userTransaction == null && this.autodetectUserTransaction) { this.userTransaction = findUserTransaction(); } // Autodetect UserTransaction object that implements TransactionManager, // and check fallback JNDI locations else. if (this.transactionManager == null && this.autodetectTransactionManager) { this.transactionManager = findTransactionManager(this.userTransaction); } // If only JTA TransactionManager specified, create UserTransaction handle for it. if (this.userTransaction == null && this.transactionManager != null) { this.userTransaction = buildUserTransaction(this.transactionManager); } // We at least need the JTA UserTransaction. if (this.userTransaction != null) { if (logger.isInfoEnabled()) { logger.info("Using JTA UserTransaction: " + this.userTransaction); } } else { throw new IllegalStateException( "Either 'userTransaction' or 'userTransactionName' or 'transactionManager' " + "or 'transactionManagerName' must be specified"); } // For transaction suspension, the JTA TransactionManager is necessary too. if (this.transactionManager != null) { if (logger.isInfoEnabled()) { logger.info("Using JTA TransactionManager: " + this.transactionManager); } } else { logger.warn("No JTA TransactionManager found: " + "transaction suspension and synchronization with existing JTA transactions not available"); } } /** * Look up the JTA UserTransaction in JNDI via the configured name. * Called by afterPropertiesSet if no direct UserTransaction reference was set. * Can be overridden in subclasses to provide a different UserTransaction object. * @param userTransactionName the JNDI name of the UserTransaction * @return the UserTransaction object * @throws TransactionSystemException if the JNDI lookup failed * @see #setJndiTemplate * @see #setUserTransactionName */ protected UserTransaction lookupUserTransaction(String userTransactionName) throws TransactionSystemException { try { if (logger.isDebugEnabled()) { logger.debug("Retrieving JTA UserTransaction from JNDI location [" + userTransactionName + "]"); } return (UserTransaction) getJndiTemplate().lookup(userTransactionName, UserTransaction.class); } catch (NamingException ex) { throw new TransactionSystemException( "JTA UserTransaction is not available at JNDI location [" + userTransactionName + "]", ex); } } /** * Look up the JTA TransactionManager in JNDI via the configured name. * Called by afterPropertiesSet if no direct TransactionManager reference was set. * Can be overridden in subclasses to provide a different TransactionManager object. * @param transactionManagerName the JNDI name of the TransactionManager * @return the UserTransaction object * @throws TransactionSystemException if the JNDI lookup failed * @see #setJndiTemplate * @see #setTransactionManagerName */ protected TransactionManager lookupTransactionManager(String transactionManagerName) throws TransactionSystemException { try { if (logger.isDebugEnabled()) { logger.debug("Retrieving JTA TransactionManager from JNDI location [" + transactionManagerName + "]"); } return (TransactionManager) getJndiTemplate().lookup(transactionManagerName, TransactionManager.class); } catch (NamingException ex) { throw new TransactionSystemException( "JTA TransactionManager is not available at JNDI location [" + transactionManagerName + "]", ex); } } /** * Allows subclasses to retrieve the JTA UserTransaction in a vendor-specific manner. * Only called if no "userTransaction" or "userTransactionName" specified. *

The default implementation simply returns null. * @return the JTA UserTransaction handle to use, or null if none found * @throws TransactionSystemException in case of errors * @see #setUserTransaction * @see #setUserTransactionName */ protected UserTransaction retrieveUserTransaction() throws TransactionSystemException { return null; } /** * Allows subclasses to retrieve the JTA TransactionManager in a vendor-specific manner. * Only called if no "transactionManager" or "transactionManagerName" specified. *

The default implementation simply returns null. * @return the JTA TransactionManager handle to use, or null if none found * @throws TransactionSystemException in case of errors * @see #setTransactionManager * @see #setTransactionManagerName */ protected TransactionManager retrieveTransactionManager() throws TransactionSystemException { return null; } /** * Find the JTA UserTransaction through a default JNDI lookup: * "java:comp/UserTransaction". * @return the JTA UserTransaction reference, or null if not found * @see #DEFAULT_USER_TRANSACTION_NAME */ protected UserTransaction findUserTransaction() { String jndiName = DEFAULT_USER_TRANSACTION_NAME; try { UserTransaction ut = (UserTransaction) getJndiTemplate().lookup(jndiName, UserTransaction.class); if (logger.isDebugEnabled()) { logger.debug("JTA UserTransaction found at default JNDI location [" + jndiName + "]"); } return ut; } catch (NamingException ex) { if (logger.isDebugEnabled()) { logger.debug("No JTA UserTransaction found at default JNDI location [" + jndiName + "]", ex); } return null; } } /** * Find the JTA TransactionManager through autodetection: checking whether the * UserTransaction object implements the TransactionManager, and checking the * fallback JNDI locations. * @param ut the JTA UserTransaction object * @return the JTA TransactionManager reference, or null if not found * @see #FALLBACK_TRANSACTION_MANAGER_NAMES */ protected TransactionManager findTransactionManager(UserTransaction ut) { if (ut instanceof TransactionManager) { if (logger.isDebugEnabled()) { logger.debug("JTA UserTransaction object [" + ut + "] implements TransactionManager"); } return (TransactionManager) ut; } // Check fallback JNDI locations. for (int i = 0; i < FALLBACK_TRANSACTION_MANAGER_NAMES.length; i++) { String jndiName = FALLBACK_TRANSACTION_MANAGER_NAMES[i]; try { TransactionManager tm = (TransactionManager) getJndiTemplate().lookup(jndiName, TransactionManager.class); if (logger.isDebugEnabled()) { logger.debug("JTA TransactionManager found at fallback JNDI location [" + jndiName + "]"); } return tm; } catch (NamingException ex) { if (logger.isDebugEnabled()) { logger.debug("No JTA TransactionManager found at fallback JNDI location [" + jndiName + "]", ex); } } } // OK, so no JTA TransactionManager is available... return null; } /** * Build a UserTransaction handle based on the given TransactionManager. * @param transactionManager the TransactionManager * @return a corresponding UserTransaction handle */ protected UserTransaction buildUserTransaction(TransactionManager transactionManager) { if (transactionManager instanceof UserTransaction) { return (UserTransaction) transactionManager; } else { return new UserTransactionAdapter(transactionManager); } } /** * This implementation returns a JtaTransactionObject instance for the * JTA UserTransaction. *

The UserTransaction object will either be looked up freshly for the * current transaction, or the cached one looked up at startup will be used. * The latter is the default: Most application servers use a shared singleton * UserTransaction that can be cached. Turn off the "cacheUserTransaction" * flag to enforce a fresh lookup for every transaction. * @see #setCacheUserTransaction */ protected Object doGetTransaction() { UserTransaction ut = getUserTransaction(); if (!this.cacheUserTransaction) { ut = lookupUserTransaction( this.userTransactionName != null ? this.userTransactionName : DEFAULT_USER_TRANSACTION_NAME); } return doGetJtaTransaction(ut); } /** * Get a JTA transaction object for the given current UserTransaction. *

Subclasses can override this to provide a JtaTransactionObject * subclass, for example holding some additional JTA handle needed. * @param ut the UserTransaction handle to use for the current transaction * @return the JtaTransactionObject holding the UserTransaction */ protected JtaTransactionObject doGetJtaTransaction(UserTransaction ut) { return new JtaTransactionObject(ut); } protected boolean isExistingTransaction(Object transaction) { JtaTransactionObject txObject = (JtaTransactionObject) transaction; try { return (txObject.getUserTransaction().getStatus() != Status.STATUS_NO_TRANSACTION); } catch (SystemException ex) { throw new TransactionSystemException("JTA failure on getStatus", ex); } } /** * This implementation returns false to cause a further invocation * of doBegin despite an already existing transaction. *

JTA implementations might support nested transactions via further * UserTransaction.begin() invocations, but never support savepoints. * @see #doBegin * @see javax.transaction.UserTransaction#begin() */ protected boolean useSavepointForNestedTransaction() { return false; } protected void doBegin(Object transaction, TransactionDefinition definition) { JtaTransactionObject txObject = (JtaTransactionObject) transaction; try { doJtaBegin(txObject, definition); } catch (NotSupportedException ex) { // assume nested transaction not supported throw new NestedTransactionNotSupportedException( "JTA implementation does not support nested transactions", ex); } catch (UnsupportedOperationException ex) { // assume nested transaction not supported throw new NestedTransactionNotSupportedException( "JTA implementation does not support nested transactions", ex); } catch (SystemException ex) { throw new CannotCreateTransactionException("JTA failure on begin", ex); } } /** * Perform a JTA begin on the JTA UserTransaction or TransactionManager. *

This implementation only supports standard JTA functionality: * that is, no per-transaction isolation levels and no transaction names. * Can be overridden in subclasses, for specific JTA implementations. *

Calls applyIsolationLevel and applyTimeout * before invoking the UserTransaction's begin method. * @param txObject the JtaTransactionObject containing the UserTransaction * @param definition TransactionDefinition instance, describing propagation * behavior, isolation level, read-only flag, timeout, and transaction name * @throws NotSupportedException if thrown by JTA methods * @throws SystemException if thrown by JTA methods * @see #getUserTransaction * @see #getTransactionManager * @see #applyIsolationLevel * @see #applyTimeout * @see JtaTransactionObject#getUserTransaction() * @see javax.transaction.UserTransaction#setTransactionTimeout * @see javax.transaction.UserTransaction#begin */ protected void doJtaBegin(JtaTransactionObject txObject, TransactionDefinition definition) throws NotSupportedException, SystemException { applyIsolationLevel(txObject, definition.getIsolationLevel()); int timeout = determineTimeout(definition); applyTimeout(txObject, timeout); txObject.getUserTransaction().begin(); } /** * Apply the given transaction isolation level. The default implementation * will throw an exception for any level other than ISOLATION_DEFAULT. *

To be overridden in subclasses for specific JTA implementations, * as alternative to overriding the full {@link #doJtaBegin} method. * @param txObject the JtaTransactionObject containing the UserTransaction * @param isolationLevel isolation level taken from transaction definition * @throws InvalidIsolationLevelException if the given isolation level * cannot be applied * @throws SystemException if thrown by the JTA implementation * @see #doJtaBegin * @see JtaTransactionObject#getUserTransaction() * @see #getTransactionManager() */ protected void applyIsolationLevel(JtaTransactionObject txObject, int isolationLevel) throws InvalidIsolationLevelException, SystemException { if (!this.allowCustomIsolationLevels && isolationLevel != TransactionDefinition.ISOLATION_DEFAULT) { throw new InvalidIsolationLevelException( "JtaTransactionManager does not support custom isolation levels by default - " + "switch 'allowCustomIsolationLevels' to 'true'"); } } /** * Apply the given transaction timeout. The default implementation will call * UserTransaction.setTransactionTimeout for a non-default timeout value. * @param txObject the JtaTransactionObject containing the UserTransaction * @param timeout timeout value taken from transaction definition * @throws SystemException if thrown by the JTA implementation * @see #doJtaBegin * @see JtaTransactionObject#getUserTransaction() * @see javax.transaction.UserTransaction#setTransactionTimeout(int) */ protected void applyTimeout(JtaTransactionObject txObject, int timeout) throws SystemException { if (timeout > TransactionDefinition.TIMEOUT_DEFAULT) { txObject.getUserTransaction().setTransactionTimeout(timeout); } } protected Object doSuspend(Object transaction) { JtaTransactionObject txObject = (JtaTransactionObject) transaction; try { return doJtaSuspend(txObject); } catch (SystemException ex) { throw new TransactionSystemException("JTA failure on suspend", ex); } } /** * Perform a JTA suspend on the JTA TransactionManager. *

Can be overridden in subclasses, for specific JTA implementations. * @param txObject the JtaTransactionObject containing the UserTransaction * @return the suspended JTA Transaction object * @throws SystemException if thrown by JTA methods * @see #getTransactionManager() * @see javax.transaction.TransactionManager#suspend() */ protected Object doJtaSuspend(JtaTransactionObject txObject) throws SystemException { if (getTransactionManager() == null) { throw new TransactionSuspensionNotSupportedException( "JtaTransactionManager needs a JTA TransactionManager for suspending a transaction: " + "specify the 'transactionManager' or 'transactionManagerName' property"); } return getTransactionManager().suspend(); } protected void doResume(Object transaction, Object suspendedResources) { JtaTransactionObject txObject = (JtaTransactionObject) transaction; try { doJtaResume(txObject, suspendedResources); } catch (InvalidTransactionException ex) { throw new IllegalTransactionStateException("Tried to resume invalid JTA transaction", ex); } catch (SystemException ex) { throw new TransactionSystemException("JTA failure on resume", ex); } } /** * Perform a JTA resume on the JTA TransactionManager. *

Can be overridden in subclasses, for specific JTA implementations. * @param txObject the JtaTransactionObject containing the UserTransaction * @param suspendedTransaction the suspended JTA Transaction object * @throws InvalidTransactionException if thrown by JTA methods * @throws SystemException if thrown by JTA methods * @see #getTransactionManager() * @see javax.transaction.TransactionManager#resume(javax.transaction.Transaction) */ protected void doJtaResume(JtaTransactionObject txObject, Object suspendedTransaction) throws InvalidTransactionException, SystemException { if (getTransactionManager() == null) { throw new TransactionSuspensionNotSupportedException( "JtaTransactionManager needs a JTA TransactionManager for suspending a transaction: " + "specify the 'transactionManager' or 'transactionManagerName' property"); } getTransactionManager().resume((Transaction) suspendedTransaction); } /** * This implementation returns "true": a JTA commit will properly handle * transactions that have been marked rollback-only at a global level. */ protected boolean shouldCommitOnGlobalRollbackOnly() { return true; } protected void doCommit(DefaultTransactionStatus status) { JtaTransactionObject txObject = (JtaTransactionObject) status.getTransaction(); try { txObject.getUserTransaction().commit(); } catch (RollbackException ex) { throw new UnexpectedRollbackException( "JTA transaction unexpectedly rolled back (maybe due to a timeout)", ex); } catch (HeuristicMixedException ex) { throw new HeuristicCompletionException(HeuristicCompletionException.STATE_MIXED, ex); } catch (HeuristicRollbackException ex) { throw new HeuristicCompletionException(HeuristicCompletionException.STATE_ROLLED_BACK, ex); } catch (SystemException ex) { throw new TransactionSystemException("JTA failure on commit", ex); } } protected void doRollback(DefaultTransactionStatus status) { JtaTransactionObject txObject = (JtaTransactionObject) status.getTransaction(); try { if (txObject.getUserTransaction().getStatus() != Status.STATUS_NO_TRANSACTION) { txObject.getUserTransaction().rollback(); } } catch (SystemException ex) { throw new TransactionSystemException("JTA failure on rollback", ex); } } protected void doSetRollbackOnly(DefaultTransactionStatus status) { JtaTransactionObject txObject = (JtaTransactionObject) status.getTransaction(); if (status.isDebug()) { logger.debug("Setting JTA transaction rollback-only"); } try { if (txObject.getUserTransaction().getStatus() != Status.STATUS_NO_TRANSACTION) { txObject.getUserTransaction().setRollbackOnly(); } } catch (IllegalStateException ex) { throw new NoTransactionException("No active JTA transaction"); } catch (SystemException ex) { throw new TransactionSystemException("JTA failure on setRollbackOnly", ex); } } protected void registerAfterCompletionWithExistingTransaction(Object transaction, List synchronizations) { JtaTransactionObject txObject = (JtaTransactionObject) transaction; logger.debug("Registering after-completion synchronization with existing JTA transaction"); try { doRegisterAfterCompletionWithJtaTransaction(txObject, synchronizations); } catch (RollbackException ex) { logger.debug("Participating in existing JTA transaction that has been marked rollback-only: " + "cannot register Spring after-completion callbacks with outer JTA transaction - " + "immediately performing Spring after-completion callbacks with outcome status 'rollback'"); invokeAfterCompletion(synchronizations, TransactionSynchronization.STATUS_ROLLED_BACK); } catch (IllegalStateException ex) { throw new NoTransactionException("No active JTA transaction"); } catch (SystemException ex) { throw new TransactionSystemException("JTA failure on registerSynchronization", ex); } } /** * Register a JTA synchronization on the JTA TransactionManager, for calling * afterCompletion on the given Spring TransactionSynchronizations. *

Can be overridden in subclasses, for specific JTA implementations. * @param synchronizations List of TransactionSynchronization objects * @throws RollbackException if thrown by JTA methods * @throws SystemException if thrown by JTA methods * @see #getTransactionManager() * @see javax.transaction.Transaction#registerSynchronization * @see #invokeAfterCompletion(java.util.List, int) */ protected void doRegisterAfterCompletionWithJtaTransaction( JtaTransactionObject txObject, List synchronizations) throws RollbackException, SystemException { if (getTransactionManager() != null) { Transaction transaction = getTransactionManager().getTransaction(); if (transaction != null) { transaction.registerSynchronization(new JtaAfterCompletionSynchronization(synchronizations)); } else { // No current JTA Transaction available. logger.debug("Participating in existing JTA transaction, but no current JTA Transaction available: " + "cannot register Spring after-completion callbacks with outer JTA transaction - " + "processing Spring after-completion callbacks with outcome status 'unknown'"); invokeAfterCompletion(synchronizations, TransactionSynchronization.STATUS_UNKNOWN); } } else { // No JTA TransactionManager available. logger.warn("Participating in existing JTA transaction, but no JTA TransactionManager available: " + "cannot register Spring after-completion callbacks with outer JTA transaction - " + "processing Spring after-completion callbacks with outcome status 'unknown'"); invokeAfterCompletion(synchronizations, TransactionSynchronization.STATUS_UNKNOWN); } } //--------------------------------------------------------------------- // Serialization support //--------------------------------------------------------------------- private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { // Rely on default serialization; just initialize state after deserialization. ois.defaultReadObject(); // Create template for client-side JNDI lookup. this.jndiTemplate = new JndiTemplate(); // Perform lookup for JTA UserTransaction and TransactionManager. initUserTransactionAndTransactionManager(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy