KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > transaction > support > AbstractPlatformTransactionManager


1 /*
2  * Copyright 2002-2007 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 JavaDoc;
20 import java.io.ObjectInputStream JavaDoc;
21 import java.io.Serializable JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.List JavaDoc;
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.hibernate.HibernateTransactionManager
84  */

85 public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable JavaDoc {
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 globalRollbackOnParticipationFailure = true;
126
127     private boolean failEarlyOnGlobalRollbackOnly = false;
128
129     private boolean rollbackOnCommitFailure = false;
130
131
132     /**
133      * Set the transaction synchronization by the name of the corresponding constant
134      * in this class, e.g. "SYNCHRONIZATION_ALWAYS".
135      * @param constantName name of the constant
136      * @see #SYNCHRONIZATION_ALWAYS
137      */

138     public final void setTransactionSynchronizationName(String JavaDoc constantName) {
139         setTransactionSynchronization(constants.asNumber(constantName).intValue());
140     }
141
142     /**
143      * Set when this transaction manager should activate the thread-bound
144      * transaction synchronization support. Default is "always".
145      * <p>Note that transaction synchronization isn't supported for
146      * multiple concurrent transactions by different transaction managers.
147      * Only one transaction manager is allowed to activate it at any time.
148      * @see #SYNCHRONIZATION_ALWAYS
149      * @see #SYNCHRONIZATION_ON_ACTUAL_TRANSACTION
150      * @see #SYNCHRONIZATION_NEVER
151      * @see TransactionSynchronizationManager
152      * @see TransactionSynchronization
153      */

154     public final void setTransactionSynchronization(int transactionSynchronization) {
155         this.transactionSynchronization = transactionSynchronization;
156     }
157
158     /**
159      * Return if this transaction manager should activate the thread-bound
160      * transaction synchronization support.
161      */

162     public final int getTransactionSynchronization() {
163         return this.transactionSynchronization;
164     }
165
166     /**
167      * Specify the default timeout that this transaction manager should apply
168      * if there is no timeout specified at the transaction level, in seconds.
169      * <p>Default is the underlying transaction infrastructure's default timeout,
170      * e.g. typically 30 seconds in case of a JTA provider, indicated by the
171      * <code>TransactionDefinition.TIMEOUT_DEFAULT</code> value.
172      * @see org.springframework.transaction.TransactionDefinition#TIMEOUT_DEFAULT
173      */

174     public final void setDefaultTimeout(int defaultTimeout) {
175         if (defaultTimeout < TransactionDefinition.TIMEOUT_DEFAULT) {
176             throw new InvalidTimeoutException("Invalid default timeout", defaultTimeout);
177         }
178         this.defaultTimeout = defaultTimeout;
179     }
180
181     /**
182      * Return the default timeout that this transaction manager should apply
183      * if there is no timeout specified at the transaction level, in seconds.
184      * <p>Returns <code>TransactionDefinition.TIMEOUT_DEFAULT</code> to indicate
185      * the underlying transaction infrastructure's default timeout.
186      */

187     public final int getDefaultTimeout() {
188         return this.defaultTimeout;
189     }
190
191     /**
192      * Set whether nested transactions are allowed. Default is "false".
193      * <p>Typically initialized with an appropriate default by the
194      * concrete transaction manager subclass.
195      */

196     public final void setNestedTransactionAllowed(boolean nestedTransactionAllowed) {
197         this.nestedTransactionAllowed = nestedTransactionAllowed;
198     }
199
200     /**
201      * Return whether nested transactions are allowed.
202      */

203     public final boolean isNestedTransactionAllowed() {
204         return this.nestedTransactionAllowed;
205     }
206
207     /**
208      * Set whether to globally mark an existing transaction as rollback-only
209      * after a participating transaction failed.
210      * <p>Default is "true": If a participating transaction (e.g. with
211      * PROPAGATION_REQUIRES or PROPAGATION_SUPPORTS encountering an existing
212      * transaction) fails, the transaction will be globally marked as rollback-only.
213      * The only possible outcome of such a transaction is a rollback: The
214      * transaction originator <i>cannot</i> make the transaction commit anymore.
215      * <p>Switch this to "false" to let the transaction originator make the rollback
216      * decision. If a participating transaction fails with an exception, the caller
217      * can still decide to continue with a different path within the transaction.
218      * However, note that this will only work as long as all participating resources
219      * are capable of continuing towards a transaction commit even after a data access
220      * failure: This is generally not the case for a Hibernate Session, for example;
221      * neither is it for a sequence of JDBC insert/update/delete operations.
222      * <p><b>Note:</b>This flag only applies to an explicit rollback attempt for a
223      * subtransaction, typically caused by an exception thrown by a data access operation
224      * (where TransactionInterceptor will trigger a <code>PlatformTransactionManager.rollback()</code>
225      * call according to a rollback rule). If the flag is off, the caller can handle the exception
226      * and decide on a rollback, independent of the rollback rules of the subtransaction.
227      * This flag does, however, <i>not</i> apply to explicit <code>setRollbackOnly</code>
228      * calls on a <code>TransactionStatus</code>, which will always cause an eventual
229      * global rollback (as it might not throw an exception after the rollback-only call).
230      * <p>The recommended solution for handling failure of a subtransaction
231      * is a "nested transaction", where the global transaction can be rolled
232      * back to a savepoint taken at the beginning of the subtransaction.
233      * PROPAGATION_NESTED provides exactly those semantics; however, it will
234      * only work when nested transaction support is available. This is the case
235      * with DataSourceTransactionManager, but not with JtaTransactionManager.
236      * @see #setNestedTransactionAllowed
237      * @see org.springframework.jdbc.datasource.DataSourceTransactionManager
238      * @see org.springframework.transaction.jta.JtaTransactionManager
239      */

240     public final void setGlobalRollbackOnParticipationFailure(boolean globalRollbackOnParticipationFailure) {
241         this.globalRollbackOnParticipationFailure = globalRollbackOnParticipationFailure;
242     }
243
244     /**
245      * Return whether to globally mark an existing transaction as rollback-only
246      * after a participating transaction failed.
247      */

248     public final boolean isGlobalRollbackOnParticipationFailure() {
249         return this.globalRollbackOnParticipationFailure;
250     }
251
252     /**
253      * Set whether to fail early in case of the transaction being globally marked
254      * as rollback-only.
255      * <p>Default is "false", only causing an UnexpectedRollbackException at the
256      * outermost transaction boundary. Switch this flag on to cause an
257      * UnexpectedRollbackException as early as the global rollback-only marker
258      * has been first detected, even from within an inner transaction boundary.
259      * <p>Note that, as of Spring 2.0, the fail-early behavior for global
260      * rollback-only markers has been unified: All transaction managers will by
261      * default only cause UnexpectedRollbackException at the outermost transaction
262      * boundary. This allows, for example, to continue unit tests even after an
263      * operation failed and the transaction will never be completed. All transaction
264      * managers will only fail earlier if this flag has explicitly been set to "true".
265      * @see org.springframework.transaction.UnexpectedRollbackException
266      */

267     public final void setFailEarlyOnGlobalRollbackOnly(boolean failEarlyOnGlobalRollbackOnly) {
268         this.failEarlyOnGlobalRollbackOnly = failEarlyOnGlobalRollbackOnly;
269     }
270
271     /**
272      * Return whether to fail early in case of the transaction being globally marked
273      * as rollback-only.
274      */

275     public final boolean isFailEarlyOnGlobalRollbackOnly() {
276         return this.failEarlyOnGlobalRollbackOnly;
277     }
278
279     /**
280      * Set whether <code>doRollback</code> should be performed on failure of the
281      * <code>doCommit</code> call. Typically not necessary and thus to be avoided,
282      * as it can potentially override the commit exception with a subsequent
283      * rollback exception.
284      * <p>Default is "false".
285      * @see #doCommit
286      * @see #doRollback
287      */

288     public final void setRollbackOnCommitFailure(boolean rollbackOnCommitFailure) {
289         this.rollbackOnCommitFailure = rollbackOnCommitFailure;
290     }
291
292     /**
293      * Return whether <code>doRollback</code> should be performed on failure of the
294      * <code>doCommit</code> call.
295      */

296     public final boolean isRollbackOnCommitFailure() {
297         return this.rollbackOnCommitFailure;
298     }
299
300
301     //---------------------------------------------------------------------
302
// Implementation of PlatformTransactionManager
303
//---------------------------------------------------------------------
304

305     /**
306      * This implementation handles propagation behavior. Delegates to
307      * <code>doGetTransaction</code>, <code>isExistingTransaction</code>
308      * and <code>doBegin</code>.
309      * @see #doGetTransaction
310      * @see #isExistingTransaction
311      * @see #doBegin
312      */

313     public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
314         Object JavaDoc transaction = doGetTransaction();
315
316         // Cache debug flag to avoid repeated checks.
317
boolean debugEnabled = logger.isDebugEnabled();
318         if (debugEnabled) {
319             logger.debug("Using transaction object [" + transaction + "]");
320         }
321
322         if (definition == null) {
323             // Use defaults if no transaction definition given.
324
definition = new DefaultTransactionDefinition();
325         }
326
327         if (isExistingTransaction(transaction)) {
328             // Existing transaction found -> check propagation behavior to find out how to behave.
329
return handleExistingTransaction(definition, transaction, debugEnabled);
330         }
331
332         // Check definition settings for new transaction.
333
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
334             throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
335         }
336
337         // No existing transaction found -> check propagation behavior to find out how to proceed.
338
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
339             throw new IllegalTransactionStateException(
340                     "No existing transaction found for transaction marked with propagation 'mandatory'");
341         }
342         else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
343                 definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
344             definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
345             Object JavaDoc suspendedResources = suspend(null);
346             if (debugEnabled) {
347                 logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
348             }
349             doBegin(transaction, definition);
350             boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
351             return newTransactionStatus(
352                     definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
353         }
354         else {
355             // Create "empty" transaction: no actual transaction, but potentially synchronization.
356
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
357             return newTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
358         }
359     }
360
361     /**
362      * Create a TransactionStatus for an existing transaction.
363      */

