0001: /*
0002: * Copyright 2002-2007 the original author or authors.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016:
0017: package org.springframework.transaction.jta;
0018:
0019: import java.io.IOException;
0020: import java.io.ObjectInputStream;
0021: import java.io.Serializable;
0022: import java.util.List;
0023: import java.util.Properties;
0024:
0025: import javax.naming.NamingException;
0026: import javax.transaction.HeuristicMixedException;
0027: import javax.transaction.HeuristicRollbackException;
0028: import javax.transaction.InvalidTransactionException;
0029: import javax.transaction.NotSupportedException;
0030: import javax.transaction.RollbackException;
0031: import javax.transaction.Status;
0032: import javax.transaction.Synchronization;
0033: import javax.transaction.SystemException;
0034: import javax.transaction.Transaction;
0035: import javax.transaction.TransactionManager;
0036: import javax.transaction.TransactionSynchronizationRegistry;
0037: import javax.transaction.UserTransaction;
0038:
0039: import org.springframework.beans.factory.InitializingBean;
0040: import org.springframework.jndi.JndiTemplate;
0041: import org.springframework.transaction.CannotCreateTransactionException;
0042: import org.springframework.transaction.HeuristicCompletionException;
0043: import org.springframework.transaction.IllegalTransactionStateException;
0044: import org.springframework.transaction.InvalidIsolationLevelException;
0045: import org.springframework.transaction.NestedTransactionNotSupportedException;
0046: import org.springframework.transaction.NoTransactionException;
0047: import org.springframework.transaction.TransactionDefinition;
0048: import org.springframework.transaction.TransactionSuspensionNotSupportedException;
0049: import org.springframework.transaction.TransactionSystemException;
0050: import org.springframework.transaction.UnexpectedRollbackException;
0051: import org.springframework.transaction.support.AbstractPlatformTransactionManager;
0052: import org.springframework.transaction.support.DefaultTransactionStatus;
0053: import org.springframework.transaction.support.TransactionSynchronization;
0054: import org.springframework.util.Assert;
0055: import org.springframework.util.ClassUtils;
0056: import org.springframework.util.StringUtils;
0057:
0058: /**
0059: * {@link org.springframework.transaction.PlatformTransactionManager} implementation
0060: * for JTA, delegating to a backend JTA provider. This is typically used to delegate
0061: * to a J2EE server's transaction coordinator, but may also be configured with a
0062: * local JTA provider which is embedded within the application.
0063: *
0064: * <p>This transaction manager is appropriate for handling distributed transactions,
0065: * i.e. transactions that span multiple resources, and for controlling transactions on
0066: * application server resources (e.g. JDBC DataSources available in JNDI) in general.
0067: * For a single JDBC DataSource, DataSourceTransactionManager is perfectly sufficient,
0068: * and for accessing a single resource with Hibernate (including transactional cache),
0069: * HibernateTransactionManager is appropriate, for example.
0070: *
0071: * <p>Transaction synchronization is active by default, to allow data access support
0072: * classes to register resources that are opened within the transaction for closing at
0073: * transaction completion time. Spring's support classes for JDBC, Hibernate, JDO etc
0074: * all perform such registration, allowing for reuse of the same Hibernate Session etc
0075: * within the transaction. Standard JTA does not even guarantee that for Connections
0076: * from a transactional JDBC DataSource: Spring's synchronization solves those issues.
0077: *
0078: * <p><b>For typical JTA transactions (REQUIRED, SUPPORTS, MANDATORY, NEVER), a plain
0079: * JtaTransactionManager definition is all you need, completely portable across all
0080: * J2EE servers.</b> This corresponds to the functionality of the JTA UserTransaction,
0081: * for which J2EE specifies a standard JNDI name ("java:comp/UserTransaction").
0082: * There is no need to configure a server-specific TransactionManager lookup for this
0083: * kind of JTA usage.
0084: *
0085: * <p><b>Note: Advanced JTA usage below. Dealing with these mechanisms is not
0086: * necessary for typical usage scenarios.</b>
0087: *
0088: * <p>Transaction suspension (REQUIRES_NEW, NOT_SUPPORTED) is just available with
0089: * a JTA TransactionManager being registered, via the "transactionManagerName" or
0090: * "transactionManager" property. The location of this well-defined JTA object is
0091: * <i>not</i> specified by J2EE; it is specific to each J2EE server, often kept
0092: * in JNDI like the JTA UserTransaction. Some well-known JNDI locations are:
0093: * <ul>
0094: * <li>"java:comp/UserTransaction" for Resin 2.x, Oracle OC4J (Orion),
0095: * JOnAS (JOTM), BEA WebLogic
0096: * <li>"java:comp/TransactionManager" for Resin 3.x
0097: * <li>"java:pm/TransactionManager" for Borland Enterprise Server and
0098: * Sun Application Server (Sun ONE 7 and later)
0099: * <li>"java:/TransactionManager" for JBoss Application Server
0100: * </ul>
0101: *
0102: * <p>All of these cases are autodetected by JtaTransactionManager, provided that the
0103: * "autodetectTransactionManager" flag is set to "true" (which it is by default).
0104: *
0105: * <p><b>Note: Support for the JTA TransactionManager interface is not required by J2EE.
0106: * Almost all J2EE servers expose it, but do so as extension to J2EE. There might be some
0107: * issues with compatibility, despite the TransactionManager interface being part of JTA.</b>
0108: * As a consequence, Spring provides various vendor-specific PlatformTransactionManagers,
0109: * which are recommended to be used if appropriate: {@link WebLogicJtaTransactionManager},
0110: * {@link WebSphereUowTransactionManager} and {@link OC4JJtaTransactionManager}.
0111: * For all other J2EE servers, the standard JtaTransactionManager is sufficient.
0112: *
0113: * <p><b>Consider using Spring 2.5's <code>tx:jta-transaction-manager</code> configuration
0114: * element for automatically picking the appropriate JTA platform transaction manager
0115: * (automatically detecting WebLogic, WebSphere and OC4J).</b>
0116: *
0117: * <p><b>This pure JtaTransactionManager supports timeouts but not per-transaction
0118: * isolation levels.</b> Custom subclasses may override {@link #doJtaBegin} for
0119: * specific JTA extensions in order to provide this functionality; Spring includes
0120: * corresponding {@link WebLogicJtaTransactionManager} and {@link OC4JJtaTransactionManager}
0121: * classes, for BEA's WebLogic Server and Oracle's OC4J, respectively. Such adapters
0122: * for specific J2EE transaction coordinators may also expose transaction names for
0123: * monitoring; with standard JTA, transaction names will simply be ignored.
0124: *
0125: * <p>JTA 1.1 adds the TransactionSynchronizationRegistry facility, as public Java EE 5
0126: * API in addition to the standard JTA UserTransaction handle. As of Spring 2.5, this
0127: * JtaTransactionManager autodetects the TransactionSynchronizationRegistry and uses
0128: * it for registering Spring-managed synchronizations when participating in an existing
0129: * JTA transaction (e.g. controlled by EJB CMT). If no TransactionSynchronizationRegistry
0130: * is available (or the JTA 1.1 API isn't available), then such synchronizations
0131: * will be registered via the (non-J2EE) JTA TransactionManager handle.
0132: *
0133: * <p>This class is serializable. However, active synchronizations do not survive
0134: * serialization.
0135: *
0136: * @author Juergen Hoeller
0137: * @since 24.03.2003
0138: * @see javax.transaction.UserTransaction
0139: * @see javax.transaction.TransactionManager
0140: * @see javax.transaction.TransactionSynchronizationRegistry
0141: * @see #setUserTransactionName
0142: * @see #setUserTransaction
0143: * @see #setTransactionManagerName
0144: * @see #setTransactionManager
0145: * @see JotmFactoryBean
0146: * @see WebSphereTransactionManagerFactoryBean
0147: * @see WebLogicJtaTransactionManager
0148: */
0149: public class JtaTransactionManager extends
0150: AbstractPlatformTransactionManager implements
0151: TransactionFactory, InitializingBean, Serializable {
0152:
0153: /**
0154: * Default JNDI location for the JTA UserTransaction. Many J2EE servers
0155: * also provide support for the JTA TransactionManager interface there.
0156: * @see #setUserTransactionName
0157: * @see #setAutodetectTransactionManager
0158: */
0159: public static final String DEFAULT_USER_TRANSACTION_NAME = "java:comp/UserTransaction";
0160:
0161: /**
0162: * Fallback JNDI locations for the JTA TransactionManager. Applied if
0163: * the JTA UserTransaction does not implement the JTA TransactionManager
0164: * interface, provided that the "autodetectTransactionManager" flag is "true".
0165: * @see #setTransactionManagerName
0166: * @see #setAutodetectTransactionManager
0167: */
0168: public static final String[] FALLBACK_TRANSACTION_MANAGER_NAMES = new String[] {
0169: "java:comp/TransactionManager",
0170: "java:pm/TransactionManager", "java:/TransactionManager" };
0171:
0172: /**
0173: * Standard Java EE 5 JNDI location for the JTA TransactionSynchronizationRegistry.
0174: * Autodetected when available.
0175: */
0176: public static final String DEFAULT_TRANSACTION_SYNCHRONIZATION_REGISTRY_NAME = "java:comp/TransactionSynchronizationRegistry";
0177:
0178: private static final String TRANSACTION_SYNCHRONIZATION_REGISTRY_CLASS_NAME = "javax.transaction.TransactionSynchronizationRegistry";
0179:
0180: private transient JndiTemplate jndiTemplate = new JndiTemplate();
0181:
0182: private transient UserTransaction userTransaction;
0183:
0184: private String userTransactionName;
0185:
0186: private boolean autodetectUserTransaction = true;
0187:
0188: private boolean cacheUserTransaction = true;
0189:
0190: private boolean userTransactionObtainedFromJndi = false;
0191:
0192: private transient TransactionManager transactionManager;
0193:
0194: private String transactionManagerName;
0195:
0196: private boolean autodetectTransactionManager = true;
0197:
0198: private String transactionSynchronizationRegistryName;
0199:
0200: private transient Object transactionSynchronizationRegistry;
0201:
0202: private boolean allowCustomIsolationLevels = false;
0203:
0204: /**
0205: * Create a new JtaTransactionManager instance, to be configured as bean.
0206: * Invoke <code>afterPropertiesSet</code> to activate the configuration.
0207: * @see #setUserTransactionName
0208: * @see #setUserTransaction
0209: * @see #setTransactionManagerName
0210: * @see #setTransactionManager
0211: * @see #afterPropertiesSet()
0212: */
0213: public JtaTransactionManager() {
0214: setNestedTransactionAllowed(true);
0215: }
0216:
0217: /**
0218: * Create a new JtaTransactionManager instance.
0219: * @param userTransaction the JTA UserTransaction to use as direct reference
0220: */
0221: public JtaTransactionManager(UserTransaction userTransaction) {
0222: this ();
0223: Assert.notNull(userTransaction,
0224: "UserTransaction must not be null");
0225: this .userTransaction = userTransaction;
0226: }
0227:
0228: /**
0229: * Create a new JtaTransactionManager instance.
0230: * @param userTransaction the JTA UserTransaction to use as direct reference
0231: * @param transactionManager the JTA TransactionManager to use as direct reference
0232: */
0233: public JtaTransactionManager(UserTransaction userTransaction,
0234: TransactionManager transactionManager) {
0235: this ();
0236: Assert.notNull(userTransaction,
0237: "UserTransaction must not be null");
0238: Assert.notNull(transactionManager,
0239: "TransactionManager must not be null");
0240: this .userTransaction = userTransaction;
0241: this .transactionManager = transactionManager;
0242: }
0243:
0244: /**
0245: * Create a new JtaTransactionManager instance.
0246: * @param transactionManager the JTA TransactionManager to use as direct reference
0247: */
0248: public JtaTransactionManager(TransactionManager transactionManager) {
0249: this ();
0250: Assert.notNull(transactionManager,
0251: "TransactionManager must not be null");
0252: this .transactionManager = transactionManager;
0253: this .userTransaction = buildUserTransaction(transactionManager);
0254: }
0255:
0256: /**
0257: * Set the JndiTemplate to use for JNDI lookups.
0258: * A default one is used if not set.
0259: */
0260: public void setJndiTemplate(JndiTemplate jndiTemplate) {
0261: if (jndiTemplate == null) {
0262: throw new IllegalArgumentException(
0263: "jndiTemplate must not be null");
0264: }
0265: this .jndiTemplate = jndiTemplate;
0266: }
0267:
0268: /**
0269: * Return the JndiTemplate used for JNDI lookups.
0270: */
0271: public JndiTemplate getJndiTemplate() {
0272: return this .jndiTemplate;
0273: }
0274:
0275: /**
0276: * Set the JNDI environment to use for JNDI lookups.
0277: * Creates a JndiTemplate with the given environment settings.
0278: * @see #setJndiTemplate
0279: */
0280: public void setJndiEnvironment(Properties jndiEnvironment) {
0281: this .jndiTemplate = new JndiTemplate(jndiEnvironment);
0282: }
0283:
0284: /**
0285: * Return the JNDI environment to use for JNDI lookups.
0286: */
0287: public Properties getJndiEnvironment() {
0288: return this .jndiTemplate.getEnvironment();
0289: }
0290:
0291: /**
0292: * Set the JTA UserTransaction to use as direct reference.
0293: * <p>Typically just used for local JTA setups; in a J2EE environment,
0294: * the UserTransaction will always be fetched from JNDI.
0295: * @see #setUserTransactionName
0296: * @see #setAutodetectUserTransaction
0297: */
0298: public void setUserTransaction(UserTransaction userTransaction) {
0299: this .userTransaction = userTransaction;
0300: }
0301:
0302: /**
0303: * Return the JTA UserTransaction that this transaction manager uses.
0304: */
0305: public UserTransaction getUserTransaction() {
0306: return this .userTransaction;
0307: }
0308:
0309: /**
0310: * Set the JNDI name of the JTA UserTransaction.
0311: * <p>Note that the UserTransaction will be autodetected at the J2EE default
0312: * location "java:comp/UserTransaction" if not specified explicitly.
0313: * @see #DEFAULT_USER_TRANSACTION_NAME
0314: * @see #setUserTransaction
0315: * @see #setAutodetectUserTransaction
0316: */
0317: public void setUserTransactionName(String userTransactionName) {
0318: this .userTransactionName = userTransactionName;
0319: }
0320:
0321: /**
0322: * Set whether to autodetect the JTA UserTransaction at its default
0323: * JNDI location "java:comp/UserTransaction", as specified by J2EE.
0324: * Will proceed without UserTransaction if none found.
0325: * <p>Default is "true", autodetecting the UserTransaction unless
0326: * it has been specified explicitly. Turn this flag off to allow for
0327: * JtaTransactionManager operating against the TransactionManager only,
0328: * despite a default UserTransaction being available.
0329: * @see #DEFAULT_USER_TRANSACTION_NAME
0330: */
0331: public void setAutodetectUserTransaction(
0332: boolean autodetectUserTransaction) {
0333: this .autodetectUserTransaction = autodetectUserTransaction;
0334: }
0335:
0336: /**
0337: * Set whether to cache the JTA UserTransaction object fetched from JNDI.
0338: * <p>Default is "true": UserTransaction lookup will only happen at startup,
0339: * reusing the same UserTransaction handle for all transactions of all threads.
0340: * This is the most efficient choice for all application servers that provide
0341: * a shared UserTransaction object (the typical case).
0342: * <p>Turn this flag off to enforce a fresh lookup of the UserTransaction
0343: * for every transaction. This is only necessary for application servers
0344: * that return a new UserTransaction for every transaction, keeping state
0345: * tied to the UserTransaction object itself rather than the current thread.
0346: * @see #setUserTransactionName
0347: */
0348: public void setCacheUserTransaction(boolean cacheUserTransaction) {
0349: this .cacheUserTransaction = cacheUserTransaction;
0350: }
0351:
0352: /**
0353: * Set the JTA TransactionManager to use as direct reference.
0354: * <p>A TransactionManager is necessary for suspending and resuming transactions,
0355: * as this not supported by the UserTransaction interface.
0356: * <p>Note that the TransactionManager will be autodetected if the JTA
0357: * UserTransaction object implements the JTA TransactionManager interface too,
0358: * as well as autodetected at various well-known fallback JNDI locations.
0359: * @see #setTransactionManagerName
0360: * @see #setAutodetectTransactionManager
0361: */
0362: public void setTransactionManager(
0363: TransactionManager transactionManager) {
0364: this .transactionManager = transactionManager;
0365: }
0366:
0367: /**
0368: * Return the JTA TransactionManager that this transaction manager uses.
0369: */
0370: public TransactionManager getTransactionManager() {
0371: return this .transactionManager;
0372: }
0373:
0374: /**
0375: * Set the JNDI name of the JTA TransactionManager.
0376: * <p>A TransactionManager is necessary for suspending and resuming transactions,
0377: * as this not supported by the UserTransaction interface.
0378: * <p>Note that the TransactionManager will be autodetected if the JTA
0379: * UserTransaction object implements the JTA TransactionManager interface too,
0380: * as well as autodetected at various well-known fallback JNDI locations.
0381: * @see #setTransactionManager
0382: * @see #setAutodetectTransactionManager
0383: */
0384: public void setTransactionManagerName(String transactionManagerName) {
0385: this .transactionManagerName = transactionManagerName;
0386: }
0387:
0388: /**
0389: * Set whether to autodetect a JTA UserTransaction object that implements
0390: * the JTA TransactionManager interface too (i.e. the JNDI location for the
0391: * TransactionManager is "java:comp/UserTransaction", same as for the UserTransaction).
0392: * Also checks the fallback JNDI locations "java:comp/TransactionManager" and
0393: * "java:/TransactionManager". Will proceed without TransactionManager if none found.
0394: * <p>Default is "true", autodetecting the TransactionManager unless it has been
0395: * specified explicitly. Can be turned off to deliberately ignore an available
0396: * TransactionManager, for example when there are known issues with suspend/resume
0397: * and any attempt to use REQUIRES_NEW or NOT_SUPPORTED should fail fast.
0398: * @see #FALLBACK_TRANSACTION_MANAGER_NAMES
0399: */
0400: public void setAutodetectTransactionManager(
0401: boolean autodetectTransactionManager) {
0402: this .autodetectTransactionManager = autodetectTransactionManager;
0403: }
0404:
0405: /**
0406: * Set the JNDI name of the JTA 1.1 TransactionSynchronizationRegistry.
0407: * <p>Note that the TransactionSynchronizationRegistry will be autodetected
0408: * at the Java EE 5 default location "java:comp/TransactionSynchronizationRegistry"
0409: * if not specified explicitly.
0410: * @see #DEFAULT_TRANSACTION_SYNCHRONIZATION_REGISTRY_NAME
0411: */
0412: public void setTransactionSynchronizationRegistryName(
0413: String transactionSynchronizationRegistryName) {
0414: this .transactionSynchronizationRegistryName = transactionSynchronizationRegistryName;
0415: }
0416:
0417: /**
0418: * Set whether to allow custom isolation levels to be specified.
0419: * <p>Default is "false", throwing an exception if a non-default isolation level
0420: * is specified for a transaction. Turn this flag on if affected resource adapters
0421: * check the thread-bound transaction context and apply the specified isolation
0422: * levels individually (e.g. through a IsolationLevelDataSourceRouter).
0423: * @see org.springframework.jdbc.datasource.lookup.IsolationLevelDataSourceRouter
0424: */
0425: public void setAllowCustomIsolationLevels(
0426: boolean allowCustomIsolationLevels) {
0427: this .allowCustomIsolationLevels = allowCustomIsolationLevels;
0428: }
0429:
0430: /**
0431: * Initialize the UserTransaction as well as the TransactionManager handle.
0432: * @see #initUserTransactionAndTransactionManager()
0433: */
0434: public void afterPropertiesSet() throws TransactionSystemException {
0435: initUserTransactionAndTransactionManager();
0436: checkUserTransactionAndTransactionManager();
0437: initTransactionSynchronizationRegistry();
0438: }
0439:
0440: /**
0441: * Initialize the UserTransaction as well as the TransactionManager handle.
0442: * @throws TransactionSystemException if initialization failed
0443: */
0444: protected void initUserTransactionAndTransactionManager()
0445: throws TransactionSystemException {
0446: // Fetch JTA UserTransaction from JNDI, if necessary.
0447: if (this .userTransaction == null) {
0448: if (StringUtils.hasLength(this .userTransactionName)) {
0449: this .userTransaction = lookupUserTransaction(this .userTransactionName);
0450: this .userTransactionObtainedFromJndi = true;
0451: } else {
0452: this .userTransaction = retrieveUserTransaction();
0453: }
0454: }
0455:
0456: // Fetch JTA TransactionManager from JNDI, if necessary.
0457: if (this .transactionManager == null) {
0458: if (StringUtils.hasLength(this .transactionManagerName)) {
0459: this .transactionManager = lookupTransactionManager(this .transactionManagerName);
0460: } else {
0461: this .transactionManager = retrieveTransactionManager();
0462: }
0463: }
0464:
0465: // Autodetect UserTransaction at its default JNDI location.
0466: if (this .userTransaction == null
0467: && this .autodetectUserTransaction) {
0468: this .userTransaction = findUserTransaction();
0469: }
0470:
0471: // Autodetect UserTransaction object that implements TransactionManager,
0472: // and check fallback JNDI locations else.
0473: if (this .transactionManager == null
0474: && this .autodetectTransactionManager) {
0475: this .transactionManager = findTransactionManager(this .userTransaction);
0476: }
0477:
0478: // If only JTA TransactionManager specified, create UserTransaction handle for it.
0479: if (this .userTransaction == null
0480: && this .transactionManager != null) {
0481: this .userTransaction = buildUserTransaction(this .transactionManager);
0482: }
0483: }
0484:
0485: /**
0486: * Check the UserTransaction as well as the TransactionManager handle,
0487: * assuming standard JTA requirements.
0488: * @throws IllegalStateException if no sufficient handles are available
0489: */
0490: protected void checkUserTransactionAndTransactionManager()
0491: throws IllegalStateException {
0492: // We at least need the JTA UserTransaction.
0493: if (this .userTransaction != null) {
0494: if (logger.isInfoEnabled()) {
0495: logger.info("Using JTA UserTransaction: "
0496: + this .userTransaction);
0497: }
0498: } else {
0499: throw new IllegalStateException(
0500: "No JTA UserTransaction available - specify either "
0501: + "'userTransaction' or 'userTransactionName' or 'transactionManager' or 'transactionManagerName'");
0502: }
0503:
0504: // For transaction suspension, the JTA TransactionManager is necessary too.
0505: if (this .transactionManager != null) {
0506: if (logger.isInfoEnabled()) {
0507: logger.info("Using JTA TransactionManager: "
0508: + this .transactionManager);
0509: }
0510: } else {
0511: logger
0512: .warn("No JTA TransactionManager found: "
0513: + "transaction suspension and synchronization with existing JTA transactions not available");
0514: }
0515: }
0516:
0517: /**
0518: * Initialize the JTA 1.1 TransactionSynchronizationRegistry, if available.
0519: * <p>To be called after {@link #initUserTransactionAndTransactionManager()},
0520: * since it may check the UserTransaction and TransactionManager handles.
0521: * @throws TransactionSystemException if initialization failed
0522: */
0523: protected void initTransactionSynchronizationRegistry() {
0524: if (StringUtils
0525: .hasLength(this .transactionSynchronizationRegistryName)) {
0526: this .transactionSynchronizationRegistry = lookupTransactionSynchronizationRegistry(this .transactionSynchronizationRegistryName);
0527: } else {
0528: this .transactionSynchronizationRegistry = retrieveTransactionSynchronizationRegistry();
0529: if (this .transactionSynchronizationRegistry == null) {
0530: this .transactionSynchronizationRegistry = findTransactionSynchronizationRegistry(
0531: this .userTransaction, this .transactionManager);
0532: }
0533: }
0534:
0535: if (this .transactionSynchronizationRegistry != null) {
0536: if (logger.isInfoEnabled()) {
0537: logger
0538: .info("Using JTA TransactionSynchronizationRegistry: "
0539: + this .transactionSynchronizationRegistry);
0540: }
0541: }
0542: }
0543:
0544: /**
0545: * Build a UserTransaction handle based on the given TransactionManager.
0546: * @param transactionManager the TransactionManager
0547: * @return a corresponding UserTransaction handle
0548: */
0549: protected UserTransaction buildUserTransaction(
0550: TransactionManager transactionManager) {
0551: if (transactionManager instanceof UserTransaction) {
0552: return (UserTransaction) transactionManager;
0553: } else {
0554: return new UserTransactionAdapter(transactionManager);
0555: }
0556: }
0557:
0558: /**
0559: * Look up the JTA UserTransaction in JNDI via the configured name.
0560: * <p>Called by <code>afterPropertiesSet</code> if no direct UserTransaction reference was set.
0561: * Can be overridden in subclasses to provide a different UserTransaction object.
0562: * @param userTransactionName the JNDI name of the UserTransaction
0563: * @return the UserTransaction object
0564: * @throws TransactionSystemException if the JNDI lookup failed
0565: * @see #setJndiTemplate
0566: * @see #setUserTransactionName
0567: */
0568: protected UserTransaction lookupUserTransaction(
0569: String userTransactionName)
0570: throws TransactionSystemException {
0571: try {
0572: if (logger.isDebugEnabled()) {
0573: logger
0574: .debug("Retrieving JTA UserTransaction from JNDI location ["
0575: + userTransactionName + "]");
0576: }
0577: return (UserTransaction) getJndiTemplate().lookup(
0578: userTransactionName, UserTransaction.class);
0579: } catch (NamingException ex) {
0580: throw new TransactionSystemException(
0581: "JTA UserTransaction is not available at JNDI location ["
0582: + userTransactionName + "]", ex);
0583: }
0584: }
0585:
0586: /**
0587: * Look up the JTA TransactionManager in JNDI via the configured name.
0588: * <p>Called by <code>afterPropertiesSet</code> if no direct TransactionManager reference was set.
0589: * Can be overridden in subclasses to provide a different TransactionManager object.
0590: * @param transactionManagerName the JNDI name of the TransactionManager
0591: * @return the UserTransaction object
0592: * @throws TransactionSystemException if the JNDI lookup failed
0593: * @see #setJndiTemplate
0594: * @see #setTransactionManagerName
0595: */
0596: protected TransactionManager lookupTransactionManager(
0597: String transactionManagerName)
0598: throws TransactionSystemException {
0599: try {
0600: if (logger.isDebugEnabled()) {
0601: logger
0602: .debug("Retrieving JTA TransactionManager from JNDI location ["
0603: + transactionManagerName + "]");
0604: }
0605: return (TransactionManager) getJndiTemplate().lookup(
0606: transactionManagerName, TransactionManager.class);
0607: } catch (NamingException ex) {
0608: throw new TransactionSystemException(
0609: "JTA TransactionManager is not available at JNDI location ["
0610: + transactionManagerName + "]", ex);
0611: }
0612: }
0613:
0614: /**
0615: * Look up the JTA TransactionSynchronizationRegistry in JNDI via the configured name.
0616: * <p>Can be overridden in subclasses to provide a different TransactionManager object.
0617: * @param registryName the JNDI name of the
0618: * TransactionSynchronizationRegistry
0619: * @return the TransactionSynchronizationRegistry object
0620: * @throws TransactionSystemException if the JNDI lookup failed
0621: * @see #setJndiTemplate
0622: * @see #setTransactionSynchronizationRegistryName
0623: */
0624: protected Object lookupTransactionSynchronizationRegistry(
0625: String registryName) throws TransactionSystemException {
0626: try {
0627: if (logger.isDebugEnabled()) {
0628: logger
0629: .debug("Retrieving JTA TransactionSynchronizationRegistry from JNDI location ["
0630: + registryName + "]");
0631: }
0632: Class registryClass = ClassUtils.forName(
0633: TRANSACTION_SYNCHRONIZATION_REGISTRY_CLASS_NAME,
0634: getClass().getClassLoader());
0635: return getJndiTemplate()
0636: .lookup(registryName, registryClass);
0637: } catch (ClassNotFoundException ex) {
0638: throw new TransactionSystemException("JTA 1.1 ["
0639: + TRANSACTION_SYNCHRONIZATION_REGISTRY_CLASS_NAME
0640: + "] not available");
0641: } catch (NamingException ex) {
0642: throw new TransactionSystemException(
0643: "JTA TransactionSynchronizationRegistry is not available at JNDI location ["
0644: + registryName + "]", ex);
0645: }
0646: }
0647:
0648: /**
0649: * Allows subclasses to retrieve the JTA UserTransaction in a vendor-specific manner.
0650: * Only called if no "userTransaction" or "userTransactionName" specified.
0651: * <p>The default implementation simply returns <code>null</code>.
0652: * @return the JTA UserTransaction handle to use, or <code>null</code> if none found
0653: * @throws TransactionSystemException in case of errors
0654: * @see #setUserTransaction
0655: * @see #setUserTransactionName
0656: */
0657: protected UserTransaction retrieveUserTransaction()
0658: throws TransactionSystemException {
0659: return null;
0660: }
0661:
0662: /**
0663: * Allows subclasses to retrieve the JTA TransactionManager in a vendor-specific manner.
0664: * Only called if no "transactionManager" or "transactionManagerName" specified.
0665: * <p>The default implementation simply returns <code>null</code>.
0666: * @return the JTA TransactionManager handle to use, or <code>null</code> if none found
0667: * @throws TransactionSystemException in case of errors
0668: * @see #setTransactionManager
0669: * @see #setTransactionManagerName
0670: */
0671: protected TransactionManager retrieveTransactionManager()
0672: throws TransactionSystemException {
0673: return null;
0674: }
0675:
0676: /**
0677: * Allows subclasses to retrieve the JTA 1.1 TransactionSynchronizationRegistry
0678: * in a vendor-specific manner.
0679: * <p>The default implementation simply returns <code>null</code>.
0680: * @return the JTA TransactionSynchronizationRegistry handle to use,
0681: * or <code>null</code> if none found
0682: * @throws TransactionSystemException in case of errors
0683: */
0684: protected Object retrieveTransactionSynchronizationRegistry()
0685: throws TransactionSystemException {
0686: return null;
0687: }
0688:
0689: /**
0690: * Find the JTA UserTransaction through a default JNDI lookup:
0691: * "java:comp/UserTransaction".
0692: * @return the JTA UserTransaction reference, or <code>null</code> if not found
0693: * @see #DEFAULT_USER_TRANSACTION_NAME
0694: */
0695: protected UserTransaction findUserTransaction() {
0696: String jndiName = DEFAULT_USER_TRANSACTION_NAME;
0697: try {
0698: UserTransaction ut = (UserTransaction) getJndiTemplate()
0699: .lookup(jndiName, UserTransaction.class);
0700: if (logger.isDebugEnabled()) {
0701: logger
0702: .debug("JTA UserTransaction found at default JNDI location ["
0703: + jndiName + "]");
0704: }
0705: this .userTransactionObtainedFromJndi = true;
0706: return ut;
0707: } catch (NamingException ex) {
0708: if (logger.isDebugEnabled()) {
0709: logger.debug(
0710: "No JTA UserTransaction found at default JNDI location ["
0711: + jndiName + "]", ex);
0712: }
0713: return null;
0714: }
0715: }
0716:
0717: /**
0718: * Find the JTA TransactionManager through autodetection: checking whether the
0719: * UserTransaction object implements the TransactionManager, and checking the
0720: * fallback JNDI locations.
0721: * @param ut the JTA UserTransaction object
0722: * @return the JTA TransactionManager reference, or <code>null</code> if not found
0723: * @see #FALLBACK_TRANSACTION_MANAGER_NAMES
0724: */
0725: protected TransactionManager findTransactionManager(
0726: UserTransaction ut) {
0727: if (ut instanceof TransactionManager) {
0728: if (logger.isDebugEnabled()) {
0729: logger.debug("JTA UserTransaction object [" + ut
0730: + "] implements TransactionManager");
0731: }
0732: return (TransactionManager) ut;
0733: }
0734:
0735: // Check fallback JNDI locations.
0736: for (int i = 0; i < FALLBACK_TRANSACTION_MANAGER_NAMES.length; i++) {
0737: String jndiName = FALLBACK_TRANSACTION_MANAGER_NAMES[i];
0738: try {
0739: TransactionManager tm = (TransactionManager) getJndiTemplate()
0740: .lookup(jndiName, TransactionManager.class);
0741: if (logger.isDebugEnabled()) {
0742: logger
0743: .debug("JTA TransactionManager found at fallback JNDI location ["
0744: + jndiName + "]");
0745: }
0746: return tm;
0747: } catch (NamingException ex) {
0748: if (logger.isDebugEnabled()) {
0749: logger.debug(
0750: "No JTA TransactionManager found at fallback JNDI location ["
0751: + jndiName + "]", ex);
0752: }
0753: }
0754: }
0755:
0756: // OK, so no JTA TransactionManager is available...
0757: return null;
0758: }
0759:
0760: /**
0761: * Find the JTA 1.1 TransactionSynchronizationRegistry through autodetection:
0762: * checking whether the UserTransaction object or TransactionManager object
0763: * implements it, and checking Java EE 5's standard JNDI location.
0764: * <p>The default implementation simply returns <code>null</code>.
0765: * @param ut the JTA UserTransaction object
0766: * @param tm the JTA TransactionManager object
0767: * @return the JTA TransactionSynchronizationRegistry handle to use,
0768: * or <code>null</code> if none found
0769: * @throws TransactionSystemException in case of errors
0770: */
0771: protected Object findTransactionSynchronizationRegistry(
0772: UserTransaction ut, TransactionManager tm)
0773: throws TransactionSystemException {
0774:
0775: try {
0776: Class registryClass = ClassUtils.forName(
0777: TRANSACTION_SYNCHRONIZATION_REGISTRY_CLASS_NAME,
0778: getClass().getClassLoader());
0779:
0780: // If we came here, we might be on Java EE 5, since the JTA 1.1 API is present.
0781: if (this .userTransactionObtainedFromJndi) {
0782: // UserTransaction has already been obtained from JNDI, so the
0783: // TransactionSynchronizationRegistry probably sits there as well.
0784: String jndiName = DEFAULT_TRANSACTION_SYNCHRONIZATION_REGISTRY_NAME;
0785: try {
0786: Object tsr = getJndiTemplate().lookup(jndiName,
0787: registryClass);
0788: if (logger.isDebugEnabled()) {
0789: logger
0790: .debug("JTA TransactionSynchronizationRegistry found at default JNDI location ["
0791: + jndiName + "]");
0792: }
0793: return tsr;
0794: } catch (NamingException ex) {
0795: if (logger.isDebugEnabled()) {
0796: logger.debug(
0797: "No JTA TransactionSynchronizationRegistry found at default JNDI location ["
0798: + jndiName + "]", ex);
0799: }
0800: }
0801: }
0802:
0803: // Check whether the UserTransaction or TransactionManager implements it...
0804: if (registryClass.isInstance(ut)) {
0805: return ut;
0806: }
0807: if (registryClass.isInstance(tm)) {
0808: return tm;
0809: }
0810:
0811: // OK, so no JTA 1.1 TransactionSynchronizationRegistry is available,
0812: // despite the API being present...
0813: return null;
0814: } catch (ClassNotFoundException ex) {
0815: logger.debug("JTA 1.1 ["
0816: + TRANSACTION_SYNCHRONIZATION_REGISTRY_CLASS_NAME
0817: + "] not available");
0818: return null;
0819: }
0820: }
0821:
0822: /**
0823: * This implementation returns a JtaTransactionObject instance for the
0824: * JTA UserTransaction.
0825: * <p>The UserTransaction object will either be looked up freshly for the
0826: * current transaction, or the cached one looked up at startup will be used.
0827: * The latter is the default: Most application servers use a shared singleton
0828: * UserTransaction that can be cached. Turn off the "cacheUserTransaction"
0829: * flag to enforce a fresh lookup for every transaction.
0830: * @see #setCacheUserTransaction
0831: */
0832: protected Object doGetTransaction() {
0833: UserTransaction ut = getUserTransaction();
0834: if (ut == null) {
0835: throw new CannotCreateTransactionException(
0836: "No JTA UserTransaction available - "
0837: + "programmatic PlatformTransactionManager.getTransaction usage not supported");
0838: }
0839: if (!this .cacheUserTransaction) {
0840: ut = lookupUserTransaction(this .userTransactionName != null ? this .userTransactionName
0841: : DEFAULT_USER_TRANSACTION_NAME);
0842: }
0843: return doGetJtaTransaction(ut);
0844: }
0845:
0846: /**
0847: * Get a JTA transaction object for the given current UserTransaction.
0848: * <p>Subclasses can override this to provide a JtaTransactionObject
0849: * subclass, for example holding some additional JTA handle needed.
0850: * @param ut the UserTransaction handle to use for the current transaction
0851: * @return the JtaTransactionObject holding the UserTransaction
0852: */
0853: protected JtaTransactionObject doGetJtaTransaction(
0854: UserTransaction ut) {
0855: return new JtaTransactionObject(ut);
0856: }
0857:
0858: protected boolean isExistingTransaction(Object transaction) {
0859: JtaTransactionObject txObject = (JtaTransactionObject) transaction;
0860: try {
0861: return (txObject.getUserTransaction().getStatus() != Status.STATUS_NO_TRANSACTION);
0862: } catch (SystemException ex) {
0863: throw new TransactionSystemException(
0864: "JTA failure on getStatus", ex);
0865: }
0866: }
0867:
0868: /**
0869: * This implementation returns false to cause a further invocation
0870: * of doBegin despite an already existing transaction.
0871: * <p>JTA implementations might support nested transactions via further
0872: * <code>UserTransaction.begin()</code> invocations, but never support savepoints.
0873: * @see #doBegin
0874: * @see javax.transaction.UserTransaction#begin()
0875: */
0876: protected boolean useSavepointForNestedTransaction() {
0877: return false;
0878: }
0879:
0880: protected void doBegin(Object transaction,
0881: TransactionDefinition definition) {
0882: JtaTransactionObject txObject = (JtaTransactionObject) transaction;
0883: try {
0884: doJtaBegin(txObject, definition);
0885: } catch (NotSupportedException ex) {
0886: // assume nested transaction not supported
0887: throw new NestedTransactionNotSupportedException(
0888: "JTA implementation does not support nested transactions",
0889: ex);
0890: } catch (UnsupportedOperationException ex) {
0891: // assume nested transaction not supported
0892: throw new NestedTransactionNotSupportedException(
0893: "JTA implementation does not support nested transactions",
0894: ex);
0895: } catch (SystemException ex) {
0896: throw new CannotCreateTransactionException(
0897: "JTA failure on begin", ex);
0898: }
0899: }
0900:
0901: /**
0902: * Perform a JTA begin on the JTA UserTransaction or TransactionManager.
0903: * <p>This implementation only supports standard JTA functionality:
0904: * that is, no per-transaction isolation levels and no transaction names.
0905: * Can be overridden in subclasses, for specific JTA implementations.
0906: * <p>Calls <code>applyIsolationLevel</code> and <code>applyTimeout</code>
0907: * before invoking the UserTransaction's <code>begin</code> method.
0908: * @param txObject the JtaTransactionObject containing the UserTransaction
0909: * @param definition TransactionDefinition instance, describing propagation
0910: * behavior, isolation level, read-only flag, timeout, and transaction name
0911: * @throws NotSupportedException if thrown by JTA methods
0912: * @throws SystemException if thrown by JTA methods
0913: * @see #getUserTransaction
0914: * @see #getTransactionManager
0915: * @see #applyIsolationLevel
0916: * @see #applyTimeout
0917: * @see JtaTransactionObject#getUserTransaction()
0918: * @see javax.transaction.UserTransaction#setTransactionTimeout
0919: * @see javax.transaction.UserTransaction#begin
0920: */
0921: protected void doJtaBegin(JtaTransactionObject txObject,
0922: TransactionDefinition definition)
0923: throws NotSupportedException, SystemException {
0924:
0925: applyIsolationLevel(txObject, definition.getIsolationLevel());
0926: int timeout = determineTimeout(definition);
0927: applyTimeout(txObject, timeout);
0928: txObject.getUserTransaction().begin();
0929: }
0930:
0931: /**
0932: * Apply the given transaction isolation level. The default implementation
0933: * will throw an exception for any level other than ISOLATION_DEFAULT.
0934: * <p>To be overridden in subclasses for specific JTA implementations,
0935: * as alternative to overriding the full {@link #doJtaBegin} method.
0936: * @param txObject the JtaTransactionObject containing the UserTransaction
0937: * @param isolationLevel isolation level taken from transaction definition
0938: * @throws InvalidIsolationLevelException if the given isolation level
0939: * cannot be applied
0940: * @throws SystemException if thrown by the JTA implementation
0941: * @see #doJtaBegin
0942: * @see JtaTransactionObject#getUserTransaction()
0943: * @see #getTransactionManager()
0944: */
0945: protected void applyIsolationLevel(JtaTransactionObject txObject,
0946: int isolationLevel) throws InvalidIsolationLevelException,
0947: SystemException {
0948:
0949: if (!this .allowCustomIsolationLevels
0950: && isolationLevel != TransactionDefinition.ISOLATION_DEFAULT) {
0951: throw new InvalidIsolationLevelException(
0952: "JtaTransactionManager does not support custom isolation levels by default - "
0953: + "switch 'allowCustomIsolationLevels' to 'true'");
0954: }
0955: }
0956:
0957: /**
0958: * Apply the given transaction timeout. The default implementation will call
0959: * <code>UserTransaction.setTransactionTimeout</code> for a non-default timeout value.
0960: * @param txObject the JtaTransactionObject containing the UserTransaction
0961: * @param timeout timeout value taken from transaction definition
0962: * @throws SystemException if thrown by the JTA implementation
0963: * @see #doJtaBegin
0964: * @see JtaTransactionObject#getUserTransaction()
0965: * @see javax.transaction.UserTransaction#setTransactionTimeout(int)
0966: */
0967: protected void applyTimeout(JtaTransactionObject txObject,
0968: int timeout) throws SystemException {
0969: if (timeout > TransactionDefinition.TIMEOUT_DEFAULT) {
0970: txObject.getUserTransaction()
0971: .setTransactionTimeout(timeout);
0972: }
0973: }
0974:
0975: protected Object doSuspend(Object transaction) {
0976: JtaTransactionObject txObject = (JtaTransactionObject) transaction;
0977: try {
0978: return doJtaSuspend(txObject);
0979: } catch (SystemException ex) {
0980: throw new TransactionSystemException(
0981: "JTA failure on suspend", ex);
0982: }
0983: }
0984:
0985: /**
0986: * Perform a JTA suspend on the JTA TransactionManager.
0987: * <p>Can be overridden in subclasses, for specific JTA implementations.
0988: * @param txObject the JtaTransactionObject containing the UserTransaction
0989: * @return the suspended JTA Transaction object
0990: * @throws SystemException if thrown by JTA methods
0991: * @see #getTransactionManager()
0992: * @see javax.transaction.TransactionManager#suspend()
0993: */
0994: protected Object doJtaSuspend(JtaTransactionObject txObject)
0995: throws SystemException {
0996: if (getTransactionManager() == null) {
0997: throw new TransactionSuspensionNotSupportedException(
0998: "JtaTransactionManager needs a JTA TransactionManager for suspending a transaction: "
0999: + "specify the 'transactionManager' or 'transactionManagerName' property");
1000: }
1001: return getTransactionManager().suspend();
1002: }
1003:
1004: protected void doResume(Object transaction,
1005: Object suspendedResources) {
1006: JtaTransactionObject txObject = (JtaTransactionObject) transaction;
1007: try {
1008: doJtaResume(txObject, suspendedResources);
1009: } catch (InvalidTransactionException ex) {
1010: throw new IllegalTransactionStateException(
1011: "Tried to resume invalid JTA transaction", ex);
1012: } catch (SystemException ex) {
1013: throw new TransactionSystemException(
1014: "JTA failure on resume", ex);
1015: }
1016: }
1017:
1018: /**
1019: * Perform a JTA resume on the JTA TransactionManager.
1020: * <p>Can be overridden in subclasses, for specific JTA implementations.
1021: * @param txObject the JtaTransactionObject containing the UserTransaction
1022: * @param suspendedTransaction the suspended JTA Transaction object
1023: * @throws InvalidTransactionException if thrown by JTA methods
1024: * @throws SystemException if thrown by JTA methods
1025: * @see #getTransactionManager()
1026: * @see javax.transaction.TransactionManager#resume(javax.transaction.Transaction)
1027: */
1028: protected void doJtaResume(JtaTransactionObject txObject,
1029: Object suspendedTransaction)
1030: throws InvalidTransactionException, SystemException {
1031:
1032: if (getTransactionManager() == null) {
1033: throw new TransactionSuspensionNotSupportedException(
1034: "JtaTransactionManager needs a JTA TransactionManager for suspending a transaction: "
1035: + "specify the 'transactionManager' or 'transactionManagerName' property");
1036: }
1037: getTransactionManager().resume(
1038: (Transaction) suspendedTransaction);
1039: }
1040:
1041: /**
1042: * This implementation returns "true": a JTA commit will properly handle
1043: * transactions that have been marked rollback-only at a global level.
1044: */
1045: protected boolean shouldCommitOnGlobalRollbackOnly() {
1046: return true;
1047: }
1048:
1049: protected void doCommit(DefaultTransactionStatus status) {
1050: JtaTransactionObject txObject = (JtaTransactionObject) status
1051: .getTransaction();
1052: try {
1053: txObject.getUserTransaction().commit();
1054: } catch (RollbackException ex) {
1055: throw new UnexpectedRollbackException(
1056: "JTA transaction unexpectedly rolled back (maybe due to a timeout)",
1057: ex);
1058: } catch (HeuristicMixedException ex) {
1059: throw new HeuristicCompletionException(
1060: HeuristicCompletionException.STATE_MIXED, ex);
1061: } catch (HeuristicRollbackException ex) {
1062: throw new HeuristicCompletionException(
1063: HeuristicCompletionException.STATE_ROLLED_BACK, ex);
1064: } catch (SystemException ex) {
1065: throw new TransactionSystemException(
1066: "JTA failure on commit", ex);
1067: }
1068: }
1069:
1070: protected void doRollback(DefaultTransactionStatus status) {
1071: JtaTransactionObject txObject = (JtaTransactionObject) status
1072: .getTransaction();
1073: try {
1074: if (txObject.getUserTransaction().getStatus() != Status.STATUS_NO_TRANSACTION) {
1075: txObject.getUserTransaction().rollback();
1076: }
1077: } catch (SystemException ex) {
1078: throw new TransactionSystemException(
1079: "JTA failure on rollback", ex);
1080: }
1081: }
1082:
1083: protected void doSetRollbackOnly(DefaultTransactionStatus status) {
1084: JtaTransactionObject txObject = (JtaTransactionObject) status
1085: .getTransaction();
1086: if (status.isDebug()) {
1087: logger.debug("Setting JTA transaction rollback-only");
1088: }
1089: try {
1090: if (txObject.getUserTransaction().getStatus() != Status.STATUS_NO_TRANSACTION) {
1091: txObject.getUserTransaction().setRollbackOnly();
1092: }
1093: } catch (IllegalStateException ex) {
1094: throw new NoTransactionException(
1095: "No active JTA transaction");
1096: } catch (SystemException ex) {
1097: throw new TransactionSystemException(
1098: "JTA failure on setRollbackOnly", ex);
1099: }
1100: }
1101:
1102: protected void registerAfterCompletionWithExistingTransaction(
1103: Object transaction, List synchronizations) {
1104: JtaTransactionObject txObject = (JtaTransactionObject) transaction;
1105: logger
1106: .debug("Registering after-completion synchronization with existing JTA transaction");
1107: try {
1108: doRegisterAfterCompletionWithJtaTransaction(txObject,
1109: synchronizations);
1110: } catch (RollbackException ex) {
1111: logger
1112: .debug("Participating in existing JTA transaction that has been marked rollback-only: "
1113: + "cannot register Spring after-completion callbacks with outer JTA transaction - "
1114: + "immediately performing Spring after-completion callbacks with outcome status 'rollback'");
1115: invokeAfterCompletion(synchronizations,
1116: TransactionSynchronization.STATUS_ROLLED_BACK);
1117: } catch (IllegalStateException ex) {
1118: logger
1119: .debug("Participating in existing JTA transaction, but no JTA transaction active anymore: "
1120: + "cannot register Spring after-completion callbacks with outer JTA transaction - "
1121: + "processing Spring after-completion callbacks with outcome status 'unknown'");
1122: invokeAfterCompletion(synchronizations,
1123: TransactionSynchronization.STATUS_UNKNOWN);
1124: } catch (SystemException ex) {
1125: throw new TransactionSystemException(
1126: "JTA failure on registerSynchronization", ex);
1127: }
1128: }
1129:
1130: /**
1131: * Register a JTA synchronization on the JTA TransactionManager, for calling
1132: * <code>afterCompletion</code> on the given Spring TransactionSynchronizations.
1133: * <p>The default implementation registers the synchronizations on the
1134: * JTA 1.1 TransactionSynchronizationRegistry, if available, or on the
1135: * JTA TransactionManager's current Transaction - again, if available.
1136: * If none of the two is available, a warning will be logged.
1137: * <p>Can be overridden in subclasses, for specific JTA implementations.
1138: * @param txObject the current transaction object
1139: * @param synchronizations List of TransactionSynchronization objects
1140: * @throws RollbackException if thrown by JTA methods
1141: * @throws SystemException if thrown by JTA methods
1142: * @see #getTransactionManager()
1143: * @see javax.transaction.Transaction#registerSynchronization
1144: * @see javax.transaction.TransactionSynchronizationRegistry#registerInterposedSynchronization
1145: */
1146: protected void doRegisterAfterCompletionWithJtaTransaction(
1147: JtaTransactionObject txObject, List synchronizations)
1148: throws RollbackException, SystemException {
1149:
1150: if (this .transactionSynchronizationRegistry != null) {
1151: // JTA 1.1 TransactionSynchronizationRegistry available - use it.
1152: new InterposedSynchronizationDelegate()
1153: .registerInterposedSynchronization(new JtaAfterCompletionSynchronization(
1154: synchronizations));
1155: }
1156:
1157: else if (getTransactionManager() != null) {
1158: // At least the JTA TransactionManager available - use that one.
1159: Transaction transaction = getTransactionManager()
1160: .getTransaction();
1161: if (transaction != null) {
1162: transaction
1163: .registerSynchronization(new JtaAfterCompletionSynchronization(
1164: synchronizations));
1165: } else {
1166: // No current JTA Transaction available - log a warning.
1167: logger
1168: .debug("Participating in existing JTA transaction, but no current JTA Transaction available: "
1169: + "cannot register Spring after-completion callbacks with outer JTA transaction - "
1170: + "processing Spring after-completion callbacks with outcome status 'unknown'");
1171: invokeAfterCompletion(synchronizations,
1172: TransactionSynchronization.STATUS_UNKNOWN);
1173: }
1174: }
1175:
1176: else {
1177: // No JTA TransactionManager available - log a warning.
1178: logger
1179: .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,
1183: TransactionSynchronization.STATUS_UNKNOWN);
1184: }
1185: }
1186:
1187: //---------------------------------------------------------------------
1188: // Implementation of TransactionFactory interface
1189: //---------------------------------------------------------------------
1190:
1191: public Transaction createTransaction(String name, int timeout)
1192: throws NotSupportedException, SystemException {
1193: TransactionManager tm = getTransactionManager();
1194: Assert.state(tm != null, "No JTA TransactionManager available");
1195: if (timeout >= 0) {
1196: tm.setTransactionTimeout(timeout);
1197: }
1198: tm.begin();
1199: return tm.getTransaction();
1200: }
1201:
1202: //---------------------------------------------------------------------
1203: // Serialization support
1204: //---------------------------------------------------------------------
1205:
1206: private void readObject(ObjectInputStream ois) throws IOException,
1207: ClassNotFoundException {
1208: // Rely on default serialization; just initialize state after deserialization.
1209: ois.defaultReadObject();
1210:
1211: // Create template for client-side JNDI lookup.
1212: this .jndiTemplate = new JndiTemplate();
1213:
1214: // Perform a fresh lookup for JTA handles.
1215: initUserTransactionAndTransactionManager();
1216: initTransactionSynchronizationRegistry();
1217: }
1218:
1219: /**
1220: * Inner class to avoid a direct dependency on the JTA 1.1 API
1221: * (javax.transaction.TransactionSynchronizationRegistry interface).
1222: */
1223: private class InterposedSynchronizationDelegate {
1224:
1225: public void registerInterposedSynchronization(
1226: Synchronization synch) {
1227: ((TransactionSynchronizationRegistry) transactionSynchronizationRegistry)
1228: .registerInterposedSynchronization(synch);
1229: }
1230: }
1231:
1232: }
|