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.SystemException;
0033: import javax.transaction.Transaction;
0034: import javax.transaction.TransactionManager;
0035: import javax.transaction.UserTransaction;
0036:
0037: import org.springframework.beans.factory.InitializingBean;
0038: import org.springframework.jndi.JndiTemplate;
0039: import org.springframework.transaction.CannotCreateTransactionException;
0040: import org.springframework.transaction.HeuristicCompletionException;
0041: import org.springframework.transaction.IllegalTransactionStateException;
0042: import org.springframework.transaction.InvalidIsolationLevelException;
0043: import org.springframework.transaction.NestedTransactionNotSupportedException;
0044: import org.springframework.transaction.NoTransactionException;
0045: import org.springframework.transaction.TransactionDefinition;
0046: import org.springframework.transaction.TransactionSuspensionNotSupportedException;
0047: import org.springframework.transaction.TransactionSystemException;
0048: import org.springframework.transaction.UnexpectedRollbackException;
0049: import org.springframework.transaction.support.AbstractPlatformTransactionManager;
0050: import org.springframework.transaction.support.DefaultTransactionStatus;
0051: import org.springframework.transaction.support.TransactionSynchronization;
0052: import org.springframework.util.Assert;
0053:
0054: /**
0055: * {@link org.springframework.transaction.PlatformTransactionManager} implementation
0056: * for JTA, delegating to a backend JTA provider. This is typically used to delegate
0057: * to a J2EE server's transaction coordinator, but may also be configured with a
0058: * local JTA provider which is embedded within the application.
0059: *
0060: * <p>This transaction manager is appropriate for handling distributed transactions,
0061: * i.e. transactions that span multiple resources, and for controlling transactions on
0062: * application server resources (e.g. JDBC DataSources available in JNDI) in general.
0063: * For a single JDBC DataSource, DataSourceTransactionManager is perfectly sufficient,
0064: * and for accessing a single resource with Hibernate (including transactional cache),
0065: * HibernateTransactionManager is appropriate, for example.
0066: *
0067: * <p>Transaction synchronization is active by default, to allow data access support
0068: * classes to register resources that are opened within the transaction for closing at
0069: * transaction completion time. Spring's support classes for JDBC, Hibernate, JDO etc
0070: * all perform such registration, allowing for reuse of the same Hibernate Session etc
0071: * within the transaction. Standard JTA does not even guarantee that for Connections
0072: * from a transactional JDBC DataSource: Spring's synchronization solves those issues.
0073: *
0074: * <p><b>For typical JTA transactions (REQUIRED, SUPPORTS, MANDATORY, NEVER), a plain
0075: * JtaTransactionManager definition is all you need, completely portable across all
0076: * J2EE servers.</b> This corresponds to the functionality of the JTA UserTransaction,
0077: * for which J2EE specifies a standard JNDI name ("java:comp/UserTransaction").
0078: * There is no need to configure a server-specific TransactionManager lookup for this
0079: * kind of JTA usage.
0080: *
0081: * <p><b>Note: Advanced JTA usage below. Dealing with these mechanisms is not
0082: * necessary for typical usage scenarios.</b>
0083: *
0084: * <p>Transaction suspension (REQUIRES_NEW, NOT_SUPPORTED) is just available with
0085: * a JTA TransactionManager being registered, via the "transactionManagerName" or
0086: * "transactionManager" property. The location of this well-defined JTA object is
0087: * <i>not</i> specified by J2EE; it is specific to each J2EE server, often kept
0088: * in JNDI like the JTA UserTransaction. Some well-known JNDI locations are:
0089: * <ul>
0090: * <li>"java:comp/UserTransaction" for Resin 2.x, Oracle OC4J (Orion),
0091: * JOnAS (JOTM), BEA WebLogic
0092: * <li>"java:comp/TransactionManager" for Resin 3.x
0093: * <li>"java:pm/TransactionManager" for Borland Enterprise Server and
0094: * Sun Application Server (Sun ONE 7 and later)
0095: * <li>"java:/TransactionManager" for JBoss Application Server
0096: * </ul>
0097: *
0098: * <p>All of these cases are autodetected by JtaTransactionManager (since Spring 1.2),
0099: * provided that the "autodetectTransactionManager" flag is set to "true" (which it is
0100: * by default). Consequently, JtaTransactionManager will support transaction suspension
0101: * out-of-the-box on many J2EE servers.
0102: *
0103: * <p>A JNDI lookup can also be factored out into a corresponding
0104: * {@link org.springframework.jndi.JndiObjectFactoryBean}, passed into
0105: * JtaTransactionManager's "transactionManager" property. Such a bean definition
0106: * can then be reused by other objects, for example Spring's LocalSessionFactoryBean
0107: * for Hibernate.
0108: *
0109: * <p>For IBM WebSphere and standalone JOTM, static accessor methods are required to
0110: * obtain the JTA TransactionManager: Therefore, WebSphere and JOTM have their own
0111: * FactoryBean implementations, to be wired with the "transactionManager" property.
0112: * In case of {@link JotmFactoryBean}, the same JTA object implements UserTransaction
0113: * too: Therefore, passing the object to the "userTransaction" property is sufficient.
0114: *
0115: * <p>It is also possible to specify a JTA TransactionManager only, either through
0116: * the corresponding constructor or through the "transactionManager" property.
0117: * In the latter case, the "userTransactionName" property needs to be set to <code>null</code>,
0118: * to suppress the default "java:comp/UserTransaction" JNDI lookup and thus enforce
0119: * use of the given JTA TransactionManager even for begin, commit and rollback.
0120: *
0121: * <p><b>Note: Support for the JTA TransactionManager interface is not required by J2EE.
0122: * Almost all J2EE servers expose it, but do so as extension to J2EE. There might be some
0123: * issues with compatibility, despite the TransactionManager interface being part of JTA.</b>
0124: * The only currently known problem is resuming a transaction on WebLogic, which by default
0125: * fails if the suspended transaction was marked rollback-only; for other usages, it works
0126: * properly. Use Spring's {@link WebLogicJtaTransactionManager} to address this issue.
0127: *
0128: * <p><b>This standard JtaTransactionManager supports timeouts but not per-transaction
0129: * isolation levels.</b> Custom subclasses may override {@link #doJtaBegin} for
0130: * specific JTA extensions in order to provide this functionality; Spring includes
0131: * corresponding {@link WebLogicJtaTransactionManager} and {@link OC4JJtaTransactionManager}
0132: * classes, for BEA's WebLogic Server and Oracle's OC4J, respectively. Such adapters
0133: * for specific J2EE transaction coordinators may also expose transaction names for
0134: * monitoring (both of the above do); with standard JTA, transaction names will simply
0135: * be ignored.
0136: *
0137: * <p>This class is serializable. However, active synchronizations do not survive
0138: * serialization.
0139: *
0140: * @author Juergen Hoeller
0141: * @since 24.03.2003
0142: * @see #setUserTransactionName
0143: * @see #setUserTransaction
0144: * @see #setTransactionManagerName
0145: * @see #setTransactionManager
0146: * @see #doJtaBegin
0147: * @see JotmFactoryBean
0148: * @see WebSphereTransactionManagerFactoryBean
0149: * @see WebLogicJtaTransactionManager
0150: * @see org.springframework.jndi.JndiObjectFactoryBean
0151: * @see org.springframework.orm.hibernate.LocalSessionFactoryBean#setJtaTransactionManager
0152: */
0153: public class JtaTransactionManager extends
0154: AbstractPlatformTransactionManager implements InitializingBean,
0155: Serializable {
0156:
0157: /**
0158: * Default JNDI location for the JTA UserTransaction. Many J2EE servers
0159: * also provide support for the JTA TransactionManager interface there.
0160: * @see #setUserTransactionName
0161: * @see #setAutodetectTransactionManager
0162: */
0163: public static final String DEFAULT_USER_TRANSACTION_NAME = "java:comp/UserTransaction";
0164:
0165: /**
0166: * Fallback JNDI locations for the JTA TransactionManager. Applied if
0167: * the JTA UserTransaction does not implement the JTA TransactionManager
0168: * interface, provided that the "autodetectTransactionManager" flag is "true".
0169: * @see #setTransactionManagerName
0170: * @see #setAutodetectTransactionManager
0171: */
0172: public static final String[] FALLBACK_TRANSACTION_MANAGER_NAMES = new String[] {
0173: "java:comp/TransactionManager",
0174: "java:pm/TransactionManager", "java:/TransactionManager" };
0175:
0176: private transient JndiTemplate jndiTemplate = new JndiTemplate();
0177:
0178: private transient UserTransaction userTransaction;
0179:
0180: private String userTransactionName;
0181:
0182: private boolean autodetectUserTransaction = true;
0183:
0184: private boolean cacheUserTransaction = true;
0185:
0186: private transient TransactionManager transactionManager;
0187:
0188: private String transactionManagerName;
0189:
0190: private boolean autodetectTransactionManager = true;
0191:
0192: private boolean allowCustomIsolationLevels = false;
0193:
0194: /**
0195: * Create a new JtaTransactionManager instance, to be configured as bean.
0196: * Invoke <code>afterPropertiesSet</code> to activate the configuration.
0197: * @see #setUserTransactionName
0198: * @see #setUserTransaction
0199: * @see #setTransactionManagerName
0200: * @see #setTransactionManager
0201: * @see #afterPropertiesSet()
0202: */
0203: public JtaTransactionManager() {
0204: setNestedTransactionAllowed(true);
0205: }
0206:
0207: /**
0208: * Create a new JtaTransactionManager instance.
0209: * @param userTransaction the JTA UserTransaction to use as direct reference
0210: */
0211: public JtaTransactionManager(UserTransaction userTransaction) {
0212: this ();
0213: Assert.notNull(userTransaction,
0214: "UserTransaction must not be null");
0215: this .userTransaction = userTransaction;
0216: }
0217:
0218: /**
0219: * Create a new JtaTransactionManager instance.
0220: * @param userTransaction the JTA UserTransaction to use as direct reference
0221: * @param transactionManager the JTA TransactionManager to use as direct reference
0222: */
0223: public JtaTransactionManager(UserTransaction userTransaction,
0224: TransactionManager transactionManager) {
0225: this ();
0226: Assert.notNull(userTransaction,
0227: "UserTransaction must not be null");
0228: Assert.notNull(transactionManager,
0229: "TransactionManager must not be null");
0230: this .userTransaction = userTransaction;
0231: this .transactionManager = transactionManager;
0232: }
0233:
0234: /**
0235: * Create a new JtaTransactionManager instance.
0236: * @param transactionManager the JTA TransactionManager to use as direct reference
0237: */
0238: public JtaTransactionManager(TransactionManager transactionManager) {
0239: this ();
0240: Assert.notNull(transactionManager,
0241: "TransactionManager must not be null");
0242: this .transactionManager = transactionManager;
0243: this .userTransaction = buildUserTransaction(transactionManager);
0244: }
0245:
0246: /**
0247: * Set the JndiTemplate to use for JNDI lookups.
0248: * A default one is used if not set.
0249: */
0250: public void setJndiTemplate(JndiTemplate jndiTemplate) {
0251: if (jndiTemplate == null) {
0252: throw new IllegalArgumentException(
0253: "jndiTemplate must not be null");
0254: }
0255: this .jndiTemplate = jndiTemplate;
0256: }
0257:
0258: /**
0259: * Return the JndiTemplate used for JNDI lookups.
0260: */
0261: public JndiTemplate getJndiTemplate() {
0262: return this .jndiTemplate;
0263: }
0264:
0265: /**
0266: * Set the JNDI environment to use for JNDI lookups.
0267: * Creates a JndiTemplate with the given environment settings.
0268: * @see #setJndiTemplate
0269: */
0270: public void setJndiEnvironment(Properties jndiEnvironment) {
0271: this .jndiTemplate = new JndiTemplate(jndiEnvironment);
0272: }
0273:
0274: /**
0275: * Return the JNDI environment to use for JNDI lookups.
0276: */
0277: public Properties getJndiEnvironment() {
0278: return this .jndiTemplate.getEnvironment();
0279: }
0280:
0281: /**
0282: * Set the JTA UserTransaction to use as direct reference.
0283: * <p>Typically just used for local JTA setups; in a J2EE environment,
0284: * the UserTransaction will always be fetched from JNDI.
0285: * @see #setUserTransactionName
0286: * @see #setAutodetectUserTransaction
0287: */
0288: public void setUserTransaction(UserTransaction userTransaction) {
0289: this .userTransaction = userTransaction;
0290: }
0291:
0292: /**
0293: * Return the JTA UserTransaction that this transaction manager uses.
0294: */
0295: public UserTransaction getUserTransaction() {
0296: return this .userTransaction;
0297: }
0298:
0299: /**
0300: * Set the JNDI name of the JTA UserTransaction.
0301: * <p>Note that the UserTransaction will be autodetected at the J2EE default
0302: * location "java:comp/UserTransaction" if not specified explicitly.
0303: * @see #DEFAULT_USER_TRANSACTION_NAME
0304: * @see #setUserTransaction
0305: * @see #setAutodetectUserTransaction
0306: */
0307: public void setUserTransactionName(String userTransactionName) {
0308: this .userTransactionName = userTransactionName;
0309: }
0310:
0311: /**
0312: * Set whether to autodetect the JTA UserTransaction at its default
0313: * JNDI location "java:comp/UserTransaction", as specified by J2EE.
0314: * Will proceed without UserTransaction if none found.
0315: * <p>Default is "true", autodetecting the UserTransaction unless
0316: * it has been specified explicitly. Turn this flag off to allow for
0317: * JtaTransactionManager operating against the TransactionManager only,
0318: * despite a default UserTransaction being available.
0319: * @see #DEFAULT_USER_TRANSACTION_NAME
0320: */
0321: public void setAutodetectUserTransaction(
0322: boolean autodetectUserTransaction) {
0323: this .autodetectUserTransaction = autodetectUserTransaction;
0324: }
0325:
0326: /**
0327: * Set whether to cache the JTA UserTransaction object fetched from JNDI.
0328: * <p>Default is "true": UserTransaction lookup will only happen at startup,
0329: * reusing the same UserTransaction handle for all transactions of all threads.
0330: * This is the most efficient choice for all application servers that provide
0331: * a shared UserTransaction object (the typical case).
0332: * <p>Turn this flag off to enforce a fresh lookup of the UserTransaction
0333: * for every transaction. This is only necessary for application servers
0334: * that return a new UserTransaction for every transaction, keeping state
0335: * tied to the UserTransaction object itself rather than the current thread.
0336: * @see #setUserTransactionName
0337: */
0338: public void setCacheUserTransaction(boolean cacheUserTransaction) {
0339: this .cacheUserTransaction = cacheUserTransaction;
0340: }
0341:
0342: /**
0343: * Set the JTA TransactionManager to use as direct reference.
0344: * <p>A TransactionManager is necessary for suspending and resuming transactions,
0345: * as this not supported by the UserTransaction interface.
0346: * <p>Note that the TransactionManager will be autodetected if the JTA
0347: * UserTransaction object implements the JTA TransactionManager interface too,
0348: * as well as autodetected at various well-known fallback JNDI locations.
0349: * @see #setTransactionManagerName
0350: * @see #setAutodetectTransactionManager
0351: */
0352: public void setTransactionManager(
0353: TransactionManager transactionManager) {
0354: this .transactionManager = transactionManager;
0355: }
0356:
0357: /**
0358: * Return the JTA TransactionManager that this transaction manager uses.
0359: */
0360: public TransactionManager getTransactionManager() {
0361: return this .transactionManager;
0362: }
0363:
0364: /**
0365: * Set the JNDI name of the JTA TransactionManager.
0366: * <p>A TransactionManager is necessary for suspending and resuming transactions,
0367: * as this not supported by the UserTransaction interface.
0368: * <p>Note that the TransactionManager will be autodetected if the JTA
0369: * UserTransaction object implements the JTA TransactionManager interface too,
0370: * as well as autodetected at various well-known fallback JNDI locations.
0371: * @see #setTransactionManager
0372: * @see #setAutodetectTransactionManager
0373: */
0374: public void setTransactionManagerName(String transactionManagerName) {
0375: this .transactionManagerName = transactionManagerName;
0376: }
0377:
0378: /**
0379: * Set whether to autodetect a JTA UserTransaction object that implements
0380: * the JTA TransactionManager interface too (i.e. the JNDI location for the
0381: * TransactionManager is "java:comp/UserTransaction", same as for the UserTransaction).
0382: * Also checks the fallback JNDI locations "java:comp/TransactionManager" and
0383: * "java:/TransactionManager". Will proceed without TransactionManager if none found.
0384: * <p>Default is "true", autodetecting the TransactionManager unless it has been
0385: * specified explicitly. Can be turned off to deliberately ignore an available
0386: * TransactionManager, for example when there are known issues with suspend/resume
0387: * and any attempt to use REQUIRES_NEW or NOT_SUPPORTED should fail fast.
0388: * @see #FALLBACK_TRANSACTION_MANAGER_NAMES
0389: */
0390: public void setAutodetectTransactionManager(
0391: boolean autodetectTransactionManager) {
0392: this .autodetectTransactionManager = autodetectTransactionManager;
0393: }
0394:
0395: /**
0396: * Set whether to allow custom isolation levels to be specified.
0397: * <p>Default is "false", throwing an exception if a non-default isolation level
0398: * is specified for a transaction. Turn this flag on if affected resource adapters
0399: * check the thread-bound transaction context and apply the specified isolation
0400: * levels individually (e.g. through a IsolationLevelDataSourceRouter).
0401: * @see org.springframework.jdbc.datasource.lookup.IsolationLevelDataSourceRouter
0402: */
0403: public void setAllowCustomIsolationLevels(
0404: boolean allowCustomIsolationLevels) {
0405: this .allowCustomIsolationLevels = allowCustomIsolationLevels;
0406: }
0407:
0408: /**
0409: * Initialize the UserTransaction as well as the TransactionManager handle.
0410: * @see #initUserTransactionAndTransactionManager()
0411: */
0412: public void afterPropertiesSet() throws TransactionSystemException {
0413: initUserTransactionAndTransactionManager();
0414: }
0415:
0416: /**
0417: * Initialize the UserTransaction as well as the TransactionManager handle.
0418: * @throws TransactionSystemException if initialization failed
0419: */
0420: protected void initUserTransactionAndTransactionManager()
0421: throws TransactionSystemException {
0422: // Fetch JTA UserTransaction from JNDI, if necessary.
0423: if (this .userTransaction == null) {
0424: if (this .userTransactionName != null) {
0425: this .userTransaction = lookupUserTransaction(this .userTransactionName);
0426: } else {
0427: this .userTransaction = retrieveUserTransaction();
0428: }
0429: }
0430:
0431: // Fetch JTA TransactionManager from JNDI, if necessary.
0432: if (this .transactionManager == null) {
0433: if (this .transactionManagerName != null) {
0434: this .transactionManager = lookupTransactionManager(this .transactionManagerName);
0435: } else {
0436: this .transactionManager = retrieveTransactionManager();
0437: }
0438: }
0439:
0440: // Autodetect UserTransaction at its default JNDI location.
0441: if (this .userTransaction == null
0442: && this .autodetectUserTransaction) {
0443: this .userTransaction = findUserTransaction();
0444: }
0445:
0446: // Autodetect UserTransaction object that implements TransactionManager,
0447: // and check fallback JNDI locations else.
0448: if (this .transactionManager == null
0449: && this .autodetectTransactionManager) {
0450: this .transactionManager = findTransactionManager(this .userTransaction);
0451: }
0452:
0453: // If only JTA TransactionManager specified, create UserTransaction handle for it.
0454: if (this .userTransaction == null
0455: && this .transactionManager != null) {
0456: this .userTransaction = buildUserTransaction(this .transactionManager);
0457: }
0458:
0459: // We at least need the JTA UserTransaction.
0460: if (this .userTransaction != null) {
0461: if (logger.isInfoEnabled()) {
0462: logger.info("Using JTA UserTransaction: "
0463: + this .userTransaction);
0464: }
0465: } else {
0466: throw new IllegalStateException(
0467: "Either 'userTransaction' or 'userTransactionName' or 'transactionManager' "
0468: + "or 'transactionManagerName' must be specified");
0469: }
0470:
0471: // For transaction suspension, the JTA TransactionManager is necessary too.
0472: if (this .transactionManager != null) {
0473: if (logger.isInfoEnabled()) {
0474: logger.info("Using JTA TransactionManager: "
0475: + this .transactionManager);
0476: }
0477: } else {
0478: logger
0479: .warn("No JTA TransactionManager found: "
0480: + "transaction suspension and synchronization with existing JTA transactions not available");
0481: }
0482: }
0483:
0484: /**
0485: * Look up the JTA UserTransaction in JNDI via the configured name.
0486: * Called by <code>afterPropertiesSet</code> if no direct UserTransaction reference was set.
0487: * Can be overridden in subclasses to provide a different UserTransaction object.
0488: * @param userTransactionName the JNDI name of the UserTransaction
0489: * @return the UserTransaction object
0490: * @throws TransactionSystemException if the JNDI lookup failed
0491: * @see #setJndiTemplate
0492: * @see #setUserTransactionName
0493: */
0494: protected UserTransaction lookupUserTransaction(
0495: String userTransactionName)
0496: throws TransactionSystemException {
0497: try {
0498: if (logger.isDebugEnabled()) {
0499: logger
0500: .debug("Retrieving JTA UserTransaction from JNDI location ["
0501: + userTransactionName + "]");
0502: }
0503: return (UserTransaction) getJndiTemplate().lookup(
0504: userTransactionName, UserTransaction.class);
0505: } catch (NamingException ex) {
0506: throw new TransactionSystemException(
0507: "JTA UserTransaction is not available at JNDI location ["
0508: + userTransactionName + "]", ex);
0509: }
0510: }
0511:
0512: /**
0513: * Look up the JTA TransactionManager in JNDI via the configured name.
0514: * Called by <code>afterPropertiesSet</code> if no direct TransactionManager reference was set.
0515: * Can be overridden in subclasses to provide a different TransactionManager object.
0516: * @param transactionManagerName the JNDI name of the TransactionManager
0517: * @return the UserTransaction object
0518: * @throws TransactionSystemException if the JNDI lookup failed
0519: * @see #setJndiTemplate
0520: * @see #setTransactionManagerName
0521: */
0522: protected TransactionManager lookupTransactionManager(
0523: String transactionManagerName)
0524: throws TransactionSystemException {
0525: try {
0526: if (logger.isDebugEnabled()) {
0527: logger
0528: .debug("Retrieving JTA TransactionManager from JNDI location ["
0529: + transactionManagerName + "]");
0530: }
0531: return (TransactionManager) getJndiTemplate().lookup(
0532: transactionManagerName, TransactionManager.class);
0533: } catch (NamingException ex) {
0534: throw new TransactionSystemException(
0535: "JTA TransactionManager is not available at JNDI location ["
0536: + transactionManagerName + "]", ex);
0537: }
0538: }
0539:
0540: /**
0541: * Allows subclasses to retrieve the JTA UserTransaction in a vendor-specific manner.
0542: * Only called if no "userTransaction" or "userTransactionName" specified.
0543: * <p>The default implementation simply returns <code>null</code>.
0544: * @return the JTA UserTransaction handle to use, or <code>null</code> if none found
0545: * @throws TransactionSystemException in case of errors
0546: * @see #setUserTransaction
0547: * @see #setUserTransactionName
0548: */
0549: protected UserTransaction retrieveUserTransaction()
0550: throws TransactionSystemException {
0551: return null;
0552: }
0553:
0554: /**
0555: * Allows subclasses to retrieve the JTA TransactionManager in a vendor-specific manner.
0556: * Only called if no "transactionManager" or "transactionManagerName" specified.
0557: * <p>The default implementation simply returns <code>null</code>.
0558: * @return the JTA TransactionManager handle to use, or <code>null</code> if none found
0559: * @throws TransactionSystemException in case of errors
0560: * @see #setTransactionManager
0561: * @see #setTransactionManagerName
0562: */
0563: protected TransactionManager retrieveTransactionManager()
0564: throws TransactionSystemException {
0565: return null;
0566: }
0567:
0568: /**
0569: * Find the JTA UserTransaction through a default JNDI lookup:
0570: * "java:comp/UserTransaction".
0571: * @return the JTA UserTransaction reference, or <code>null</code> if not found
0572: * @see #DEFAULT_USER_TRANSACTION_NAME
0573: */
0574: protected UserTransaction findUserTransaction() {
0575: String jndiName = DEFAULT_USER_TRANSACTION_NAME;
0576: try {
0577: UserTransaction ut = (UserTransaction) getJndiTemplate()
0578: .lookup(jndiName, UserTransaction.class);
0579: if (logger.isDebugEnabled()) {
0580: logger
0581: .debug("JTA UserTransaction found at default JNDI location ["
0582: + jndiName + "]");
0583: }
0584: return ut;
0585: } catch (NamingException ex) {
0586: if (logger.isDebugEnabled()) {
0587: logger.debug(
0588: "No JTA UserTransaction found at default JNDI location ["
0589: + jndiName + "]", ex);
0590: }
0591: return null;
0592: }
0593: }
0594:
0595: /**
0596: * Find the JTA TransactionManager through autodetection: checking whether the
0597: * UserTransaction object implements the TransactionManager, and checking the
0598: * fallback JNDI locations.
0599: * @param ut the JTA UserTransaction object
0600: * @return the JTA TransactionManager reference, or <code>null</code> if not found
0601: * @see #FALLBACK_TRANSACTION_MANAGER_NAMES
0602: */
0603: protected TransactionManager findTransactionManager(
0604: UserTransaction ut) {
0605: if (ut instanceof TransactionManager) {
0606: if (logger.isDebugEnabled()) {
0607: logger.debug("JTA UserTransaction object [" + ut
0608: + "] implements TransactionManager");
0609: }
0610: return (TransactionManager) ut;
0611: }
0612:
0613: // Check fallback JNDI locations.
0614: for (int i = 0; i < FALLBACK_TRANSACTION_MANAGER_NAMES.length; i++) {
0615: String jndiName = FALLBACK_TRANSACTION_MANAGER_NAMES[i];
0616: try {
0617: TransactionManager tm = (TransactionManager) getJndiTemplate()
0618: .lookup(jndiName, TransactionManager.class);
0619: if (logger.isDebugEnabled()) {
0620: logger
0621: .debug("JTA TransactionManager found at fallback JNDI location ["
0622: + jndiName + "]");
0623: }
0624: return tm;
0625: } catch (NamingException ex) {
0626: if (logger.isDebugEnabled()) {
0627: logger.debug(
0628: "No JTA TransactionManager found at fallback JNDI location ["
0629: + jndiName + "]", ex);
0630: }
0631: }
0632: }
0633:
0634: // OK, so no JTA TransactionManager is available...
0635: return null;
0636: }
0637:
0638: /**
0639: * Build a UserTransaction handle based on the given TransactionManager.
0640: * @param transactionManager the TransactionManager
0641: * @return a corresponding UserTransaction handle
0642: */
0643: protected UserTransaction buildUserTransaction(
0644: TransactionManager transactionManager) {
0645: if (transactionManager instanceof UserTransaction) {
0646: return (UserTransaction) transactionManager;
0647: } else {
0648: return new UserTransactionAdapter(transactionManager);
0649: }
0650: }
0651:
0652: /**
0653: * This implementation returns a JtaTransactionObject instance for the
0654: * JTA UserTransaction.
0655: * <p>The UserTransaction object will either be looked up freshly for the
0656: * current transaction, or the cached one looked up at startup will be used.
0657: * The latter is the default: Most application servers use a shared singleton
0658: * UserTransaction that can be cached. Turn off the "cacheUserTransaction"
0659: * flag to enforce a fresh lookup for every transaction.
0660: * @see #setCacheUserTransaction
0661: */
0662: protected Object doGetTransaction() {
0663: UserTransaction ut = getUserTransaction();
0664: if (!this .cacheUserTransaction) {
0665: ut = lookupUserTransaction(this .userTransactionName != null ? this .userTransactionName
0666: : DEFAULT_USER_TRANSACTION_NAME);
0667: }
0668: return doGetJtaTransaction(ut);
0669: }
0670:
0671: /**
0672: * Get a JTA transaction object for the given current UserTransaction.
0673: * <p>Subclasses can override this to provide a JtaTransactionObject
0674: * subclass, for example holding some additional JTA handle needed.
0675: * @param ut the UserTransaction handle to use for the current transaction
0676: * @return the JtaTransactionObject holding the UserTransaction
0677: */
0678: protected JtaTransactionObject doGetJtaTransaction(
0679: UserTransaction ut) {
0680: return new JtaTransactionObject(ut);
0681: }
0682:
0683: protected boolean isExistingTransaction(Object transaction) {
0684: JtaTransactionObject txObject = (JtaTransactionObject) transaction;
0685: try {
0686: return (txObject.getUserTransaction().getStatus() != Status.STATUS_NO_TRANSACTION);
0687: } catch (SystemException ex) {
0688: throw new TransactionSystemException(
0689: "JTA failure on getStatus", ex);
0690: }
0691: }
0692:
0693: /**
0694: * This implementation returns false to cause a further invocation
0695: * of doBegin despite an already existing transaction.
0696: * <p>JTA implementations might support nested transactions via further
0697: * <code>UserTransaction.begin()</code> invocations, but never support savepoints.
0698: * @see #doBegin
0699: * @see javax.transaction.UserTransaction#begin()
0700: */
0701: protected boolean useSavepointForNestedTransaction() {
0702: return false;
0703: }
0704:
0705: protected void doBegin(Object transaction,
0706: TransactionDefinition definition) {
0707: JtaTransactionObject txObject = (JtaTransactionObject) transaction;
0708: try {
0709: doJtaBegin(txObject, definition);
0710: } catch (NotSupportedException ex) {
0711: // assume nested transaction not supported
0712: throw new NestedTransactionNotSupportedException(
0713: "JTA implementation does not support nested transactions",
0714: ex);
0715: } catch (UnsupportedOperationException ex) {
0716: // assume nested transaction not supported
0717: throw new NestedTransactionNotSupportedException(
0718: "JTA implementation does not support nested transactions",
0719: ex);
0720: } catch (SystemException ex) {
0721: throw new CannotCreateTransactionException(
0722: "JTA failure on begin", ex);
0723: }
0724: }
0725:
0726: /**
0727: * Perform a JTA begin on the JTA UserTransaction or TransactionManager.
0728: * <p>This implementation only supports standard JTA functionality:
0729: * that is, no per-transaction isolation levels and no transaction names.
0730: * Can be overridden in subclasses, for specific JTA implementations.
0731: * <p>Calls <code>applyIsolationLevel</code> and <code>applyTimeout</code>
0732: * before invoking the UserTransaction's <code>begin</code> method.
0733: * @param txObject the JtaTransactionObject containing the UserTransaction
0734: * @param definition TransactionDefinition instance, describing propagation
0735: * behavior, isolation level, read-only flag, timeout, and transaction name
0736: * @throws NotSupportedException if thrown by JTA methods
0737: * @throws SystemException if thrown by JTA methods
0738: * @see #getUserTransaction
0739: * @see #getTransactionManager
0740: * @see #applyIsolationLevel
0741: * @see #applyTimeout
0742: * @see JtaTransactionObject#getUserTransaction()
0743: * @see javax.transaction.UserTransaction#setTransactionTimeout
0744: * @see javax.transaction.UserTransaction#begin
0745: */
0746: protected void doJtaBegin(JtaTransactionObject txObject,
0747: TransactionDefinition definition)
0748: throws NotSupportedException, SystemException {
0749:
0750: applyIsolationLevel(txObject, definition.getIsolationLevel());
0751: int timeout = determineTimeout(definition);
0752: applyTimeout(txObject, timeout);
0753: txObject.getUserTransaction().begin();
0754: }
0755:
0756: /**
0757: * Apply the given transaction isolation level. The default implementation
0758: * will throw an exception for any level other than ISOLATION_DEFAULT.
0759: * <p>To be overridden in subclasses for specific JTA implementations,
0760: * as alternative to overriding the full {@link #doJtaBegin} method.
0761: * @param txObject the JtaTransactionObject containing the UserTransaction
0762: * @param isolationLevel isolation level taken from transaction definition
0763: * @throws InvalidIsolationLevelException if the given isolation level
0764: * cannot be applied
0765: * @throws SystemException if thrown by the JTA implementation
0766: * @see #doJtaBegin
0767: * @see JtaTransactionObject#getUserTransaction()
0768: * @see #getTransactionManager()
0769: */
0770: protected void applyIsolationLevel(JtaTransactionObject txObject,
0771: int isolationLevel) throws InvalidIsolationLevelException,
0772: SystemException {
0773:
0774: if (!this .allowCustomIsolationLevels
0775: && isolationLevel != TransactionDefinition.ISOLATION_DEFAULT) {
0776: throw new InvalidIsolationLevelException(
0777: "JtaTransactionManager does not support custom isolation levels by default - "
0778: + "switch 'allowCustomIsolationLevels' to 'true'");
0779: }
0780: }
0781:
0782: /**
0783: * Apply the given transaction timeout. The default implementation will call
0784: * <code>UserTransaction.setTransactionTimeout</code> for a non-default timeout value.
0785: * @param txObject the JtaTransactionObject containing the UserTransaction
0786: * @param timeout timeout value taken from transaction definition
0787: * @throws SystemException if thrown by the JTA implementation
0788: * @see #doJtaBegin
0789: * @see JtaTransactionObject#getUserTransaction()
0790: * @see javax.transaction.UserTransaction#setTransactionTimeout(int)
0791: */
0792: protected void applyTimeout(JtaTransactionObject txObject,
0793: int timeout) throws SystemException {
0794: if (timeout > TransactionDefinition.TIMEOUT_DEFAULT) {
0795: txObject.getUserTransaction()
0796: .setTransactionTimeout(timeout);
0797: }
0798: }
0799:
0800: protected Object doSuspend(Object transaction) {
0801: JtaTransactionObject txObject = (JtaTransactionObject) transaction;
0802: try {
0803: return doJtaSuspend(txObject);
0804: } catch (SystemException ex) {
0805: throw new TransactionSystemException(
0806: "JTA failure on suspend", ex);
0807: }
0808: }
0809:
0810: /**
0811: * Perform a JTA suspend on the JTA TransactionManager.
0812: * <p>Can be overridden in subclasses, for specific JTA implementations.
0813: * @param txObject the JtaTransactionObject containing the UserTransaction
0814: * @return the suspended JTA Transaction object
0815: * @throws SystemException if thrown by JTA methods
0816: * @see #getTransactionManager()
0817: * @see javax.transaction.TransactionManager#suspend()
0818: */
0819: protected Object doJtaSuspend(JtaTransactionObject txObject)
0820: throws SystemException {
0821: if (getTransactionManager() == null) {
0822: throw new TransactionSuspensionNotSupportedException(
0823: "JtaTransactionManager needs a JTA TransactionManager for suspending a transaction: "
0824: + "specify the 'transactionManager' or 'transactionManagerName' property");
0825: }
0826: return getTransactionManager().suspend();
0827: }
0828:
0829: protected void doResume(Object transaction,
0830: Object suspendedResources) {
0831: JtaTransactionObject txObject = (JtaTransactionObject) transaction;
0832: try {
0833: doJtaResume(txObject, suspendedResources);
0834: } catch (InvalidTransactionException ex) {
0835: throw new IllegalTransactionStateException(
0836: "Tried to resume invalid JTA transaction", ex);
0837: } catch (SystemException ex) {
0838: throw new TransactionSystemException(
0839: "JTA failure on resume", ex);
0840: }
0841: }
0842:
0843: /**
0844: * Perform a JTA resume on the JTA TransactionManager.
0845: * <p>Can be overridden in subclasses, for specific JTA implementations.
0846: * @param txObject the JtaTransactionObject containing the UserTransaction
0847: * @param suspendedTransaction the suspended JTA Transaction object
0848: * @throws InvalidTransactionException if thrown by JTA methods
0849: * @throws SystemException if thrown by JTA methods
0850: * @see #getTransactionManager()
0851: * @see javax.transaction.TransactionManager#resume(javax.transaction.Transaction)
0852: */
0853: protected void doJtaResume(JtaTransactionObject txObject,
0854: Object suspendedTransaction)
0855: throws InvalidTransactionException, SystemException {
0856:
0857: if (getTransactionManager() == null) {
0858: throw new TransactionSuspensionNotSupportedException(
0859: "JtaTransactionManager needs a JTA TransactionManager for suspending a transaction: "
0860: + "specify the 'transactionManager' or 'transactionManagerName' property");
0861: }
0862: getTransactionManager().resume(
0863: (Transaction) suspendedTransaction);
0864: }
0865:
0866: /**
0867: * This implementation returns "true": a JTA commit will properly handle
0868: * transactions that have been marked rollback-only at a global level.
0869: */
0870: protected boolean shouldCommitOnGlobalRollbackOnly() {
0871: return true;
0872: }
0873:
0874: protected void doCommit(DefaultTransactionStatus status) {
0875: JtaTransactionObject txObject = (JtaTransactionObject) status
0876: .getTransaction();
0877: try {
0878: txObject.getUserTransaction().commit();
0879: } catch (RollbackException ex) {
0880: throw new UnexpectedRollbackException(
0881: "JTA transaction unexpectedly rolled back (maybe due to a timeout)",
0882: ex);
0883: } catch (HeuristicMixedException ex) {
0884: throw new HeuristicCompletionException(
0885: HeuristicCompletionException.STATE_MIXED, ex);
0886: } catch (HeuristicRollbackException ex) {
0887: throw new HeuristicCompletionException(
0888: HeuristicCompletionException.STATE_ROLLED_BACK, ex);
0889: } catch (SystemException ex) {
0890: throw new TransactionSystemException(
0891: "JTA failure on commit", ex);
0892: }
0893: }
0894:
0895: protected void doRollback(DefaultTransactionStatus status) {
0896: JtaTransactionObject txObject = (JtaTransactionObject) status
0897: .getTransaction();
0898: try {
0899: if (txObject.getUserTransaction().getStatus() != Status.STATUS_NO_TRANSACTION) {
0900: txObject.getUserTransaction().rollback();
0901: }
0902: } catch (SystemException ex) {
0903: throw new TransactionSystemException(
0904: "JTA failure on rollback", ex);
0905: }
0906: }
0907:
0908: protected void doSetRollbackOnly(DefaultTransactionStatus status) {
0909: JtaTransactionObject txObject = (JtaTransactionObject) status
0910: .getTransaction();
0911: if (status.isDebug()) {
0912: logger.debug("Setting JTA transaction rollback-only");
0913: }
0914: try {
0915: if (txObject.getUserTransaction().getStatus() != Status.STATUS_NO_TRANSACTION) {
0916: txObject.getUserTransaction().setRollbackOnly();
0917: }
0918: } catch (IllegalStateException ex) {
0919: throw new NoTransactionException(
0920: "No active JTA transaction");
0921: } catch (SystemException ex) {
0922: throw new TransactionSystemException(
0923: "JTA failure on setRollbackOnly", ex);
0924: }
0925: }
0926:
0927: protected void registerAfterCompletionWithExistingTransaction(
0928: Object transaction, List synchronizations) {
0929: JtaTransactionObject txObject = (JtaTransactionObject) transaction;
0930: logger
0931: .debug("Registering after-completion synchronization with existing JTA transaction");
0932: try {
0933: doRegisterAfterCompletionWithJtaTransaction(txObject,
0934: synchronizations);
0935: } catch (RollbackException ex) {
0936: logger
0937: .debug("Participating in existing JTA transaction that has been marked rollback-only: "
0938: + "cannot register Spring after-completion callbacks with outer JTA transaction - "
0939: + "immediately performing Spring after-completion callbacks with outcome status 'rollback'");
0940: invokeAfterCompletion(synchronizations,
0941: TransactionSynchronization.STATUS_ROLLED_BACK);
0942: } catch (IllegalStateException ex) {
0943: throw new NoTransactionException(
0944: "No active JTA transaction");
0945: } catch (SystemException ex) {
0946: throw new TransactionSystemException(
0947: "JTA failure on registerSynchronization", ex);
0948: }
0949: }
0950:
0951: /**
0952: * Register a JTA synchronization on the JTA TransactionManager, for calling
0953: * <code>afterCompletion</code> on the given Spring TransactionSynchronizations.
0954: * <p>Can be overridden in subclasses, for specific JTA implementations.
0955: * @param synchronizations List of TransactionSynchronization objects
0956: * @throws RollbackException if thrown by JTA methods
0957: * @throws SystemException if thrown by JTA methods
0958: * @see #getTransactionManager()
0959: * @see javax.transaction.Transaction#registerSynchronization
0960: * @see #invokeAfterCompletion(java.util.List, int)
0961: */
0962: protected void doRegisterAfterCompletionWithJtaTransaction(
0963: JtaTransactionObject txObject, List synchronizations)
0964: throws RollbackException, SystemException {
0965:
0966: if (getTransactionManager() != null) {
0967: Transaction transaction = getTransactionManager()
0968: .getTransaction();
0969: if (transaction != null) {
0970: transaction
0971: .registerSynchronization(new JtaAfterCompletionSynchronization(
0972: synchronizations));
0973: } else {
0974: // No current JTA Transaction available.
0975: logger
0976: .debug("Participating in existing JTA transaction, but no current JTA Transaction available: "
0977: + "cannot register Spring after-completion callbacks with outer JTA transaction - "
0978: + "processing Spring after-completion callbacks with outcome status 'unknown'");
0979: invokeAfterCompletion(synchronizations,
0980: TransactionSynchronization.STATUS_UNKNOWN);
0981: }
0982: } else {
0983: // No JTA TransactionManager available.
0984: logger
0985: .warn("Participating in existing JTA transaction, but no JTA TransactionManager available: "
0986: + "cannot register Spring after-completion callbacks with outer JTA transaction - "
0987: + "processing Spring after-completion callbacks with outcome status 'unknown'");
0988: invokeAfterCompletion(synchronizations,
0989: TransactionSynchronization.STATUS_UNKNOWN);
0990: }
0991: }
0992:
0993: //---------------------------------------------------------------------
0994: // Serialization support
0995: //---------------------------------------------------------------------
0996:
0997: private void readObject(ObjectInputStream ois) throws IOException,
0998: ClassNotFoundException {
0999: // Rely on default serialization; just initialize state after deserialization.
1000: ois.defaultReadObject();
1001:
1002: // Create template for client-side JNDI lookup.
1003: this .jndiTemplate = new JndiTemplate();
1004:
1005: // Perform lookup for JTA UserTransaction and TransactionManager.
1006: initUserTransactionAndTransactionManager();
1007: }
1008:
1009: }
|