364     private TransactionStatus handleExistingTransaction(
365             TransactionDefinition definition, Object JavaDoc transaction, boolean debugEnabled)
366             throws TransactionException {
367
368         if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
369             throw new IllegalTransactionStateException(
370                     "Existing transaction found for transaction marked with propagation 'never'");
371         }
372
373         if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
374             if (debugEnabled) {
375                 logger.debug("Suspending current transaction");
376             }
377             Object JavaDoc suspendedResources = suspend(transaction);
378             boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
379             return newTransactionStatus(
380                     definition, null, false, newSynchronization, debugEnabled, suspendedResources);
381         }
382
383         if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
384             if (debugEnabled) {
385                 logger.debug("Suspending current transaction, creating new transaction with name [" +
386                         definition.getName() + "]");
387             }
388             SuspendedResourcesHolder suspendedResources = suspend(transaction);
389             try {
390                 doBegin(transaction, definition);
391             }
392             catch (TransactionException beginEx) {
393                 try {
394                     resume(transaction, suspendedResources);
395                 }
396                 catch (TransactionException resumeEx) {
397                     logger.error(
398                             "Inner transaction begin exception overridden by outer transaction resume exception", beginEx);
399                     throw resumeEx;
400                 }
401                 throw beginEx;
402             }
403             boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
404             return newTransactionStatus(
405                     definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
406         }
407
408         if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
409             if (!isNestedTransactionAllowed()) {
410                 throw new NestedTransactionNotSupportedException(
411                         "Transaction manager does not allow nested transactions by default - " +
412                         "specify 'nestedTransactionAllowed' property with value 'true'");
413             }
414             if (debugEnabled) {
415                 logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
416             }
417             if (useSavepointForNestedTransaction()) {
418                 // Create savepoint within existing Spring-managed transaction,
419
// through the SavepointManager API implemented by TransactionStatus.
420
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
421
DefaultTransactionStatus status =
422                         newTransactionStatus(definition, transaction, false, false, debugEnabled, null);
423                 status.createAndHoldSavepoint();
424                 return status;
425             }
426             else {
427                 // Nested transaction through nested begin and commit/rollback calls.
428
// Usually only for JTA: Spring synchronization might get activated here
429
// in case of a pre-existing JTA transaction.
430
doBegin(transaction, definition);
431                 boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
432                 return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);
433             }
434         }
435
436         // Assumably PROPAGATION_SUPPORTS.
437
if (debugEnabled) {
438             logger.debug("Participating in existing transaction");
439         }
440         boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
441         return newTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
442     }
443
444     /**
445      * Create a new TransactionStatus for the given arguments,
446      * initializing transaction synchronization as appropriate.
447      */

