#region License

/*
 * Copyright 2002-2004 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#endregion

using System;
using System.Collections;
using System.Data;
using Common.Logging;

namespace Spring.Transaction.Support
{
    /// <summary>
    /// Abstract base class that allows for easy implementation of concrete platform transaction managers.
    /// </summary>
    /// <remarks>
    /// <p>Provides the following workflow handling:
    /// <ul>
    /// <li>Determines if there is an existing transaction</li>
    /// <li>Applies the appropriate propagation behavior</li>
    /// <li>Suspends and resumes transactions if necessary</li>
    /// <li>Checks the rollback-only flag on commit</li>
    /// <li>Applies the appropriate modification on rollback (actual rollback or setting rollback-only)</li>
    /// <li>Triggers registered synchronization callbacks (if transaction synchronization is active)</li>
    /// </ul>
    /// </p>
    /// <p>
    /// Transaction synchronization is a generic mechanism for registering
    /// callbacks that get invoked at transaction completion time. The same mechanism
    /// can also be used for custom synchronization efforts.
    /// </p>
    /// <p>
    /// The state of this class is serializable. It's up to subclasses if
    /// they wish to make their state to be serializable.
    /// They should implement <see cref="System.Runtime.Serialization.ISerializable"/> if they need
    /// to restore any transient state.
    /// </p>
    /// </remarks>
    /// <author>Juergen Hoeller</author>
    /// <author>Mark Pollack (.NET)</author>
    /// <author>Griffin Caprio (.NET)</author>
    [Serializable]
    public abstract class AbstractPlatformTransactionManager : IPlatformTransactionManager
    {
        #region Private SuspendedResourcesHolder Helper class

        private class SuspendedResourcesHolder
        {
            private IList _suspendedSynchronizations;
            private object _suspendedResources;
            private string _name;
            private bool _readOnly;
            private IsolationLevel _isolationLevel;
            private bool _wasActive;


            public SuspendedResourcesHolder(object suspendedResources)
            {
                _suspendedResources = suspendedResources;
            }

            public SuspendedResourcesHolder(IList suspendedSynchronizations, object suspendedResources,
                                             string name, bool readOnly, IsolationLevel isolationLevel, bool wasActive)
            {
                _suspendedSynchronizations = suspendedSynchronizations;
                _suspendedResources = suspendedResources;
                _name = name;
                _readOnly = readOnly;
                _isolationLevel = isolationLevel;
                _wasActive = wasActive;
            }

            public IList SuspendedSynchronizations
            {
                get { return _suspendedSynchronizations; }
            }

            public object SuspendedResources
            {
                get { return _suspendedResources; }
            }


            public string Name
            {
                get { return _name; }
            }

            public bool ReadOnly
            {
                get { return _readOnly; }
            }

            public IsolationLevel IsolationLevel
            {
                get { return _isolationLevel; }
            }

            public bool WasActive
            {
                get { return _wasActive; }
            }
        }

        #endregion

        #region Private Variables

        private TransactionSynchronizationState _transactionSyncState = TransactionSynchronizationState.Always;
        private bool _nestedTransactionsAllowed;
        private bool _rollbackOnCommitFailure;
        private bool _failEarlyOnGlobalRollbackOnly;
        private int _defaultTimeout = DefaultTransactionDefinition.TIMEOUT_DEFAULT;

        #region Logging Definition

        [NonSerialized()] protected readonly ILog log;

        #endregion

        protected AbstractPlatformTransactionManager()
        {
            log = LogManager.GetLogger(this.GetType());
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Sets and gets when this transaction manager should activate the thread-bound
        /// transaction synchronization support. Default is "always".
        /// </summary>
        /// <remarks>
        /// <p>
        /// Note that transaction synchronization isn't supported for
        /// multiple concurrent transactions by different transaction managers.
        /// Only one transaction manager is allowed to activate it at any time.
        /// </p>
        /// <see cref="Spring.Transaction.Support.TransactionSynchronizationState"/>
        /// </remarks>
        public TransactionSynchronizationState TransactionSynchronization
        {
            set { _transactionSyncState = value; }
            get { return _transactionSyncState; }
        }

        /// <summary>
        /// Sets and gets whether nested transactions are allowed. Default is false.
        /// </summary>
        /// <remarks>
        /// <p>
        /// Typically initialized with an appropriate default by the
        /// concrete transaction manager subclass.
        /// </p>
        /// </remarks>
        public bool NestedTransactionsAllowed
        {
            get { return _nestedTransactionsAllowed; }
            set { _nestedTransactionsAllowed = value; }
        }

        /// <summary>
        /// Sets and gets a flag that determines whether or not the
        /// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoRollback(DefaultTransactionStatus)"/>
        /// method must be invoked if a call to the
        /// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoCommit(DefaultTransactionStatus)"/>
        /// method fails. Default is false.
        /// </summary>
        /// <remarks>
        /// Typically not necessary and thus to be avoided as it can override the
        /// commit exception with a subsequent rollback exception.
        /// </remarks>
        public bool RollbackOnCommitFailure
        {
            get { return _rollbackOnCommitFailure; }
            set { _rollbackOnCommitFailure = value; }
        }


        /// <summary>
        /// Gets or sets a value indicating whether to fail early in case of the transaction being
        /// globally marked as rollback-only.
        /// </summary>
        /// <remarks>
        ///  Default is "false", only causing an UnexpectedRollbackException at the
        ///  outermost transaction boundary. Switch this flag on to cause an
        ///  UnexpectedRollbackException as early as the global rollback-only marker
        ///  has been first detected, even from within an inner transaction boundary.
        /// </remarks>
        /// <value>
        /// 	<c>true</c> if fail early on global rollback; otherwise, <c>false</c>.
        /// </value>
        public bool FailEarlyOnGlobalRollbackOnly
        {
            get { return _failEarlyOnGlobalRollbackOnly; }
            set { _failEarlyOnGlobalRollbackOnly = value; }
        }

        /// <summary>
        /// Gets or sets the default timeout that this transaction manager should apply if there
        /// is no timeout specified at the transaction level, in seconds.
        /// </summary>
        /// <remarks>Returns DefaultTransactionDefinition.TIMEOUT_DEFAULT to indicate the
        /// underlying transaction infrastructure's default timeout.</remarks>
        /// <value>The default timeout.</value>
        public int DefaultTimeout
        {
            get
            {
                return _defaultTimeout;
            }
            set
            {
                if (_defaultTimeout < DefaultTransactionDefinition.TIMEOUT_DEFAULT)
                {
                    throw new InvalidTimeoutException("Invalid default timeout", _defaultTimeout);
                } 
                _defaultTimeout = value;
            }
        }

        #endregion

        #region Protected Methods

        /// <summary>
        /// Return the current transaction object.
        /// </summary>
        /// <returns>The current transaction object.</returns>
        /// <exception cref="Spring.Transaction.CannotCreateTransactionException">
        /// If transaction support is not available.
        /// </exception>
        /// <exception cref="Spring.Transaction.TransactionException">
        /// In the case of lookup or system errors.
        /// </exception>
        protected abstract object DoGetTransaction();

        /// <summary>
        /// Check if the given transaction object indicates an existing transaction
        /// (that is, a transaction which has already started).
        /// </summary>
        /// <remarks>
        /// The result will be evaluated according to the specified propagation
        /// behavior for the new transaction. An existing transaction might get
        /// suspended (in case of PROPAGATION_REQUIRES_NEW), or the new transaction
        /// might participate in the existing one (in case of PROPAGATION_REQUIRED).
        /// Default implementation returns false, assuming that detection of or
        /// participating in existing transactions is generally not supported.
        /// Subclasses are of course encouraged to provide such support.</remarks>
        /// <param name="transaction">
        /// Transaction object returned by
        /// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoGetTransaction"/>.
        /// </param>
        /// <returns>True if there is an existing transaction.</returns>
        /// <exception cref="Spring.Transaction.TransactionException">
        /// In the case of system errors.
        /// </exception>
        protected virtual bool IsExistingTransaction(object transaction)
        {
            return false;
        }

        /// <summary>
        /// Begin a new transaction with the given transaction definition.
        /// </summary>
        /// <remarks>
        /// Does not have to care about applying the propagation behavior,
        /// as this has already been handled by this abstract manager.
        /// </remarks>
        /// <param name="transaction">
        /// Transaction object returned by
        /// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoGetTransaction"/>.
        /// </param>
        /// <param name="definition">
        /// <see cref="Spring.Transaction.ITransactionDefinition"/> instance, describing
        /// propagation behavior, isolation level, timeout etc.
        /// </param>
        /// <exception cref="Spring.Transaction.TransactionException">
        /// In the case of creation or system errors.
        /// </exception>
        protected abstract void DoBegin(object transaction, ITransactionDefinition definition);

        /// <summary>
        /// Suspend the resources of the current transaction.
        /// </summary>
        /// <remarks>
        /// Transaction synchronization will already have been suspended.
        /// <para>
        /// Default implementation throws a TransactionSuspensionNotSupportedException,
        /// assuming that transaction suspension is generally not supported.
        /// </para>
        /// </remarks>
        /// <param name="transaction">
        /// Transaction object returned by
        /// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoGetTransaction"/>.
        /// </param>
        /// <returns>
        /// An object that holds suspended resources (will be kept unexamined for passing it into
        /// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoResume"/>.)
        /// </returns>
        /// <exception cref="Spring.Transaction.IllegalTransactionStateException">
        /// If suspending is not supported by the transaction manager implementation.
        /// </exception>
        /// <exception cref="Spring.Transaction.TransactionException">
        /// in case of system errors.
        /// </exception>
        protected virtual object DoSuspend(object transaction)
        {
            throw new TransactionSuspensionNotSupportedException(
                "Transaction manager [" + GetType().Name + "] does not support transaction suspension");
        }

        /// <summary>
        /// Resume the resources of the current transaction.
        /// </summary>
        /// <remarks>Transaction synchronization will be resumed afterwards.
        /// <para>
        /// Default implementation throws a TransactionSuspensionNotSupportedException,
        /// assuming that transaction suspension is generally not supported.
        /// </para>
        /// </remarks>
        /// <param name="transaction">
        /// Transaction object returned by
        /// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoGetTransaction"/>.
        /// </param>
        /// <param name="suspendedResources">
        /// The object that holds suspended resources as returned by
        /// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoSuspend"/>.
        /// </param>
        /// <exception cref="Spring.Transaction.IllegalTransactionStateException">
        /// If suspending is not supported by the transaction manager implementation.
        /// </exception>
        /// <exception cref="Spring.Transaction.TransactionException">
        /// In the case of system errors.
        /// </exception>
        protected virtual void DoResume(object transaction, object suspendedResources)
        {
            throw new TransactionSuspensionNotSupportedException(
                "Transaction manager [" + GetType().Name + "] does not support transaction suspension");
        }

        /// <summary>
        /// Perform an actual commit on the given transaction.
        /// </summary>
        /// <param name="status">The status representation of the transaction.</param>
        /// <remarks>
        /// <p>
        /// An implementation does not need to check the rollback-only flag.
        /// </p>
        /// </remarks>
        /// <exception cref="Spring.Transaction.TransactionException">
        /// In the case of system errors.
        /// </exception>
        protected abstract void DoCommit(DefaultTransactionStatus status);

        /// <summary>
        /// Perform an actual rollback on the given transaction.
        /// </summary>
        /// <param name="status">The status representation of the transaction.</param>
        /// <remarks>
        /// An implementation does not need to check the new transaction flag.
        /// </remarks>
        /// <exception cref="Spring.Transaction.TransactionException">
        /// In the case of system errors.
        /// </exception>
        protected abstract void DoRollback(DefaultTransactionStatus status);

        /// <summary>
        /// Set the given transaction rollback-only. Only called on rollback
        /// if the current transaction takes part in an existing one.
        /// </summary>
        /// <remarks>Default implementation throws an IllegalTransactionStateException,
        /// assuming that participating in existing transactions is generally not
        /// supported. Subclasses are of course encouraged to provide such support.
        /// </remarks>
        /// <param name="status">The status representation of the transaction.</param>
        /// <exception cref="Spring.Transaction.TransactionException">
        /// In the case of system errors.
        /// </exception>
        protected virtual void DoSetRollbackOnly(DefaultTransactionStatus status)
        {
            throw new IllegalTransactionStateException(
                "Participating in existing transactions is not supported - when 'IsExistingTransaction' " +
                "returns true, appropriate 'DoSetRollbackOnly' behavior must be provided");
        }

        /// <summary>
        /// Return whether to use a savepoint for a nested transaction. Default is true,
        /// which causes delegation to <see cref="Spring.Transaction.Support.DefaultTransactionStatus"/>
        /// for holding a savepoint.
        /// </summary>
        /// <returns></returns>
        /// <remarks>
        /// <p>
        /// Subclasses can override this to return false, causing a further
        /// invocation of
        /// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoBegin"/>
        /// despite an already existing transaction.
        /// </p>
        /// </remarks>
        protected virtual bool UseSavepointForNestedTransaction()
        {
            return true;
        }

        /// <summary>
        /// Register the given list of transaction synchronizations with the existing transaction.
        /// </summary>
        /// <remarks>
        /// Invoked when the control of the Spring transaction manager and thus all Spring
        /// transaction synchronizations end, without the transaction being completed yet. This
        /// is for example the case when participating in an existing System.Transactions or
        /// EnterpriseServices transaction invoked via their APIs.
        /// <para>
        /// The default implementation simply invokes the <code>AfterCompletion</code> methods
        /// immediately, passing in TransactionSynchronizationStatus.Unknown. 
        /// This is the best we can do if there's no chance to determine the actual 
        /// outcome of the outer transaction.
        /// </para>
        /// </remarks>
        /// <param name="transaction">The transaction transaction object returned by <code>DoGetTransaction</code>.</param>
        /// <param name="synchronizations">The lList of TransactionSynchronization objects.</param>
        /// <exception cref="TransactionException">In case of errors</exception>
        /// <seealso cref="InvokeAfterCompletion"/>
        /// <seealso cref="ITransactionSynchronization.AfterCompletion"/>
        /// <seealso cref="TransactionSynchronizationStatus.Unknown"/>
        protected virtual void RegisterAfterCompletionWithExistingTransaction(Object transaction, IList synchronizations)
		{

		    log.Debug("Cannot register Spring after-completion synchronization with existing transaction - " +
				"processing Spring after-completion callbacks immediately, with outcome status 'unknown'");
		    InvokeAfterCompletion(synchronizations, TransactionSynchronizationStatus.Unknown);
	    }

        /// <summary>
        /// Cleanup resources after transaction completion.
        /// </summary>
        /// <param name="transaction">
        /// Transaction object returned by
        /// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoGetTransaction"/>.
        /// </param>
        /// <remarks>
        /// <para>
        /// Called after <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoCommit"/>
        /// and
        /// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoRollback"/>
        /// execution on any outcome.
        /// </para>
        /// <para>
        /// Should not throw any exceptions but just issue warnings on errors.
        /// </para>
        /// <para>
        /// Default implementation does nothing.
        /// </para>
        /// </remarks>
        protected virtual void DoCleanupAfterCompletion(object transaction)
        {
        }

        #endregion

        #region IPlatformTransactionManager Members

        /// <summary>
        /// Return a currently active transaction or create a new one.
        /// </summary>
        /// <remarks>
        /// <p>
        /// This implementation handles propagation behavior.
        /// </p>
        /// <p>
        /// Delegates to
        /// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoGetTransaction"/>,
        /// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.IsExistingTransaction"/>,
        /// and
        /// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoBegin"/>.
        /// </p>
        /// <p>
        /// Note that parameters like isolation level or timeout will only be applied
        /// to new transactions, and thus be ignored when participating in active ones.
        /// Furthermore, they aren't supported by every transaction manager:
        /// a proper implementation should throw an exception when custom values
        /// that it doesn't support are specified.
        /// </p>
        /// </remarks>
        /// <param name="definition">
        /// <see cref="Spring.Transaction.ITransactionDefinition"/> instance (can be null for
        /// defaults), describing propagation behavior, isolation level, timeout etc.
        /// </param>
        /// <exception cref="Spring.Transaction.TransactionException">
        /// In case of lookup, creation, or system errors.
        /// </exception>
        /// <returns>
        /// <see cref="Spring.Transaction.ITransactionStatus"/> representing the new or current transaction.
        /// </returns>
        public ITransactionStatus GetTransaction(ITransactionDefinition definition)
        {
            object transaction = DoGetTransaction();
            bool debugEnabled = log.IsDebugEnabled;
            bool newSynchronization;

            if (debugEnabled)
            {
                log.Debug("Using transaction object [" + transaction + "]");
            }

            if (definition == null)
            {
                definition = new DefaultTransactionDefinition();
            }
            if (IsExistingTransaction(transaction))
            {
                // Existing transaction found -> check propagation behavior to find out how to behave.
                return HandleExistingTransaction(definition, transaction, debugEnabled);
            }

            // Check definition settings for new transaction.
            if (definition.TransactionTimeout < DefaultTransactionDefinition.TIMEOUT_DEFAULT)
            {
                throw new InvalidTimeoutException("Invalid transaction timeout", definition.TransactionTimeout);
            }

            // No existing transaction found -> check propagation behavior to find out how to proceed.
            if (definition.PropagationBehavior == TransactionPropagation.Mandatory)
            {
                throw new IllegalTransactionStateException(
                    "Transaction propagation 'mandatory' but no existing transaction found");
            }
            else if (definition.PropagationBehavior == TransactionPropagation.Required ||
                     definition.PropagationBehavior == TransactionPropagation.RequiresNew ||
                      definition.PropagationBehavior == TransactionPropagation.Nested)
            {
                object suspendedResources = Suspend(null);
                if (debugEnabled)
                {
                    log.Debug("Creating new transaction with name [" + definition.Name + "]:" + definition);
                }
                try
                {
                    DoBegin(transaction, definition);
                } catch (TransactionException)
                {
                    Resume(null, suspendedResources);
                    throw;
                }
                newSynchronization = (_transactionSyncState != TransactionSynchronizationState.Never);
                return NewTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled,
                                            suspendedResources);
            }
            else
            {
                // Create "empty" transaction: no actual transaction, but potentially synchronization.
                newSynchronization = (_transactionSyncState == TransactionSynchronizationState.Always);
                return NewTransactionStatus(definition, null, false, newSynchronization, debugEnabled, null);

            }

        }

        private ITransactionStatus HandleExistingTransaction(ITransactionDefinition definition, object transaction, bool debugEnabled)
        {
            //bool newSynchronization;
            if (definition.PropagationBehavior == TransactionPropagation.Never)
            {
                throw new IllegalTransactionStateException(
                    "Transaction propagation 'never' but existing transaction found.");
            }
            if (definition.PropagationBehavior == TransactionPropagation.NotSupported)
            {
                if (debugEnabled)
                {
                    log.Debug("Suspending current transaction");
                }
                object suspendedResources = Suspend(transaction);
                bool newSynchronization = (_transactionSyncState == TransactionSynchronizationState.Always);
                return
                    NewTransactionStatus(definition, null, false, newSynchronization, debugEnabled,
                                         suspendedResources);
            }

            if (definition.PropagationBehavior == TransactionPropagation.RequiresNew)
            {
                if (debugEnabled)
                {
                    log.Debug("Suspending current transaction, creating new transaction with name [" +
                              definition.Name + "]:" + definition);
                }
                object suspendedResources = Suspend(transaction);
                try
                {
                    DoBegin(transaction, definition);
                }
                catch (TransactionException beginEx)
                {
                    try
                    {
                        Resume(transaction, suspendedResources);
                    }
                    catch (TransactionException resumeEx)
                    {
                        log.Error(
                            "Inner transaction begin exception overridden by outer transaction resume exception");
                        log.Error("Begin Transaction Exception", beginEx);
                        log.Error("Resume Transaction Exception", resumeEx);
                        throw;
                    }
                    throw;
                }
                bool newSynchronization = (_transactionSyncState != TransactionSynchronizationState.Never);
                return
                    NewTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
            }
            if (definition.PropagationBehavior == TransactionPropagation.Nested)
            {
                if (!NestedTransactionsAllowed)
                {
                    throw new NestedTransactionNotSupportedException(
                        "Transaction manager does not allow nested transactions by default - " +
                        "specify 'NestedTransactionsAllowed' property with value 'true'");
                }
                if (debugEnabled)
                {
                    log.Debug("Creating nested transaction with name [" + definition.Name + "]:" + definition);
                }

                if (UseSavepointForNestedTransaction())
                {
                    DefaultTransactionStatus status =
                        NewTransactionStatus(definition, transaction, false, false, debugEnabled, null);
                    status.CreateAndHoldSavepoint(DateTime.Now.ToLongTimeString());
                    return status;
                }
                else
                {
                    DoBegin(transaction, definition);
                    bool newSynchronization = (_transactionSyncState != TransactionSynchronizationState.Never);
                    return NewTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);

                }
            }
            // Assumably PROPAGATION_SUPPORTS.
            if (debugEnabled) {
			    log.Debug("Participating in existing transaction");
		    }
            bool newSynch = (_transactionSyncState != TransactionSynchronizationState.Never);
            return NewTransactionStatus(definition, transaction, false, newSynch, debugEnabled, null);

        }

        /// <summary>
        /// This implementation of commit handles participating in existing transactions
        /// and programmatic rollback requests.
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <param name="transactionStatus">
        /// ITransactionStatus object returned by the
        /// <see cref="Spring.Transaction.IPlatformTransactionManager.GetTransaction"/>() method.
        /// </param>
        /// <exception cref="Spring.Transaction.TransactionException">
        /// In case of commit or system errors.
        /// </exception>
        public void Commit(ITransactionStatus transactionStatus)
        {
            if (transactionStatus.Completed)
            {
                throw new IllegalTransactionStateException(
                    "Transaction is already completed - do not call commit or rollback more than once per transaction");
            }

            DefaultTransactionStatus defaultStatus = (DefaultTransactionStatus) transactionStatus;
            if (defaultStatus.LocalRollbackOnly)
            {
                if (defaultStatus.Debug)
                {
                    log.Debug("Transaction code has requested rollback");
                }
                ProcessRollback(defaultStatus);
                return;
            }
            if ( !ShouldCommitOnGlobalRollbackOnly && defaultStatus.GlobalRollbackOnly)
            {
                if (defaultStatus.Debug)
                {
                    log.Debug("Global transaction is marked as rollback-only but transactional code requested commit");
                }
                ProcessRollback(defaultStatus);
                // Throw UnexpectedRollbackException only at outermost transaction boundary
                // or if explicitly asked to.
                if (defaultStatus.IsNewTransaction || FailEarlyOnGlobalRollbackOnly)
                {
                    throw new UnexpectedRollbackException(
                            "Transaction rolled back because it has been marked as rollback-only");
                }
                return;
            }
            ProcessCommit(defaultStatus);
           
        }

        protected virtual bool ShouldCommitOnGlobalRollbackOnly
        {
            get { return false; }
        }

        private void ProcessCommit(DefaultTransactionStatus status)
        {
            try
            {
                bool beforeCompletionInvoked = false;
                try
                {
                    TriggerBeforeCommit(status);
                    TriggerBeforeCompletion(status);
                    beforeCompletionInvoked = true;
                    bool globalRollbackOnly = false;
                    if (status.IsNewTransaction || FailEarlyOnGlobalRollbackOnly)
                    {
                        globalRollbackOnly = status.GlobalRollbackOnly;
                    }
                    if (status.HasSavepoint)
                    {
                        status.ReleaseHeldSavepoint();
                    }
                    else if (status.IsNewTransaction)
                    {
                        DoCommit(status);
                    }
                    // Throw UnexpectedRollbackException if we have a global rollback-only
                    // marker but still didn't get a corresponding exception from commit.
                    if (globalRollbackOnly)
                    {
                        throw new UnexpectedRollbackException(
                                "Transaction silently rolled back because it has been marked as rollback-only");
                    }
                }
                catch (UnexpectedRollbackException)
                {
                    TriggerAfterCompletion(status, TransactionSynchronizationStatus.Rolledback);
                    throw;
                }
                catch (TransactionException ex)
                {
                    if (RollbackOnCommitFailure)
                    {
                        DoRollbackOnCommitException(status, ex);
                    }
                    else
                    {
                        TriggerAfterCompletion(status, TransactionSynchronizationStatus.Unknown);
                    }
                    throw;
                }
                catch (Exception ex)
                {
                    if (!beforeCompletionInvoked)
                    {
                        TriggerBeforeCompletion(status);
                    }
                    DoRollbackOnCommitException(status, ex);
                    throw;
                }
                // Trigger AfterCommit callbacks, with an exception thrown there
                // propagated to callers but the transaction still considered as commited.
                try
                {
                    TriggerAfterCommit(status);
                }
                finally
                {
                    TriggerAfterCompletion(status, TransactionSynchronizationStatus.Committed);
                }
            }
            finally
            {
                CleanupAfterCompletion(status);
            }
        }

        private void TriggerAfterCommit(DefaultTransactionStatus status)
        {
            if (status.NewSynchronization)
            {
                if (status.Debug)
                {
                    log.Debug("Trigger AfterCommit Synchronization");
                }
                IList synchronizations = TransactionSynchronizationManager.Synchronizations;
                foreach (ITransactionSynchronization currentTxnSynchronization in synchronizations)
                {
                    try
                    {
                        currentTxnSynchronization.AfterCommit();
                    }
                    catch (Exception e)
                    {
                        log.Error("TransactionSynchronization.AfterCommit thew exception", e);
                    }
                }
            }
        }

        /// <summary>
        /// Roll back the given transaction, with regard to its status.
        /// </summary>
        /// <remarks>
        /// <p>
        /// This implementation handles participating in existing transactions.
        /// </p>
        /// <p>
        /// Delegates to
        /// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoRollback"/>,
        /// and
        /// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoSetRollbackOnly"/>.
        /// </p>
        /// <p>
        /// If the transaction wasn't a new one, just set it rollback-only
        /// to take part in the surrounding transaction properly.
        /// </p>
        /// </remarks>
        /// <param name="transactionStatus">
        /// ITransactionStatusObject returned by the
        /// <see cref="Spring.Transaction.IPlatformTransactionManager.GetTransaction"/>() method.
        /// </param>
        /// <exception cref="Spring.Transaction.TransactionException">
        /// In case of system errors.
        /// </exception>
        public void Rollback(ITransactionStatus transactionStatus)
        {
            if (transactionStatus.Completed)
            {
                throw new IllegalTransactionStateException(
                    "Transaction is already completed - do not call commit or rollback more than once per transaction");
            }
            DefaultTransactionStatus defaultStatus = (DefaultTransactionStatus) transactionStatus;
            ProcessRollback(defaultStatus);
        }

        private void ProcessRollback(DefaultTransactionStatus status)
        {
            try
            {
                try
                {
                    TriggerBeforeCompletion(status);
                    if (status.HasSavepoint)
                    {
                        if (status.Debug)
                        {
                            log.Debug("Rolling back transaction to savepoint.");
                        }
                        status.RollbackToHeldSavepoint();
                    }
                    else if (status.IsNewTransaction)
                    {
                        if (status.Debug)
                        {
                            log.Debug("Initiating transaction rollback");
                        }
                        DoRollback(status);
                    }
                    else if (status.HasTransaction())
                    {
                        if (status.LocalRollbackOnly)
                        {
                            if(status.Debug)
                            {
                                log.Debug("Participating transaction failed - marking existing transaction as rollback-only");
                            }
                        }
                        DoSetRollbackOnly(status);
                    }
                    else
                    {
                        log.Debug("Should roll back transaction but cannot - no transaction available.");
                    }
                }
                catch (Exception)
                {
                    TriggerAfterCompletion(status, TransactionSynchronizationStatus.Unknown);
                    throw;
                }
                TriggerAfterCompletion(status, TransactionSynchronizationStatus.Rolledback);
            }
            finally
            {
                CleanupAfterCompletion(status);
            }
        }

        #endregion

        #region Protected Method

        private DefaultTransactionStatus NewTransactionStatus(ITransactionDefinition definition,
                                              object transaction, bool newTransaction,
                                              bool newSynchronization, bool debug,
                                              object suspendedResources)
        {
            bool actualNewSynchronization = newSynchronization &&
                                !TransactionSynchronizationManager.SynchronizationActive;
            if (actualNewSynchronization)
            {
                TransactionSynchronizationManager.ActualTransactionActive = (transaction != null);
                TransactionSynchronizationManager.CurrentTransactionIsolationLevel =
                    definition.TransactionIsolationLevel;
                TransactionSynchronizationManager.CurrentTransactionReadOnly = definition.ReadOnly;
                TransactionSynchronizationManager.CurrentTransactionName = definition.Name;
                TransactionSynchronizationManager.InitSynchronization();
            }
            return
                new DefaultTransactionStatus(transaction, newTransaction, actualNewSynchronization, definition.ReadOnly, debug,
                                             suspendedResources);
        }

        /// <summary>
        /// Determines the timeout to use for the given definition.  Will fall back to this manager's default
        /// timeout if the transaction definition doesn't specify a non-default value.
        /// </summary>
        /// <param name="definition">The transaction definition.</param>
        /// <returns>the actual timeout to use.</returns>
        protected int DetermineTimeout(ITransactionDefinition definition)
        {
            if (definition.TransactionTimeout != DefaultTransactionDefinition.TIMEOUT_DEFAULT)
            {
                return definition.TransactionTimeout;
            }
            return _defaultTimeout;
        }

        #endregion

        #region Private Methods





        /// <summary>
        /// Suspend the given transaction. Suspends transaction synchronization first,
        /// then delegates to the doSuspend template method.
        /// </summary>
        /// <param name="transaction">the current transaction object</param>
        /// <returns>an object that holds suspended resources</returns>
        private object Suspend(object transaction)
        {
            if (TransactionSynchronizationManager.SynchronizationActive)
            {
                IList suspendedSynchronizations = DoSuspendSynchronization();

                try
                {
                    object suspendedResources = null;
                    if (transaction != null)
                    {
                        suspendedResources = DoSuspend(transaction);
                    }

                    string name = TransactionSynchronizationManager.CurrentTransactionName;
                    TransactionSynchronizationManager.CurrentTransactionName = null;
                    bool readOnly = TransactionSynchronizationManager.CurrentTransactionReadOnly;
                    TransactionSynchronizationManager.CurrentTransactionReadOnly = false;
                    IsolationLevel isolationLevel = TransactionSynchronizationManager.CurrentTransactionIsolationLevel;
                    TransactionSynchronizationManager.CurrentTransactionIsolationLevel = IsolationLevel.Unspecified;
                    bool wasActive = TransactionSynchronizationManager.ActualTransactionActive;
                    TransactionSynchronizationManager.ActualTransactionActive = false;


                    return new SuspendedResourcesHolder(suspendedSynchronizations, suspendedResources,
                        name, readOnly, isolationLevel, wasActive);

                } catch (TransactionException)
                {
                    // DoSuspend failed - original transaction is still active
                    DoResumeSynchronization(suspendedSynchronizations);
                    throw;
                }
            }
            else if (transaction != null)
            {
                // Transaction active but no synchronization active.
                object suspendedResources = DoSuspend(transaction);
                return new SuspendedResourcesHolder(suspendedResources);                
            }
            else
            {
                // Neither transaction nor synchronization active.
                return null;
            }

        }

        private IList DoSuspendSynchronization()
        {
            IList suspendedSynchronizations = TransactionSynchronizationManager.Synchronizations;
            foreach (ITransactionSynchronization currentTxnSynchronization in suspendedSynchronizations)
            {
                currentTxnSynchronization.Suspend();
            }
            TransactionSynchronizationManager.ClearSynchronization();
            return suspendedSynchronizations;
        }

        private void DoResumeSynchronization(IList suspendedSynchronizations)
        {
            TransactionSynchronizationManager.InitSynchronization();
            foreach (ITransactionSynchronization currentTxnSynchronization in suspendedSynchronizations)
            {
                currentTxnSynchronization.Resume();
                TransactionSynchronizationManager.RegisterSynchronization(currentTxnSynchronization);
            }
        }

        /// <summary>
        /// Resume the given transaction. Delegates to the doResume template method
        /// first, then resuming transaction synchronization.
        /// </summary>
        /// <param name="transaction">the current transaction object</param>
        /// <param name="suspendedResources"> the object that holds suspended resources, as returned by suspend</param>
        private void Resume(object transaction, object suspendedResources)
        {
            SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder) suspendedResources;
            if (resourcesHolder != null)
            {
                object suspendedResourcesObject = resourcesHolder.SuspendedResources;
                if (suspendedResourcesObject != null)
                {
                    DoResume(transaction, suspendedResourcesObject);
                }
                IList suspendedSynchronizations = resourcesHolder.SuspendedSynchronizations;
                if (suspendedSynchronizations != null)
                {
                    TransactionSynchronizationManager.ActualTransactionActive = resourcesHolder.WasActive;
                    TransactionSynchronizationManager.CurrentTransactionIsolationLevel = resourcesHolder.IsolationLevel;
                    TransactionSynchronizationManager.CurrentTransactionReadOnly = resourcesHolder.ReadOnly;
                    TransactionSynchronizationManager.CurrentTransactionName = resourcesHolder.Name;
                    DoResumeSynchronization(suspendedSynchronizations);
                }
                
            }
        }



        /// <summary>
        /// Invoke doRollback, handling rollback exceptions properly.
        /// </summary>
        /// <param name="status">object representing the transaction</param>
        /// <param name="exception">the thrown application exception or error</param>
        /// <exception cref="Spring.Transaction.TransactionException">
        /// in case of a rollback error
        /// </exception>
        private void DoRollbackOnCommitException(DefaultTransactionStatus status, Exception exception)
        {
            try
            {
                if (status.IsNewTransaction)
                {
                    if (status.Debug)
                    {
                        log.Debug("Initiating transaction rollback on commit exception.");
                    }
                    DoRollback(status);
                }
                else if (status.HasTransaction())
                {
                    if (status.Debug)
                    {
                        log.Debug("Marking existing transaction as rollback-only after commit exception", exception);
                    }
                    DoSetRollbackOnly(status);
                }
            }
            catch (Exception)
            {
                //TODO investigate rollback behavior...
                log.Error("Commit exception overridden by rollback exception", exception);
                TriggerAfterCompletion(status, TransactionSynchronizationStatus.Unknown);
                throw;
            }
            TriggerAfterCompletion(status, TransactionSynchronizationStatus.Rolledback);
        }

        /// <summary>
        /// Trigger beforeCommit callback.
        /// </summary>
        /// <param name="status">object representing the transaction</param>
        private void TriggerBeforeCommit(DefaultTransactionStatus status)
        {
            if (status.NewSynchronization)
            {
                IList synchronizations = TransactionSynchronizationManager.Synchronizations;
                foreach (ITransactionSynchronization currentTxnSynchronization in synchronizations)
                {
                    currentTxnSynchronization.BeforeCommit(status.ReadOnly);
                }
            }
        }

        /// <summary>
        /// Trigger beforeCompletion callback.
        /// </summary>
        /// <param name="status">object representing the transaction</param>
        private void TriggerBeforeCompletion(DefaultTransactionStatus status)
        {
            if (status.NewSynchronization)
            {
                if (status.Debug)
                {
                    log.Debug("Trigger BeforeCompletion Synchronization");
                }
                IList synchronizations = TransactionSynchronizationManager.Synchronizations;
                foreach (ITransactionSynchronization synchronization in synchronizations)
                {
                    try
                    {
                        synchronization.BeforeCompletion();
                    }
                    catch (Exception e)
                    {
                        log.Error("TransactionSynchronization.BeforeCompletion threw exception", e);
                    }
                }
            }
        }

        /// <summary>
        /// Trigger afterCompletion callback, handling exceptions properly.
        /// </summary>
        /// <param name="status">object representing the transaction</param>
        /// <param name="completionStatus">
        /// Completion status according to <see cref="Spring.Transaction.Support.TransactionSynchronizationStatus"/>
        /// </param>
        private void TriggerAfterCompletion(DefaultTransactionStatus status, TransactionSynchronizationStatus completionStatus)
        {
            if (status.NewSynchronization)
            {
                IList synchronizations = TransactionSynchronizationManager.Synchronizations;
                if (!status.HasTransaction() || status.IsNewTransaction)
                {
                    if (status.Debug)
                    {
                        log.Debug("Triggering afterCompletion synchronization");
                    }
                    InvokeAfterCompletion(synchronizations, completionStatus);
                }
                else
                {
                    //TODO investigate parallel of JTA/System.Txs
                    log.Info("Transaction controlled outside of spring tx manager.");
                    RegisterAfterCompletionWithExistingTransaction(status.Transaction, synchronizations);
                }
            }
        }

        private void InvokeAfterCompletion(IList synchronizations, TransactionSynchronizationStatus status)
        {
            foreach (ITransactionSynchronization synchronization in synchronizations)
            {
                try
                {
                    synchronization.AfterCompletion(status);
                } catch (Exception e)
                {
                    log.Error("TransactionSynchronization.AfterCompletion threw exception", e);
                }
            }
        }

        /// <summary>
        /// Clean up after completion, clearing synchronization if necessary,
        /// and invoking doCleanupAfterCompletion.
        /// </summary>
        /// <param name="status">object representing the transaction</param>
        private void CleanupAfterCompletion(DefaultTransactionStatus status)
        {
            status.Completed = true;
            if (status.NewSynchronization)
            {
                TransactionSynchronizationManager.Clear();                
            }
            if (status.IsNewTransaction)
            {
                DoCleanupAfterCompletion(status.Transaction);
            }
            if (status.SuspendedResources != null)
            {
                if (status.Debug)
                {
                    log.Debug("Resuming suspended transaction");
                }
                Resume(status.Transaction, status.SuspendedResources);
            }
        }

        #endregion
    }
}