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