448     protected DefaultTransactionStatus newTransactionStatus(
449             TransactionDefinition definition, Object JavaDoc transaction, boolean newTransaction,
450             boolean newSynchronization, boolean debug, Object JavaDoc suspendedResources) {
451
452         boolean actualNewSynchronization = newSynchronization &&
453                 !TransactionSynchronizationManager.isSynchronizationActive();
454         if (actualNewSynchronization) {
455             TransactionSynchronizationManager.setActualTransactionActive(transaction != null);
456             TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
457                     (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) ?
458                             new Integer JavaDoc(definition.getIsolationLevel()) : null);
459             TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
460             TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
461             TransactionSynchronizationManager.initSynchronization();
462         }
463         return new DefaultTransactionStatus(
464                 transaction, newTransaction, actualNewSynchronization,
465                 definition.isReadOnly(), debug, suspendedResources);
466     }
467
468     /**
469      * Determine the actual timeout to use for the given definition.
470      * Will fall back to this manager's default timeout if the
471      * transaction definition doesn't specify a non-default value.
472      * @param definition the transaction definition
473      * @return the actual timeout to use
474      * @see org.springframework.transaction.TransactionDefinition#getTimeout()
475      * @see #setDefaultTimeout
476      */

477     protected int determineTimeout(TransactionDefinition definition) {
478         if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {
479             return definition.getTimeout();
480         }
481         return this.defaultTimeout;
482     }
483
484
485     /**
486      * Suspend the given transaction. Suspends transaction synchronization first,
487      * then delegates to the <code>doSuspend</code> template method.
488      * @param transaction the current transaction object
489      * (or <code>null</code> to just suspend active synchronizations, if any)
490      * @return an object that holds suspended resources
491      * (or <code>null</code> if neither transaction nor synchronization active)
492      * @see #doSuspend
493      * @see #resume
494      */

495     protected final SuspendedResourcesHolder suspend(Object JavaDoc transaction) throws TransactionException {
496         if (TransactionSynchronizationManager.isSynchronizationActive()) {
497             List JavaDoc suspendedSynchronizations = doSuspendSynchronization();
498             try {
499                 Object JavaDoc suspendedResources = null;
500                 if (transaction != null) {
501                     suspendedResources = doSuspend(transaction);
502                 }
503                 String JavaDoc name = TransactionSynchronizationManager.getCurrentTransactionName();
504                 TransactionSynchronizationManager.setCurrentTransactionName(null);
505                 boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
506                 TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
507                 Integer JavaDoc isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
508                 TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
509                 TransactionSynchronizationManager.setActualTransactionActive(false);
510                 return new SuspendedResourcesHolder(
511                         suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel);
512             }
513             catch (TransactionException ex) {
514                 // doSuspend failed - original transaction is still active...
515
doResumeSynchronization(suspendedSynchronizations);
516                 throw ex;
517             }
518         }
519         else if (transaction != null) {
520             // Transaction active but no synchronization active.
521
Object JavaDoc suspendedResources = doSuspend(transaction);
522             return new SuspendedResourcesHolder(suspendedResources, null, null, false, null);
523         }
524         else {
525             // Neither transaction nor synchronization active.
526
return null;
527         }
528     }
529
530     /**
531      * Resume the given transaction. Delegates to the <code>doResume</code>
532      * template method first, then resuming transaction synchronization.
533      * @param transaction the current transaction object
534      * @param resourcesHolder the object that holds suspended resources,
535      * as returned by <code>suspend</code> (or <code>null</code> to just
536      * resume synchronizations, if any)
537      * @see #doResume
538      * @see #suspend
539      */

