0001: /*
0002: * Copyright 2002-2007 the original author or authors.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016:
0017: package org.springframework.transaction.support;
0018:
0019: import java.io.IOException;
0020: import java.io.ObjectInputStream;
0021: import java.io.Serializable;
0022: import java.util.Iterator;
0023: import java.util.List;
0024:
0025: import org.apache.commons.logging.Log;
0026: import org.apache.commons.logging.LogFactory;
0027:
0028: import org.springframework.core.Constants;
0029: import org.springframework.transaction.IllegalTransactionStateException;
0030: import org.springframework.transaction.InvalidTimeoutException;
0031: import org.springframework.transaction.NestedTransactionNotSupportedException;
0032: import org.springframework.transaction.PlatformTransactionManager;
0033: import org.springframework.transaction.TransactionDefinition;
0034: import org.springframework.transaction.TransactionException;
0035: import org.springframework.transaction.TransactionStatus;
0036: import org.springframework.transaction.TransactionSuspensionNotSupportedException;
0037: import org.springframework.transaction.UnexpectedRollbackException;
0038:
0039: /**
0040: * Abstract base class that implements Spring's standard transaction workflow,
0041: * serving as basis for concrete platform transaction managers like
0042: * {@link org.springframework.transaction.jta.JtaTransactionManager} and
0043: * {@link org.springframework.jdbc.datasource.DataSourceTransactionManager}.
0044: *
0045: * <p>This base class provides the following workflow handling:
0046: * <ul>
0047: * <li>determines if there is an existing transaction;
0048: * <li>applies the appropriate propagation behavior;
0049: * <li>suspends and resumes transactions if necessary;
0050: * <li>checks the rollback-only flag on commit;
0051: * <li>applies the appropriate modification on rollback
0052: * (actual rollback or setting rollback-only);
0053: * <li>triggers registered synchronization callbacks
0054: * (if transaction synchronization is active).
0055: * </ul>
0056: *
0057: * <p>Subclasses have to implement specific template methods for specific
0058: * states of a transaction, e.g.: begin, suspend, resume, commit, rollback.
0059: * The most important of them are abstract and must be provided by a concrete
0060: * implementation; for the rest, defaults are provided, so overriding is optional.
0061: *
0062: * <p>Transaction synchronization is a generic mechanism for registering callbacks
0063: * that get invoked at transaction completion time. This is mainly used internally
0064: * by the data access support classes for JDBC, Hibernate, JDO, etc when running
0065: * within a JTA transaction: They register resources that are opened within the
0066: * transaction for closing at transaction completion time, allowing e.g. for reuse
0067: * of the same Hibernate Session within the transaction. The same mechanism can
0068: * also be leveraged for custom synchronization needs in an application.
0069: *
0070: * <p>The state of this class is serializable, to allow for serializing the
0071: * transaction strategy along with proxies that carry a transaction interceptor.
0072: * It is up to subclasses if they wish to make their state to be serializable too.
0073: * They should implement the <code>java.io.Serializable</code> marker interface in
0074: * that case, and potentially a private <code>readObject()</code> method (according
0075: * to Java serialization rules) if they need to restore any transient state.
0076: *
0077: * @author Juergen Hoeller
0078: * @since 28.03.2003
0079: * @see #setTransactionSynchronization
0080: * @see TransactionSynchronizationManager
0081: * @see org.springframework.transaction.jta.JtaTransactionManager
0082: * @see org.springframework.jdbc.datasource.DataSourceTransactionManager
0083: * @see org.springframework.orm.hibernate3.HibernateTransactionManager
0084: */
0085: public abstract class AbstractPlatformTransactionManager implements
0086: PlatformTransactionManager, Serializable {
0087:
0088: /**
0089: * Always activate transaction synchronization, even for "empty" transactions
0090: * that result from PROPAGATION_SUPPORTS with no existing backend transaction.
0091: * @see org.springframework.transaction.TransactionDefinition#PROPAGATION_SUPPORTS
0092: * @see org.springframework.transaction.TransactionDefinition#PROPAGATION_NOT_SUPPORTED
0093: * @see org.springframework.transaction.TransactionDefinition#PROPAGATION_NEVER
0094: */
0095: public static final int SYNCHRONIZATION_ALWAYS = 0;
0096:
0097: /**
0098: * Activate transaction synchronization only for actual transactions,
0099: * that is, not for empty ones that result from PROPAGATION_SUPPORTS with
0100: * no existing backend transaction.
0101: * @see org.springframework.transaction.TransactionDefinition#PROPAGATION_REQUIRED
0102: * @see org.springframework.transaction.TransactionDefinition#PROPAGATION_MANDATORY
0103: * @see org.springframework.transaction.TransactionDefinition#PROPAGATION_REQUIRES_NEW
0104: */
0105: public static final int SYNCHRONIZATION_ON_ACTUAL_TRANSACTION = 1;
0106:
0107: /**
0108: * Never active transaction synchronization, not even for actual transactions.
0109: */
0110: public static final int SYNCHRONIZATION_NEVER = 2;
0111:
0112: /** Constants instance for AbstractPlatformTransactionManager */
0113: private static final Constants constants = new Constants(
0114: AbstractPlatformTransactionManager.class);
0115:
0116: /** Transient to optimize serialization */
0117: protected transient Log logger = LogFactory.getLog(getClass());
0118:
0119: private int transactionSynchronization = SYNCHRONIZATION_ALWAYS;
0120:
0121: private int defaultTimeout = TransactionDefinition.TIMEOUT_DEFAULT;
0122:
0123: private boolean nestedTransactionAllowed = false;
0124:
0125: private boolean globalRollbackOnParticipationFailure = true;
0126:
0127: private boolean failEarlyOnGlobalRollbackOnly = false;
0128:
0129: private boolean rollbackOnCommitFailure = false;
0130:
0131: /**
0132: * Set the transaction synchronization by the name of the corresponding constant
0133: * in this class, e.g. "SYNCHRONIZATION_ALWAYS".
0134: * @param constantName name of the constant
0135: * @see #SYNCHRONIZATION_ALWAYS
0136: */
0137: public final void setTransactionSynchronizationName(
0138: String constantName) {
0139: setTransactionSynchronization(constants.asNumber(constantName)
0140: .intValue());
0141: }
0142:
0143: /**
0144: * Set when this transaction manager should activate the thread-bound
0145: * transaction synchronization support. Default is "always".
0146: * <p>Note that transaction synchronization isn't supported for
0147: * multiple concurrent transactions by different transaction managers.
0148: * Only one transaction manager is allowed to activate it at any time.
0149: * @see #SYNCHRONIZATION_ALWAYS
0150: * @see #SYNCHRONIZATION_ON_ACTUAL_TRANSACTION
0151: * @see #SYNCHRONIZATION_NEVER
0152: * @see TransactionSynchronizationManager
0153: * @see TransactionSynchronization
0154: */
0155: public final void setTransactionSynchronization(
0156: int transactionSynchronization) {
0157: this .transactionSynchronization = transactionSynchronization;
0158: }
0159:
0160: /**
0161: * Return if this transaction manager should activate the thread-bound
0162: * transaction synchronization support.
0163: */
0164: public final int getTransactionSynchronization() {
0165: return this .transactionSynchronization;
0166: }
0167:
0168: /**
0169: * Specify the default timeout that this transaction manager should apply
0170: * if there is no timeout specified at the transaction level, in seconds.
0171: * <p>Default is the underlying transaction infrastructure's default timeout,
0172: * e.g. typically 30 seconds in case of a JTA provider, indicated by the
0173: * <code>TransactionDefinition.TIMEOUT_DEFAULT</code> value.
0174: * @see org.springframework.transaction.TransactionDefinition#TIMEOUT_DEFAULT
0175: */
0176: public final void setDefaultTimeout(int defaultTimeout) {
0177: if (defaultTimeout < TransactionDefinition.TIMEOUT_DEFAULT) {
0178: throw new InvalidTimeoutException(
0179: "Invalid default timeout", defaultTimeout);
0180: }
0181: this .defaultTimeout = defaultTimeout;
0182: }
0183:
0184: /**
0185: * Return the default timeout that this transaction manager should apply
0186: * if there is no timeout specified at the transaction level, in seconds.
0187: * <p>Returns <code>TransactionDefinition.TIMEOUT_DEFAULT</code> to indicate
0188: * the underlying transaction infrastructure's default timeout.
0189: */
0190: public final int getDefaultTimeout() {
0191: return this .defaultTimeout;
0192: }
0193:
0194: /**
0195: * Set whether nested transactions are allowed. Default is "false".
0196: * <p>Typically initialized with an appropriate default by the
0197: * concrete transaction manager subclass.
0198: */
0199: public final void setNestedTransactionAllowed(
0200: boolean nestedTransactionAllowed) {
0201: this .nestedTransactionAllowed = nestedTransactionAllowed;
0202: }
0203:
0204: /**
0205: * Return whether nested transactions are allowed.
0206: */
0207: public final boolean isNestedTransactionAllowed() {
0208: return this .nestedTransactionAllowed;
0209: }
0210:
0211: /**
0212: * Set whether to globally mark an existing transaction as rollback-only
0213: * after a participating transaction failed.
0214: * <p>Default is "true": If a participating transaction (e.g. with
0215: * PROPAGATION_REQUIRES or PROPAGATION_SUPPORTS encountering an existing
0216: * transaction) fails, the transaction will be globally marked as rollback-only.
0217: * The only possible outcome of such a transaction is a rollback: The
0218: * transaction originator <i>cannot</i> make the transaction commit anymore.
0219: * <p>Switch this to "false" to let the transaction originator make the rollback
0220: * decision. If a participating transaction fails with an exception, the caller
0221: * can still decide to continue with a different path within the transaction.
0222: * However, note that this will only work as long as all participating resources
0223: * are capable of continuing towards a transaction commit even after a data access
0224: * failure: This is generally not the case for a Hibernate Session, for example;
0225: * neither is it for a sequence of JDBC insert/update/delete operations.
0226: * <p><b>Note:</b>This flag only applies to an explicit rollback attempt for a
0227: * subtransaction, typically caused by an exception thrown by a data access operation
0228: * (where TransactionInterceptor will trigger a <code>PlatformTransactionManager.rollback()</code>
0229: * call according to a rollback rule). If the flag is off, the caller can handle the exception
0230: * and decide on a rollback, independent of the rollback rules of the subtransaction.
0231: * This flag does, however, <i>not</i> apply to explicit <code>setRollbackOnly</code>
0232: * calls on a <code>TransactionStatus</code>, which will always cause an eventual
0233: * global rollback (as it might not throw an exception after the rollback-only call).
0234: * <p>The recommended solution for handling failure of a subtransaction
0235: * is a "nested transaction", where the global transaction can be rolled
0236: * back to a savepoint taken at the beginning of the subtransaction.
0237: * PROPAGATION_NESTED provides exactly those semantics; however, it will
0238: * only work when nested transaction support is available. This is the case
0239: * with DataSourceTransactionManager, but not with JtaTransactionManager.
0240: * @see #setNestedTransactionAllowed
0241: * @see org.springframework.jdbc.datasource.DataSourceTransactionManager
0242: * @see org.springframework.transaction.jta.JtaTransactionManager
0243: */
0244: public final void setGlobalRollbackOnParticipationFailure(
0245: boolean globalRollbackOnParticipationFailure) {
0246: this .globalRollbackOnParticipationFailure = globalRollbackOnParticipationFailure;
0247: }
0248:
0249: /**
0250: * Return whether to globally mark an existing transaction as rollback-only
0251: * after a participating transaction failed.
0252: */
0253: public final boolean isGlobalRollbackOnParticipationFailure() {
0254: return this .globalRollbackOnParticipationFailure;
0255: }
0256:
0257: /**
0258: * Set whether to fail early in case of the transaction being globally marked
0259: * as rollback-only.
0260: * <p>Default is "false", only causing an UnexpectedRollbackException at the
0261: * outermost transaction boundary. Switch this flag on to cause an
0262: * UnexpectedRollbackException as early as the global rollback-only marker
0263: * has been first detected, even from within an inner transaction boundary.
0264: * <p>Note that, as of Spring 2.0, the fail-early behavior for global
0265: * rollback-only markers has been unified: All transaction managers will by
0266: * default only cause UnexpectedRollbackException at the outermost transaction
0267: * boundary. This allows, for example, to continue unit tests even after an
0268: * operation failed and the transaction will never be completed. All transaction
0269: * managers will only fail earlier if this flag has explicitly been set to "true".
0270: * @see org.springframework.transaction.UnexpectedRollbackException
0271: */
0272: public final void setFailEarlyOnGlobalRollbackOnly(
0273: boolean failEarlyOnGlobalRollbackOnly) {
0274: this .failEarlyOnGlobalRollbackOnly = failEarlyOnGlobalRollbackOnly;
0275: }
0276:
0277: /**
0278: * Return whether to fail early in case of the transaction being globally marked
0279: * as rollback-only.
0280: */
0281: public final boolean isFailEarlyOnGlobalRollbackOnly() {
0282: return this .failEarlyOnGlobalRollbackOnly;
0283: }
0284:
0285: /**
0286: * Set whether <code>doRollback</code> should be performed on failure of the
0287: * <code>doCommit</code> call. Typically not necessary and thus to be avoided,
0288: * as it can potentially override the commit exception with a subsequent
0289: * rollback exception.
0290: * <p>Default is "false".
0291: * @see #doCommit
0292: * @see #doRollback
0293: */
0294: public final void setRollbackOnCommitFailure(
0295: boolean rollbackOnCommitFailure) {
0296: this .rollbackOnCommitFailure = rollbackOnCommitFailure;
0297: }
0298:
0299: /**
0300: * Return whether <code>doRollback</code> should be performed on failure of the
0301: * <code>doCommit</code> call.
0302: */
0303: public final boolean isRollbackOnCommitFailure() {
0304: return this .rollbackOnCommitFailure;
0305: }
0306:
0307: //---------------------------------------------------------------------
0308: // Implementation of PlatformTransactionManager
0309: //---------------------------------------------------------------------
0310:
0311: /**
0312: * This implementation handles propagation behavior. Delegates to
0313: * <code>doGetTransaction</code>, <code>isExistingTransaction</code>
0314: * and <code>doBegin</code>.
0315: * @see #doGetTransaction
0316: * @see #isExistingTransaction
0317: * @see #doBegin
0318: */
0319: public final TransactionStatus getTransaction(
0320: TransactionDefinition definition)
0321: throws TransactionException {
0322: Object transaction = doGetTransaction();
0323:
0324: // Cache debug flag to avoid repeated checks.
0325: boolean debugEnabled = logger.isDebugEnabled();
0326: if (debugEnabled) {
0327: logger.debug("Using transaction object [" + transaction
0328: + "]");
0329: }
0330:
0331: if (definition == null) {
0332: // Use defaults if no transaction definition given.
0333: definition = new DefaultTransactionDefinition();
0334: }
0335:
0336: if (isExistingTransaction(transaction)) {
0337: // Existing transaction found -> check propagation behavior to find out how to behave.
0338: return handleExistingTransaction(definition, transaction,
0339: debugEnabled);
0340: }
0341:
0342: // Check definition settings for new transaction.
0343: if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
0344: throw new InvalidTimeoutException(
0345: "Invalid transaction timeout", definition
0346: .getTimeout());
0347: }
0348:
0349: // No existing transaction found -> check propagation behavior to find out how to proceed.
0350: if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
0351: throw new IllegalTransactionStateException(
0352: "No existing transaction found for transaction marked with propagation 'mandatory'");
0353: } else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED
0354: || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW
0355: || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
0356: SuspendedResourcesHolder suspendedResources = suspend(null);
0357: if (debugEnabled) {
0358: logger.debug("Creating new transaction with name ["
0359: + definition.getName() + "]: " + definition);
0360: }
0361: try {
0362: doBegin(transaction, definition);
0363: } catch (TransactionException ex) {
0364: resume(null, suspendedResources);
0365: throw ex;
0366: }
0367: boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
0368: return newTransactionStatus(definition, transaction, true,
0369: newSynchronization, debugEnabled,
0370: suspendedResources);
0371: } else {
0372: // Create "empty" transaction: no actual transaction, but potentially synchronization.
0373: boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
0374: return newTransactionStatus(definition, null, true,
0375: newSynchronization, debugEnabled, null);
0376: }
0377: }
0378:
0379: /**
0380: * Create a TransactionStatus for an existing transaction.
0381: */
0382: private TransactionStatus handleExistingTransaction(
0383: TransactionDefinition definition, Object transaction,
0384: boolean debugEnabled) throws TransactionException {
0385:
0386: if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
0387: throw new IllegalTransactionStateException(
0388: "Existing transaction found for transaction marked with propagation 'never'");
0389: }
0390:
0391: if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
0392: if (debugEnabled) {
0393: logger.debug("Suspending current transaction");
0394: }
0395: Object suspendedResources = suspend(transaction);
0396: boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
0397: return newTransactionStatus(definition, null, false,
0398: newSynchronization, debugEnabled,
0399: suspendedResources);
0400: }
0401:
0402: if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
0403: if (debugEnabled) {
0404: logger
0405: .debug("Suspending current transaction, creating new transaction with name ["
0406: + definition.getName() + "]");
0407: }
0408: SuspendedResourcesHolder suspendedResources = suspend(transaction);
0409: try {
0410: doBegin(transaction, definition);
0411: } catch (TransactionException beginEx) {
0412: try {
0413: resume(transaction, suspendedResources);
0414: } catch (TransactionException resumeEx) {
0415: logger
0416: .error(
0417: "Inner transaction begin exception overridden by outer transaction resume exception",
0418: beginEx);
0419: throw resumeEx;
0420: }
0421: throw beginEx;
0422: }
0423: boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
0424: return newTransactionStatus(definition, transaction, true,
0425: newSynchronization, debugEnabled,
0426: suspendedResources);
0427: }
0428:
0429: if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
0430: if (!isNestedTransactionAllowed()) {
0431: throw new NestedTransactionNotSupportedException(
0432: "Transaction manager does not allow nested transactions by default - "
0433: + "specify 'nestedTransactionAllowed' property with value 'true'");
0434: }
0435: if (debugEnabled) {
0436: logger.debug("Creating nested transaction with name ["
0437: + definition.getName() + "]");
0438: }
0439: if (useSavepointForNestedTransaction()) {
0440: // Create savepoint within existing Spring-managed transaction,
0441: // through the SavepointManager API implemented by TransactionStatus.
0442: // Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
0443: DefaultTransactionStatus status = newTransactionStatus(
0444: definition, transaction, false, false,
0445: debugEnabled, null);
0446: status.createAndHoldSavepoint();
0447: return status;
0448: } else {
0449: // Nested transaction through nested begin and commit/rollback calls.
0450: // Usually only for JTA: Spring synchronization might get activated here
0451: // in case of a pre-existing JTA transaction.
0452: doBegin(transaction, definition);
0453: boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
0454: return newTransactionStatus(definition, transaction,
0455: true, newSynchronization, debugEnabled, null);
0456: }
0457: }
0458:
0459: // Assumably PROPAGATION_SUPPORTS.
0460: if (debugEnabled) {
0461: logger.debug("Participating in existing transaction");
0462: }
0463: boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
0464: return newTransactionStatus(definition, transaction, false,
0465: newSynchronization, debugEnabled, null);
0466: }
0467:
0468: /**
0469: * Create a new TransactionStatus for the given arguments,
0470: * initializing transaction synchronization as appropriate.
0471: */
0472: protected DefaultTransactionStatus newTransactionStatus(
0473: TransactionDefinition definition, Object transaction,
0474: boolean newTransaction, boolean newSynchronization,
0475: boolean debug, Object suspendedResources) {
0476:
0477: boolean actualNewSynchronization = newSynchronization
0478: && !TransactionSynchronizationManager
0479: .isSynchronizationActive();
0480: if (actualNewSynchronization) {
0481: TransactionSynchronizationManager
0482: .setActualTransactionActive(transaction != null);
0483: TransactionSynchronizationManager
0484: .setCurrentTransactionIsolationLevel((definition
0485: .getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) ? new Integer(
0486: definition.getIsolationLevel())
0487: : null);
0488: TransactionSynchronizationManager
0489: .setCurrentTransactionReadOnly(definition
0490: .isReadOnly());
0491: TransactionSynchronizationManager
0492: .setCurrentTransactionName(definition.getName());
0493: TransactionSynchronizationManager.initSynchronization();
0494: }
0495: return new DefaultTransactionStatus(transaction,
0496: newTransaction, actualNewSynchronization, definition
0497: .isReadOnly(), debug, suspendedResources);
0498: }
0499:
0500: /**
0501: * Determine the actual timeout to use for the given definition.
0502: * Will fall back to this manager's default timeout if the
0503: * transaction definition doesn't specify a non-default value.
0504: * @param definition the transaction definition
0505: * @return the actual timeout to use
0506: * @see org.springframework.transaction.TransactionDefinition#getTimeout()
0507: * @see #setDefaultTimeout
0508: */
0509: protected int determineTimeout(TransactionDefinition definition) {
0510: if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {
0511: return definition.getTimeout();
0512: }
0513: return this .defaultTimeout;
0514: }
0515:
0516: /**
0517: * Suspend the given transaction. Suspends transaction synchronization first,
0518: * then delegates to the <code>doSuspend</code> template method.
0519: * @param transaction the current transaction object
0520: * (or <code>null</code> to just suspend active synchronizations, if any)
0521: * @return an object that holds suspended resources
0522: * (or <code>null</code> if neither transaction nor synchronization active)
0523: * @see #doSuspend
0524: * @see #resume
0525: */
0526: protected final SuspendedResourcesHolder suspend(Object transaction)
0527: throws TransactionException {
0528: if (TransactionSynchronizationManager.isSynchronizationActive()) {
0529: List suspendedSynchronizations = doSuspendSynchronization();
0530: try {
0531: Object suspendedResources = null;
0532: if (transaction != null) {
0533: suspendedResources = doSuspend(transaction);
0534: }
0535: String name = TransactionSynchronizationManager
0536: .getCurrentTransactionName();
0537: TransactionSynchronizationManager
0538: .setCurrentTransactionName(null);
0539: boolean readOnly = TransactionSynchronizationManager
0540: .isCurrentTransactionReadOnly();
0541: TransactionSynchronizationManager
0542: .setCurrentTransactionReadOnly(false);
0543: Integer isolationLevel = TransactionSynchronizationManager
0544: .getCurrentTransactionIsolationLevel();
0545: TransactionSynchronizationManager
0546: .setCurrentTransactionIsolationLevel(null);
0547: boolean wasActive = TransactionSynchronizationManager
0548: .isActualTransactionActive();
0549: TransactionSynchronizationManager
0550: .setActualTransactionActive(false);
0551: return new SuspendedResourcesHolder(suspendedResources,
0552: suspendedSynchronizations, name, readOnly,
0553: isolationLevel, wasActive);
0554: } catch (TransactionException ex) {
0555: // doSuspend failed - original transaction is still active...
0556: doResumeSynchronization(suspendedSynchronizations);
0557: throw ex;
0558: }
0559: } else if (transaction != null) {
0560: // Transaction active but no synchronization active.
0561: Object suspendedResources = doSuspend(transaction);
0562: return new SuspendedResourcesHolder(suspendedResources);
0563: } else {
0564: // Neither transaction nor synchronization active.
0565: return null;
0566: }
0567: }
0568:
0569: /**
0570: * Resume the given transaction. Delegates to the <code>doResume</code>
0571: * template method first, then resuming transaction synchronization.
0572: * @param transaction the current transaction object
0573: * @param resourcesHolder the object that holds suspended resources,
0574: * as returned by <code>suspend</code> (or <code>null</code> to just
0575: * resume synchronizations, if any)
0576: * @see #doResume
0577: * @see #suspend
0578: */
0579: protected final void resume(Object transaction,
0580: SuspendedResourcesHolder resourcesHolder)
0581: throws TransactionException {
0582:
0583: if (resourcesHolder != null) {
0584: Object suspendedResources = resourcesHolder.suspendedResources;
0585: if (suspendedResources != null) {
0586: doResume(transaction, suspendedResources);
0587: }
0588: List suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
0589: if (suspendedSynchronizations != null) {
0590: TransactionSynchronizationManager
0591: .setActualTransactionActive(resourcesHolder.wasActive);
0592: TransactionSynchronizationManager
0593: .setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
0594: TransactionSynchronizationManager
0595: .setCurrentTransactionReadOnly(resourcesHolder.readOnly);
0596: TransactionSynchronizationManager
0597: .setCurrentTransactionName(resourcesHolder.name);
0598: doResumeSynchronization(suspendedSynchronizations);
0599: }
0600: }
0601: }
0602:
0603: /**
0604: * Suspend all current synchronizations and deactivate transaction
0605: * synchronization for the current thread.
0606: * @return the List of suspended TransactionSynchronization objects
0607: */
0608: private List doSuspendSynchronization() {
0609: List suspendedSynchronizations = TransactionSynchronizationManager
0610: .getSynchronizations();
0611: for (Iterator it = suspendedSynchronizations.iterator(); it
0612: .hasNext();) {
0613: ((TransactionSynchronization) it.next()).suspend();
0614: }
0615: TransactionSynchronizationManager.clearSynchronization();
0616: return suspendedSynchronizations;
0617: }
0618:
0619: /**
0620: * Reactivate transaction synchronization for the current thread
0621: * and resume all given synchronizations.
0622: * @param suspendedSynchronizations List of TransactionSynchronization objects
0623: */
0624: private void doResumeSynchronization(List suspendedSynchronizations) {
0625: TransactionSynchronizationManager.initSynchronization();
0626: for (Iterator it = suspendedSynchronizations.iterator(); it
0627: .hasNext();) {
0628: TransactionSynchronization synchronization = (TransactionSynchronization) it
0629: .next();
0630: synchronization.resume();
0631: TransactionSynchronizationManager
0632: .registerSynchronization(synchronization);
0633: }
0634: }
0635:
0636: /**
0637: * This implementation of commit handles participating in existing
0638: * transactions and programmatic rollback requests.
0639: * Delegates to <code>isRollbackOnly</code>, <code>doCommit</code>
0640: * and <code>rollback</code>.
0641: * @see org.springframework.transaction.TransactionStatus#isRollbackOnly()
0642: * @see #doCommit
0643: * @see #rollback
0644: */
0645: public final void commit(TransactionStatus status)
0646: throws TransactionException {
0647: if (status.isCompleted()) {
0648: throw new IllegalTransactionStateException(
0649: "Transaction is already completed - do not call commit or rollback more than once per transaction");
0650: }
0651:
0652: DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
0653: if (defStatus.isLocalRollbackOnly()) {
0654: if (defStatus.isDebug()) {
0655: logger
0656: .debug("Transactional code has requested rollback");
0657: }
0658: processRollback(defStatus);
0659: return;
0660: }
0661: if (!shouldCommitOnGlobalRollbackOnly()
0662: && defStatus.isGlobalRollbackOnly()) {
0663: if (defStatus.isDebug()) {
0664: logger
0665: .debug("Global transaction is marked as rollback-only but transactional code requested commit");
0666: }
0667: processRollback(defStatus);
0668: // Throw UnexpectedRollbackException only at outermost transaction boundary
0669: // or if explicitly asked to.
0670: if (status.isNewTransaction()
0671: || isFailEarlyOnGlobalRollbackOnly()) {
0672: throw new UnexpectedRollbackException(
0673: "Transaction rolled back because it has been marked as rollback-only");
0674: }
0675: return;
0676: }
0677:
0678: processCommit(defStatus);
0679: }
0680:
0681: /**
0682: * Process an actual commit.
0683: * Rollback-only flags have already been checked and applied.
0684: * @param status object representing the transaction
0685: * @throws TransactionException in case of commit failure
0686: */
0687: private void processCommit(DefaultTransactionStatus status)
0688: throws TransactionException {
0689: try {
0690: boolean beforeCompletionInvoked = false;
0691: try {
0692: triggerBeforeCommit(status);
0693: triggerBeforeCompletion(status);
0694: beforeCompletionInvoked = true;
0695: boolean globalRollbackOnly = false;
0696: if (status.isNewTransaction()
0697: || isFailEarlyOnGlobalRollbackOnly()) {
0698: globalRollbackOnly = status.isGlobalRollbackOnly();
0699: }
0700: if (status.hasSavepoint()) {
0701: if (status.isDebug()) {
0702: logger.debug("Releasing transaction savepoint");
0703: }
0704: status.releaseHeldSavepoint();
0705: } else if (status.isNewTransaction()) {
0706: if (status.isDebug()) {
0707: logger.debug("Initiating transaction commit");
0708: }
0709: doCommit(status);
0710: }
0711: // Throw UnexpectedRollbackException if we have a global rollback-only
0712: // marker but still didn't get a corresponding exception from commit.
0713: if (globalRollbackOnly) {
0714: throw new UnexpectedRollbackException(
0715: "Transaction silently rolled back because it has been marked as rollback-only");
0716: }
0717: } catch (UnexpectedRollbackException ex) {
0718: // can only be caused by doCommit
0719: triggerAfterCompletion(status,
0720: TransactionSynchronization.STATUS_ROLLED_BACK);
0721: throw ex;
0722: } catch (TransactionException ex) {
0723: // can only be caused by doCommit
0724: if (isRollbackOnCommitFailure()) {
0725: doRollbackOnCommitException(status, ex);
0726: } else {
0727: triggerAfterCompletion(status,
0728: TransactionSynchronization.STATUS_UNKNOWN);
0729: }
0730: throw ex;
0731: } catch (RuntimeException ex) {
0732: if (!beforeCompletionInvoked) {
0733: triggerBeforeCompletion(status);
0734: }
0735: doRollbackOnCommitException(status, ex);
0736: throw ex;
0737: } catch (Error err) {
0738: if (!beforeCompletionInvoked) {
0739: triggerBeforeCompletion(status);
0740: }
0741: doRollbackOnCommitException(status, err);
0742: throw err;
0743: }
0744:
0745: // Trigger afterCommit callbacks, with an exception thrown there
0746: // propagated to callers but the transaction still considered as committed.
0747: try {
0748: triggerAfterCommit(status);
0749: } finally {
0750: triggerAfterCompletion(status,
0751: TransactionSynchronization.STATUS_COMMITTED);
0752: }
0753:
0754: } finally {
0755: cleanupAfterCompletion(status);
0756: }
0757: }
0758:
0759: /**
0760: * This implementation of rollback handles participating in existing
0761: * transactions. Delegates to <code>doRollback</code> and
0762: * <code>doSetRollbackOnly</code>.
0763: * @see #doRollback
0764: * @see #doSetRollbackOnly
0765: */
0766: public final void rollback(TransactionStatus status)
0767: throws TransactionException {
0768: if (status.isCompleted()) {
0769: throw new IllegalTransactionStateException(
0770: "Transaction is already completed - do not call commit or rollback more than once per transaction");
0771: }
0772:
0773: DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
0774: processRollback(defStatus);
0775: }
0776:
0777: /**
0778: * Process an actual rollback.
0779: * The completed flag has already been checked.
0780: * @param status object representing the transaction
0781: * @throws TransactionException in case of rollback failure
0782: */
0783: private void processRollback(DefaultTransactionStatus status) {
0784: try {
0785: try {
0786: triggerBeforeCompletion(status);
0787: if (status.hasSavepoint()) {
0788: if (status.isDebug()) {
0789: logger
0790: .debug("Rolling back transaction to savepoint");
0791: }
0792: status.rollbackToHeldSavepoint();
0793: } else if (status.isNewTransaction()) {
0794: if (status.isDebug()) {
0795: logger.debug("Initiating transaction rollback");
0796: }
0797: doRollback(status);
0798: } else if (status.hasTransaction()) {
0799: if (status.isLocalRollbackOnly()
0800: || isGlobalRollbackOnParticipationFailure()) {
0801: if (status.isDebug()) {
0802: logger
0803: .debug("Participating transaction failed - marking existing transaction as rollback-only");
0804: }
0805: doSetRollbackOnly(status);
0806: } else {
0807: if (status.isDebug()) {
0808: logger
0809: .debug("Participating transaction failed - letting transaction originator decide on rollback");
0810: }
0811: }
0812: } else {
0813: logger
0814: .debug("Should roll back transaction but cannot - no transaction available");
0815: }
0816: } catch (RuntimeException ex) {
0817: triggerAfterCompletion(status,
0818: TransactionSynchronization.STATUS_UNKNOWN);
0819: throw ex;
0820: } catch (Error err) {
0821: triggerAfterCompletion(status,
0822: TransactionSynchronization.STATUS_UNKNOWN);
0823: throw err;
0824: }
0825: triggerAfterCompletion(status,
0826: TransactionSynchronization.STATUS_ROLLED_BACK);
0827: } finally {
0828: cleanupAfterCompletion(status);
0829: }
0830: }
0831:
0832: /**
0833: * Invoke <code>doRollback</code>, handling rollback exceptions properly.
0834: * @param status object representing the transaction
0835: * @param ex the thrown application exception or error
0836: * @throws TransactionException in case of rollback failure
0837: * @see #doRollback
0838: */
0839: private void doRollbackOnCommitException(
0840: DefaultTransactionStatus status, Throwable ex)
0841: throws TransactionException {
0842: try {
0843: if (status.isNewTransaction()) {
0844: if (status.isDebug()) {
0845: logger
0846: .debug(
0847: "Initiating transaction rollback after commit exception",
0848: ex);
0849: }
0850: doRollback(status);
0851: } else if (status.hasTransaction()
0852: && isGlobalRollbackOnParticipationFailure()) {
0853: if (status.isDebug()) {
0854: logger
0855: .debug(
0856: "Marking existing transaction as rollback-only after commit exception",
0857: ex);
0858: }
0859: doSetRollbackOnly(status);
0860: }
0861: } catch (RuntimeException rbex) {
0862: logger
0863: .error(
0864: "Commit exception overridden by rollback exception",
0865: ex);
0866: triggerAfterCompletion(status,
0867: TransactionSynchronization.STATUS_UNKNOWN);
0868: throw rbex;
0869: } catch (Error rberr) {
0870: logger
0871: .error(
0872: "Commit exception overridden by rollback exception",
0873: ex);
0874: triggerAfterCompletion(status,
0875: TransactionSynchronization.STATUS_UNKNOWN);
0876: throw rberr;
0877: }
0878: triggerAfterCompletion(status,
0879: TransactionSynchronization.STATUS_ROLLED_BACK);
0880: }
0881:
0882: /**
0883: * Trigger <code>beforeCommit</code> callbacks.
0884: * @param status object representing the transaction
0885: */
0886: protected final void triggerBeforeCommit(
0887: DefaultTransactionStatus status) {
0888: if (status.isNewSynchronization()) {
0889: if (status.isDebug()) {
0890: logger.debug("Triggering beforeCommit synchronization");
0891: }
0892: TransactionSynchronizationUtils.triggerBeforeCommit(status
0893: .isReadOnly());
0894: }
0895: }
0896:
0897: /**
0898: * Trigger <code>beforeCompletion</code> callbacks.
0899: * @param status object representing the transaction
0900: */
0901: protected final void triggerBeforeCompletion(
0902: DefaultTransactionStatus status) {
0903: if (status.isNewSynchronization()) {
0904: if (status.isDebug()) {
0905: logger
0906: .debug("Triggering beforeCompletion synchronization");
0907: }
0908: TransactionSynchronizationUtils.triggerBeforeCompletion();
0909: }
0910: }
0911:
0912: /**
0913: * Trigger <code>afterCommit</code> callbacks.
0914: * @param status object representing the transaction
0915: */
0916: private void triggerAfterCommit(DefaultTransactionStatus status) {
0917: if (status.isNewSynchronization()) {
0918: if (status.isDebug()) {
0919: logger.debug("Triggering afterCommit synchronization");
0920: }
0921: TransactionSynchronizationUtils.triggerAfterCommit();
0922: }
0923: }
0924:
0925: /**
0926: * Trigger <code>afterCompletion</code> callbacks.
0927: * @param status object representing the transaction
0928: * @param completionStatus completion status according to TransactionSynchronization constants
0929: */
0930: private void triggerAfterCompletion(
0931: DefaultTransactionStatus status, int completionStatus) {
0932: if (status.isNewSynchronization()) {
0933: List synchronizations = TransactionSynchronizationManager
0934: .getSynchronizations();
0935: if (!status.hasTransaction() || status.isNewTransaction()) {
0936: if (status.isDebug()) {
0937: logger
0938: .debug("Triggering afterCompletion synchronization");
0939: }
0940: // No transaction or new transaction for the current scope ->
0941: // invoke the afterCompletion callbacks immediately
0942: invokeAfterCompletion(synchronizations,
0943: completionStatus);
0944: } else {
0945: // Existing transaction that we participate in, controlled outside
0946: // of the scope of this Spring transaction manager -> try to register
0947: // an afterCompletion callback with the existing (JTA) transaction.
0948: registerAfterCompletionWithExistingTransaction(status
0949: .getTransaction(), synchronizations);
0950: }
0951: }
0952: }
0953:
0954: /**
0955: * Actually invoke the <code>afterCompletion</code> methods of the
0956: * given Spring TransactionSynchronization objects.
0957: * <p>To be called by this abstract manager itself, or by special implementations
0958: * of the <code>registerAfterCompletionWithExistingTransaction</code> callback.
0959: * @param synchronizations List of TransactionSynchronization objects
0960: * @param completionStatus the completion status according to the
0961: * constants in the TransactionSynchronization interface
0962: * @see #registerAfterCompletionWithExistingTransaction(Object, java.util.List)
0963: * @see TransactionSynchronization#STATUS_COMMITTED
0964: * @see TransactionSynchronization#STATUS_ROLLED_BACK
0965: * @see TransactionSynchronization#STATUS_UNKNOWN
0966: */
0967: protected final void invokeAfterCompletion(List synchronizations,
0968: int completionStatus) {
0969: TransactionSynchronizationUtils.invokeAfterCompletion(
0970: synchronizations, completionStatus);
0971: }
0972:
0973: /**
0974: * Clean up after completion, clearing synchronization if necessary,
0975: * and invoking doCleanupAfterCompletion.
0976: * @param status object representing the transaction
0977: * @see #doCleanupAfterCompletion
0978: */
0979: private void cleanupAfterCompletion(DefaultTransactionStatus status) {
0980: status.setCompleted();
0981: if (status.isNewSynchronization()) {
0982: TransactionSynchronizationManager.clear();
0983: }
0984: if (status.isNewTransaction()) {
0985: doCleanupAfterCompletion(status.getTransaction());
0986: }
0987: if (status.getSuspendedResources() != null) {
0988: if (status.isDebug()) {
0989: logger.debug("Resuming suspended transaction");
0990: }
0991: resume(status.getTransaction(),
0992: (SuspendedResourcesHolder) status
0993: .getSuspendedResources());
0994: }
0995: }
0996:
0997: //---------------------------------------------------------------------
0998: // Template methods to be implemented in subclasses
0999: //---------------------------------------------------------------------
1000:
1001: /**
1002: * Return a transaction object for the current transaction state.
1003: * <p>The returned object will usually be specific to the concrete transaction
1004: * manager implementation, carrying corresponding transaction state in a
1005: * modifiable fashion. This object will be passed into the other template
1006: * methods (e.g. doBegin and doCommit), either directly or as part of a
1007: * DefaultTransactionStatus instance.
1008: * <p>The returned object should contain information about any existing
1009: * transaction, that is, a transaction that has already started before the
1010: * current <code>getTransaction</code> call on the transaction manager.
1011: * Consequently, a <code>doGetTransaction</code> implementation will usually
1012: * look for an existing transaction and store corresponding state in the
1013: * returned transaction object.
1014: * @return the current transaction object
1015: * @throws org.springframework.transaction.CannotCreateTransactionException
1016: * if transaction support is not available
1017: * @throws TransactionException in case of lookup or system errors
1018: * @see #doBegin
1019: * @see #doCommit
1020: * @see #doRollback
1021: * @see DefaultTransactionStatus#getTransaction
1022: */
1023: protected abstract Object doGetTransaction()
1024: throws TransactionException;
1025:
1026: /**
1027: * Check if the given transaction object indicates an existing transaction
1028: * (that is, a transaction which has already started).
1029: * <p>The result will be evaluated according to the specified propagation
1030: * behavior for the new transaction. An existing transaction might get
1031: * suspended (in case of PROPAGATION_REQUIRES_NEW), or the new transaction
1032: * might participate in the existing one (in case of PROPAGATION_REQUIRED).
1033: * <p>The default implementation returns <code>false</code>, assuming that
1034: * participating in existing transactions is generally not supported.
1035: * Subclasses are of course encouraged to provide such support.
1036: * @param transaction transaction object returned by doGetTransaction
1037: * @return if there is an existing transaction
1038: * @throws TransactionException in case of system errors
1039: * @see #doGetTransaction
1040: */
1041: protected boolean isExistingTransaction(Object transaction)
1042: throws TransactionException {
1043: return false;
1044: }
1045:
1046: /**
1047: * Return whether to use a savepoint for a nested transaction.
1048: * <p>Default is <code>true</code>, which causes delegation to DefaultTransactionStatus
1049: * for creating and holding a savepoint. If the transaction object does not implement
1050: * the SavepointManager interface, a NestedTransactionNotSupportedException will be
1051: * thrown. Else, the SavepointManager will be asked to create a new savepoint to
1052: * demarcate the start of the nested transaction.
1053: * <p>Subclasses can override this to return <code>false</code>, causing a further
1054: * call to <code>doBegin</code> - within the context of an already existing transaction.
1055: * The <code>doBegin</code> implementation needs to handle this accordingly in such
1056: * a scenario. This is appropriate for JTA, for example.
1057: * @see DefaultTransactionStatus#createAndHoldSavepoint
1058: * @see DefaultTransactionStatus#rollbackToHeldSavepoint
1059: * @see DefaultTransactionStatus#releaseHeldSavepoint
1060: * @see #doBegin
1061: */
1062: protected boolean useSavepointForNestedTransaction() {
1063: return true;
1064: }
1065:
1066: /**
1067: * Begin a new transaction with semantics according to the given transaction
1068: * definition. Does not have to care about applying the propagation behavior,
1069: * as this has already been handled by this abstract manager.
1070: * <p>This method gets called when the transaction manager has decided to actually
1071: * start a new transaction. Either there wasn't any transaction before, or the
1072: * previous transaction has been suspended.
1073: * <p>A special scenario is a nested transaction without savepoint: If
1074: * <code>useSavepointForNestedTransaction()</code> returns "false", this method
1075: * will be called to start a nested transaction when necessary. In such a context,
1076: * there will be an active transaction: The implementation of this method has
1077: * to detect this and start an appropriate nested transaction.
1078: * @param transaction transaction object returned by <code>doGetTransaction</code>
1079: * @param definition TransactionDefinition instance, describing propagation
1080: * behavior, isolation level, read-only flag, timeout, and transaction name
1081: * @throws TransactionException in case of creation or system errors
1082: */
1083: protected abstract void doBegin(Object transaction,
1084: TransactionDefinition definition)
1085: throws TransactionException;
1086:
1087: /**
1088: * Suspend the resources of the current transaction.
1089: * Transaction synchronization will already have been suspended.
1090: * <p>The default implementation throws a TransactionSuspensionNotSupportedException,
1091: * assuming that transaction suspension is generally not supported.
1092: * @param transaction transaction object returned by <code>doGetTransaction</code>
1093: * @return an object that holds suspended resources
1094: * (will be kept unexamined for passing it into doResume)
1095: * @throws org.springframework.transaction.TransactionSuspensionNotSupportedException
1096: * if suspending is not supported by the transaction manager implementation
1097: * @throws TransactionException in case of system errors
1098: * @see #doResume
1099: */
1100: protected Object doSuspend(Object transaction)
1101: throws TransactionException {
1102: throw new TransactionSuspensionNotSupportedException(
1103: "Transaction manager [" + getClass().getName()
1104: + "] does not support transaction suspension");
1105: }
1106:
1107: /**
1108: * Resume the resources of the current transaction.
1109: * Transaction synchronization will be resumed afterwards.
1110: * <p>The default implementation throws a TransactionSuspensionNotSupportedException,
1111: * assuming that transaction suspension is generally not supported.
1112: * @param transaction transaction object returned by <code>doGetTransaction</code>
1113: * @param suspendedResources the object that holds suspended resources,
1114: * as returned by doSuspend
1115: * @throws org.springframework.transaction.TransactionSuspensionNotSupportedException
1116: * if resuming is not supported by the transaction manager implementation
1117: * @throws TransactionException in case of system errors
1118: * @see #doSuspend
1119: */
1120: protected void doResume(Object transaction,
1121: Object suspendedResources) throws TransactionException {
1122: throw new TransactionSuspensionNotSupportedException(
1123: "Transaction manager [" + getClass().getName()
1124: + "] does not support transaction suspension");
1125: }
1126:
1127: /**
1128: * Return whether to call <code>doCommit</code> on a transaction that has been
1129: * marked as rollback-only in a global fashion.
1130: * <p>Does not apply if an application locally sets the transaction to rollback-only
1131: * via the TransactionStatus, but only to the transaction itself being marked as
1132: * rollback-only by the transaction coordinator.
1133: * <p>Default is "false": Local transaction strategies usually don't hold the rollback-only
1134: * marker in the transaction itself, therefore they can't handle rollback-only transactions
1135: * as part of transaction commit. Hence, AbstractPlatformTransactionManager will trigger
1136: * a rollback in that case, throwing an UnexpectedRollbackException afterwards.
1137: * <p>Override this to return "true" if the concrete transaction manager expects a
1138: * <code>doCommit</code> call even for a rollback-only transaction, allowing for
1139: * special handling there. This will, for example, be the case for JTA, where
1140: * <code>UserTransaction.commit</code> will check the read-only flag itself and
1141: * throw a corresponding RollbackException, which might include the specific reason
1142: * (such as a transaction timeout).
1143: * <p>If this method returns "true" but the <code>doCommit</code> implementation does not
1144: * throw an exception, this transaction manager will throw an UnexpectedRollbackException
1145: * itself. This should not be the typical case; it is mainly checked to cover misbehaving
1146: * JTA providers that silently roll back even when the rollback has not been requested
1147: * by the calling code.
1148: * @see #doCommit
1149: * @see DefaultTransactionStatus#isGlobalRollbackOnly()
1150: * @see DefaultTransactionStatus#isLocalRollbackOnly()
1151: * @see org.springframework.transaction.TransactionStatus#setRollbackOnly()
1152: * @see org.springframework.transaction.UnexpectedRollbackException
1153: * @see javax.transaction.UserTransaction#commit()
1154: * @see javax.transaction.RollbackException
1155: */
1156: protected boolean shouldCommitOnGlobalRollbackOnly() {
1157: return false;
1158: }
1159:
1160: /**
1161: * Perform an actual commit of the given transaction.
1162: * <p>An implementation does not need to check the "new transaction" flag
1163: * or the rollback-only flag; this will already have been handled before.
1164: * Usually, a straight commit will be performed on the transaction object
1165: * contained in the passed-in status.
1166: * @param status the status representation of the transaction
1167: * @throws TransactionException in case of commit or system errors
1168: * @see DefaultTransactionStatus#getTransaction
1169: */
1170: protected abstract void doCommit(DefaultTransactionStatus status)
1171: throws TransactionException;
1172:
1173: /**
1174: * Perform an actual rollback of the given transaction.
1175: * <p>An implementation does not need to check the "new transaction" flag;
1176: * this will already have been handled before. Usually, a straight rollback
1177: * will be performed on the transaction object contained in the passed-in status.
1178: * @param status the status representation of the transaction
1179: * @throws TransactionException in case of system errors
1180: * @see DefaultTransactionStatus#getTransaction
1181: */
1182: protected abstract void doRollback(DefaultTransactionStatus status)
1183: throws TransactionException;
1184:
1185: /**
1186: * Set the given transaction rollback-only. Only called on rollback
1187: * if the current transaction participates in an existing one.
1188: * <p>The default implementation throws an IllegalTransactionStateException,
1189: * assuming that participating in existing transactions is generally not
1190: * supported. Subclasses are of course encouraged to provide such support.
1191: * @param status the status representation of the transaction
1192: * @throws TransactionException in case of system errors
1193: */
1194: protected void doSetRollbackOnly(DefaultTransactionStatus status)
1195: throws TransactionException {
1196: throw new IllegalTransactionStateException(
1197: "Participating in existing transactions is not supported - when 'isExistingTransaction' "
1198: + "returns true, appropriate 'doSetRollbackOnly' behavior must be provided");
1199: }
1200:
1201: /**
1202: * Register the given list of transaction synchronizations with the existing transaction.
1203: * <p>Invoked when the control of the Spring transaction manager and thus all Spring
1204: * transaction synchronizations end, without the transaction being completed yet. This
1205: * is for example the case when participating in an existing JTA or EJB CMT transaction.
1206: * <p>The default implementation simply invokes the <code>afterCompletion</code> methods
1207: * immediately, passing in "STATUS_UNKNOWN". This is the best we can do if there's no
1208: * chance to determine the actual outcome of the outer transaction.
1209: * @param transaction transaction object returned by <code>doGetTransaction</code>
1210: * @param synchronizations List of TransactionSynchronization objects
1211: * @throws TransactionException in case of system errors
1212: * @see #invokeAfterCompletion(java.util.List, int)
1213: * @see TransactionSynchronization#afterCompletion(int)
1214: * @see TransactionSynchronization#STATUS_UNKNOWN
1215: */
1216: protected void registerAfterCompletionWithExistingTransaction(
1217: Object transaction, List synchronizations)
1218: throws TransactionException {
1219:
1220: logger
1221: .debug("Cannot register Spring after-completion synchronization with existing transaction - "
1222: + "processing Spring after-completion callbacks immediately, with outcome status 'unknown'");
1223: invokeAfterCompletion(synchronizations,
1224: TransactionSynchronization.STATUS_UNKNOWN);
1225: }
1226:
1227: /**
1228: * Cleanup resources after transaction completion.
1229: * <p>Called after <code>doCommit</code> and <code>doRollback</code> execution,
1230: * on any outcome. The default implementation does nothing.
1231: * <p>Should not throw any exceptions but just issue warnings on errors.
1232: * @param transaction transaction object returned by <code>doGetTransaction</code>
1233: */
1234: protected void doCleanupAfterCompletion(Object transaction) {
1235: }
1236:
1237: //---------------------------------------------------------------------
1238: // Serialization support
1239: //---------------------------------------------------------------------
1240:
1241: private void readObject(ObjectInputStream ois) throws IOException,
1242: ClassNotFoundException {
1243: // Rely on default serialization; just initialize state after deserialization.
1244: ois.defaultReadObject();
1245:
1246: // Initialize transient fields.
1247: this .logger = LogFactory.getLog(getClass());
1248: }
1249:
1250: /**
1251: * Holder for suspended resources.
1252: * Used internally by <code>suspend</code> and <code>resume</code>.
1253: */
1254: protected static class SuspendedResourcesHolder {
1255:
1256: private final Object suspendedResources;
1257: private List suspendedSynchronizations;
1258: private String name;
1259: private boolean readOnly;
1260: private Integer isolationLevel;
1261: private boolean wasActive;
1262:
1263: private SuspendedResourcesHolder(Object suspendedResources) {
1264: this .suspendedResources = suspendedResources;
1265: }
1266:
1267: private SuspendedResourcesHolder(Object suspendedResources,
1268: List suspendedSynchronizations, String name,
1269: boolean readOnly, Integer isolationLevel,
1270: boolean wasActive) {
1271: this.suspendedResources = suspendedResources;
1272: this.suspendedSynchronizations = suspendedSynchronizations;
1273: this.name = name;
1274: this.readOnly = readOnly;
1275: this.isolationLevel = isolationLevel;
1276: this.wasActive = wasActive;
1277: }
1278: }
1279:
1280: }
|