Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » transaction » support » [javadoc | source]
    1   /*
    2    * Copyright 2002-2008 the original author or authors.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
   10    * Unless required by applicable law or agreed to in writing, software
   11    * distributed under the License is distributed on an "AS IS" BASIS,
   12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13    * See the License for the specific language governing permissions and
   14    * limitations under the License.
   15    */
   16   
   17   package org.springframework.transaction.support;
   18   
   19   import java.io.IOException;
   20   import java.io.ObjectInputStream;
   21   import java.io.Serializable;
   22   import java.util.Iterator;
   23   import java.util.List;
   24   
   25   import org.apache.commons.logging.Log;
   26   import org.apache.commons.logging.LogFactory;
   27   
   28   import org.springframework.core.Constants;
   29   import org.springframework.transaction.IllegalTransactionStateException;
   30   import org.springframework.transaction.InvalidTimeoutException;
   31   import org.springframework.transaction.NestedTransactionNotSupportedException;
   32   import org.springframework.transaction.PlatformTransactionManager;
   33   import org.springframework.transaction.TransactionDefinition;
   34   import org.springframework.transaction.TransactionException;
   35   import org.springframework.transaction.TransactionStatus;
   36   import org.springframework.transaction.TransactionSuspensionNotSupportedException;
   37   import org.springframework.transaction.UnexpectedRollbackException;
   38   
   39   /**
   40    * Abstract base class that implements Spring's standard transaction workflow,
   41    * serving as basis for concrete platform transaction managers like
   42    * {@link org.springframework.transaction.jta.JtaTransactionManager} and
   43    * {@link org.springframework.jdbc.datasource.DataSourceTransactionManager}.
   44    *
   45    * <p>This base class provides the following workflow handling:
   46    * <ul>
   47    * <li>determines if there is an existing transaction;
   48    * <li>applies the appropriate propagation behavior;
   49    * <li>suspends and resumes transactions if necessary;
   50    * <li>checks the rollback-only flag on commit;
   51    * <li>applies the appropriate modification on rollback
   52    * (actual rollback or setting rollback-only);
   53    * <li>triggers registered synchronization callbacks
   54    * (if transaction synchronization is active).
   55    * </ul>
   56    *
   57    * <p>Subclasses have to implement specific template methods for specific
   58    * states of a transaction, e.g.: begin, suspend, resume, commit, rollback.
   59    * The most important of them are abstract and must be provided by a concrete
   60    * implementation; for the rest, defaults are provided, so overriding is optional.
   61    *
   62    * <p>Transaction synchronization is a generic mechanism for registering callbacks
   63    * that get invoked at transaction completion time. This is mainly used internally
   64    * by the data access support classes for JDBC, Hibernate, JDO, etc when running
   65    * within a JTA transaction: They register resources that are opened within the
   66    * transaction for closing at transaction completion time, allowing e.g. for reuse
   67    * of the same Hibernate Session within the transaction. The same mechanism can
   68    * also be leveraged for custom synchronization needs in an application.
   69    * 
   70    * <p>The state of this class is serializable, to allow for serializing the
   71    * transaction strategy along with proxies that carry a transaction interceptor.
   72    * It is up to subclasses if they wish to make their state to be serializable too.
   73    * They should implement the <code>java.io.Serializable</code> marker interface in
   74    * that case, and potentially a private <code>readObject()</code> method (according
   75    * to Java serialization rules) if they need to restore any transient state.
   76    *
   77    * @author Juergen Hoeller
   78    * @since 28.03.2003
   79    * @see #setTransactionSynchronization
   80    * @see TransactionSynchronizationManager
   81    * @see org.springframework.transaction.jta.JtaTransactionManager
   82    * @see org.springframework.jdbc.datasource.DataSourceTransactionManager
   83    * @see org.springframework.orm.hibernate3.HibernateTransactionManager
   84    */
   85   public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {
   86   
   87   	/**
   88   	 * Always activate transaction synchronization, even for "empty" transactions
   89   	 * that result from PROPAGATION_SUPPORTS with no existing backend transaction.
   90   	 * @see org.springframework.transaction.TransactionDefinition#PROPAGATION_SUPPORTS
   91   	 * @see org.springframework.transaction.TransactionDefinition#PROPAGATION_NOT_SUPPORTED
   92   	 * @see org.springframework.transaction.TransactionDefinition#PROPAGATION_NEVER
   93   	 */
   94   	public static final int SYNCHRONIZATION_ALWAYS = 0;
   95   
   96   	/**
   97   	 * Activate transaction synchronization only for actual transactions,
   98   	 * that is, not for empty ones that result from PROPAGATION_SUPPORTS with
   99   	 * no existing backend transaction.
  100   	 * @see org.springframework.transaction.TransactionDefinition#PROPAGATION_REQUIRED
  101   	 * @see org.springframework.transaction.TransactionDefinition#PROPAGATION_MANDATORY
  102   	 * @see org.springframework.transaction.TransactionDefinition#PROPAGATION_REQUIRES_NEW
  103   	 */
  104   	public static final int SYNCHRONIZATION_ON_ACTUAL_TRANSACTION = 1;
  105   
  106   	/**
  107   	 * Never active transaction synchronization, not even for actual transactions.
  108   	 */
  109   	public static final int SYNCHRONIZATION_NEVER = 2;
  110   
  111   
  112   	/** Constants instance for AbstractPlatformTransactionManager */
  113   	private static final Constants constants = new Constants(AbstractPlatformTransactionManager.class);
  114   
  115   
  116   	/** Transient to optimize serialization */
  117   	protected transient Log logger = LogFactory.getLog(getClass());
  118   
  119   	private int transactionSynchronization = SYNCHRONIZATION_ALWAYS;
  120   
  121   	private int defaultTimeout = TransactionDefinition.TIMEOUT_DEFAULT;
  122   
  123   	private boolean nestedTransactionAllowed = false;
  124   
  125   	private boolean validateExistingTransaction = false;
  126   
  127   	private boolean globalRollbackOnParticipationFailure = true;
  128   
  129   	private boolean failEarlyOnGlobalRollbackOnly = false;
  130   
  131   	private boolean rollbackOnCommitFailure = false;
  132   
  133   
  134   	/**
  135   	 * Set the transaction synchronization by the name of the corresponding constant
  136   	 * in this class, e.g. "SYNCHRONIZATION_ALWAYS".
  137   	 * @param constantName name of the constant
  138   	 * @see #SYNCHRONIZATION_ALWAYS
  139   	 */
  140   	public final void setTransactionSynchronizationName(String constantName) {
  141   		setTransactionSynchronization(constants.asNumber(constantName).intValue());
  142   	}
  143   
  144   	/**
  145   	 * Set when this transaction manager should activate the thread-bound
  146   	 * transaction synchronization support. Default is "always".
  147   	 * <p>Note that transaction synchronization isn't supported for
  148   	 * multiple concurrent transactions by different transaction managers.
  149   	 * Only one transaction manager is allowed to activate it at any time.
  150   	 * @see #SYNCHRONIZATION_ALWAYS
  151   	 * @see #SYNCHRONIZATION_ON_ACTUAL_TRANSACTION
  152   	 * @see #SYNCHRONIZATION_NEVER
  153   	 * @see TransactionSynchronizationManager
  154   	 * @see TransactionSynchronization
  155   	 */
  156   	public final void setTransactionSynchronization(int transactionSynchronization) {
  157   		this.transactionSynchronization = transactionSynchronization;
  158   	}
  159   
  160   	/**
  161   	 * Return if this transaction manager should activate the thread-bound
  162   	 * transaction synchronization support.
  163   	 */
  164   	public final int getTransactionSynchronization() {
  165   		return this.transactionSynchronization;
  166   	}
  167   
  168   	/**
  169   	 * Specify the default timeout that this transaction manager should apply
  170   	 * if there is no timeout specified at the transaction level, in seconds.
  171   	 * <p>Default is the underlying transaction infrastructure's default timeout,
  172   	 * e.g. typically 30 seconds in case of a JTA provider, indicated by the
  173   	 * <code>TransactionDefinition.TIMEOUT_DEFAULT</code> value.
  174   	 * @see org.springframework.transaction.TransactionDefinition#TIMEOUT_DEFAULT
  175   	 */
  176   	public final void setDefaultTimeout(int defaultTimeout) {
  177   		if (defaultTimeout < TransactionDefinition.TIMEOUT_DEFAULT) {
  178   			throw new InvalidTimeoutException("Invalid default timeout", defaultTimeout);
  179   		}
  180   		this.defaultTimeout = defaultTimeout;
  181   	}
  182   
  183   	/**
  184   	 * Return the default timeout that this transaction manager should apply
  185   	 * if there is no timeout specified at the transaction level, in seconds.
  186   	 * <p>Returns <code>TransactionDefinition.TIMEOUT_DEFAULT</code> to indicate
  187   	 * the underlying transaction infrastructure's default timeout.
  188   	 */
  189   	public final int getDefaultTimeout() {
  190   		return this.defaultTimeout;
  191   	}
  192   
  193   	/**
  194   	 * Set whether nested transactions are allowed. Default is "false".
  195   	 * <p>Typically initialized with an appropriate default by the
  196   	 * concrete transaction manager subclass.
  197   	 */
  198   	public final void setNestedTransactionAllowed(boolean nestedTransactionAllowed) {
  199   		this.nestedTransactionAllowed = nestedTransactionAllowed;
  200   	}
  201   
  202   	/**
  203   	 * Return whether nested transactions are allowed.
  204   	 */
  205   	public final boolean isNestedTransactionAllowed() {
  206   		return this.nestedTransactionAllowed;
  207   	}
  208   
  209   	/**
  210   	 * Set whether existing transactions should be validated before participating
  211   	 * in them.
  212   	 * <p>When participating in an existing transaction (e.g. with
  213   	 * PROPAGATION_REQUIRES or PROPAGATION_SUPPORTS encountering an existing
  214   	 * transaction), this outer transaction's characteristics will apply even
  215   	 * to the inner transaction scope. Validation will detect incompatible
  216   	 * isolation level and read-only settings on the inner transaction definition
  217   	 * and reject participation accordingly through throwing a corresponding exception.
  218   	 * <p>Default is "false", leniently ignoring inner transaction settings,
  219   	 * simply overriding them with the outer transaction's characteristics.
  220   	 * Switch this flag to "true" in order to enforce strict validation.
  221   	 */
  222   	public final void setValidateExistingTransaction(boolean validateExistingTransaction) {
  223   		this.validateExistingTransaction = validateExistingTransaction;
  224   	}
  225   
  226   	/**
  227   	 * Return whether existing transactions should be validated before participating
  228   	 * in them.
  229   	 */
  230   	public final boolean isValidateExistingTransaction() {
  231   		return this.validateExistingTransaction;
  232   	}
  233   
  234   	/**
  235   	 * Set whether to globally mark an existing transaction as rollback-only
  236   	 * after a participating transaction failed.
  237   	 * <p>Default is "true": If a participating transaction (e.g. with
  238   	 * PROPAGATION_REQUIRES or PROPAGATION_SUPPORTS encountering an existing
  239   	 * transaction) fails, the transaction will be globally marked as rollback-only.
  240   	 * The only possible outcome of such a transaction is a rollback: The
  241   	 * transaction originator <i>cannot</i> make the transaction commit anymore.
  242   	 * <p>Switch this to "false" to let the transaction originator make the rollback
  243   	 * decision. If a participating transaction fails with an exception, the caller
  244   	 * can still decide to continue with a different path within the transaction.
  245   	 * However, note that this will only work as long as all participating resources
  246   	 * are capable of continuing towards a transaction commit even after a data access
  247   	 * failure: This is generally not the case for a Hibernate Session, for example;
  248   	 * neither is it for a sequence of JDBC insert/update/delete operations.
  249   	 * <p><b>Note:</b>This flag only applies to an explicit rollback attempt for a
  250   	 * subtransaction, typically caused by an exception thrown by a data access operation
  251   	 * (where TransactionInterceptor will trigger a <code>PlatformTransactionManager.rollback()</code>
  252   	 * call according to a rollback rule). If the flag is off, the caller can handle the exception
  253   	 * and decide on a rollback, independent of the rollback rules of the subtransaction.
  254   	 * This flag does, however, <i>not</i> apply to explicit <code>setRollbackOnly</code>
  255   	 * calls on a <code>TransactionStatus</code>, which will always cause an eventual
  256   	 * global rollback (as it might not throw an exception after the rollback-only call).
  257   	 * <p>The recommended solution for handling failure of a subtransaction
  258   	 * is a "nested transaction", where the global transaction can be rolled
  259   	 * back to a savepoint taken at the beginning of the subtransaction.
  260   	 * PROPAGATION_NESTED provides exactly those semantics; however, it will
  261   	 * only work when nested transaction support is available. This is the case
  262   	 * with DataSourceTransactionManager, but not with JtaTransactionManager.
  263   	 * @see #setNestedTransactionAllowed
  264   	 * @see org.springframework.jdbc.datasource.DataSourceTransactionManager
  265   	 * @see org.springframework.transaction.jta.JtaTransactionManager
  266   	 */
  267   	public final void setGlobalRollbackOnParticipationFailure(boolean globalRollbackOnParticipationFailure) {
  268   		this.globalRollbackOnParticipationFailure = globalRollbackOnParticipationFailure;
  269   	}
  270   
  271   	/**
  272   	 * Return whether to globally mark an existing transaction as rollback-only
  273   	 * after a participating transaction failed.
  274   	 */
  275   	public final boolean isGlobalRollbackOnParticipationFailure() {
  276   		return this.globalRollbackOnParticipationFailure;
  277   	}
  278   
  279   	/**
  280   	 * Set whether to fail early in case of the transaction being globally marked
  281   	 * as rollback-only.
  282   	 * <p>Default is "false", only causing an UnexpectedRollbackException at the
  283   	 * outermost transaction boundary. Switch this flag on to cause an
  284   	 * UnexpectedRollbackException as early as the global rollback-only marker
  285   	 * has been first detected, even from within an inner transaction boundary.
  286   	 * <p>Note that, as of Spring 2.0, the fail-early behavior for global
  287   	 * rollback-only markers has been unified: All transaction managers will by
  288   	 * default only cause UnexpectedRollbackException at the outermost transaction
  289   	 * boundary. This allows, for example, to continue unit tests even after an
  290   	 * operation failed and the transaction will never be completed. All transaction
  291   	 * managers will only fail earlier if this flag has explicitly been set to "true".
  292   	 * @see org.springframework.transaction.UnexpectedRollbackException
  293   	 */
  294   	public final void setFailEarlyOnGlobalRollbackOnly(boolean failEarlyOnGlobalRollbackOnly) {
  295   		this.failEarlyOnGlobalRollbackOnly = failEarlyOnGlobalRollbackOnly;
  296   	}
  297   
  298   	/**
  299   	 * Return whether to fail early in case of the transaction being globally marked
  300   	 * as rollback-only.
  301   	 */
  302   	public final boolean isFailEarlyOnGlobalRollbackOnly() {
  303   		return this.failEarlyOnGlobalRollbackOnly;
  304   	}
  305   
  306   	/**
  307   	 * Set whether <code>doRollback</code> should be performed on failure of the
  308   	 * <code>doCommit</code> call. Typically not necessary and thus to be avoided,
  309   	 * as it can potentially override the commit exception with a subsequent
  310   	 * rollback exception.
  311   	 * <p>Default is "false".
  312   	 * @see #doCommit
  313   	 * @see #doRollback
  314   	 */
  315   	public final void setRollbackOnCommitFailure(boolean rollbackOnCommitFailure) {
  316   		this.rollbackOnCommitFailure = rollbackOnCommitFailure;
  317   	}
  318   
  319   	/**
  320   	 * Return whether <code>doRollback</code> should be performed on failure of the
  321   	 * <code>doCommit</code> call.
  322   	 */
  323   	public final boolean isRollbackOnCommitFailure() {
  324   		return this.rollbackOnCommitFailure;
  325   	}
  326   
  327   
  328   	//---------------------------------------------------------------------
  329   	// Implementation of PlatformTransactionManager
  330   	//---------------------------------------------------------------------
  331   
  332   	/**
  333   	 * This implementation handles propagation behavior. Delegates to
  334   	 * <code>doGetTransaction</code>, <code>isExistingTransaction</code>
  335   	 * and <code>doBegin</code>.
  336   	 * @see #doGetTransaction
  337   	 * @see #isExistingTransaction
  338   	 * @see #doBegin
  339   	 */
  340   	public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
  341   		Object transaction = doGetTransaction();
  342   
  343   		// Cache debug flag to avoid repeated checks.
  344   		boolean debugEnabled = logger.isDebugEnabled();
  345   
  346   		if (definition == null) {
  347   			// Use defaults if no transaction definition given.
  348   			definition = new DefaultTransactionDefinition();
  349   		}
  350   
  351   		if (isExistingTransaction(transaction)) {
  352   			// Existing transaction found -> check propagation behavior to find out how to behave.
  353   			return handleExistingTransaction(definition, transaction, debugEnabled);
  354   		}
  355   
  356   		// Check definition settings for new transaction.
  357   		if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
  358   			throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
  359   		}
  360   
  361   		// No existing transaction found -> check propagation behavior to find out how to proceed.
  362   		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
  363   			throw new IllegalTransactionStateException(
  364   					"No existing transaction found for transaction marked with propagation 'mandatory'");
  365   		}
  366   		else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
  367   				definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
  368   		    definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
  369   			SuspendedResourcesHolder suspendedResources = suspend(null);
  370   			if (debugEnabled) {
  371   				logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
  372   			}
  373   			try {
  374   				doBegin(transaction, definition);
  375   			}
  376   			catch (RuntimeException ex) {
  377   				resume(null, suspendedResources);
  378   				throw ex;
  379   			}
  380   			catch (Error err) {
  381   				resume(null, suspendedResources);
  382   				throw err;
  383   			}
  384   			boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
  385   			return newTransactionStatus(
  386   					definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
  387   		}
  388   		else {
  389   			// Create "empty" transaction: no actual transaction, but potentially synchronization.
  390   			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
  391   			return newTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
  392   		}
  393   	}
  394   
  395   	/**
  396   	 * Create a TransactionStatus for an existing transaction.
  397   	 */
  398   	private TransactionStatus handleExistingTransaction(
  399   			TransactionDefinition definition, Object transaction, boolean debugEnabled)
  400   			throws TransactionException {
  401   
  402   		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
  403   			throw new IllegalTransactionStateException(
  404   					"Existing transaction found for transaction marked with propagation 'never'");
  405   		}
  406   
  407   		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
  408   			if (debugEnabled) {
  409   				logger.debug("Suspending current transaction");
  410   			}
  411   			Object suspendedResources = suspend(transaction);
  412   			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
  413   			return newTransactionStatus(
  414   					definition, null, false, newSynchronization, debugEnabled, suspendedResources);
  415   		}
  416   
  417   		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
  418   			if (debugEnabled) {
  419   				logger.debug("Suspending current transaction, creating new transaction with name [" +
  420   						definition.getName() + "]");
  421   			}
  422   			SuspendedResourcesHolder suspendedResources = suspend(transaction);
  423   			try {
  424   				doBegin(transaction, definition);
  425   			}
  426   			catch (RuntimeException beginEx) {
  427   				resumeAfterBeginException(transaction, suspendedResources, beginEx);
  428   				throw beginEx;
  429   			}
  430   			catch (Error beginErr) {
  431   				resumeAfterBeginException(transaction, suspendedResources, beginErr);
  432   				throw beginErr;
  433   			}
  434   			boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
  435   			return newTransactionStatus(
  436   					definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
  437   		}
  438   
  439   		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
  440   			if (!isNestedTransactionAllowed()) {
  441   				throw new NestedTransactionNotSupportedException(
  442   						"Transaction manager does not allow nested transactions by default - " +
  443   						"specify 'nestedTransactionAllowed' property with value 'true'");
  444   			}
  445   			if (debugEnabled) {
  446   				logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
  447   			}
  448   			if (useSavepointForNestedTransaction()) {
  449   				// Create savepoint within existing Spring-managed transaction,
  450   				// through the SavepointManager API implemented by TransactionStatus.
  451   				// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
  452   				DefaultTransactionStatus status =
  453   						newTransactionStatus(definition, transaction, false, false, debugEnabled, null);
  454   				status.createAndHoldSavepoint();
  455   				return status;
  456   			}
  457   			else {
  458   				// Nested transaction through nested begin and commit/rollback calls.
  459   				// Usually only for JTA: Spring synchronization might get activated here
  460   				// in case of a pre-existing JTA transaction.
  461   				doBegin(transaction, definition);
  462   				boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
  463   				return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);
  464   			}
  465   		}
  466   
  467   		// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
  468   		if (debugEnabled) {
  469   			logger.debug("Participating in existing transaction");
  470   		}
  471   		if (isValidateExistingTransaction()) {
  472   			if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
  473   				Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
  474   				if (currentIsolationLevel == null || currentIsolationLevel.intValue() != definition.getIsolationLevel()) {
  475   					Constants isoConstants = DefaultTransactionDefinition.constants;
  476   					throw new IllegalTransactionStateException("Participating transaction with definition [" +
  477   							definition + "] specifies isolation level which is incompatible with existing transaction: " +
  478   							(currentIsolationLevel != null ?
  479   									isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
  480   									"(unknown)"));
  481   				}
  482   			}
  483   			if (!definition.isReadOnly()) {
  484   				if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
  485   					throw new IllegalTransactionStateException("Participating transaction with definition [" +
  486   							definition + "] is not marked as read-only but existing transaction is");
  487   				}
  488   			}
  489   		}
  490   		boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
  491   		return newTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
  492   	}
  493   
  494   	/**
  495   	 * Create a new TransactionStatus for the given arguments,
  496   	 * initializing transaction synchronization as appropriate.
  497   	 */
  498   	protected DefaultTransactionStatus newTransactionStatus(
  499   			TransactionDefinition definition, Object transaction, boolean newTransaction,
  500   			boolean newSynchronization, boolean debug, Object suspendedResources) {
  501   
  502   		boolean actualNewSynchronization = newSynchronization &&
  503   				!TransactionSynchronizationManager.isSynchronizationActive();
  504   		if (actualNewSynchronization) {
  505   			TransactionSynchronizationManager.setActualTransactionActive(transaction != null);
  506   			TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
  507   					(definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) ?
  508   							new Integer(definition.getIsolationLevel()) : null);
  509   			TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
  510   			TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
  511   			TransactionSynchronizationManager.initSynchronization();
  512   		}
  513   		return new DefaultTransactionStatus(
  514   				transaction, newTransaction, actualNewSynchronization,
  515   				definition.isReadOnly(), debug, suspendedResources);
  516   	}
  517   
  518   	/**
  519   	 * Determine the actual timeout to use for the given definition.
  520   	 * Will fall back to this manager's default timeout if the
  521   	 * transaction definition doesn't specify a non-default value.
  522   	 * @param definition the transaction definition
  523   	 * @return the actual timeout to use
  524   	 * @see org.springframework.transaction.TransactionDefinition#getTimeout()
  525   	 * @see #setDefaultTimeout
  526   	 */
  527   	protected int determineTimeout(TransactionDefinition definition) {
  528   		if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {
  529   			return definition.getTimeout();
  530   		}
  531   		return this.defaultTimeout;
  532   	}
  533   
  534   
  535   	/**
  536   	 * Suspend the given transaction. Suspends transaction synchronization first,
  537   	 * then delegates to the <code>doSuspend</code> template method.
  538   	 * @param transaction the current transaction object
  539   	 * (or <code>null</code> to just suspend active synchronizations, if any)
  540   	 * @return an object that holds suspended resources
  541   	 * (or <code>null</code> if neither transaction nor synchronization active)
  542   	 * @see #doSuspend
  543   	 * @see #resume
  544   	 */
  545   	protected final SuspendedResourcesHolder suspend(Object transaction) throws TransactionException {
  546   		if (TransactionSynchronizationManager.isSynchronizationActive()) {
  547   			List suspendedSynchronizations = doSuspendSynchronization();
  548   			try {
  549   				Object suspendedResources = null;
  550   				if (transaction != null) {
  551   					suspendedResources = doSuspend(transaction);
  552   				}
  553   				String name = TransactionSynchronizationManager.getCurrentTransactionName();
  554   				TransactionSynchronizationManager.setCurrentTransactionName(null);
  555   				boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
  556   				TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
  557   				Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
  558   				TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
  559   				boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
  560   				TransactionSynchronizationManager.setActualTransactionActive(false);
  561   				return new SuspendedResourcesHolder(
  562   						suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
  563   			}
  564   			catch (RuntimeException ex) {
  565   				// doSuspend failed - original transaction is still active...
  566   				doResumeSynchronization(suspendedSynchronizations);
  567   				throw ex;
  568   			}
  569   			catch (Error err) {
  570   				// doSuspend failed - original transaction is still active...
  571   				doResumeSynchronization(suspendedSynchronizations);
  572   				throw err;
  573   			}
  574   		}
  575   		else if (transaction != null) {
  576   			// Transaction active but no synchronization active.
  577   			Object suspendedResources = doSuspend(transaction);
  578   			return new SuspendedResourcesHolder(suspendedResources);
  579   		}
  580   		else {
  581   			// Neither transaction nor synchronization active.
  582   			return null;
  583   		}
  584   	}
  585   
  586   	/**
  587   	 * Resume the given transaction. Delegates to the <code>doResume</code>
  588   	 * template method first, then resuming transaction synchronization.
  589   	 * @param transaction the current transaction object
  590   	 * @param resourcesHolder the object that holds suspended resources,
  591   	 * as returned by <code>suspend</code> (or <code>null</code> to just
  592   	 * resume synchronizations, if any)
  593   	 * @see #doResume
  594   	 * @see #suspend
  595   	 */
  596   	protected final void resume(Object transaction, SuspendedResourcesHolder resourcesHolder)
  597   			throws TransactionException {
  598   
  599   		if (resourcesHolder != null) {
  600   			Object suspendedResources = resourcesHolder.suspendedResources;
  601   			if (suspendedResources != null) {
  602   				doResume(transaction, suspendedResources);
  603   			}
  604   			List suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
  605   			if (suspendedSynchronizations != null) {
  606   				TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);
  607   				TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
  608   				TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
  609   				TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
  610   				doResumeSynchronization(suspendedSynchronizations);
  611   			}
  612   		}
  613   	}
  614   
  615   	/**
  616   	 * Resume outer transaction after inner transaction begin failed.
  617   	 */
  618   	private void resumeAfterBeginException(
  619   			Object transaction, SuspendedResourcesHolder suspendedResources, Throwable beginEx) {
  620   
  621   		String exMessage = "Inner transaction begin exception overridden by outer transaction resume exception";
  622   		try {
  623   			resume(transaction, suspendedResources);
  624   		}
  625   		catch (RuntimeException resumeEx) {
  626   			logger.error(exMessage, beginEx);
  627   			throw resumeEx;
  628   		}
  629   		catch (Error resumeErr) {
  630   			logger.error(exMessage, beginEx);
  631   			throw resumeErr;
  632   		}
  633   	}
  634   
  635   	/**
  636   	 * Suspend all current synchronizations and deactivate transaction
  637   	 * synchronization for the current thread.
  638   	 * @return the List of suspended TransactionSynchronization objects
  639   	 */
  640   	private List doSuspendSynchronization() {
  641   		List suspendedSynchronizations = TransactionSynchronizationManager.getSynchronizations();
  642   		for (Iterator it = suspendedSynchronizations.iterator(); it.hasNext();) {
  643   			((TransactionSynchronization) it.next()).suspend();
  644   		}
  645   		TransactionSynchronizationManager.clearSynchronization();
  646   		return suspendedSynchronizations;
  647   	}
  648   
  649   	/**
  650   	 * Reactivate transaction synchronization for the current thread
  651   	 * and resume all given synchronizations.
  652   	 * @param suspendedSynchronizations List of TransactionSynchronization objects
  653   	 */
  654   	private void doResumeSynchronization(List suspendedSynchronizations) {
  655   		TransactionSynchronizationManager.initSynchronization();
  656   		for (Iterator it = suspendedSynchronizations.iterator(); it.hasNext();) {
  657   			TransactionSynchronization synchronization = (TransactionSynchronization) it.next();
  658   			synchronization.resume();
  659   			TransactionSynchronizationManager.registerSynchronization(synchronization);
  660   		}
  661   	}
  662   
  663   
  664   	/**
  665   	 * This implementation of commit handles participating in existing
  666   	 * transactions and programmatic rollback requests.
  667   	 * Delegates to <code>isRollbackOnly</code>, <code>doCommit</code>
  668   	 * and <code>rollback</code>.
  669   	 * @see org.springframework.transaction.TransactionStatus#isRollbackOnly()
  670   	 * @see #doCommit
  671   	 * @see #rollback
  672   	 */
  673   	public final void commit(TransactionStatus status) throws TransactionException {
  674   		if (status.isCompleted()) {
  675   			throw new IllegalTransactionStateException(
  676   					"Transaction is already completed - do not call commit or rollback more than once per transaction");
  677   		}
  678   
  679   		DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
  680   		if (defStatus.isLocalRollbackOnly()) {
  681   			if (defStatus.isDebug()) {
  682   				logger.debug("Transactional code has requested rollback");
  683   			}
  684   			processRollback(defStatus);
  685   			return;
  686   		}
  687   		if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
  688   			if (defStatus.isDebug()) {
  689   				logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
  690   			}
  691   			processRollback(defStatus);
  692   			// Throw UnexpectedRollbackException only at outermost transaction boundary
  693   			// or if explicitly asked to.
  694   			if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
  695   				throw new UnexpectedRollbackException(
  696   						"Transaction rolled back because it has been marked as rollback-only");
  697   			}
  698   			return;
  699   		}
  700   
  701   		processCommit(defStatus);
  702   	}
  703   
  704   	/**
  705   	 * Process an actual commit.
  706   	 * Rollback-only flags have already been checked and applied.
  707   	 * @param status object representing the transaction
  708   	 * @throws TransactionException in case of commit failure
  709   	 */
  710   	private void processCommit(DefaultTransactionStatus status) throws TransactionException {
  711   		try {
  712   			boolean beforeCompletionInvoked = false;
  713   			try {
  714   				prepareForCommit(status);
  715   				triggerBeforeCommit(status);
  716   				triggerBeforeCompletion(status);
  717   				beforeCompletionInvoked = true;
  718   				boolean globalRollbackOnly = false;
  719   				if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
  720   					globalRollbackOnly = status.isGlobalRollbackOnly();
  721   				}
  722   				if (status.hasSavepoint()) {
  723   					if (status.isDebug()) {
  724   						logger.debug("Releasing transaction savepoint");
  725   					}
  726   					status.releaseHeldSavepoint();
  727   				}
  728   				else if (status.isNewTransaction()) {
  729   					if (status.isDebug()) {
  730   						logger.debug("Initiating transaction commit");
  731   					}
  732   					doCommit(status);
  733   				}
  734   				// Throw UnexpectedRollbackException if we have a global rollback-only
  735   				// marker but still didn't get a corresponding exception from commit.
  736   				if (globalRollbackOnly) {
  737   					throw new UnexpectedRollbackException(
  738   							"Transaction silently rolled back because it has been marked as rollback-only");
  739   				}
  740   			}
  741   			catch (UnexpectedRollbackException ex) {
  742   				// can only be caused by doCommit
  743   				triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
  744   				throw ex;
  745   			}
  746   			catch (TransactionException ex) {
  747   				// can only be caused by doCommit
  748   				if (isRollbackOnCommitFailure()) {
  749   					doRollbackOnCommitException(status, ex);
  750   				}
  751   				else {
  752   					triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
  753   				}
  754   				throw ex;
  755   			}
  756   			catch (RuntimeException ex) {
  757   				if (!beforeCompletionInvoked) {
  758   					triggerBeforeCompletion(status);
  759   				}
  760   				doRollbackOnCommitException(status, ex);
  761   				throw ex;
  762   			}
  763   			catch (Error err) {
  764   				if (!beforeCompletionInvoked) {
  765   					triggerBeforeCompletion(status);
  766   				}
  767   				doRollbackOnCommitException(status, err);
  768   				throw err;
  769   			}
  770   
  771   			// Trigger afterCommit callbacks, with an exception thrown there
  772   			// propagated to callers but the transaction still considered as committed.
  773   			try {
  774   				triggerAfterCommit(status);
  775   			}
  776   			finally {
  777   				triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
  778   			}
  779   
  780   		}
  781   		finally {
  782   			cleanupAfterCompletion(status);
  783   		}
  784   	}
  785   
  786   	/**
  787   	 * This implementation of rollback handles participating in existing
  788   	 * transactions. Delegates to <code>doRollback</code> and
  789   	 * <code>doSetRollbackOnly</code>.
  790   	 * @see #doRollback
  791   	 * @see #doSetRollbackOnly
  792   	 */
  793   	public final void rollback(TransactionStatus status) throws TransactionException {
  794   		if (status.isCompleted()) {
  795   			throw new IllegalTransactionStateException(
  796   					"Transaction is already completed - do not call commit or rollback more than once per transaction");
  797   		}
  798   
  799   		DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
  800   		processRollback(defStatus);
  801   	}
  802   
  803   	/**
  804   	 * Process an actual rollback.
  805   	 * The completed flag has already been checked.
  806   	 * @param status object representing the transaction
  807   	 * @throws TransactionException in case of rollback failure
  808   	 */
  809   	private void processRollback(DefaultTransactionStatus status) {
  810   		try {
  811   			try {
  812   				triggerBeforeCompletion(status);
  813   				if (status.hasSavepoint()) {
  814   					if (status.isDebug()) {
  815   						logger.debug("Rolling back transaction to savepoint");
  816   					}
  817   					status.rollbackToHeldSavepoint();
  818   				}
  819   				else if (status.isNewTransaction()) {
  820   					if (status.isDebug()) {
  821   						logger.debug("Initiating transaction rollback");
  822   					}
  823   					doRollback(status);
  824   				}
  825   				else if (status.hasTransaction()) {
  826   					if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
  827   						if (status.isDebug()) {
  828   							logger.debug(
  829   									"Participating transaction failed - marking existing transaction as rollback-only");
  830   						}
  831   						doSetRollbackOnly(status);
  832   					}
  833   					else {
  834   						if (status.isDebug()) {
  835   							logger.debug(
  836   									"Participating transaction failed - letting transaction originator decide on rollback");
  837   						}
  838   					}
  839   				}
  840   				else {
  841   					logger.debug("Should roll back transaction but cannot - no transaction available");
  842   				}
  843   			}
  844   			catch (RuntimeException ex) {
  845   				triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
  846   				throw ex;
  847   			}
  848   			catch (Error err) {
  849   				triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
  850   				throw err;
  851   			}
  852   			triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
  853   		}
  854   		finally {
  855   			cleanupAfterCompletion(status);
  856   		}
  857   	}
  858   
  859   	/**
  860   	 * Invoke <code>doRollback</code>, handling rollback exceptions properly.
  861   	 * @param status object representing the transaction
  862   	 * @param ex the thrown application exception or error
  863   	 * @throws TransactionException in case of rollback failure
  864   	 * @see #doRollback
  865   	 */
  866   	private void doRollbackOnCommitException(DefaultTransactionStatus status, Throwable ex)
  867   	    throws TransactionException {
  868   		try {
  869   			if (status.isNewTransaction()) {
  870   				if (status.isDebug()) {
  871   					logger.debug("Initiating transaction rollback after commit exception", ex);
  872   				}
  873   				doRollback(status);
  874   			}
  875   			else if (status.hasTransaction() && isGlobalRollbackOnParticipationFailure()) {
  876   				if (status.isDebug()) {
  877   					logger.debug("Marking existing transaction as rollback-only after commit exception", ex);
  878   				}
  879   				doSetRollbackOnly(status);
  880   			}
  881   		}
  882   		catch (RuntimeException rbex) {
  883   			logger.error("Commit exception overridden by rollback exception", ex);
  884   			triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
  885   			throw rbex;
  886   		}
  887   		catch (Error rberr) {
  888   			logger.error("Commit exception overridden by rollback exception", ex);
  889   			triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
  890   			throw rberr;
  891   		}
  892   		triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
  893   	}
  894   
  895   
  896   	/**
  897   	 * Trigger <code>beforeCommit</code> callbacks.
  898   	 * @param status object representing the transaction
  899   	 */
  900   	protected final void triggerBeforeCommit(DefaultTransactionStatus status) {
  901   		if (status.isNewSynchronization()) {
  902   			if (status.isDebug()) {
  903   				logger.trace("Triggering beforeCommit synchronization");
  904   			}
  905   			TransactionSynchronizationUtils.triggerBeforeCommit(status.isReadOnly());
  906   		}
  907   	}
  908   
  909   	/**
  910   	 * Trigger <code>beforeCompletion</code> callbacks.
  911   	 * @param status object representing the transaction
  912   	 */
  913   	protected final void triggerBeforeCompletion(DefaultTransactionStatus status) {
  914   		if (status.isNewSynchronization()) {
  915   			if (status.isDebug()) {
  916   				logger.trace("Triggering beforeCompletion synchronization");
  917   			}
  918   			TransactionSynchronizationUtils.triggerBeforeCompletion();
  919   		}
  920   	}
  921   
  922   	/**
  923   	 * Trigger <code>afterCommit</code> callbacks.
  924   	 * @param status object representing the transaction
  925   	 */
  926   	private void triggerAfterCommit(DefaultTransactionStatus status) {
  927   		if (status.isNewSynchronization()) {
  928   			if (status.isDebug()) {
  929   				logger.trace("Triggering afterCommit synchronization");
  930   			}
  931   			TransactionSynchronizationUtils.triggerAfterCommit();
  932   		}
  933   	}
  934   
  935   	/**
  936   	 * Trigger <code>afterCompletion</code> callbacks.
  937   	 * @param status object representing the transaction
  938   	 * @param completionStatus completion status according to TransactionSynchronization constants
  939   	 */
  940   	private void triggerAfterCompletion(DefaultTransactionStatus status, int completionStatus) {
  941   		if (status.isNewSynchronization()) {
  942   			List synchronizations = TransactionSynchronizationManager.getSynchronizations();
  943   			if (!status.hasTransaction() || status.isNewTransaction()) {
  944   				if (status.isDebug()) {
  945   					logger.trace("Triggering afterCompletion synchronization");
  946   				}
  947   				// No transaction or new transaction for the current scope ->
  948   				// invoke the afterCompletion callbacks immediately
  949   				invokeAfterCompletion(synchronizations, completionStatus);
  950   			}
  951   			else {
  952   				// Existing transaction that we participate in, controlled outside
  953   				// of the scope of this Spring transaction manager -> try to register
  954   				// an afterCompletion callback with the existing (JTA) transaction.
  955   				registerAfterCompletionWithExistingTransaction(status.getTransaction(), synchronizations);
  956   			}
  957   		}
  958   	}
  959   
  960   	/**
  961   	 * Actually invoke the <code>afterCompletion</code> methods of the
  962   	 * given Spring TransactionSynchronization objects.
  963   	 * <p>To be called by this abstract manager itself, or by special implementations
  964   	 * of the <code>registerAfterCompletionWithExistingTransaction</code> callback.
  965   	 * @param synchronizations List of TransactionSynchronization objects
  966   	 * @param completionStatus the completion status according to the
  967   	 * constants in the TransactionSynchronization interface
  968   	 * @see #registerAfterCompletionWithExistingTransaction(Object, java.util.List)
  969   	 * @see TransactionSynchronization#STATUS_COMMITTED
  970   	 * @see TransactionSynchronization#STATUS_ROLLED_BACK
  971   	 * @see TransactionSynchronization#STATUS_UNKNOWN
  972   	 */
  973   	protected final void invokeAfterCompletion(List synchronizations, int completionStatus) {
  974   		TransactionSynchronizationUtils.invokeAfterCompletion(synchronizations, completionStatus);
  975   	}
  976   
  977   	/**
  978   	 * Clean up after completion, clearing synchronization if necessary,
  979   	 * and invoking doCleanupAfterCompletion.
  980   	 * @param status object representing the transaction
  981   	 * @see #doCleanupAfterCompletion
  982   	 */
  983   	private void cleanupAfterCompletion(DefaultTransactionStatus status) {
  984   		status.setCompleted();
  985   		if (status.isNewSynchronization()) {
  986   			TransactionSynchronizationManager.clear();
  987   		}
  988   		if (status.isNewTransaction()) {
  989   			doCleanupAfterCompletion(status.getTransaction());
  990   		}
  991   		if (status.getSuspendedResources() != null) {
  992   			if (status.isDebug()) {
  993   				logger.debug("Resuming suspended transaction");
  994   			}
  995   			resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspendedResources());
  996   		}
  997   	}
  998   
  999   
 1000   	//---------------------------------------------------------------------
 1001   	// Template methods to be implemented in subclasses
 1002   	//---------------------------------------------------------------------
 1003   
 1004   	/**
 1005   	 * Return a transaction object for the current transaction state.
 1006   	 * <p>The returned object will usually be specific to the concrete transaction
 1007   	 * manager implementation, carrying corresponding transaction state in a
 1008   	 * modifiable fashion. This object will be passed into the other template
 1009   	 * methods (e.g. doBegin and doCommit), either directly or as part of a
 1010   	 * DefaultTransactionStatus instance.
 1011   	 * <p>The returned object should contain information about any existing
 1012   	 * transaction, that is, a transaction that has already started before the
 1013   	 * current <code>getTransaction</code> call on the transaction manager.
 1014   	 * Consequently, a <code>doGetTransaction</code> implementation will usually
 1015   	 * look for an existing transaction and store corresponding state in the
 1016   	 * returned transaction object.
 1017   	 * @return the current transaction object
 1018   	 * @throws org.springframework.transaction.CannotCreateTransactionException
 1019   	 * if transaction support is not available
 1020   	 * @throws TransactionException in case of lookup or system errors
 1021   	 * @see #doBegin
 1022   	 * @see #doCommit
 1023   	 * @see #doRollback
 1024   	 * @see DefaultTransactionStatus#getTransaction
 1025   	 */
 1026   	protected abstract Object doGetTransaction() throws TransactionException;
 1027   
 1028   	/**
 1029   	 * Check if the given transaction object indicates an existing transaction
 1030   	 * (that is, a transaction which has already started).
 1031   	 * <p>The result will be evaluated according to the specified propagation
 1032   	 * behavior for the new transaction. An existing transaction might get
 1033   	 * suspended (in case of PROPAGATION_REQUIRES_NEW), or the new transaction
 1034   	 * might participate in the existing one (in case of PROPAGATION_REQUIRED).
 1035   	 * <p>The default implementation returns <code>false</code>, assuming that
 1036   	 * participating in existing transactions is generally not supported.
 1037   	 * Subclasses are of course encouraged to provide such support.
 1038   	 * @param transaction transaction object returned by doGetTransaction
 1039   	 * @return if there is an existing transaction
 1040   	 * @throws TransactionException in case of system errors
 1041   	 * @see #doGetTransaction
 1042   	 */
 1043   	protected boolean isExistingTransaction(Object transaction) throws TransactionException {
 1044   		return false;
 1045   	}
 1046   
 1047   	/**
 1048   	 * Return whether to use a savepoint for a nested transaction.
 1049   	 * <p>Default is <code>true</code>, which causes delegation to DefaultTransactionStatus
 1050   	 * for creating and holding a savepoint. If the transaction object does not implement
 1051   	 * the SavepointManager interface, a NestedTransactionNotSupportedException will be
 1052   	 * thrown. Else, the SavepointManager will be asked to create a new savepoint to
 1053   	 * demarcate the start of the nested transaction.
 1054   	 * <p>Subclasses can override this to return <code>false</code>, causing a further
 1055   	 * call to <code>doBegin</code> - within the context of an already existing transaction.
 1056   	 * The <code>doBegin</code> implementation needs to handle this accordingly in such
 1057   	 * a scenario. This is appropriate for JTA, for example.
 1058   	 * @see DefaultTransactionStatus#createAndHoldSavepoint
 1059   	 * @see DefaultTransactionStatus#rollbackToHeldSavepoint
 1060   	 * @see DefaultTransactionStatus#releaseHeldSavepoint
 1061   	 * @see #doBegin
 1062   	 */
 1063   	protected boolean useSavepointForNestedTransaction() {
 1064   		return true;
 1065   	}
 1066   
 1067   	/**
 1068   	 * Begin a new transaction with semantics according to the given transaction
 1069   	 * definition. Does not have to care about applying the propagation behavior,
 1070   	 * as this has already been handled by this abstract manager.
 1071   	 * <p>This method gets called when the transaction manager has decided to actually
 1072   	 * start a new transaction. Either there wasn't any transaction before, or the
 1073   	 * previous transaction has been suspended.
 1074   	 * <p>A special scenario is a nested transaction without savepoint: If
 1075   	 * <code>useSavepointForNestedTransaction()</code> returns "false", this method
 1076   	 * will be called to start a nested transaction when necessary. In such a context,
 1077   	 * there will be an active transaction: The implementation of this method has
 1078   	 * to detect this and start an appropriate nested transaction.
 1079   	 * @param transaction transaction object returned by <code>doGetTransaction</code>
 1080   	 * @param definition TransactionDefinition instance, describing propagation
 1081   	 * behavior, isolation level, read-only flag, timeout, and transaction name
 1082   	 * @throws TransactionException in case of creation or system errors
 1083   	 */
 1084   	protected abstract void doBegin(Object transaction, TransactionDefinition definition)
 1085   	    throws TransactionException;
 1086   
 1087   	/**
 1088   	 * Suspend the resources of the current transaction.
 1089   	 * Transaction synchronization will already have been suspended.
 1090   	 * <p>The default implementation throws a TransactionSuspensionNotSupportedException,
 1091   	 * assuming that transaction suspension is generally not supported.
 1092   	 * @param transaction transaction object returned by <code>doGetTransaction</code>
 1093   	 * @return an object that holds suspended resources
 1094   	 * (will be kept unexamined for passing it into doResume)
 1095   	 * @throws org.springframework.transaction.TransactionSuspensionNotSupportedException
 1096   	 * if suspending is not supported by the transaction manager implementation
 1097   	 * @throws TransactionException in case of system errors
 1098   	 * @see #doResume
 1099   	 */
 1100   	protected Object doSuspend(Object transaction) throws TransactionException {
 1101   		throw new TransactionSuspensionNotSupportedException(
 1102   				"Transaction manager [" + getClass().getName() + "] does not support transaction suspension");
 1103   	}
 1104   
 1105   	/**
 1106   	 * Resume the resources of the current transaction.
 1107   	 * Transaction synchronization will be resumed afterwards.
 1108   	 * <p>The default implementation throws a TransactionSuspensionNotSupportedException,
 1109   	 * assuming that transaction suspension is generally not supported.
 1110   	 * @param transaction transaction object returned by <code>doGetTransaction</code>
 1111   	 * @param suspendedResources the object that holds suspended resources,
 1112   	 * as returned by doSuspend
 1113   	 * @throws org.springframework.transaction.TransactionSuspensionNotSupportedException
 1114   	 * if resuming is not supported by the transaction manager implementation
 1115   	 * @throws TransactionException in case of system errors
 1116   	 * @see #doSuspend
 1117   	 */
 1118   	protected void doResume(Object transaction, Object suspendedResources) throws TransactionException {
 1119   		throw new TransactionSuspensionNotSupportedException(
 1120   				"Transaction manager [" + getClass().getName() + "] does not support transaction suspension");
 1121   	}
 1122   
 1123   	/**
 1124   	 * Return whether to call <code>doCommit</code> on a transaction that has been
 1125   	 * marked as rollback-only in a global fashion.
 1126   	 * <p>Does not apply if an application locally sets the transaction to rollback-only
 1127   	 * via the TransactionStatus, but only to the transaction itself being marked as
 1128   	 * rollback-only by the transaction coordinator.
 1129   	 * <p>Default is "false": Local transaction strategies usually don't hold the rollback-only
 1130   	 * marker in the transaction itself, therefore they can't handle rollback-only transactions
 1131   	 * as part of transaction commit. Hence, AbstractPlatformTransactionManager will trigger
 1132   	 * a rollback in that case, throwing an UnexpectedRollbackException afterwards.
 1133   	 * <p>Override this to return "true" if the concrete transaction manager expects a
 1134   	 * <code>doCommit</code> call even for a rollback-only transaction, allowing for
 1135   	 * special handling there. This will, for example, be the case for JTA, where
 1136   	 * <code>UserTransaction.commit</code> will check the read-only flag itself and
 1137   	 * throw a corresponding RollbackException, which might include the specific reason
 1138   	 * (such as a transaction timeout).
 1139   	 * <p>If this method returns "true" but the <code>doCommit</code> implementation does not
 1140   	 * throw an exception, this transaction manager will throw an UnexpectedRollbackException
 1141   	 * itself. This should not be the typical case; it is mainly checked to cover misbehaving
 1142   	 * JTA providers that silently roll back even when the rollback has not been requested
 1143   	 * by the calling code.
 1144   	 * @see #doCommit
 1145   	 * @see DefaultTransactionStatus#isGlobalRollbackOnly()
 1146   	 * @see DefaultTransactionStatus#isLocalRollbackOnly()
 1147   	 * @see org.springframework.transaction.TransactionStatus#setRollbackOnly()
 1148   	 * @see org.springframework.transaction.UnexpectedRollbackException
 1149   	 * @see javax.transaction.UserTransaction#commit()
 1150   	 * @see javax.transaction.RollbackException
 1151   	 */
 1152   	protected boolean shouldCommitOnGlobalRollbackOnly() {
 1153   		return false;
 1154   	}
 1155   
 1156   	/**
 1157   	 * Make preparations for commit, to be performed before the
 1158   	 * <code>beforeCommit</code> synchronization callbacks occur.
 1159   	 * <p>Note that exceptions will get propagated to the commit caller
 1160   	 * and cause a rollback of the transaction.
 1161   	 * @param status the status representation of the transaction
 1162   	 * @throws RuntimeException in case of errors; will be <b>propagated to the caller</b>
 1163   	 * (note: do not throw TransactionException subclasses here!)
 1164   	 */
 1165   	protected void prepareForCommit(DefaultTransactionStatus status) {
 1166   	}
 1167   
 1168   	/**
 1169   	 * Perform an actual commit of the given transaction.
 1170   	 * <p>An implementation does not need to check the "new transaction" flag
 1171   	 * or the rollback-only flag; this will already have been handled before.
 1172   	 * Usually, a straight commit will be performed on the transaction object
 1173   	 * contained in the passed-in status.
 1174   	 * @param status the status representation of the transaction
 1175   	 * @throws TransactionException in case of commit or system errors
 1176   	 * @see DefaultTransactionStatus#getTransaction
 1177   	 */
 1178   	protected abstract void doCommit(DefaultTransactionStatus status) throws TransactionException;
 1179   
 1180   	/**
 1181   	 * Perform an actual rollback of the given transaction.
 1182   	 * <p>An implementation does not need to check the "new transaction" flag;
 1183   	 * this will already have been handled before. Usually, a straight rollback
 1184   	 * will be performed on the transaction object contained in the passed-in status.
 1185   	 * @param status the status representation of the transaction
 1186   	 * @throws TransactionException in case of system errors
 1187   	 * @see DefaultTransactionStatus#getTransaction
 1188   	 */
 1189   	protected abstract void doRollback(DefaultTransactionStatus status) throws TransactionException;
 1190   
 1191   	/**
 1192   	 * Set the given transaction rollback-only. Only called on rollback
 1193   	 * if the current transaction participates in an existing one.
 1194   	 * <p>The default implementation throws an IllegalTransactionStateException,
 1195   	 * assuming that participating in existing transactions is generally not
 1196   	 * supported. Subclasses are of course encouraged to provide such support.
 1197   	 * @param status the status representation of the transaction
 1198   	 * @throws TransactionException in case of system errors
 1199   	 */
 1200   	protected void doSetRollbackOnly(DefaultTransactionStatus status) throws TransactionException {
 1201   		throw new IllegalTransactionStateException(
 1202   				"Participating in existing transactions is not supported - when 'isExistingTransaction' " +
 1203   				"returns true, appropriate 'doSetRollbackOnly' behavior must be provided");
 1204   	}
 1205   
 1206   	/**
 1207   	 * Register the given list of transaction synchronizations with the existing transaction.
 1208   	 * <p>Invoked when the control of the Spring transaction manager and thus all Spring
 1209   	 * transaction synchronizations end, without the transaction being completed yet. This
 1210   	 * is for example the case when participating in an existing JTA or EJB CMT transaction.
 1211   	 * <p>The default implementation simply invokes the <code>afterCompletion</code> methods
 1212   	 * immediately, passing in "STATUS_UNKNOWN". This is the best we can do if there's no
 1213   	 * chance to determine the actual outcome of the outer transaction.
 1214   	 * @param transaction transaction object returned by <code>doGetTransaction</code>
 1215   	 * @param synchronizations List of TransactionSynchronization objects
 1216   	 * @throws TransactionException in case of system errors
 1217   	 * @see #invokeAfterCompletion(java.util.List, int)
 1218   	 * @see TransactionSynchronization#afterCompletion(int)
 1219   	 * @see TransactionSynchronization#STATUS_UNKNOWN
 1220   	 */
 1221   	protected void registerAfterCompletionWithExistingTransaction(Object transaction, List synchronizations)
 1222   			throws TransactionException {
 1223   
 1224   		logger.debug("Cannot register Spring after-completion synchronization with existing transaction - " +
 1225   				"processing Spring after-completion callbacks immediately, with outcome status 'unknown'");
 1226   		invokeAfterCompletion(synchronizations, TransactionSynchronization.STATUS_UNKNOWN);
 1227   	}
 1228   
 1229   	/**
 1230   	 * Cleanup resources after transaction completion.
 1231   	 * <p>Called after <code>doCommit</code> and <code>doRollback</code> execution,
 1232   	 * on any outcome. The default implementation does nothing.
 1233   	 * <p>Should not throw any exceptions but just issue warnings on errors.
 1234   	 * @param transaction transaction object returned by <code>doGetTransaction</code>
 1235   	 */
 1236   	protected void doCleanupAfterCompletion(Object transaction) {
 1237   	}
 1238   
 1239   
 1240   	//---------------------------------------------------------------------
 1241   	// Serialization support
 1242   	//---------------------------------------------------------------------
 1243   
 1244   	private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
 1245   		// Rely on default serialization; just initialize state after deserialization.
 1246   		ois.defaultReadObject();
 1247   
 1248   		// Initialize transient fields.
 1249   		this.logger = LogFactory.getLog(getClass());
 1250   	}
 1251   
 1252   
 1253   	/**
 1254   	 * Holder for suspended resources.
 1255   	 * Used internally by <code>suspend</code> and <code>resume</code>.
 1256   	 */
 1257   	protected static class SuspendedResourcesHolder {
 1258   
 1259   		private final Object suspendedResources;
 1260   		private List suspendedSynchronizations;
 1261   		private String name;
 1262   		private boolean readOnly;
 1263   		private Integer isolationLevel;
 1264   		private boolean wasActive;
 1265   
 1266   		private SuspendedResourcesHolder(Object suspendedResources) {
 1267   			this.suspendedResources = suspendedResources;
 1268   		}
 1269   
 1270   		private SuspendedResourcesHolder(
 1271   				Object suspendedResources, List suspendedSynchronizations,
 1272   				String name, boolean readOnly, Integer isolationLevel, boolean wasActive) {
 1273   			this.suspendedResources = suspendedResources;
 1274   			this.suspendedSynchronizations = suspendedSynchronizations;
 1275   			this.name = name;
 1276   			this.readOnly = readOnly;
 1277   			this.isolationLevel = isolationLevel;
 1278   			this.wasActive = wasActive;
 1279   		}
 1280   	}
 1281   
 1282   }

Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » transaction » support » [javadoc | source]