540     protected final void resume(Object JavaDoc transaction, SuspendedResourcesHolder resourcesHolder) throws TransactionException {
541         Object JavaDoc suspendedResources = resourcesHolder.suspendedResources;
542         if (suspendedResources != null) {
543             doResume(transaction, suspendedResources);
544         }
545         List JavaDoc suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
546         if (suspendedSynchronizations != null) {
547             TransactionSynchronizationManager.setActualTransactionActive(suspendedResources != null);
548             TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
549             TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
550             TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
551             doResumeSynchronization(suspendedSynchronizations);
552         }
553     }
554
555     /**
556      * Suspend all current synchronizations and deactivate transaction
557      * synchronization for the current thread.
558      * @return the List of suspended TransactionSynchronization objects
559      */

560     private List JavaDoc doSuspendSynchronization() {
561         List JavaDoc suspendedSynchronizations = TransactionSynchronizationManager.getSynchronizations();
562         for (Iterator JavaDoc it = suspendedSynchronizations.iterator(); it.hasNext();) {
563             ((TransactionSynchronization) it.next()).suspend();
564         }
565         TransactionSynchronizationManager.clearSynchronization();
566         return suspendedSynchronizations;
567     }
568
569     /**
570      * Reactivate transaction synchronization for the current thread
571      * and resume all given synchronizations.
572      * @param suspendedSynchronizations List of TransactionSynchronization objects
573      */

574     private void doResumeSynchronization(List JavaDoc suspendedSynchronizations) {
575         TransactionSynchronizationManager.initSynchronization();
576         for (Iterator JavaDoc it = suspendedSynchronizations.iterator(); it.hasNext();) {
577             TransactionSynchronization synchronization = (TransactionSynchronization) it.next();
578             synchronization.resume();
579             TransactionSynchronizationManager.registerSynchronization(synchronization);
580         }
581     }
582
583
584     /**
585      * This implementation of commit handles participating in existing
586      * transactions and programmatic rollback requests.
587      * Delegates to <code>isRollbackOnly</code>, <code>doCommit</code>
588      * and <code>rollback</code>.
589      * @see org.springframework.transaction.TransactionStatus#isRollbackOnly()
590      * @see #doCommit
591      * @see #rollback
592      */

593     public final void commit(TransactionStatus status) throws TransactionException {
594         if (status.isCompleted()) {
595             throw new IllegalTransactionStateException(
596                     "Transaction is already completed - do not call commit or rollback more than once per transaction");
597         }
598
599         DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
600         if (defStatus.isLocalRollbackOnly()) {
601             if (defStatus.isDebug()) {
602                 logger.debug("Transactional code has requested rollback");
603             }
604             processRollback(defStatus);
605             return;
606         }
607         if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
608             if (defStatus.isDebug()) {
609                 logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
610             }
611             processRollback(defStatus);
612             // Throw UnexpectedRollbackException only at outermost transaction boundary
613
// or if explicitly asked to.
614
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
615                 throw new UnexpectedRollbackException(
616                         "Transaction rolled back because it has been marked as rollback-only");
617             }
618             return;
619         }
620
621         processCommit(defStatus);
622     }
623
624     /**
625      * Process an actual commit.
626      * Rollback-only flags have already been checked and applied.
627      * @param status object representing the transaction
628      * @throws TransactionException in case of commit failure
629      */

630     private void processCommit(DefaultTransactionStatus status) throws TransactionException {
631         try {
632             boolean beforeCompletionInvoked = false;
633             try {
634                 triggerBeforeCommit(status);
635                 triggerBeforeCompletion(status);
636                 beforeCompletionInvoked = true;
637                 boolean globalRollbackOnly = false;
638                 if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
639                     globalRollbackOnly = status.isGlobalRollbackOnly();
640                 }
641                 if (status.hasSavepoint()) {
642                     if (status.isDebug()) {
643                         logger.debug("Releasing transaction savepoint");
644                     }
645                     status.releaseHeldSavepoint();
646                 }
647                 else if (status.isNewTransaction()) {
648                     if (status.isDebug()) {
649                         logger.debug("Initiating transaction commit");
650                     }
651                     doCommit(status);
652                 }
653                 // Throw UnexpectedRollbackException if we have a global rollback-only
654
// marker but still didn't get a corresponding exception from commit.
655
if (globalRollbackOnly) {
656                     throw new UnexpectedRollbackException(
657                             "Transaction silently rolled back because it has been marked as rollback-only");
658                 }
659             }
660             catch (UnexpectedRollbackException ex) {
661                 // can only be caused by doCommit
662
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
663                 throw ex;
664             }
665             catch (TransactionException ex) {
666                 // can only be caused by doCommit
667
if (isRollbackOnCommitFailure()) {
668                     doRollbackOnCommitException(status, ex);
669                 }
670                 else {
671                     triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
672                 }
673                 throw ex;
674             }
675             catch (RuntimeException JavaDoc ex) {
676                 if (!beforeCompletionInvoked) {
677                     triggerBeforeCompletion(status);
678                 }
679                 doRollbackOnCommitException(status, ex);
680                 throw ex;
681             }
682             catch (Error JavaDoc err) {
683                 if (!beforeCompletionInvoked) {
684                     triggerBeforeCompletion(status);
685                 }
686                 doRollbackOnCommitException(status, err);
687                 throw err;
688             }
689
690             // Trigger afterCommit callbacks, with an exception thrown there
691
// propagated to callers but the transaction still considered as committed.
692
try {
693                 triggerAfterCommit(status);
694             }
695             finally {
696                 triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
697             }
698
699         }
700         finally {
701             cleanupAfterCompletion(status);
702         }
703     }
704
705     /**
706      * This implementation of rollback handles participating in existing
707      * transactions. Delegates to <code>doRollback</code> and
708      * <code>doSetRollbackOnly</code>.
709      * @see #doRollback
710      * @see #doSetRollbackOnly
711      */

712     public final void rollback(TransactionStatus status) throws TransactionException {
713         if (status.isCompleted()) {
714             throw new IllegalTransactionStateException(
715                     "Transaction is already completed - do not call commit or rollback more than once per transaction");
716         }
717
718         DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
719         processRollback(defStatus);
720     }
721
722     /**
723      * Process an actual rollback.
724      * The completed flag has already been checked.
725      * @param status object representing the transaction
726      * @throws TransactionException in case of rollback failure
727      */

728     private void processRollback(DefaultTransactionStatus status) {
729         try {
730             try {
731                 triggerBeforeCompletion(status);
732                 if (status.hasSavepoint()) {
733                     if (status.isDebug()) {
734                         logger.debug("Rolling back transaction to savepoint");
735                     }
736                     status.rollbackToHeldSavepoint();
737                 }
738                 else if (status.isNewTransaction()) {
739                     if (status.isDebug()) {
740                         logger.debug("Initiating transaction rollback");
741                     }
742                     doRollback(status);
743                 }
744                 else if (status.hasTransaction()) {
745                     if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
746                         if (status.isDebug()) {
747                             logger.debug(
748                                     "Participating transaction failed - marking existing transaction as rollback-only");
749                         }
750                         doSetRollbackOnly(status);
751                     }
752                     else {
753                         if (status.isDebug()) {
754                             logger.debug(
755                                     "Participating transaction failed - letting transaction originator decide on rollback");
756                         }
757                     }
758                 }
759                 else {
760                     logger.debug("Should roll back transaction but cannot - no transaction available");
761                 }
762             }
763             catch (RuntimeException JavaDoc ex) {
764                 triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
765                 throw ex;
766             }
767             catch (Error JavaDoc err) {
768                 triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
769                 throw err;
770             }
771             triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
772         }
773         finally {
774             cleanupAfterCompletion(status);
775         }
776     }
777
778     /**
779      * Invoke <code>doRollback</code>, handling rollback exceptions properly.
780      * @param status object representing the transaction
781      * @param ex the thrown application exception or error
782      * @throws TransactionException in case of rollback failure
783      * @see #doRollback
784      */

785     private void doRollbackOnCommitException(DefaultTransactionStatus status, Throwable JavaDoc ex)
786         throws TransactionException {
787         try {
788             if (status.isNewTransaction()) {
789                 if (status.isDebug()) {
790                     logger.debug("Initiating transaction rollback after commit exception", ex);
791                 }
792                 doRollback(status);
793             }
794             else if (status.hasTransaction() && isGlobalRollbackOnParticipationFailure()) {
795                 if (status.isDebug()) {
796                     logger.debug("Marking existing transaction as rollback-only after commit exception", ex);
797                 }
798                 doSetRollbackOnly(status);
799             }
800         }
801         catch (RuntimeException JavaDoc rbex) {
802             logger.error("Commit exception overridden by rollback exception", ex);
803             triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
804             throw rbex;
805         }
806         catch (Error JavaDoc rberr) {
807             logger.error("Commit exception overridden by rollback exception", ex);
808             triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
809             throw rberr;
810         }
811         triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
812     }
813
814
815     /**
816      * Trigger <code>beforeCommit</code> callbacks.
817      * @param status object representing the transaction
818      */

819     protected final void triggerBeforeCommit(DefaultTransactionStatus status) {
820         if (status.isNewSynchronization()) {
821             if (status.isDebug()) {
822                 logger.debug("Triggering beforeCommit synchronization");
823             }
824             TransactionSynchronizationUtils.triggerBeforeCommit(status.isReadOnly());
825         }
826     }
827
828     /**
829      * Trigger <code>beforeCompletion</code> callbacks.
830      * @param status object representing the transaction
831      */

832     protected final void triggerBeforeCompletion(DefaultTransactionStatus status) {
833         if (status.isNewSynchronization()) {
834             if (status.isDebug()) {
835                 logger.debug("Triggering beforeCompletion synchronization");
836             }
837             TransactionSynchronizationUtils.triggerBeforeCompletion();
838         }
839     }
840
841     /**
842      * Trigger <code>afterCommit</code> callbacks.
843      * @param status object representing the transaction
844      */

845     private void triggerAfterCommit(DefaultTransactionStatus status) {
846         if (status.isNewSynchronization()) {
847             if (status.isDebug()) {
848                 logger.debug("Triggering afterCommit synchronization");
849             }
850             TransactionSynchronizationUtils.triggerAfterCommit();
851         }
852     }
853
854     /**
855      * Trigger <code>afterCompletion</code> callbacks.
856      * @param status object representing the transaction
857      * @param completionStatus completion status according to TransactionSynchronization constants
858      */

859     private void triggerAfterCompletion(DefaultTransactionStatus status, int completionStatus) {
860         if (status.isNewSynchronization()) {
861             List JavaDoc synchronizations = TransactionSynchronizationManager.getSynchronizations();
862             if (!status.hasTransaction() || status.isNewTransaction()) {
863                 if (status.isDebug()) {
864                     logger.debug("Triggering afterCompletion synchronization");
865                 }
866                 // No transaction or new transaction for the current scope ->
867
// invoke the afterCompletion callbacks immediately
868
invokeAfterCompletion(synchronizations, completionStatus);
869             }
870             else {
871                 // Existing transaction that we participate in, controlled outside
872
// of the scope of this Spring transaction manager -> try to register
873
// an afterCompletion callback with the existing (JTA) transaction.
874
registerAfterCompletionWithExistingTransaction(status.getTransaction(), synchronizations);
875             }
876         }
877     }
878
879     /**
880      * Actually invoke the <code>afterCompletion</code> methods of the
881      * given Spring TransactionSynchronization objects.
882      * <p>To be called by this abstract manager itself, or by special implementations
883      * of the <code>registerAfterCompletionWithExistingTransaction</code> callback.
884      * @param synchronizations List of TransactionSynchronization objects
885      * @param completionStatus the completion status according to the
886      * constants in the TransactionSynchronization interface
887      * @see #registerAfterCompletionWithExistingTransaction(Object, java.util.List)
888      * @see TransactionSynchronization#STATUS_COMMITTED
889      * @see TransactionSynchronization#STATUS_ROLLED_BACK
890      * @see TransactionSynchronization#STATUS_UNKNOWN
891      */

892     protected final void invokeAfterCompletion(List JavaDoc synchronizations, int completionStatus) {
893         TransactionSynchronizationUtils.invokeAfterCompletion(synchronizations, completionStatus);
894     }
895
896     /**
897      * Clean up after completion, clearing synchronization if necessary,
898      * and invoking doCleanupAfterCompletion.
899      * @param status object representing the transaction
900      * @see #doCleanupAfterCompletion
901      */

902     private void cleanupAfterCompletion(DefaultTransactionStatus status) {
903         status.setCompleted();
904         if (status.isNewSynchronization()) {
905             TransactionSynchronizationManager.clear();
906         }
907         if (status.isNewTransaction()) {
908             doCleanupAfterCompletion(status.getTransaction());
909         }
910         if (status.getSuspendedResources() != null) {
911             if (status.isDebug()) {
912                 logger.debug("Resuming suspended transaction");
913             }
914             resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspendedResources());
915         }
916     }
917
918
919     //---------------------------------------------------------------------
920
// Template methods to be implemented in subclasses
921
//---------------------------------------------------------------------
922

923     /**
924      * Return a transaction object for the current transaction state.
925      * <p>The returned object will usually be specific to the concrete transaction
926      * manager implementation, carrying corresponding transaction state in a
927      * modifiable fashion. This object will be passed into the other template
928      * methods (e.g. doBegin and doCommit), either directly or as part of a
929      * DefaultTransactionStatus instance.
930      * <p>The returned object should contain information about any existing
931      * transaction, that is, a transaction that has already started before the
932      * current <code>getTransaction</code> call on the transaction manager.
933      * Consequently, a <code>doGetTransaction</code> implementation will usually
934      * look for an existing transaction and store corresponding state in the
935      * returned transaction object.
936      * @return the current transaction object
937      * @throws org.springframework.transaction.CannotCreateTransactionException
938      * if transaction support is not available
939      * @throws TransactionException in case of lookup or system errors
940      * @see #doBegin
941      * @see #doCommit
942      * @see #doRollback
943      * @see DefaultTransactionStatus#getTransaction
944      */

945     protected abstract Object JavaDoc doGetTransaction() throws TransactionException;
946
947     /**
948      * Check if the given transaction object indicates an existing transaction
949      * (that is, a transaction which has already started).
950      * <p>The result will be evaluated according to the specified propagation
951      * behavior for the new transaction. An existing transaction might get
952      * suspended (in case of PROPAGATION_REQUIRES_NEW), or the new transaction
953      * might participate in the existing one (in case of PROPAGATION_REQUIRED).
954      * <p>The default implementation returns <code>false</code>, assuming that
955      * participating in existing transactions is generally not supported.
956      * Subclasses are of course encouraged to provide such support.
957      * @param transaction transaction object returned by doGetTransaction
958      * @return if there is an existing transaction
959      * @throws TransactionException in case of system errors
960      * @see #doGetTransaction
961      */

962     protected boolean isExistingTransaction(Object JavaDoc transaction) throws TransactionException {
963         return false;
964     }
965
966     /**
967      * Return whether to use a savepoint for a nested transaction.
968      * <p>Default is <code>true</code>, which causes delegation to DefaultTransactionStatus
969      * for creating and holding a savepoint. If the transaction object does not implement
970      * the SavepointManager interface, a NestedTransactionNotSupportedException will be
971      * thrown. Else, the SavepointManager will be asked to create a new savepoint to
972      * demarcate the start of the nested transaction.
973      * <p>Subclasses can override this to return <code>false</code>, causing a further
974      * call to <code>doBegin</code> - within the context of an already existing transaction.
975      * The <code>doBegin</code> implementation needs to handle this accordingly in such
976      * a scenario. This is appropriate for JTA, for example.
977      * @see DefaultTransactionStatus#createAndHoldSavepoint
978      * @see DefaultTransactionStatus#rollbackToHeldSavepoint
979      * @see DefaultTransactionStatus#releaseHeldSavepoint
980      * @see #doBegin
981      */

982     protected boolean useSavepointForNestedTransaction() {
983         return true;
984     }
985
986     /**
987      * Begin a new transaction with semantics according to the given transaction
988      * definition. Does not have to care about applying the propagation behavior,
989      * as this has already been handled by this abstract manager.
990      * <p>This method gets called when the transaction manager has decided to actually
991      * start a new transaction. Either there wasn't any transaction before, or the
992      * previous transaction has been suspended.
993      * <p>A special scenario is a nested transaction without savepoint: If
994      * <code>useSavepointForNestedTransaction()</code> returns "false", this method
995      * will be called to start a nested transaction when necessary. In such a context,
996      * there will be an active transaction: The implementation of this method has
997      * to detect this and start an appropriate nested transaction.
998      * @param transaction transaction object returned by <code>doGetTransaction</code>
999      * @param definition TransactionDefinition instance, describing propagation
1000     * behavior, isolation level, read-only flag, timeout, and transaction name
1001     * @throws TransactionException in case of creation or system errors
1002     */

1003    protected abstract void doBegin(Object JavaDoc transaction, TransactionDefinition definition)
1004        throws TransactionException;
1005
1006    /**
1007     * Suspend the resources of the current transaction.
1008     * Transaction synchronization will already have been suspended.
1009     * <p>The default implementation throws a TransactionSuspensionNotSupportedException,
1010     * assuming that transaction suspension is generally not supported.
1011     * @param transaction transaction object returned by <code>doGetTransaction</code>
1012     * @return an object that holds suspended resources
1013     * (will be kept unexamined for passing it into doResume)
1014     * @throws org.springframework.transaction.TransactionSuspensionNotSupportedException
1015     * if suspending is not supported by the transaction manager implementation
1016     * @throws TransactionException in case of system errors
1017     * @see #doResume
1018     */

1019    protected Object JavaDoc doSuspend(Object JavaDoc transaction) throws TransactionException {
1020        throw new TransactionSuspensionNotSupportedException(
1021                "Transaction manager [" + getClass().getName() + "] does not support transaction suspension");
1022    }
1023
1024    /**
1025     * Resume the resources of the current transaction.
1026     * Transaction synchronization will be resumed afterwards.
1027     * <p>The default implementation throws a TransactionSuspensionNotSupportedException,
1028     * assuming that transaction suspension is generally not supported.
1029     * @param transaction transaction object returned by <code>doGetTransaction</code>
1030     * @param suspendedResources the object that holds suspended resources,
1031     * as returned by doSuspend
1032     * @throws org.springframework.transaction.TransactionSuspensionNotSupportedException
1033     * if resuming is not supported by the transaction manager implementation
1034     * @throws TransactionException in case of system errors
1035     * @see #doSuspend
1036     */

1037    protected void doResume(Object JavaDoc transaction, Object JavaDoc suspendedResources) throws TransactionException {
1038        throw new TransactionSuspensionNotSupportedException(
1039                "Transaction manager [" + getClass().getName() + "] does not support transaction suspension");
1040    }
1041
1042    /**
1043     * Return whether to call <code>doCommit</code> on a transaction that has been
1044     * marked as rollback-only in a global fashion.
1045     * <p>Does not apply if an application locally sets the transaction to rollback-only
1046     * via the TransactionStatus, but only to the transaction itself being marked as
1047     * rollback-only by the transaction coordinator.
1048     * <p>Default is "false": Local transaction strategies usually don't hold the rollback-only
1049     * marker in the transaction itself, therefore they can't handle rollback-only transactions
1050     * as part of transaction commit. Hence, AbstractPlatformTransactionManager will trigger
1051     * a rollback in that case, throwing an UnexpectedRollbackException afterwards.
1052     * <p>Override this to return "true" if the concrete transaction manager expects a
1053     * <code>doCommit</code> call even for a rollback-only transaction, allowing for
1054     * special handling there. This will, for example, be the case for JTA, where
1055     * <code>UserTransaction.commit</code> will check the read-only flag itself and
1056     * throw a corresponding RollbackException, which might include the specific reason
1057     * (such as a transaction timeout).
1058     * <p>If this method returns "true" but the <code>doCommit</code> implementation does not
1059     * throw an exception, this transaction manager will throw an UnexpectedRollbackException
1060     * itself. This should not be the typical case; it is mainly checked to cover misbehaving
1061     * JTA providers that silently roll back even when the rollback has not been requested
1062     * by the calling code.
1063     * @see #doCommit
1064     * @see DefaultTransactionStatus#isGlobalRollbackOnly()
1065     * @see DefaultTransactionStatus#isLocalRollbackOnly()
1066     * @see org.springframework.transaction.TransactionStatus#setRollbackOnly()
1067     * @see org.springframework.transaction.UnexpectedRollbackException
1068     * @see javax.transaction.UserTransaction#commit()
1069     * @see javax.transaction.RollbackException
1070     */

1071    protected boolean shouldCommitOnGlobalRollbackOnly() {
1072        return false;
1073    }
1074
1075    /**
1076     * Perform an actual commit of the given transaction.
1077     * <p>An implementation does not need to check the "new transaction" flag
1078     * or the rollback-only flag; this will already have been handled before.
1079     * Usually, a straight commit will be performed on the transaction object
1080     * contained in the passed-in status.
1081     * @param status the status representation of the transaction
1082     * @throws TransactionException in case of commit or system errors
1083     * @see DefaultTransactionStatus#getTransaction
1084     */

1085    protected abstract void doCommit(DefaultTransactionStatus status) throws TransactionException;
1086
1087    /**
1088     * Perform an actual rollback of the given transaction.
1089     * <p>An implementation does not need to check the "new transaction" flag;
1090     * this will already have been handled before. Usually, a straight rollback
1091     * will be performed on the transaction object contained in the passed-in status.
1092     * @param status the status representation of the transaction
1093     * @throws TransactionException in case of system errors
1094     * @see DefaultTransactionStatus#getTransaction
1095     */

1096    protected abstract void doRollback(DefaultTransactionStatus status) throws TransactionException;
1097
1098    /**
1099     * Set the given transaction rollback-only. Only called on rollback
1100     * if the current transaction participates in an existing one.
1101     * <p>The default implementation throws an IllegalTransactionStateException,
1102     * assuming that participating in existing transactions is generally not
1103     * supported. Subclasses are of course encouraged to provide such support.
1104     * @param status the status representation of the transaction
1105     * @throws TransactionException in case of system errors
1106     */

1107    protected void doSetRollbackOnly(DefaultTransactionStatus status) throws TransactionException {
1108        throw new IllegalTransactionStateException(
1109                "Participating in existing transactions is not supported - when 'isExistingTransaction' " +
1110                "returns true, appropriate 'doSetRollbackOnly' behavior must be provided");
1111    }
1112
1113    /**
1114     * Register the given list of transaction synchronizations with the existing transaction.
1115     * <p>Invoked when the control of the Spring transaction manager and thus all Spring
1116     * transaction synchronizations end, without the transaction being completed yet. This
1117     * is for example the case when participating in an existing JTA or EJB CMT transaction.
1118     * <p>The default implementation simply invokes the <code>afterCompletion</code> methods
1119     * immediately, passing in "STATUS_UNKNOWN". This is the best we can do if there's no
1120     * chance to determine the actual outcome of the outer transaction.
1121     * @param transaction transaction object returned by <code>doGetTransaction</code>
1122     * @param synchronizations List of TransactionSynchronization objects
1123     * @throws TransactionException in case of system errors
1124     * @see #invokeAfterCompletion(java.util.List, int)
1125     * @see TransactionSynchronization#afterCompletion(int)
1126     * @see TransactionSynchronization#STATUS_UNKNOWN
1127     */

1128    protected void registerAfterCompletionWithExistingTransaction(Object JavaDoc transaction, List JavaDoc synchronizations)
1129            throws TransactionException {
1130
1131        logger.debug("Cannot register Spring after-completion synchronization with existing transaction - " +
1132                "processing Spring after-completion callbacks immediately, with outcome status 'unknown'");
1133        invokeAfterCompletion(synchronizations, TransactionSynchronization.STATUS_UNKNOWN);
1134    }
1135
1136    /**
1137     * Cleanup resources after transaction completion.
1138     * <p>Called after <code>doCommit</code> and <code>doRollback</code> execution,
1139     * on any outcome. The default implementation does nothing.
1140     * <p>Should not throw any exceptions but just issue warnings on errors.
1141     * @param transaction transaction object returned by <code>doGetTransaction</code>
1142     */

1143    protected void doCleanupAfterCompletion(Object JavaDoc transaction) {
1144    }
1145
1146
1147    //---------------------------------------------------------------------
1148
// Serialization support
1149
//---------------------------------------------------------------------
1150

1151    private void readObject(ObjectInputStream JavaDoc ois) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
1152        // Rely on default serialization; just initialize state after deserialization.
1153
ois.defaultReadObject();
1154
1155        // Initialize transient fields.
1156
this.logger = LogFactory.getLog(getClass());
1157    }
1158
1159
1160    /**
1161     * Holder for suspended resources.
1162     * Used internally by <code>suspend</code> and <code>resume</code>.
1163     */

1164    protected static class SuspendedResourcesHolder {
1165
1166        private final Object JavaDoc suspendedResources;
1167        private final List JavaDoc suspendedSynchronizations;
1168        private final String JavaDoc name;
1169        private final boolean readOnly;
1170        private final Integer JavaDoc isolationLevel;
1171
1172        private SuspendedResourcesHolder(
1173                Object JavaDoc suspendedResources, List JavaDoc suspendedSynchronizations,
1174                String JavaDoc name, boolean readOnly, Integer JavaDoc isolationLevel) {
1175            this.suspendedResources = suspendedResources;
1176            this.suspendedSynchronizations = suspendedSynchronizations;
1177            this.name = name;
1178            this.readOnly = readOnly;
1179            this.isolationLevel = isolationLevel;
1180        }
1181    }
1182
1183}
1184
Popular Tags