Requires the object factory to be known, to be able to resolve the object
- /// name to an interceptor instance on session creation. Typically used for
- /// prototype interceptors, i.e. a new interceptor instance per session.
- ///
- ///
Can also be used for shared interceptor instances, but it is recommended
- /// to set the interceptor reference directly in such a scenario.
- ///
- ///
- public string EntityInterceptorObjectName
- {
- set
- {
- entityInterceptor = value;
- }
- }
-
- ///
- /// Gets or sets the ADO.NET exception translator for this transaction manager.
- ///
- ///
- /// Applied to ADO.NET Exceptions (wrapped by Hibernate's ADOException)
- ///
- /// The ADO exception translator.
- public IAdoExceptionTranslator AdoExceptionTranslator
- {
- get { return adoExceptionTranslator; }
- set { adoExceptionTranslator = value; }
- }
-
- ///
- /// Gets the default IAdoException translator, lazily creating it if nece
- ///
- /// The default IAdoException translator.
- public IAdoExceptionTranslator DefaultAdoExceptionTranslator
- {
- get
- { lock(this)
- {
- if (defaultExceptionTranslator == null)
- {
- if (dbProvider != null)
- {
- defaultExceptionTranslator = new ErrorCodeExceptionTranslator(dbProvider);
- }
- else
- {
- defaultExceptionTranslator = SessionFactoryUtils.NewAdoExceptionTranslator(SessionFactory);
- }
- }
- return defaultExceptionTranslator;
- }
- }
- }
-
- ///
- /// Gets or sets the SessionFactory that this instance should manage transactions for.
- ///
- /// The session factory.
- public ISessionFactory SessionFactory
- {
- get { return sessionFactory; }
- set { sessionFactory = value; }
- }
-
-
- ///
- /// Gets the resource factory that this transaction manager operates on,
- /// For the HibenratePlatformTransactionManager this the SessionFactory
- ///
- /// The SessionFactory.
- public object ResourceFactory
- {
- get { return sessionFactory; }
- }
-
- ///
- /// Set whether to autodetect a ADO.NET connection used by the Hibernate SessionFactory,
- /// if set via LocalSessionFactoryObject's DbProvider. Default is "true".
- ///
- ///
- /// true if [autodetect data source]; otherwise, false.
- ///
- ///
- ///
Can be turned off to deliberately ignore an available IDbProvider,
- /// to not expose Hibernate transactions as ADO.NET transactions for that IDbProvider.
- ///
- ///
- public bool AutodetectDbProvider
- {
- set { autodetectDbProvider = value; }
- }
-
- #endregion
-
- #region Methods
-
- #endregion
-
-
- ///
- /// The object factory just needs to be known for resolving entity interceptor
- /// It does not need to be set for any other mode of operation.
- ///
- ///
- /// Owning
- /// (may not be ). The object can immediately
- /// call methods on the factory.
- ///
- public IObjectFactory ObjectFactory
- {
- set
- {
- objectFactory = value;
- }
- }
-
- ///
- /// Return the current transaction object.
- ///
- /// The current transaction object.
- ///
- /// If transaction support is not available.
- ///
- ///
- /// In the case of lookup or system errors.
- ///
- protected override object DoGetTransaction()
- {
- HibernateTransactionObject txObject = new HibernateTransactionObject();
- txObject.SavepointAllowed = NestedTransactionsAllowed;
- if (TransactionSynchronizationManager.HasResource(SessionFactory))
- {
- SessionHolder sessionHolder =
- (SessionHolder) TransactionSynchronizationManager.GetResource(SessionFactory);
- if (log.IsDebugEnabled)
- {
- log.Debug("Found thread-bound Session [" + sessionHolder.Session +
- "] for Hibernate transaction");
- }
- txObject.SetSessionHolder(sessionHolder, false);
- if (DbProvider != null)
- {
- ConnectionHolder conHolder = (ConnectionHolder)
- TransactionSynchronizationManager.GetResource(DbProvider);
- txObject.ConnectionHolder = conHolder;
- }
- }
- return txObject;
- }
-
- ///
- /// Check if the given transaction object indicates an existing,
- /// i.e. already begun, transaction.
- ///
- ///
- /// Transaction object returned by
- /// .
- ///
- /// True if there is an existing transaction.
- ///
- /// In the case of system errors.
- ///
- protected override bool IsExistingTransaction(object transaction)
- {
- return ((HibernateTransactionObject) transaction).HasTransaction();
- }
-
- ///
- /// Begin a new transaction with the given transaction definition.
- ///
- ///
- /// Transaction object returned by
- /// .
- ///
- ///
- /// instance, describing
- /// propagation behavior, isolation level, timeout etc.
- ///
- ///
- /// Does not have to care about applying the propagation behavior,
- /// as this has already been handled by this abstract manager.
- ///
- ///
- /// In the case of creation or system errors.
- ///
- protected override void DoBegin(object transaction, ITransactionDefinition definition)
- {
- HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
-
- if (DbProvider != null && TransactionSynchronizationManager.HasResource(DbProvider)
- && !txObject.ConnectionHolder.SynchronizedWithTransaction)
- {
- throw new IllegalTransactionStateException(
- "Pre-bound ADO.NET Connection found - HibernateTransactionManager does not support " +
- "running within AdoTransactionManager if told to manage the DbProvider itself. " +
- "It is recommended to use a single HibernateTransactionManager for all transactions " +
- "on a single DbProvider, no matter whether Hibernate or ADO.NET access.");
- }
- ISession session = null;
- try
- {
-
- if (txObject.SessionHolder == null || txObject.SessionHolder.SynchronizedWithTransaction)
- {
- IInterceptor interceptor = EntityInterceptor;
- ISession newSession = (interceptor != null ?
- SessionFactory.OpenSession(interceptor) : SessionFactory.OpenSession());
-
- if (log.IsDebugEnabled)
- {
- log.Debug("Opened new Session [" + newSession + "] for Hibernate transaction");
- }
- txObject.SetSessionHolder(new SessionHolder(newSession), true);
-
- }
- txObject.SessionHolder.SynchronizedWithTransaction = true;
- session = txObject.SessionHolder.Session;
-
- IDbConnection con = session.Connection;
- //TODO isolation level mgmt
- //IsolationLevel previousIsolationLevel =
-
- if (definition.ReadOnly && txObject.NewSessionHolder)
- {
- // Just set to NEVER in case of a new Session for this transaction.
- session.FlushMode = FlushMode.Never;
- }
-
- if (!definition.ReadOnly && !txObject.NewSessionHolder)
- {
- // We need AUTO or COMMIT for a non-read-only transaction.
- FlushMode flushMode = session.FlushMode;
- if (FlushMode.Never == flushMode)
- {
- session.FlushMode = FlushMode.Auto;
- txObject.SessionHolder.PreviousFlushMode = flushMode;
- }
- }
-
- // Add the Hibernate transaction to the session holder.
- // for now pass in tx options isolation level.
- ITransaction hibernateTx = session.BeginTransaction(definition.TransactionIsolationLevel);
- IDbTransaction adoTx = GetIDbTransaction(hibernateTx);
-
- // Add the Hibernate transaction to the session holder.
- txObject.SessionHolder.Transaction = hibernateTx;
-
- // Register transaction timeout.
- int timeout = DetermineTimeout(definition);
- if (timeout != DefaultTransactionDefinition.TIMEOUT_DEFAULT)
- {
- txObject.SessionHolder.TimeoutInSeconds = timeout;
- }
-
- // Register the Hibernate Session's ADO.NET Connection/TX pair for the DbProvider, if set.
- if (DbProvider != null)
- {
- //investigate passing null for tx.
- ConnectionHolder conHolder = new ConnectionHolder(con, adoTx);
- if (timeout != DefaultTransactionDefinition.TIMEOUT_DEFAULT)
- {
- conHolder.TimeoutInMillis = definition.TransactionTimeout;
- }
- if (log.IsDebugEnabled)
- {
- log.Debug("Exposing Hibernate transaction as ADO transaction [" + con + "]");
- }
- TransactionSynchronizationManager.BindResource(DbProvider, conHolder);
- txObject.ConnectionHolder = conHolder;
- }
-
- // Bind the session holder to the thread.
- if (txObject.NewSessionHolder)
- {
- TransactionSynchronizationManager.BindResource(SessionFactory, txObject.SessionHolder);
- }
-
- } catch (Exception ex)
- {
- SessionFactoryUtils.CloseSession(session);
- throw new CannotCreateTransactionException("Could not open Hibernate Session for transaction", ex);
- }
-
-
- }
-
-
-
-
- ///
- /// Suspend the resources of the current transaction.
- ///
- ///
- /// Transaction object returned by
- /// .
- ///
- ///
- /// An object that holds suspended resources (will be kept unexamined for passing it into
- /// .)
- ///
- ///
- /// Transaction synchronization will already have been suspended.
- ///
- ///
- /// If suspending is not supported by the transaction manager implementation.
- ///
- ///
- /// in case of system errors.
- ///
- protected override object DoSuspend(object transaction)
- {
- HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
- txObject.SetSessionHolder(null, false);
- SessionHolder sessionHolder =
- (SessionHolder) TransactionSynchronizationManager.UnbindResource(SessionFactory);
- ConnectionHolder connectionHolder = null;
- if (DbProvider != null)
- {
- connectionHolder = (ConnectionHolder) TransactionSynchronizationManager.UnbindResource(DbProvider);
- }
- return new SuspendedResourcesHolder(sessionHolder, connectionHolder);
-
- }
-
- ///
- /// Resume the resources of the current transaction.
- ///
- ///
- /// Transaction object returned by
- /// .
- ///
- ///
- /// The object that holds suspended resources as returned by
- /// .
- ///
- ///
- /// Transaction synchronization will be resumed afterwards.
- ///
- ///
- /// If suspending is not supported by the transaction manager implementation.
- ///
- ///
- /// In the case of system errors.
- ///
- protected override void DoResume(object transaction, object suspendedResources)
- {
- SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder) suspendedResources;
- if (TransactionSynchronizationManager.HasResource(SessionFactory))
- {
- // From non-transactional code running in active transaction synchronization
- // -> can be safely removed, will be closed on transaction completion.
- TransactionSynchronizationManager.UnbindResource(SessionFactory);
- }
- TransactionSynchronizationManager.BindResource(SessionFactory, resourcesHolder.SessionHolder);
- if (DbProvider != null)
- {
- TransactionSynchronizationManager.BindResource(DbProvider, resourcesHolder.ConnectionHolder);
- }
- }
-
- ///
- /// Perform an actual commit on the given transaction.
- ///
- /// The status representation of the transaction.
- ///
- ///
- /// An implementation does not need to check the rollback-only flag.
- ///
- ///
- ///
- /// In the case of system errors.
- ///
- protected override void DoCommit(DefaultTransactionStatus status)
- {
- HibernateTransactionObject txObject = (HibernateTransactionObject) status.Transaction;
- if (status.Debug)
- {
- log.Debug("Committing Hibernate transaction on Session [" +
- txObject.SessionHolder.Session + "]");
- }
- try
- {
- txObject.SessionHolder.Transaction.Commit();
- }
- // Note, unfortunate collision of namespaces/classname for NHibernate.TransactionException
- // and Spring.Data.NHibernate requires this wierd construct.
- catch (Exception ex)
- {
- Type nhibTxExceptiontype = TypeResolutionUtils.ResolveType("NHibernate.TransactionException, NHibernate");
- if (ex.GetType().Equals(nhibTxExceptiontype))
- {
- // assumably from commit call to the underlying ADO.NET connection
- throw new TransactionSystemException("Could not commit Hibernate transaction", ex);
- }
- HibernateException hibEx = ex as HibernateException;
- if (hibEx != null)
- {
- // assumably failed to flush changes to database
- throw ConvertHibernateAccessException(hibEx);
- }
- throw;
- }
- }
-
- ///
- /// Perform an actual rollback on the given transaction.
- ///
- /// The status representation of the transaction.
- ///
- /// An implementation does not need to check the new transaction flag.
- ///
- ///
- /// In the case of system errors.
- ///
- protected override void DoRollback(DefaultTransactionStatus status)
- {
- HibernateTransactionObject txObject = (HibernateTransactionObject) status.Transaction;
- if (status.Debug)
- {
- log.Debug("Rolling back Hibernate transaction on Session [" +
- txObject.SessionHolder.Session + "]");
- }
- try
- {
- txObject.SessionHolder.Transaction.Rollback();
- }
- catch (HibernateTransactionException ex)
- {
- throw new TransactionSystemException("Could not roll back Hibernate transaction", ex);
- }
- catch (HibernateException ex)
- {
- // Shouldn't really happen, as a rollback doesn't cause a flush.
- throw ConvertHibernateAccessException(ex);
- }
- finally
- {
- if (!txObject.NewSessionHolder)
- {
- // Clear all pending inserts/updates/deletes in the Session.
- // Necessary for pre-bound Sessions, to avoid inconsistent state.
- txObject.SessionHolder.Session.Clear();
- }
- }
-
-
-
- }
-
-
- ///
- /// Set the given transaction rollback-only. Only called on rollback
- /// if the current transaction takes part in an existing one.
- ///
- /// The status representation of the transaction.
- ///
- /// In the case of system errors.
- ///
- protected override void DoSetRollbackOnly(DefaultTransactionStatus status)
- {
- HibernateTransactionObject txObject = (HibernateTransactionObject) status.Transaction;
- if (status.Debug)
- {
- log.Debug("Setting Hibernate transaction on Session [" +
- txObject.SessionHolder.Session + "] rollback-only");
- }
- txObject.SetRollbackOnly();
- }
-
-
- ///
- /// Gets the ADO.NET IDbTransaction object from the NHibernate ITransaction object.
- ///
- /// The hibernate transaction.
- /// The ADO.NET transaction. Null if could not get the transaction. Warning
- /// messages will be logged in that case.
- protected IDbTransaction GetIDbTransaction(ITransaction hibernateTx)
- {
- AdoTransaction hibernateAdoTx = hibernateTx as AdoTransaction;
-
- IDbTransaction adoTransaction = null;
- if (hibernateAdoTx != null)
- {
- try
- {
- FieldInfo fi = hibernateAdoTx.GetType().GetField("trans", BindingFlags.Instance | BindingFlags.NonPublic);
- adoTransaction = fi.GetValue(hibernateAdoTx) as IDbTransaction;
- }
- catch (Exception e)
- {
- log.Warn("Could not extract IDbTransaction from Hibernate AdoTransaction using field name trans.", e);
- }
- }
- else
- {
- log.Warn("Hibernate ITransaction not of expected type AdoTransaction. Could not extract IDbTransaction from Hibernate AdoTransaction.");
- }
- return adoTransaction;
- }
-
- ///
- /// Convert the given HibernateException to an appropriate exception from
- /// the Spring.Dao hierarchy. Can be overridden in subclasses.
- ///
- /// The HibernateException that occured.
- /// The corresponding DataAccessException instance
- protected virtual DataAccessException ConvertHibernateAccessException(HibernateException ex)
- {
- if (AdoExceptionTranslator != null && ex is ADOException)
- {
- return ConvertAdoAccessException((ADOException) ex, AdoExceptionTranslator);
- }
- else if (ex is ADOException)
- {
- return ConvertAdoAccessException((ADOException)ex, DefaultAdoExceptionTranslator);
- }
- return SessionFactoryUtils.ConvertHibernateAccessException(ex);
- }
-
- ///
- /// Convert the given ADOException to an appropriate exception from the
- /// the Spring.Dao hierarchy. Can be overridden in subclasses.
- ///
- /// The ADOException that occured, wrapping the underlying
- /// ADO.NET thrown exception.
- /// The translator to convert hibernate ADOExceptions.
- ///
- /// The corresponding DataAccessException instance
- ///
- protected virtual DataAccessException ConvertAdoAccessException(ADOException ex, IAdoExceptionTranslator translator)
- {
- return translator.Translate("Hibernate flusing: " + ex.Message, null, ex.InnerException);
- }
-
- ///
- /// Cleanup resources after transaction completion.
- ///
- /// Transaction object returned by
- /// .
- ///
- ///
- /// This implemenation unbinds the SessionFactory and
- /// DbProvider from thread local storage and closes the
- /// ISession.
- ///
- ///
- /// Called after
- /// and
- ///
- /// execution on any outcome.
- ///
- ///
- /// Should not throw any exceptions but just issue warnings on errors.
- ///
- ///
- protected override void DoCleanupAfterCompletion( object transaction )
- {
- HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
-
- // Remove the session holder from the thread.
- if (txObject.NewSessionHolder)
- {
- TransactionSynchronizationManager.UnbindResource(SessionFactory);
- }
- // Remove the ADO.NET connection holder from the thread, if exposed.
- if (DbProvider != null)
- {
- TransactionSynchronizationManager.UnbindResource(DbProvider);
- }
- /*
- try
- {
- //TODO investigate isolation level settings...
- //IDbConnection con = txObject.SessionHolder.Session.Connection;
- //AdoUtils.ResetConnectionAfterTransaction(con, txObject.PreviousIsolationLevel);
- }
- catch (HibernateException ex)
- {
- log.Info("Could not access ADO.NET IDbConnection of Hibernate Session", ex);
- }
- */
- ISession session = txObject.SessionHolder.Session;
- if (txObject.NewSessionHolder)
- {
- if (log.IsDebugEnabled)
- {
- log.Debug("Closing Hibernate Session [" + session + "] after transaction");
- }
- SessionFactoryUtils.CloseSessionOrRegisterDeferredClose(session, SessionFactory);
- }
- else
- {
- if (log.IsDebugEnabled)
- {
- log.Debug("Not closing pre-bound Hibernate Session [" + session + "] after transaction");
- }
- if (txObject.SessionHolder.AssignedPreviousFlushMode)
- {
- session.FlushMode = txObject.SessionHolder.PreviousFlushMode;
- }
- }
- txObject.SessionHolder.Clear();
-
-
- }
-
- private class HibernateTransactionObject : AdoTransactionObjectSupport
- {
-
- private SessionHolder sessionHolder;
-
- private bool newSessionHolder;
-
-
- public void SetSessionHolder(SessionHolder sessionHolder, bool newSessionHolder)
- {
- this.sessionHolder = sessionHolder;
- this.newSessionHolder = newSessionHolder;
- }
-
-
- public SessionHolder SessionHolder
- {
- get
- {
- return sessionHolder;
- }
- }
-
- public bool NewSessionHolder
- {
- get
- {
- return newSessionHolder;
- }
- }
-
- public bool HasTransaction()
- {
- return (this.sessionHolder != null && this.sessionHolder.Transaction != null);
- }
-
- public void SetRollbackOnly()
- {
- SessionHolder.RollbackOnly = true;
- if (ConnectionHolder != null)
- {
- ConnectionHolder.RollbackOnly = true;
- }
- }
-
- ///
- /// Return whether the transaction is internally marked as rollback-only.
- ///
- ///
- /// True of the transaction is marked as rollback-only.
- public override bool RollbackOnly
- {
- get
- {
- return SessionHolder.RollbackOnly ||
- (ConnectionHolder != null && ConnectionHolder.RollbackOnly);
- }
- }
- }
-
- private class SuspendedResourcesHolder
- {
-
- private readonly SessionHolder sessionHolder;
-
- private readonly ConnectionHolder connectionHolder;
-
- public SuspendedResourcesHolder(SessionHolder sessionHolder, ConnectionHolder conHolder)
- {
- this.sessionHolder = sessionHolder;
- this.connectionHolder = conHolder;
- }
-
- public SessionHolder SessionHolder
- {
- get
- {
- return sessionHolder;
- }
-
- }
-
- public ConnectionHolder ConnectionHolder
- {
- get
- {
- return connectionHolder;
- }
-
- }
- }
-
- ///
- /// Invoked by an
- /// after it has injected all of an object's dependencies.
- ///
- ///
- ///
- /// This method allows the object instance to perform the kind of
- /// initialization only possible when all of it's dependencies have
- /// been injected (set), and to throw an appropriate exception in the
- /// event of misconfiguration.
- ///
- ///
- /// Please do consult the class level documentation for the
- /// interface for a
- /// description of exactly when this method is invoked. In
- /// particular, it is worth noting that the
- ///
- /// and
- /// callbacks will have been invoked prior to this method being
- /// called.
- ///
Requires the object factory to be known, to be able to resolve the object
+ /// name to an interceptor instance on session creation. Typically used for
+ /// prototype interceptors, i.e. a new interceptor instance per session.
+ ///
+ ///
Can also be used for shared interceptor instances, but it is recommended
+ /// to set the interceptor reference directly in such a scenario.
+ ///
+ ///
+ public string EntityInterceptorObjectName
+ {
+ set
+ {
+ entityInterceptor = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the ADO.NET exception translator for this transaction manager.
+ ///
+ ///
+ /// Applied to ADO.NET Exceptions (wrapped by Hibernate's ADOException)
+ ///
+ /// The ADO exception translator.
+ public IAdoExceptionTranslator AdoExceptionTranslator
+ {
+ get { return adoExceptionTranslator; }
+ set { adoExceptionTranslator = value; }
+ }
+
+ ///
+ /// Gets the default IAdoException translator, lazily creating it if nece
+ ///
+ /// The default IAdoException translator.
+ public IAdoExceptionTranslator DefaultAdoExceptionTranslator
+ {
+ get
+ {
+ lock (this)
+ {
+ if (defaultExceptionTranslator == null)
+ {
+ if (dbProvider != null)
+ {
+ defaultExceptionTranslator = new ErrorCodeExceptionTranslator(dbProvider);
+ }
+ else
+ {
+ defaultExceptionTranslator = SessionFactoryUtils.NewAdoExceptionTranslator(SessionFactory);
+ }
+ }
+ return defaultExceptionTranslator;
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets the SessionFactory that this instance should manage transactions for.
+ ///
+ /// The session factory.
+ public ISessionFactory SessionFactory
+ {
+ get { return sessionFactory; }
+ set { sessionFactory = value; }
+ }
+
+
+ ///
+ /// Gets the resource factory that this transaction manager operates on,
+ /// For the HibenratePlatformTransactionManager this the SessionFactory
+ ///
+ /// The SessionFactory.
+ public object ResourceFactory
+ {
+ get { return sessionFactory; }
+ }
+
+ ///
+ /// Set whether to autodetect a ADO.NET connection used by the Hibernate SessionFactory,
+ /// if set via LocalSessionFactoryObject's DbProvider. Default is "true".
+ ///
+ ///
+ /// true if [autodetect data source]; otherwise, false.
+ ///
+ ///
+ ///
Can be turned off to deliberately ignore an available IDbProvider,
+ /// to not expose Hibernate transactions as ADO.NET transactions for that IDbProvider.
+ ///
+ ///
+ public bool AutodetectDbProvider
+ {
+ set { autodetectDbProvider = value; }
+ }
+
+ #endregion
+
+ #region Methods
+
+ #endregion
+
+
+ ///
+ /// The object factory just needs to be known for resolving entity interceptor
+ /// It does not need to be set for any other mode of operation.
+ ///
+ ///
+ /// Owning
+ /// (may not be ). The object can immediately
+ /// call methods on the factory.
+ ///
+ public IObjectFactory ObjectFactory
+ {
+ set
+ {
+ objectFactory = value;
+ }
+ }
+
+ ///
+ /// Return the current transaction object.
+ ///
+ /// The current transaction object.
+ ///
+ /// If transaction support is not available.
+ ///
+ ///
+ /// In the case of lookup or system errors.
+ ///
+ protected override object DoGetTransaction()
+ {
+ HibernateTransactionObject txObject = new HibernateTransactionObject();
+ txObject.SavepointAllowed = NestedTransactionsAllowed;
+ if (TransactionSynchronizationManager.HasResource(SessionFactory))
+ {
+ SessionHolder sessionHolder =
+ (SessionHolder)TransactionSynchronizationManager.GetResource(SessionFactory);
+ if (log.IsDebugEnabled)
+ {
+ log.Debug("Found thread-bound Session [" + sessionHolder.Session +
+ "] for Hibernate transaction");
+ }
+ txObject.SetSessionHolder(sessionHolder, false);
+ if (DbProvider != null)
+ {
+ ConnectionHolder conHolder = (ConnectionHolder)
+ TransactionSynchronizationManager.GetResource(DbProvider);
+ txObject.ConnectionHolder = conHolder;
+ }
+ }
+ return txObject;
+ }
+
+ ///
+ /// Check if the given transaction object indicates an existing,
+ /// i.e. already begun, transaction.
+ ///
+ ///
+ /// Transaction object returned by
+ /// .
+ ///
+ /// True if there is an existing transaction.
+ ///
+ /// In the case of system errors.
+ ///
+ protected override bool IsExistingTransaction(object transaction)
+ {
+ return ((HibernateTransactionObject)transaction).HasTransaction();
+ }
+
+ ///
+ /// Begin a new transaction with the given transaction definition.
+ ///
+ ///
+ /// Transaction object returned by
+ /// .
+ ///
+ ///
+ /// instance, describing
+ /// propagation behavior, isolation level, timeout etc.
+ ///
+ ///
+ /// Does not have to care about applying the propagation behavior,
+ /// as this has already been handled by this abstract manager.
+ ///
+ ///
+ /// In the case of creation or system errors.
+ ///
+ protected override void DoBegin(object transaction, ITransactionDefinition definition)
+ {
+ HibernateTransactionObject txObject = (HibernateTransactionObject)transaction;
+
+ if (DbProvider != null && TransactionSynchronizationManager.HasResource(DbProvider)
+ && !txObject.ConnectionHolder.SynchronizedWithTransaction)
+ {
+ throw new IllegalTransactionStateException(
+ "Pre-bound ADO.NET Connection found - HibernateTransactionManager does not support " +
+ "running within AdoTransactionManager if told to manage the DbProvider itself. " +
+ "It is recommended to use a single HibernateTransactionManager for all transactions " +
+ "on a single DbProvider, no matter whether Hibernate or ADO.NET access.");
+ }
+ ISession session = null;
+ try
+ {
+
+
+ if (txObject.SessionHolder == null || txObject.SessionHolder.SynchronizedWithTransaction)
+ //if (txObject.SessionHolder == null || (txObject.SessionHolder.SynchronizedWithTransaction && TransactionSynchronizationManager.SynchronizationActive))
+ //if (txObject.SessionHolder == null || (txObject.SessionHolder.SynchronizedWithTransaction && TransactionSynchronizationManager.ActualTransactionActive))
+ {
+ IInterceptor interceptor = EntityInterceptor;
+ ISession newSession = (interceptor != null ?
+ SessionFactory.OpenSession(interceptor) : SessionFactory.OpenSession());
+
+ if (log.IsDebugEnabled)
+ {
+ log.Debug("Opened new Session [" + newSession + "] for Hibernate transaction");
+ }
+ txObject.SetSessionHolder(new SessionHolder(newSession), true);
+
+ }
+ txObject.SessionHolder.SynchronizedWithTransaction = true;
+ session = txObject.SessionHolder.Session;
+
+ IDbConnection con = session.Connection;
+ //TODO isolation level mgmt
+ //IsolationLevel previousIsolationLevel =
+
+ if (definition.ReadOnly && txObject.NewSessionHolder)
+ {
+ // Just set to NEVER in case of a new Session for this transaction.
+ session.FlushMode = FlushMode.Never;
+ }
+
+ if (!definition.ReadOnly && !txObject.NewSessionHolder)
+ {
+ // We need AUTO or COMMIT for a non-read-only transaction.
+ FlushMode flushMode = session.FlushMode;
+ if (FlushMode.Never == flushMode)
+ {
+ session.FlushMode = FlushMode.Auto;
+ txObject.SessionHolder.PreviousFlushMode = flushMode;
+ }
+ }
+
+ // Add the Hibernate transaction to the session holder.
+ // for now pass in tx options isolation level.
+ ITransaction hibernateTx = session.BeginTransaction(definition.TransactionIsolationLevel);
+ IDbTransaction adoTx = GetIDbTransaction(hibernateTx);
+
+ // Add the Hibernate transaction to the session holder.
+ txObject.SessionHolder.Transaction = hibernateTx;
+
+ // Register transaction timeout.
+ int timeout = DetermineTimeout(definition);
+ if (timeout != DefaultTransactionDefinition.TIMEOUT_DEFAULT)
+ {
+ txObject.SessionHolder.TimeoutInSeconds = timeout;
+ }
+
+ // Register the Hibernate Session's ADO.NET Connection/TX pair for the DbProvider, if set.
+ if (DbProvider != null)
+ {
+ //investigate passing null for tx.
+ ConnectionHolder conHolder = new ConnectionHolder(con, adoTx);
+ if (timeout != DefaultTransactionDefinition.TIMEOUT_DEFAULT)
+ {
+ conHolder.TimeoutInMillis = definition.TransactionTimeout;
+ }
+ if (log.IsDebugEnabled)
+ {
+ log.Debug("Exposing Hibernate transaction as ADO transaction [" + con + "]");
+ }
+ TransactionSynchronizationManager.BindResource(DbProvider, conHolder);
+ txObject.ConnectionHolder = conHolder;
+ }
+
+ // Bind the session holder to the thread.
+ if (txObject.NewSessionHolder)
+ {
+ TransactionSynchronizationManager.BindResource(SessionFactory, txObject.SessionHolder);
+ }
+
+ }
+ catch (Exception ex)
+ {
+ SessionFactoryUtils.CloseSession(session);
+ throw new CannotCreateTransactionException("Could not open Hibernate Session for transaction", ex);
+ }
+
+
+ }
+
+
+
+
+ ///
+ /// Suspend the resources of the current transaction.
+ ///
+ ///
+ /// Transaction object returned by
+ /// .
+ ///
+ ///
+ /// An object that holds suspended resources (will be kept unexamined for passing it into
+ /// .)
+ ///
+ ///
+ /// Transaction synchronization will already have been suspended.
+ ///
+ ///
+ /// If suspending is not supported by the transaction manager implementation.
+ ///
+ ///
+ /// in case of system errors.
+ ///
+ protected override object DoSuspend(object transaction)
+ {
+ HibernateTransactionObject txObject = (HibernateTransactionObject)transaction;
+ txObject.SetSessionHolder(null, false);
+ SessionHolder sessionHolder =
+ (SessionHolder)TransactionSynchronizationManager.UnbindResource(SessionFactory);
+ ConnectionHolder connectionHolder = null;
+ if (DbProvider != null)
+ {
+ connectionHolder = (ConnectionHolder)TransactionSynchronizationManager.UnbindResource(DbProvider);
+ }
+ return new SuspendedResourcesHolder(sessionHolder, connectionHolder);
+
+ }
+
+ ///
+ /// Resume the resources of the current transaction.
+ ///
+ ///
+ /// Transaction object returned by
+ /// .
+ ///
+ ///
+ /// The object that holds suspended resources as returned by
+ /// .
+ ///
+ ///
+ /// Transaction synchronization will be resumed afterwards.
+ ///
+ ///
+ /// If suspending is not supported by the transaction manager implementation.
+ ///
+ ///
+ /// In the case of system errors.
+ ///
+ protected override void DoResume(object transaction, object suspendedResources)
+ {
+ SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder)suspendedResources;
+ if (TransactionSynchronizationManager.HasResource(SessionFactory))
+ {
+ // From non-transactional code running in active transaction synchronization
+ // -> can be safely removed, will be closed on transaction completion.
+ TransactionSynchronizationManager.UnbindResource(SessionFactory);
+ }
+ TransactionSynchronizationManager.BindResource(SessionFactory, resourcesHolder.SessionHolder);
+ if (DbProvider != null)
+ {
+ TransactionSynchronizationManager.BindResource(DbProvider, resourcesHolder.ConnectionHolder);
+ }
+ }
+
+ ///
+ /// Perform an actual commit on the given transaction.
+ ///
+ /// The status representation of the transaction.
+ ///
+ ///
+ /// An implementation does not need to check the rollback-only flag.
+ ///
+ ///
+ ///
+ /// In the case of system errors.
+ ///
+ protected override void DoCommit(DefaultTransactionStatus status)
+ {
+ HibernateTransactionObject txObject = (HibernateTransactionObject)status.Transaction;
+ if (status.Debug)
+ {
+ log.Debug("Committing Hibernate transaction on Session [" +
+ txObject.SessionHolder.Session + "]");
+ }
+ try
+ {
+ txObject.SessionHolder.Transaction.Commit();
+ }
+ // Note, unfortunate collision of namespaces/classname for NHibernate.TransactionException
+ // and Spring.Data.NHibernate requires this wierd construct.
+ catch (Exception ex)
+ {
+ Type nhibTxExceptiontype = TypeResolutionUtils.ResolveType("NHibernate.TransactionException, NHibernate");
+ if (ex.GetType().Equals(nhibTxExceptiontype))
+ {
+ // assumably from commit call to the underlying ADO.NET connection
+ throw new TransactionSystemException("Could not commit Hibernate transaction", ex);
+ }
+ HibernateException hibEx = ex as HibernateException;
+ if (hibEx != null)
+ {
+ // assumably failed to flush changes to database
+ throw ConvertHibernateAccessException(hibEx);
+ }
+ throw;
+ }
+ }
+
+ ///
+ /// Perform an actual rollback on the given transaction.
+ ///
+ /// The status representation of the transaction.
+ ///
+ /// An implementation does not need to check the new transaction flag.
+ ///
+ ///
+ /// In the case of system errors.
+ ///
+ protected override void DoRollback(DefaultTransactionStatus status)
+ {
+ HibernateTransactionObject txObject = (HibernateTransactionObject)status.Transaction;
+
+ if (status.Debug)
+ {
+ log.Debug("Rolling back Hibernate transaction on Session [" +
+ txObject.SessionHolder.Session + "]");
+ }
+ try
+ {
+ IDbTransaction adoTx = GetIDbTransaction(txObject.SessionHolder.Transaction);
+
+ if (adoTx != null && adoTx.Connection != null)
+ {
+ txObject.SessionHolder.Transaction.Rollback();
+ }
+ }
+ catch (HibernateTransactionException ex)
+ {
+ throw new TransactionSystemException("Could not roll back Hibernate transaction", ex);
+ }
+ catch (HibernateException ex)
+ {
+ // Shouldn't really happen, as a rollback doesn't cause a flush.
+ throw ConvertHibernateAccessException(ex);
+ }
+ finally
+ {
+ if (!txObject.NewSessionHolder)
+ {
+ // Clear all pending inserts/updates/deletes in the Session.
+ // Necessary for pre-bound Sessions, to avoid inconsistent state.
+ txObject.SessionHolder.Session.Clear();
+ }
+ }
+
+
+
+ }
+
+
+ ///
+ /// Set the given transaction rollback-only. Only called on rollback
+ /// if the current transaction takes part in an existing one.
+ ///
+ /// The status representation of the transaction.
+ ///
+ /// In the case of system errors.
+ ///
+ protected override void DoSetRollbackOnly(DefaultTransactionStatus status)
+ {
+ HibernateTransactionObject txObject = (HibernateTransactionObject)status.Transaction;
+ if (status.Debug)
+ {
+ log.Debug("Setting Hibernate transaction on Session [" +
+ txObject.SessionHolder.Session + "] rollback-only");
+ }
+ txObject.SetRollbackOnly();
+ }
+
+
+ ///
+ /// Gets the ADO.NET IDbTransaction object from the NHibernate ITransaction object.
+ ///
+ /// The hibernate transaction.
+ /// The ADO.NET transaction. Null if could not get the transaction. Warning
+ /// messages will be logged in that case.
+ protected virtual IDbTransaction GetIDbTransaction(ITransaction hibernateTx)
+ {
+ AdoTransaction hibernateAdoTx = hibernateTx as AdoTransaction;
+
+ IDbTransaction adoTransaction = null;
+ if (hibernateAdoTx != null)
+ {
+ try
+ {
+ FieldInfo fi = hibernateAdoTx.GetType().GetField("trans", BindingFlags.Instance | BindingFlags.NonPublic);
+ adoTransaction = fi.GetValue(hibernateAdoTx) as IDbTransaction;
+ }
+ catch (Exception e)
+ {
+ log.Warn("Could not extract IDbTransaction from Hibernate AdoTransaction using field name trans.", e);
+ }
+ }
+ else
+ {
+ log.Warn("Hibernate ITransaction not of expected type AdoTransaction. Could not extract IDbTransaction from Hibernate AdoTransaction.");
+ }
+ return adoTransaction;
+ }
+
+ ///
+ /// Convert the given HibernateException to an appropriate exception from
+ /// the Spring.Dao hierarchy. Can be overridden in subclasses.
+ ///
+ /// The HibernateException that occured.
+ /// The corresponding DataAccessException instance
+ protected virtual DataAccessException ConvertHibernateAccessException(HibernateException ex)
+ {
+ if (AdoExceptionTranslator != null && ex is ADOException)
+ {
+ return ConvertAdoAccessException((ADOException)ex, AdoExceptionTranslator);
+ }
+ else if (ex is ADOException)
+ {
+ return ConvertAdoAccessException((ADOException)ex, DefaultAdoExceptionTranslator);
+ }
+ return SessionFactoryUtils.ConvertHibernateAccessException(ex);
+ }
+
+ ///
+ /// Convert the given ADOException to an appropriate exception from the
+ /// the Spring.Dao hierarchy. Can be overridden in subclasses.
+ ///
+ /// The ADOException that occured, wrapping the underlying
+ /// ADO.NET thrown exception.
+ /// The translator to convert hibernate ADOExceptions.
+ ///
+ /// The corresponding DataAccessException instance
+ ///
+ protected virtual DataAccessException ConvertAdoAccessException(ADOException ex, IAdoExceptionTranslator translator)
+ {
+ return translator.Translate("Hibernate flusing: " + ex.Message, null, ex.InnerException);
+ }
+
+ ///
+ /// Cleanup resources after transaction completion.
+ ///
+ /// Transaction object returned by
+ /// .
+ ///
+ ///
+ /// This implemenation unbinds the SessionFactory and
+ /// DbProvider from thread local storage and closes the
+ /// ISession.
+ ///
+ ///
+ /// Called after
+ /// and
+ ///
+ /// execution on any outcome.
+ ///
+ ///
+ /// Should not throw any exceptions but just issue warnings on errors.
+ ///
+ ///
+ protected override void DoCleanupAfterCompletion(object transaction)
+ {
+ HibernateTransactionObject txObject = (HibernateTransactionObject)transaction;
+
+ // Remove the session holder from the thread.
+ if (txObject.NewSessionHolder)
+ {
+ TransactionSynchronizationManager.UnbindResource(SessionFactory);
+ }
+ // Remove the ADO.NET connection holder from the thread, if exposed.
+ if (DbProvider != null)
+ {
+ TransactionSynchronizationManager.UnbindResource(DbProvider);
+ }
+
+ ISession session = txObject.SessionHolder.Session;
+ if (txObject.NewSessionHolder)
+ {
+ if (log.IsDebugEnabled)
+ {
+ log.Debug("Closing Hibernate Session [" + session + "] after transaction");
+ }
+ SessionFactoryUtils.CloseSessionOrRegisterDeferredClose(session, SessionFactory);
+ }
+ else
+ {
+ if (log.IsDebugEnabled)
+ {
+ log.Debug("Not closing pre-bound Hibernate Session [" + session + "] after transaction");
+ }
+ if (txObject.SessionHolder.AssignedPreviousFlushMode)
+ {
+ session.FlushMode = txObject.SessionHolder.PreviousFlushMode;
+ }
+ }
+ txObject.SessionHolder.Clear();
+
+
+ }
+
+ private class HibernateTransactionObject : AdoTransactionObjectSupport
+ {
+
+ private SessionHolder sessionHolder;
+
+ private bool newSessionHolder;
+
+
+ public void SetSessionHolder(SessionHolder sessionHolder, bool newSessionHolder)
+ {
+ this.sessionHolder = sessionHolder;
+ this.newSessionHolder = newSessionHolder;
+ }
+
+
+ public SessionHolder SessionHolder
+ {
+ get
+ {
+ return sessionHolder;
+ }
+ }
+
+ public bool NewSessionHolder
+ {
+ get
+ {
+ return newSessionHolder;
+ }
+ }
+
+ public bool HasTransaction()
+ {
+ return (this.sessionHolder != null && this.sessionHolder.Transaction != null);
+ }
+
+ public void SetRollbackOnly()
+ {
+ SessionHolder.RollbackOnly = true;
+ if (ConnectionHolder != null)
+ {
+ ConnectionHolder.RollbackOnly = true;
+ }
+ }
+
+ ///
+ /// Return whether the transaction is internally marked as rollback-only.
+ ///
+ ///
+ /// True of the transaction is marked as rollback-only.
+ public override bool RollbackOnly
+ {
+ get
+ {
+ return SessionHolder.RollbackOnly ||
+ (ConnectionHolder != null && ConnectionHolder.RollbackOnly);
+ }
+ }
+ }
+
+ private class SuspendedResourcesHolder
+ {
+
+ private readonly SessionHolder sessionHolder;
+
+ private readonly ConnectionHolder connectionHolder;
+
+ public SuspendedResourcesHolder(SessionHolder sessionHolder, ConnectionHolder conHolder)
+ {
+ this.sessionHolder = sessionHolder;
+ this.connectionHolder = conHolder;
+ }
+
+ public SessionHolder SessionHolder
+ {
+ get
+ {
+ return sessionHolder;
+ }
+
+ }
+
+ public ConnectionHolder ConnectionHolder
+ {
+ get
+ {
+ return connectionHolder;
+ }
+
+ }
+ }
+
+ ///
+ /// Invoked by an
+ /// after it has injected all of an object's dependencies.
+ ///
+ ///
+ ///
+ /// This method allows the object instance to perform the kind of
+ /// initialization only possible when all of it's dependencies have
+ /// been injected (set), and to throw an appropriate exception in the
+ /// event of misconfiguration.
+ ///
+ ///
+ /// Please do consult the class level documentation for the
+ /// interface for a
+ /// description of exactly when this method is invoked. In
+ /// particular, it is worth noting that the
+ ///
+ /// and
+ /// callbacks will have been invoked prior to this method being
+ /// called.
+ ///
+ ///
+ ///
+ /// In the event of misconfiguration (such as the failure to set a
+ /// required property) or if initialization fails.
+ ///
+ public void AfterPropertiesSet()
+ {
+ if (SessionFactory == null)
+ {
+ throw new ArgumentException("sessionFactory is required");
+ }
+ if (this.entityInterceptor is string && this.objectFactory == null)
+ {
+ throw new ArgumentException("objectFactory is required for entityInterceptorBeanName");
+ }
+
+ // Try to derive a DbProvider given the SessionFactory.
+ if (this.autodetectDbProvider && DbProvider == null)
+ {
+ IDbProvider sfDbProvider = SessionFactoryUtils.GetDbProvider(SessionFactory);
+ if (sfDbProvider != null)
+ {
+ // Use the SessionFactory's DataSource for exposing transactions to ADO.NET code.
+ if (log.IsInfoEnabled)
+ {
+ log.Info("Derived DbProvider [" + sfDbProvider.DbMetadata.ProductName +
+ "] of Hibernate SessionFactory for HibernateTransactionManager");
+ }
+ DbProvider = sfDbProvider;
+ }
+ else
+ {
+ log.Info("Could not auto detect DbProvider from SessionFactory configuration");
+ }
+
+ }
+ }
+ }
+}
diff --git a/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/SessionFactoryUtils.cs b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/SessionFactoryUtils.cs
index 53a009c4..723c246a 100644
--- a/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/SessionFactoryUtils.cs
+++ b/src/Spring/Spring.Data.NHibernate12/Data/NHibernate/SessionFactoryUtils.cs
@@ -260,7 +260,7 @@ namespace Spring.Data.NHibernate
// Spring transaction management is active ->
// register pre-bound Session with it for transactional flushing.
session = sessionHolder.ValidatedSession;
- if (!sessionHolder.SynchronizedWithTransaction)
+ if (session != null && !sessionHolder.SynchronizedWithTransaction)
{
log.Debug("Registering Spring transaction synchronization for existing Hibernate Session");
TransactionSynchronizationManager.RegisterSynchronization(
diff --git a/src/Spring/Spring.Data.NHibernate21/Data/NHibernate/HibernateTxScopeTransactionManager.cs b/src/Spring/Spring.Data.NHibernate21/Data/NHibernate/HibernateTxScopeTransactionManager.cs
index a5dd49e8..5e1c5c05 100644
--- a/src/Spring/Spring.Data.NHibernate21/Data/NHibernate/HibernateTxScopeTransactionManager.cs
+++ b/src/Spring/Spring.Data.NHibernate21/Data/NHibernate/HibernateTxScopeTransactionManager.cs
@@ -716,6 +716,20 @@ namespace Spring.Data.NHibernate
protected override void DoRollback(DefaultTransactionStatus status)
{
HibernateTransactionObject txObject = (HibernateTransactionObject)status.Transaction;
+
+ if (!txObject.NewSessionHolder)
+ {
+ // Clear all pending inserts/updates/deletes in the Session.
+ // Necessary for pre-bound Sessions, to avoid inconsistent state.
+ txObject.SessionHolder.Session.Clear();
+ }
+
+ DoTxScopeRollback(status);
+ return;
+
+
+/* HibernateTransactionObject txObject = (HibernateTransactionObject)status.Transaction;
+
if (status.Debug)
{
log.Debug("Rolling back Hibernate transaction on Session [" +
@@ -723,6 +737,11 @@ namespace Spring.Data.NHibernate
}
try
{
+ if (txObject.SessionHolder.Session != null && txObject.SessionHolder.Transaction != null && !txObject.SessionHolder.Transaction.IsActive)
+ {
+ return;
+ }
+
IDbTransaction adoTx = GetIDbTransaction(txObject.SessionHolder.Transaction);
if (adoTx != null && adoTx.Connection != null)
@@ -737,7 +756,7 @@ namespace Spring.Data.NHibernate
txObject.SessionHolder.Session + "] was null");
}
}
-
+
}
catch (HibernateTransactionException ex)
{
@@ -757,7 +776,7 @@ namespace Spring.Data.NHibernate
txObject.SessionHolder.Session.Clear();
}
DoTxScopeRollback(status);
- }
+ }*/
}
///
diff --git a/src/Spring/Spring.Data/Data/Core/TxScopeTransactionManager.cs b/src/Spring/Spring.Data/Data/Core/TxScopeTransactionManager.cs
index 2664b2e3..0d8af06d 100644
--- a/src/Spring/Spring.Data/Data/Core/TxScopeTransactionManager.cs
+++ b/src/Spring/Spring.Data/Data/Core/TxScopeTransactionManager.cs
@@ -1,288 +1,288 @@
-#if NET_2_0
-
-#region License
-
-/*
- * Copyright 2007 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.Transactions;
-using Spring.Data.Support;
-using Spring.Objects.Factory;
-using Spring.Transaction;
-using Spring.Transaction.Support;
-
-namespace Spring.Data.Core
-{
- ///
- /// TransactionManager that uses TransactionScope provided by System.Transactions.
- ///
- /// Mark Pollack (.NET)
- public class TxScopeTransactionManager : AbstractPlatformTransactionManager, IInitializingObject
- {
- private readonly ITransactionScopeAdapter txAdapter;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public TxScopeTransactionManager()
- {
- // noop
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// This is indented only for unit testing purposes and should not be
- /// called by production application code.
- /// The tx adapter.
- public TxScopeTransactionManager(ITransactionScopeAdapter txAdapter)
- {
- this.txAdapter = txAdapter;
- }
-
- #region IInitializingObject Members
-
- ///
- /// No-op initialization
- ///
- public void AfterPropertiesSet()
- {
- // placeholder for more advanced configurations.
- }
-
- #endregion
-
- protected override object DoGetTransaction()
- {
- PromotableTxScopeTransactionObject txObject = new PromotableTxScopeTransactionObject();
-
- if (txAdapter != null)
- {
- txObject.TxScopeAdapter = txAdapter;
- }
- return txObject;
- }
-
- protected override bool IsExistingTransaction(object transaction)
- {
- PromotableTxScopeTransactionObject txObject =
- (PromotableTxScopeTransactionObject)transaction;
- return txObject.TxScopeAdapter.IsExistingTransaction;
- }
-
- protected override void DoBegin(object transaction, Spring.Transaction.ITransactionDefinition definition)
- {
- PromotableTxScopeTransactionObject txObject =
- (PromotableTxScopeTransactionObject)transaction;
- try
- {
- DoTxScopeBegin(txObject, definition);
- }
- catch (Exception e)
- {
- throw new CannotCreateTransactionException("Transaction Scope failure on begin", e);
- }
- }
-
- protected override object DoSuspend(object transaction)
- {
- // Passing the current TxScopeAdapter as the 'suspended resource', even though it is not used just to avoid passing null
- // TxScopeTransactionManager is not binding any resources to the local thread, instead delegating to
- // System.Transactions to handle thread local resources.
- PromotableTxScopeTransactionObject txMgrStateObject = (PromotableTxScopeTransactionObject) transaction;
- return txMgrStateObject.TxScopeAdapter;
- }
-
- protected override void DoResume(object transaction, object suspendedResources)
- {
- }
-
- protected override void DoCommit(DefaultTransactionStatus status)
- {
- PromotableTxScopeTransactionObject txObject =
- (PromotableTxScopeTransactionObject)status.Transaction;
- try
- {
- txObject.TxScopeAdapter.Complete();
- txObject.TxScopeAdapter.Dispose();
- }
- catch (TransactionAbortedException ex)
- {
- throw new UnexpectedRollbackException("Transaction unexpectedly rolled back (maybe due to a timeout)", ex);
- }
- catch (TransactionInDoubtException ex)
- {
- throw new HeuristicCompletionException(TransactionOutcomeState.Unknown, ex);
- }
- catch (Exception ex)
- {
- throw new TransactionSystemException("Failure on Transaction Scope Commit", ex);
- }
- }
-
- protected override void DoRollback(DefaultTransactionStatus status)
- {
- PromotableTxScopeTransactionObject txObject =
- (PromotableTxScopeTransactionObject)status.Transaction;
-
- try
- {
-
- txObject.TxScopeAdapter.Dispose();
- }
- catch (Exception e)
- {
- throw new Spring.Transaction.TransactionSystemException("Failure on Transaction Scope rollback.", e);
- }
- }
-
-
- protected override void DoSetRollbackOnly(DefaultTransactionStatus status)
- {
- if (status.Debug)
- {
- log.Debug("Setting transaction rollback-only");
- }
- try
- {
- System.Transactions.Transaction.Current.Rollback();
- } catch (Exception ex)
- {
- throw new TransactionSystemException("Failure on System.Transactions.Transaction.Current.Rollback", ex);
- }
- }
-
- protected override bool ShouldCommitOnGlobalRollbackOnly
- {
- get { return true; }
- }
-
- private void DoTxScopeBegin(PromotableTxScopeTransactionObject txObject,
- Spring.Transaction.ITransactionDefinition definition)
- {
-
- TransactionScopeOption txScopeOption = CreateTransactionScopeOptions(definition);
- TransactionOptions txOptions = CreateTransactionOptions(definition);
- txObject.TxScopeAdapter.CreateTransactionScope(txScopeOption, txOptions, definition.EnterpriseServicesInteropOption);
-
- }
-
- private static TransactionOptions CreateTransactionOptions(ITransactionDefinition definition)
- {
- TransactionOptions txOptions = new TransactionOptions();
- switch (definition.TransactionIsolationLevel )
- {
- case System.Data.IsolationLevel.Chaos:
- txOptions.IsolationLevel = IsolationLevel.Chaos;
- break;
- case System.Data.IsolationLevel.ReadCommitted:
- txOptions.IsolationLevel = IsolationLevel.ReadCommitted;
- break;
- case System.Data.IsolationLevel.ReadUncommitted:
- txOptions.IsolationLevel = IsolationLevel.ReadUncommitted;
- break;
- case System.Data.IsolationLevel.RepeatableRead:
- txOptions.IsolationLevel = IsolationLevel.RepeatableRead;
- break;
- case System.Data.IsolationLevel.Serializable:
- txOptions.IsolationLevel = IsolationLevel.Serializable;
- break;
- case System.Data.IsolationLevel.Snapshot:
- txOptions.IsolationLevel = IsolationLevel.Snapshot;
- break;
- case System.Data.IsolationLevel.Unspecified:
- txOptions.IsolationLevel = IsolationLevel.Unspecified;
- break;
- }
-
- if (definition.TransactionTimeout != DefaultTransactionDefinition.TIMEOUT_DEFAULT)
- {
- txOptions.Timeout = new TimeSpan(0, 0, definition.TransactionTimeout);
- }
- return txOptions;
- }
-
- private static TransactionScopeOption CreateTransactionScopeOptions(ITransactionDefinition definition)
- {
- TransactionScopeOption txScopeOption;
- if (definition.PropagationBehavior == TransactionPropagation.Required)
- {
- txScopeOption = TransactionScopeOption.Required;
- }
- else if (definition.PropagationBehavior == TransactionPropagation.RequiresNew)
- {
- txScopeOption = TransactionScopeOption.RequiresNew;
- }
- else if (definition.PropagationBehavior == TransactionPropagation.NotSupported)
+#if NET_2_0
+
+#region License
+
+/*
+ * Copyright 2007 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.Transactions;
+using Spring.Data.Support;
+using Spring.Objects.Factory;
+using Spring.Transaction;
+using Spring.Transaction.Support;
+
+namespace Spring.Data.Core
+{
+ ///
+ /// TransactionManager that uses TransactionScope provided by System.Transactions.
+ ///
+ /// Mark Pollack (.NET)
+ public class TxScopeTransactionManager : AbstractPlatformTransactionManager, IInitializingObject
+ {
+ private readonly ITransactionScopeAdapter txAdapter;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public TxScopeTransactionManager()
+ {
+ // noop
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// This is indented only for unit testing purposes and should not be
+ /// called by production application code.
+ /// The tx adapter.
+ public TxScopeTransactionManager(ITransactionScopeAdapter txAdapter)
+ {
+ this.txAdapter = txAdapter;
+ }
+
+ #region IInitializingObject Members
+
+ ///
+ /// No-op initialization
+ ///
+ public void AfterPropertiesSet()
+ {
+ // placeholder for more advanced configurations.
+ }
+
+ #endregion
+
+ protected override object DoGetTransaction()
+ {
+ PromotableTxScopeTransactionObject txObject = new PromotableTxScopeTransactionObject();
+
+ if (txAdapter != null)
+ {
+ txObject.TxScopeAdapter = txAdapter;
+ }
+ return txObject;
+ }
+
+ protected override bool IsExistingTransaction(object transaction)
+ {
+ PromotableTxScopeTransactionObject txObject =
+ (PromotableTxScopeTransactionObject)transaction;
+ return txObject.TxScopeAdapter.IsExistingTransaction;
+ }
+
+ protected override void DoBegin(object transaction, Spring.Transaction.ITransactionDefinition definition)
+ {
+ PromotableTxScopeTransactionObject txObject =
+ (PromotableTxScopeTransactionObject)transaction;
+ try
+ {
+ DoTxScopeBegin(txObject, definition);
+ }
+ catch (Exception e)
+ {
+ throw new CannotCreateTransactionException("Transaction Scope failure on begin", e);
+ }
+ }
+
+ protected override object DoSuspend(object transaction)
+ {
+ // Passing the current TxScopeAdapter as the 'suspended resource', even though it is not used just to avoid passing null
+ // TxScopeTransactionManager is not binding any resources to the local thread, instead delegating to
+ // System.Transactions to handle thread local resources.
+ PromotableTxScopeTransactionObject txMgrStateObject = (PromotableTxScopeTransactionObject) transaction;
+ return txMgrStateObject.TxScopeAdapter;
+ }
+
+ protected override void DoResume(object transaction, object suspendedResources)
+ {
+ }
+
+ protected override void DoCommit(DefaultTransactionStatus status)
+ {
+ PromotableTxScopeTransactionObject txObject =
+ (PromotableTxScopeTransactionObject)status.Transaction;
+ try
+ {
+ txObject.TxScopeAdapter.Complete();
+ txObject.TxScopeAdapter.Dispose();
+ }
+ catch (TransactionAbortedException ex)
+ {
+ throw new UnexpectedRollbackException("Transaction unexpectedly rolled back (maybe due to a timeout)", ex);
+ }
+ catch (TransactionInDoubtException ex)
+ {
+ throw new HeuristicCompletionException(TransactionOutcomeState.Unknown, ex);
+ }
+ catch (Exception ex)
+ {
+ throw new TransactionSystemException("Failure on Transaction Scope Commit", ex);
+ }
+ }
+
+ protected override void DoRollback(DefaultTransactionStatus status)
+ {
+ PromotableTxScopeTransactionObject txObject =
+ (PromotableTxScopeTransactionObject)status.Transaction;
+
+ try
+ {
+
+ txObject.TxScopeAdapter.Dispose();
+ }
+ catch (Exception e)
+ {
+ throw new Spring.Transaction.TransactionSystemException("Failure on Transaction Scope rollback.", e);
+ }
+ }
+
+
+ protected override void DoSetRollbackOnly(DefaultTransactionStatus status)
+ {
+ if (status.Debug)
+ {
+ log.Debug("Setting transaction rollback-only");
+ }
+ try
+ {
+ System.Transactions.Transaction.Current.Rollback();
+ } catch (Exception ex)
+ {
+ throw new TransactionSystemException("Failure on System.Transactions.Transaction.Current.Rollback", ex);
+ }
+ }
+
+ protected override bool ShouldCommitOnGlobalRollbackOnly
+ {
+ get { return true; }
+ }
+
+ private void DoTxScopeBegin(PromotableTxScopeTransactionObject txObject,
+ Spring.Transaction.ITransactionDefinition definition)
+ {
+
+ TransactionScopeOption txScopeOption = CreateTransactionScopeOptions(definition);
+ TransactionOptions txOptions = CreateTransactionOptions(definition);
+ txObject.TxScopeAdapter.CreateTransactionScope(txScopeOption, txOptions, definition.EnterpriseServicesInteropOption);
+
+ }
+
+ private static TransactionOptions CreateTransactionOptions(ITransactionDefinition definition)
+ {
+ TransactionOptions txOptions = new TransactionOptions();
+ switch (definition.TransactionIsolationLevel )
+ {
+ case System.Data.IsolationLevel.Chaos:
+ txOptions.IsolationLevel = IsolationLevel.Chaos;
+ break;
+ case System.Data.IsolationLevel.ReadCommitted:
+ txOptions.IsolationLevel = IsolationLevel.ReadCommitted;
+ break;
+ case System.Data.IsolationLevel.ReadUncommitted:
+ txOptions.IsolationLevel = IsolationLevel.ReadUncommitted;
+ break;
+ case System.Data.IsolationLevel.RepeatableRead:
+ txOptions.IsolationLevel = IsolationLevel.RepeatableRead;
+ break;
+ case System.Data.IsolationLevel.Serializable:
+ txOptions.IsolationLevel = IsolationLevel.Serializable;
+ break;
+ case System.Data.IsolationLevel.Snapshot:
+ txOptions.IsolationLevel = IsolationLevel.Snapshot;
+ break;
+ case System.Data.IsolationLevel.Unspecified:
+ txOptions.IsolationLevel = IsolationLevel.Unspecified;
+ break;
+ }
+
+ if (definition.TransactionTimeout != DefaultTransactionDefinition.TIMEOUT_DEFAULT)
+ {
+ txOptions.Timeout = new TimeSpan(0, 0, definition.TransactionTimeout);
+ }
+ return txOptions;
+ }
+
+ private static TransactionScopeOption CreateTransactionScopeOptions(ITransactionDefinition definition)
+ {
+ TransactionScopeOption txScopeOption;
+ if (definition.PropagationBehavior == TransactionPropagation.Required)
+ {
+ txScopeOption = TransactionScopeOption.Required;
+ }
+ else if (definition.PropagationBehavior == TransactionPropagation.RequiresNew)
+ {
+ txScopeOption = TransactionScopeOption.RequiresNew;
+ }
+ else if (definition.PropagationBehavior == TransactionPropagation.NotSupported)
{
txScopeOption = TransactionScopeOption.Suppress;
- }
- else
- {
- throw new Spring.Transaction.TransactionSystemException("Transaction Propagation Behavior" +
- definition.PropagationBehavior +
- " not supported by TransactionScope. Use Required or RequiredNew");
- }
- return txScopeOption;
- }
-
- ///
- /// The transaction resource object that encapsulates the state and functionality
- /// contained in TransactionScope and Transaction.Current via the ITransactionScopeAdapter
- /// property.
- ///
- public class PromotableTxScopeTransactionObject : ISmartTransactionObject
- {
- private ITransactionScopeAdapter txScopeAdapter;
-
- ///
- /// Initializes a new instance of the class.
- /// Will create an instance of .
- ///
- public PromotableTxScopeTransactionObject()
- {
- txScopeAdapter = new DefaultTransactionScopeAdapter();
- }
-
- ///
- /// Gets or sets the transaction scope adapter.
- ///
- /// The transaction scope adapter.
- public ITransactionScopeAdapter TxScopeAdapter
- {
- get { return txScopeAdapter; }
- set { txScopeAdapter = value; }
- }
-
-
- ///
- /// Return whether the transaction is internally marked as rollback-only.
- ///
- ///
- /// True of the transaction is marked as rollback-only.
- public bool RollbackOnly
- {
- get {
- return txScopeAdapter.RollbackOnly;
- }
- }
- }
- }
-}
+ }
+ else
+ {
+ throw new Spring.Transaction.TransactionSystemException("Transaction Propagation Behavior" +
+ definition.PropagationBehavior +
+ " not supported by TransactionScope. Use Required or RequiredNew");
+ }
+ return txScopeOption;
+ }
+
+ ///
+ /// The transaction resource object that encapsulates the state and functionality
+ /// contained in TransactionScope and Transaction.Current via the ITransactionScopeAdapter
+ /// property.
+ ///
+ public class PromotableTxScopeTransactionObject : ISmartTransactionObject
+ {
+ private ITransactionScopeAdapter txScopeAdapter;
+
+ ///
+ /// Initializes a new instance of the class.
+ /// Will create an instance of .
+ ///
+ public PromotableTxScopeTransactionObject()
+ {
+ txScopeAdapter = new DefaultTransactionScopeAdapter();
+ }
+
+ ///
+ /// Gets or sets the transaction scope adapter.
+ ///
+ /// The transaction scope adapter.
+ public ITransactionScopeAdapter TxScopeAdapter
+ {
+ get { return txScopeAdapter; }
+ set { txScopeAdapter = value; }
+ }
+
+
+ ///
+ /// Return whether the transaction is internally marked as rollback-only.
+ ///
+ ///
+ /// True of the transaction is marked as rollback-only.
+ public bool RollbackOnly
+ {
+ get {
+ return txScopeAdapter.RollbackOnly;
+ }
+ }
+ }
+ }
+}
#endif
\ No newline at end of file
diff --git a/src/Spring/Spring.Data/Transaction/Support/AbstractPlatformTransactionManager.cs b/src/Spring/Spring.Data/Transaction/Support/AbstractPlatformTransactionManager.cs
index b10021e4..e132100c 100644
--- a/src/Spring/Spring.Data/Transaction/Support/AbstractPlatformTransactionManager.cs
+++ b/src/Spring/Spring.Data/Transaction/Support/AbstractPlatformTransactionManager.cs
@@ -1,422 +1,423 @@
-#region License
-
-/*
- * Copyright 2002-2010 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
-{
- ///
- /// Abstract base class that allows for easy implementation of concrete platform transaction managers.
- ///
- ///
- ///
Provides the following workflow handling:
- ///
- ///
Determines if there is an existing transaction
- ///
Applies the appropriate propagation behavior
- ///
Suspends and resumes transactions if necessary
- ///
Checks the rollback-only flag on commit
- ///
Applies the appropriate modification on rollback (actual rollback or setting rollback-only)
- ///
Triggers registered synchronization callbacks (if transaction synchronization is active)
- ///
- ///
- ///
- /// 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.
- ///
- ///
- /// 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 if they need
- /// to restore any transient state.
- ///
- ///
- /// Juergen Hoeller
- /// Mark Pollack (.NET)
- /// Griffin Caprio (.NET)
- [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()
+#region License
+
+/*
+ * Copyright 2002-2010 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
+{
+ ///
+ /// Abstract base class that allows for easy implementation of concrete platform transaction managers.
+ ///
+ ///
+ ///
Provides the following workflow handling:
+ ///
+ ///
Determines if there is an existing transaction
+ ///
Applies the appropriate propagation behavior
+ ///
Suspends and resumes transactions if necessary
+ ///
Checks the rollback-only flag on commit
+ ///
Applies the appropriate modification on rollback (actual rollback or setting rollback-only)
+ ///
Triggers registered synchronization callbacks (if transaction synchronization is active)
+ ///
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ /// 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 if they need
+ /// to restore any transient state.
+ ///
- /// 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.
- ///
- ///
- ///
- public TransactionSynchronizationState TransactionSynchronization
- {
- set { _transactionSyncState = value; }
- get { return _transactionSyncState; }
- }
-
- ///
- /// Sets and gets whether nested transactions are allowed. Default is false.
- ///
- ///
- ///
- /// Typically initialized with an appropriate default by the
- /// concrete transaction manager subclass.
- ///
- ///
- public bool NestedTransactionsAllowed
- {
- get { return _nestedTransactionsAllowed; }
- set { _nestedTransactionsAllowed = value; }
- }
-
- ///
- /// Sets and gets a flag that determines whether or not the
- ///
- /// method must be invoked if a call to the
- ///
- /// method fails. Default is false.
- ///
- ///
- /// Typically not necessary and thus to be avoided as it can override the
- /// commit exception with a subsequent rollback exception.
- ///
- public bool RollbackOnCommitFailure
- {
- get { return _rollbackOnCommitFailure; }
- set { _rollbackOnCommitFailure = value; }
- }
-
-
- ///
- /// Gets or sets a value indicating whether to fail early in case of the transaction being
- /// globally marked as rollback-only.
- ///
- ///
- /// 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.
- ///
- ///
- /// true if fail early on global rollback; otherwise, false.
- ///
- public bool FailEarlyOnGlobalRollbackOnly
- {
- get { return _failEarlyOnGlobalRollbackOnly; }
- set { _failEarlyOnGlobalRollbackOnly = value; }
- }
-
- ///
- /// Gets or sets the default timeout that this transaction manager should apply if there
- /// is no timeout specified at the transaction level, in seconds.
- ///
- /// Returns DefaultTransactionDefinition.TIMEOUT_DEFAULT to indicate the
- /// underlying transaction infrastructure's default timeout.
- /// The default timeout.
- 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
-
- ///
- /// Return the current transaction object.
- ///
- /// The current transaction object.
- ///
- /// If transaction support is not available.
- ///
- ///
- /// In the case of lookup or system errors.
- ///
- protected abstract object DoGetTransaction();
-
- ///
- /// Check if the given transaction object indicates an existing transaction
- /// (that is, a transaction which has already started).
- ///
- ///
- /// 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.
- ///
- /// Transaction object returned by
- /// .
- ///
- /// True if there is an existing transaction.
- ///
- /// In the case of system errors.
- ///
- protected virtual bool IsExistingTransaction(object transaction)
- {
- return false;
- }
-
- ///
- /// Begin a new transaction with the given transaction definition.
- ///
- ///
- /// Does not have to care about applying the propagation behavior,
- /// as this has already been handled by this abstract manager.
- ///
- ///
- /// Transaction object returned by
- /// .
- ///
- ///
- /// instance, describing
- /// propagation behavior, isolation level, timeout etc.
- ///
- ///
- /// In the case of creation or system errors.
- ///
- protected abstract void DoBegin(object transaction, ITransactionDefinition definition);
-
- ///
- /// Suspend the resources of the current transaction.
- ///
- ///
- /// Transaction synchronization will already have been suspended.
- ///
- /// Default implementation throws a TransactionSuspensionNotSupportedException,
- /// assuming that transaction suspension is generally not supported.
- ///
- ///
- ///
- /// Transaction object returned by
- /// .
- ///
- ///
- /// An object that holds suspended resources (will be kept unexamined for passing it into
- /// .)
- ///
- ///
- /// If suspending is not supported by the transaction manager implementation.
- ///
- ///
- /// in case of system errors.
- ///
- protected virtual object DoSuspend(object transaction)
- {
- throw new TransactionSuspensionNotSupportedException(
- "Transaction manager [" + GetType().Name + "] does not support transaction suspension");
- }
-
- ///
- /// Resume the resources of the current transaction.
- ///
- /// Transaction synchronization will be resumed afterwards.
- ///
- /// Default implementation throws a TransactionSuspensionNotSupportedException,
- /// assuming that transaction suspension is generally not supported.
- ///
- ///
- ///
- /// Transaction object returned by
- /// .
- ///
- ///
- /// The object that holds suspended resources as returned by
- /// .
- ///
- ///
- /// If suspending is not supported by the transaction manager implementation.
- ///
- ///
- /// In the case of system errors.
- ///
- protected virtual void DoResume(object transaction, object suspendedResources)
- {
- throw new TransactionSuspensionNotSupportedException(
- "Transaction manager [" + GetType().Name + "] does not support transaction suspension");
- }
-
- ///
- /// Perform an actual commit on the given transaction.
- ///
- /// The status representation of the transaction.
- ///
- ///
- /// An implementation does not need to check the rollback-only flag.
- ///
- ///
- ///
- /// In the case of system errors.
- ///
- protected abstract void DoCommit(DefaultTransactionStatus status);
-
- ///
- /// Perform an actual rollback on the given transaction.
- ///
- /// The status representation of the transaction.
- ///
- /// An implementation does not need to check the new transaction flag.
- ///
- ///
- /// In the case of system errors.
- ///
- protected abstract void DoRollback(DefaultTransactionStatus status);
-
- ///
- /// Set the given transaction rollback-only. Only called on rollback
- /// if the current transaction takes part in an existing one.
- ///
- /// Default implementation throws an IllegalTransactionStateException,
- /// assuming that participating in existing transactions is generally not
- /// supported. Subclasses are of course encouraged to provide such support.
- ///
- /// The status representation of the transaction.
- ///
- /// In the case of system errors.
- ///
- 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");
- }
-
- ///
- /// Return whether to use a savepoint for a nested transaction. Default is true,
- /// which causes delegation to
- /// for holding a savepoint.
- ///
- ///
- ///
- ///
- /// Subclasses can override this to return false, causing a further
- /// invocation of
- ///
- /// despite an already existing transaction.
- ///
- ///
- protected virtual bool UseSavepointForNestedTransaction()
- {
- return true;
+ #endregion
+
+ #region Public Properties
+
+ ///
+ /// Sets and gets when this transaction manager should activate the thread-bound
+ /// transaction synchronization support. Default is "always".
+ ///
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ ///
+ public TransactionSynchronizationState TransactionSynchronization
+ {
+ set { _transactionSyncState = value; }
+ get { return _transactionSyncState; }
+ }
+
+ ///
+ /// Sets and gets whether nested transactions are allowed. Default is false.
+ ///
+ ///
+ ///
+ /// Typically initialized with an appropriate default by the
+ /// concrete transaction manager subclass.
+ ///
+ ///
+ public bool NestedTransactionsAllowed
+ {
+ get { return _nestedTransactionsAllowed; }
+ set { _nestedTransactionsAllowed = value; }
+ }
+
+ ///
+ /// Sets and gets a flag that determines whether or not the
+ ///
+ /// method must be invoked if a call to the
+ ///
+ /// method fails. Default is false.
+ ///
+ ///
+ /// Typically not necessary and thus to be avoided as it can override the
+ /// commit exception with a subsequent rollback exception.
+ ///
+ public bool RollbackOnCommitFailure
+ {
+ get { return _rollbackOnCommitFailure; }
+ set { _rollbackOnCommitFailure = value; }
+ }
+
+
+ ///
+ /// Gets or sets a value indicating whether to fail early in case of the transaction being
+ /// globally marked as rollback-only.
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ /// true if fail early on global rollback; otherwise, false.
+ ///
+ public bool FailEarlyOnGlobalRollbackOnly
+ {
+ get { return _failEarlyOnGlobalRollbackOnly; }
+ set { _failEarlyOnGlobalRollbackOnly = value; }
+ }
+
+ ///
+ /// Gets or sets the default timeout that this transaction manager should apply if there
+ /// is no timeout specified at the transaction level, in seconds.
+ ///
+ /// Returns DefaultTransactionDefinition.TIMEOUT_DEFAULT to indicate the
+ /// underlying transaction infrastructure's default timeout.
+ /// The default timeout.
+ 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
+
+ ///
+ /// Return the current transaction object.
+ ///
+ /// The current transaction object.
+ ///
+ /// If transaction support is not available.
+ ///
+ ///
+ /// In the case of lookup or system errors.
+ ///
+ protected abstract object DoGetTransaction();
+
+ ///
+ /// Check if the given transaction object indicates an existing transaction
+ /// (that is, a transaction which has already started).
+ ///
+ ///
+ /// 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.
+ ///
+ /// Transaction object returned by
+ /// .
+ ///
+ /// True if there is an existing transaction.
+ ///
+ /// In the case of system errors.
+ ///
+ protected virtual bool IsExistingTransaction(object transaction)
+ {
+ return false;
+ }
+
+ ///
+ /// Begin a new transaction with the given transaction definition.
+ ///
+ ///
+ /// Does not have to care about applying the propagation behavior,
+ /// as this has already been handled by this abstract manager.
+ ///
+ ///
+ /// Transaction object returned by
+ /// .
+ ///
+ ///
+ /// instance, describing
+ /// propagation behavior, isolation level, timeout etc.
+ ///
+ ///
+ /// In the case of creation or system errors.
+ ///
+ protected abstract void DoBegin(object transaction, ITransactionDefinition definition);
+
+ ///
+ /// Suspend the resources of the current transaction.
+ ///
+ ///
+ /// Transaction synchronization will already have been suspended.
+ ///
+ /// Default implementation throws a TransactionSuspensionNotSupportedException,
+ /// assuming that transaction suspension is generally not supported.
+ ///
+ ///
+ ///
+ /// Transaction object returned by
+ /// .
+ ///
+ ///
+ /// An object that holds suspended resources (will be kept unexamined for passing it into
+ /// .)
+ ///
+ ///
+ /// If suspending is not supported by the transaction manager implementation.
+ ///
+ ///
+ /// in case of system errors.
+ ///
+ protected virtual object DoSuspend(object transaction)
+ {
+ throw new TransactionSuspensionNotSupportedException(
+ "Transaction manager [" + GetType().Name + "] does not support transaction suspension");
+ }
+
+ ///
+ /// Resume the resources of the current transaction.
+ ///
+ /// Transaction synchronization will be resumed afterwards.
+ ///
+ /// Default implementation throws a TransactionSuspensionNotSupportedException,
+ /// assuming that transaction suspension is generally not supported.
+ ///
+ ///
+ ///
+ /// Transaction object returned by
+ /// .
+ ///
+ ///
+ /// The object that holds suspended resources as returned by
+ /// .
+ ///
+ ///
+ /// If suspending is not supported by the transaction manager implementation.
+ ///
+ ///
+ /// In the case of system errors.
+ ///
+ protected virtual void DoResume(object transaction, object suspendedResources)
+ {
+ throw new TransactionSuspensionNotSupportedException(
+ "Transaction manager [" + GetType().Name + "] does not support transaction suspension");
+ }
+
+ ///
+ /// Perform an actual commit on the given transaction.
+ ///
+ /// The status representation of the transaction.
+ ///
+ ///
+ /// An implementation does not need to check the rollback-only flag.
+ ///
+ ///
+ ///
+ /// In the case of system errors.
+ ///
+ protected abstract void DoCommit(DefaultTransactionStatus status);
+
+ ///
+ /// Perform an actual rollback on the given transaction.
+ ///
+ /// The status representation of the transaction.
+ ///
+ /// An implementation does not need to check the new transaction flag.
+ ///
+ ///
+ /// In the case of system errors.
+ ///
+ protected abstract void DoRollback(DefaultTransactionStatus status);
+
+ ///
+ /// Set the given transaction rollback-only. Only called on rollback
+ /// if the current transaction takes part in an existing one.
+ ///
+ /// Default implementation throws an IllegalTransactionStateException,
+ /// assuming that participating in existing transactions is generally not
+ /// supported. Subclasses are of course encouraged to provide such support.
+ ///
+ /// The status representation of the transaction.
+ ///
+ /// In the case of system errors.
+ ///
+ 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");
+ }
+
+ ///
+ /// Return whether to use a savepoint for a nested transaction. Default is true,
+ /// which causes delegation to
+ /// for holding a savepoint.
+ ///
+ ///
+ ///
+ ///
+ /// Subclasses can override this to return false, causing a further
+ /// invocation of
+ ///
+ /// despite an already existing transaction.
+ ///
+ ///
+ protected virtual bool UseSavepointForNestedTransaction()
+ {
+ return true;
}
///
@@ -441,774 +442,836 @@ namespace Spring.Transaction.Support
///
///
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);
- }
-
- ///
- /// Cleanup resources after transaction completion.
- ///
- ///
- /// Transaction object returned by
- /// .
- ///
- ///
- ///
- /// Called after
- /// and
- ///
- /// execution on any outcome.
- ///
- ///
- /// Should not throw any exceptions but just issue warnings on errors.
- ///
- ///
- /// Default implementation does nothing.
- ///
- ///
- protected virtual void DoCleanupAfterCompletion(object transaction)
- {
- }
-
- #endregion
-
- #region IPlatformTransactionManager Members
-
- ///
- /// Return a currently active transaction or create a new one.
- ///
- ///
- ///
- /// This implementation handles propagation behavior.
- ///
- ///
- /// Delegates to
- /// ,
- /// ,
- /// and
- /// .
- ///
- ///
- /// 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.
- ///
- ///
- ///
- /// instance (can be null for
- /// defaults), describing propagation behavior, isolation level, timeout etc.
- ///
- ///
- /// In case of lookup, creation, or system errors.
- ///
- ///
- /// representing the new or current transaction.
- ///
- 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);
-
- }
-
- ///
- /// This implementation of commit handles participating in existing transactions
- /// and programmatic rollback requests.
- ///
- ///
- ///
- ///
- /// ITransactionStatus object returned by the
- /// () method.
- ///
- ///
- /// In case of commit or system errors.
- ///
- 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);
- }
- }
- }
- }
-
- ///
- /// Roll back the given transaction, with regard to its status.
- ///
- ///
- ///
- /// This implementation handles participating in existing transactions.
- ///
- ///
- /// Delegates to
- /// ,
- /// and
- /// .
- ///
- ///
- /// If the transaction wasn't a new one, just set it rollback-only
- /// to take part in the surrounding transaction properly.
- ///
- ///
- ///
- /// ITransactionStatusObject returned by the
- /// () method.
- ///
- ///
- /// In case of system errors.
- ///
- 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);
- }
-
- ///
- /// 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.
- ///
- /// The transaction definition.
- /// the actual timeout to use.
- protected int DetermineTimeout(ITransactionDefinition definition)
- {
- if (definition.TransactionTimeout != DefaultTransactionDefinition.TIMEOUT_DEFAULT)
- {
- return definition.TransactionTimeout;
- }
- return _defaultTimeout;
- }
-
- #endregion
-
- #region Private Methods
-
-
-
-
-
- ///
- /// Suspend the given transaction. Suspends transaction synchronization first,
- /// then delegates to the doSuspend template method.
- ///
- /// the current transaction object
- /// an object that holds suspended resources
- 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);
- }
- }
-
- ///
- /// Resume the given transaction. Delegates to the doResume template method
- /// first, then resuming transaction synchronization.
- ///
- /// the current transaction object
- /// the object that holds suspended resources, as returned by suspend
- 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);
- }
-
- }
- }
-
-
-
- ///
- /// Invoke doRollback, handling rollback exceptions properly.
- ///
- /// object representing the transaction
- /// the thrown application exception or error
- ///
- /// in case of a rollback error
- ///
- 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);
- }
-
- ///
- /// Trigger beforeCommit callback.
- ///
- /// object representing the transaction
- private void TriggerBeforeCommit(DefaultTransactionStatus status)
- {
- if (status.NewSynchronization)
- {
- IList synchronizations = TransactionSynchronizationManager.Synchronizations;
- foreach (ITransactionSynchronization currentTxnSynchronization in synchronizations)
- {
- currentTxnSynchronization.BeforeCommit(status.ReadOnly);
- }
- }
- }
-
- ///
- /// Trigger beforeCompletion callback.
- ///
- /// object representing the transaction
- 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);
- }
- }
- }
- }
-
- ///
- /// Trigger afterCompletion callback, handling exceptions properly.
- ///
- /// object representing the transaction
- ///
- /// Completion status according to
- ///
- 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.Debug("Cannot register Spring after-completion synchronization with existing transaction - " +
+ "processing Spring after-completion callbacks immediately, with outcome status 'unknown'");
+ InvokeAfterCompletion(synchronizations, TransactionSynchronizationStatus.Unknown);
+ }
+
+ ///
+ /// Cleanup resources after transaction completion.
+ ///
+ ///
+ /// Transaction object returned by
+ /// .
+ ///
+ ///
+ ///
+ /// Called after
+ /// and
+ ///
+ /// execution on any outcome.
+ ///
+ ///
+ /// Should not throw any exceptions but just issue warnings on errors.
+ ///
+ ///
+ /// Default implementation does nothing.
+ ///
+ ///
+ protected virtual void DoCleanupAfterCompletion(object transaction)
+ {
+ }
+
+ #endregion
+
+ #region IPlatformTransactionManager Members
+
+ ///
+ /// Return a currently active transaction or create a new one.
+ ///
+ ///
+ ///
+ /// This implementation handles propagation behavior.
+ ///
+ ///
+ /// Delegates to
+ /// ,
+ /// ,
+ /// and
+ /// .
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ ///
+ /// instance (can be null for
+ /// defaults), describing propagation behavior, isolation level, timeout etc.
+ ///
+ ///
+ /// In case of lookup, creation, or system errors.
+ ///
+ ///
+ /// representing the new or current transaction.
+ ///
+ public ITransactionStatus GetTransaction(ITransactionDefinition definition)
+ {
+ object transaction = DoGetTransaction();
+ bool debugEnabled = log.IsDebugEnabled;
+
+ 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
+ {
+ bool newSynchronization = (_transactionSyncState != TransactionSynchronizationState.Never);
+ DefaultTransactionStatus status = NewTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled,
+ suspendedResources);
+ DoBegin(transaction, definition);
+ PrepareSynchronization(status, definition);
+ return status;
+ }
+ catch (TransactionException)
+ {
+ Resume(null, suspendedResources);
+ throw;
+ }
+ }
+ else
+ {
+ // Create "empty" transaction: no actual transaction, but potentially synchronization.
+ bool newSynchronization = (_transactionSyncState == TransactionSynchronizationState.Always);
+ return PrepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
+
+ }
+
+ }
+
+ protected DefaultTransactionStatus PrepareTransactionStatus(
+ ITransactionDefinition definition, Object transaction, bool newTransaction,
+ bool newSynchronization, bool debug, Object suspendedResources)
+ {
+ DefaultTransactionStatus status = NewTransactionStatus(
+ definition, transaction, newTransaction, newSynchronization, debug, suspendedResources);
+ PrepareSynchronization(status, definition);
+ return status;
+ }
+
+
+ protected void PrepareSynchronization(DefaultTransactionStatus status, ITransactionDefinition definition)
+ {
+ if (status.NewSynchronization)
+ {
+ TransactionSynchronizationManager.ActualTransactionActive = status.HasTransaction();
+ TransactionSynchronizationManager.CurrentTransactionIsolationLevel = definition.TransactionIsolationLevel != System.Data.IsolationLevel.Unspecified ? definition.TransactionIsolationLevel : IsolationLevel.Unspecified;
+ TransactionSynchronizationManager.CurrentTransactionReadOnly = definition.ReadOnly;
+ TransactionSynchronizationManager.CurrentTransactionName = definition.Name;
+ TransactionSynchronizationManager.InitSynchronization();
+ }
+ }
+
+ 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
+ PrepareTransactionStatus(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
+ {
+ bool newSynchronization = (_transactionSyncState != TransactionSynchronizationState.Never);
+ DefaultTransactionStatus status = NewTransactionStatus(
+ definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
+ PrepareSynchronization(status, definition);
+ DoBegin(transaction, definition);
+ return status;
+ }
+ catch (TransactionException beginEx)
+ {
+ try
+ {
+ Resume(transaction, suspendedResources);
+ //TODO: java code rethrows the ex here...should we do so as well?
+ //throw;
+ }
+ 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;
+ }
+ }
+ 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 =
+ PrepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
+ status.CreateAndHoldSavepoint(DateTime.Now.ToLongTimeString());
+ return status;
+ }
+ else
+ {
+ bool newSynchronization = (_transactionSyncState != TransactionSynchronizationState.Never);
+ DefaultTransactionStatus status = NewTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);
+ PrepareSynchronization(status, definition);
+ DoBegin(transaction, definition);
+ return status;
+
+ }
+ }
+ // Assumably PROPAGATION_SUPPORTS.
+ if (debugEnabled)
+ {
+ log.Debug("Participating in existing transaction");
+ }
+
+ //TODO: this block related to un-ported java feature permitting setting the ValidateExistingTransaction flag
+ // default is FALSE anyway so skipping this validation block should have no effect on code excecution path
+ /*if (isValidateExistingTransaction())
+ {
+ if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT)
+ {
+ Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
+ if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel())
+ {
+ Constants isoConstants = DefaultTransactionDefinition.constants;
+ throw new IllegalTransactionStateException("Participating transaction with definition [" +
+ definition + "] specifies isolation level which is incompatible with existing transaction: " +
+ (currentIsolationLevel != null ?
+ isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
+ "(unknown)"));
+ }
+ }*/
+
+ if (!definition.ReadOnly)
+ {
+ if (TransactionSynchronizationManager.CurrentTransactionReadOnly)
+ {
+ throw new IllegalTransactionStateException("Participating transaction with definition [" +
+ definition + "] is not marked as read-only but existing transaction is");
+ }
+ }
+
+ bool newSynch = (_transactionSyncState != TransactionSynchronizationState.Never);
+ return PrepareTransactionStatus(definition, transaction, false, newSynch, debugEnabled, null);
+
+ }
+
+ ///
+ /// This implementation of commit handles participating in existing transactions
+ /// and programmatic rollback requests.
+ ///
+ ///
+ ///
+ ///
+ /// ITransactionStatus object returned by the
+ /// () method.
+ ///
+ ///
+ /// In case of commit or system errors.
+ ///
+ 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);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Roll back the given transaction, with regard to its status.
+ ///
+ ///
+ ///
+ /// This implementation handles participating in existing transactions.
+ ///
+ ///
+ /// Delegates to
+ /// ,
+ /// and
+ /// .
+ ///
+ ///
+ /// If the transaction wasn't a new one, just set it rollback-only
+ /// to take part in the surrounding transaction properly.
+ ///
+ ///
+ ///
+ /// ITransactionStatusObject returned by the
+ /// () method.
+ ///
+ ///
+ /// In case of system errors.
+ ///
+ 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);
+ }
+
+ ///
+ /// 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.
+ ///
+ /// The transaction definition.
+ /// the actual timeout to use.
+ protected int DetermineTimeout(ITransactionDefinition definition)
+ {
+ if (definition.TransactionTimeout != DefaultTransactionDefinition.TIMEOUT_DEFAULT)
+ {
+ return definition.TransactionTimeout;
+ }
+ return _defaultTimeout;
+ }
+
+ #endregion
+
+ #region Private Methods
+
+
+
+
+
+ ///
+ /// Suspend the given transaction. Suspends transaction synchronization first,
+ /// then delegates to the doSuspend template method.
+ ///
+ /// the current transaction object
+ /// an object that holds suspended resources
+ 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);
+ }
+ }
+
+ ///
+ /// Resume the given transaction. Delegates to the doResume template method
+ /// first, then resuming transaction synchronization.
+ ///
+ /// the current transaction object
+ /// the object that holds suspended resources, as returned by suspend
+ 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);
+ }
+
+ }
+ }
+
+
+
+ ///
+ /// Invoke doRollback, handling rollback exceptions properly.
+ ///
+ /// object representing the transaction
+ /// the thrown application exception or error
+ ///
+ /// in case of a rollback error
+ ///
+ 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);
+ }
+
+ ///
+ /// Trigger beforeCommit callback.
+ ///
+ /// object representing the transaction
+ private void TriggerBeforeCommit(DefaultTransactionStatus status)
+ {
+ if (status.NewSynchronization)
+ {
+ IList synchronizations = TransactionSynchronizationManager.Synchronizations;
+ foreach (ITransactionSynchronization currentTxnSynchronization in synchronizations)
+ {
+ currentTxnSynchronization.BeforeCommit(status.ReadOnly);
+ }
+ }
+ }
+
+ ///
+ /// Trigger beforeCompletion callback.
+ ///
+ /// object representing the transaction
+ 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);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Trigger afterCompletion callback, handling exceptions properly.
+ ///
+ /// object representing the transaction
+ ///
+ /// Completion status according to
+ ///
+ 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);
- }
- }
- }
-
- ///
- /// Clean up after completion, clearing synchronization if necessary,
- /// and invoking doCleanupAfterCompletion.
- ///
- /// object representing the transaction
- 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
- }
+ 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);
+ }
+ }
+ }
+
+ ///
+ /// Clean up after completion, clearing synchronization if necessary,
+ /// and invoking doCleanupAfterCompletion.
+ ///
+ /// object representing the transaction
+ 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
+ }
}
\ No newline at end of file
diff --git a/src/Spring/Spring.Data/Transaction/Support/TransactionSynchronizationManager.cs b/src/Spring/Spring.Data/Transaction/Support/TransactionSynchronizationManager.cs
index 91b51fcd..6ec964cb 100644
--- a/src/Spring/Spring.Data/Transaction/Support/TransactionSynchronizationManager.cs
+++ b/src/Spring/Spring.Data/Transaction/Support/TransactionSynchronizationManager.cs
@@ -1,492 +1,492 @@
-#region License
-
-/*
- * Copyright 2002-2010 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 System.Globalization;
-using System.Threading;
-using Spring.Core;
-using Spring.Threading;
-using Spring.Util;
-
-namespace Spring.Transaction.Support
-{
- ///
- /// Internal class that manages resources and transaction synchronizations per thread.
- ///
- ///
- /// Supports one resource per key without overwriting, i.e. a resource needs to
- /// be removed before a new one can be set for the same key.
- /// Supports a list of transaction synchronizations if synchronization is active.
- ///
- /// Resource management code should check for thread-bound resources via GetResource().
- /// It is normally not supposed
- /// to bind resources to threads, as this is the responsiblity of transaction managers.
- /// A further option is to lazily bind on first use if transaction synchronization
- /// is active, for performing transactions that span an arbitrary number of resources.
- ///
- ///
- /// Transaction synchronization must be activated and deactivated by a transaction
- /// manager via
- /// InitSynchronization
- /// and
- /// ClearSynchronization.
- /// This is automatically supported by
- /// .
- ///
- ///
- /// Resource management code should only register synchronizations when this
- /// manager is active, and perform resource cleanup immediately else.
- /// If transaction synchronization isn't active, there is either no current
- /// transaction, or the transaction manager doesn't support synchronizations.
- ///
- /// Note that this class uses following naming convention for the
- /// named 'data slots' for storage of thread local data, 'Spring.Transaction:Name'
- /// where Name is either
- ///
- /// Juergen Hoeller
- /// Griffin Caprio (.NET)
- /// Mark Pollack (.NET)
- public sealed class TransactionSynchronizationManager
- {
- #region Logging
-
- private static readonly Common.Logging.ILog LOG = Common.Logging.LogManager.GetLogger(typeof (TransactionSynchronizationManager));
-
- #endregion
-
- #region Fields
- private static readonly string syncsDataSlotName = "Spring.Transactions:syncList";
-
- private static readonly string resourcesDataSlotName = "Spring.Transactions:resources";
-
- private static readonly string currentTxReadOnlyDataSlotName = "Spring.Transactions:currentTxReadOnly";
-
- private static readonly string currentTxNameDataSlotName = "Spring.Transactions:currentTxName";
-
- private static readonly string currentTxIsolationLevelDataSlotName = "Spring.Transactions:currentTxIsolationLevel";
-
- private static readonly string actualTxActiveDataSlotName = "Spring.Transactions:actualTxActive";
-
- private static IComparer syncComparer = new OrderComparator();
-
- #endregion
-
- #region Management of transaction-associated resource handles
- ///
- /// Return all resources that are bound to the current thread.
- ///
- /// Main for debugging purposes. Resource manager should always
- /// invoke HasResource for a specific resource key that they are interested in.
- ///
- /// IDictionary with resource keys and resource objects or empty
- /// dictionary if none is bound.
- public static IDictionary ResourceDictionary
- {
- get
- {
- IDictionary resources = LogicalThreadContext.GetData(resourcesDataSlotName) as IDictionary;
- if (resources != null)
- {
- //TODO add readonly wrapper in Spring.Collections.
- return resources;
- }
- else
- {
- return new Hashtable();
- }
- }
- }
-
- ///
- /// Check if there is a resource for the given key bound to the current thread.
- ///
- /// key to check
- /// if there is a value bound to the current thread
- public static bool HasResource(Object key)
- {
- AssertUtils.ArgumentNotNull(key, "Key must not be null");
- return ResourceDictionary.Contains(key);
- }
-
- ///
- /// Retrieve a resource for the given key that is bound to the current thread.
- ///
- /// key to check
- /// a value bound to the current thread, or null if none.
- public static object GetResource(Object key)
- {
- AssertUtils.ArgumentNotNull(key, "Key must not be null");
- IDictionary resources = LogicalThreadContext.GetData(resourcesDataSlotName) as IDictionary;
- if (resources == null)
- {
- return null;
- }
- //Check for contains since indexer returning null behavior changes in 2.0
- if (!resources.Contains(key))
- {
- return null;
- }
- object val = resources[key];
-
- if (val != null && LOG.IsDebugEnabled)
- {
- LOG.Debug("Retrieved value [" + Describe(val) + "] for key [" + Describe(key) + "] bound to thread [" +
- SystemUtils.ThreadId + "]");
- }
- return val;
- }
-
- ///
- /// Bind the given resource for teh given key to the current thread
- ///
- /// key to bind the value to
- /// value to bind
- public static void BindResource(Object key, Object value)
- {
- AssertUtils.ArgumentNotNull(key, "Key value for thread local storage of transactional resources must not be null");
- AssertUtils.ArgumentNotNull(value, "Transactional resource to bind to thread local storage must not be null" );
-
- IDictionary resources = LogicalThreadContext.GetData(resourcesDataSlotName) as IDictionary;
- //Set thread local resource storage if not found
- if (resources == null)
- {
- resources = new Hashtable();
- LogicalThreadContext.SetData(resourcesDataSlotName, resources);
- }
- if (resources.Contains(key))
- {
- throw new InvalidOperationException("Already value [" + resources[key] + "] for key [" + key +
- "] bound to thread [" + SystemUtils.ThreadId + "]");
- }
- resources.Add(key, value);
- if (LOG.IsDebugEnabled)
- {
- LOG.Debug("Bound value [" + Describe(value) + "] for key [" + Describe(key) + "] to thread [" +
- SystemUtils.ThreadId + "]");
- }
- }
-
-
- ///
- /// Unbind a resource for the given key from the current thread
- ///
- /// key to check
- /// the previously bound value
- /// if there is no value bound to the thread
- public static object UnbindResource(Object key)
- {
- AssertUtils.ArgumentNotNull(key, "Key must not be null");
-
- IDictionary resources = LogicalThreadContext.GetData(resourcesDataSlotName) as IDictionary;
- if (resources == null || !resources.Contains(key))
- {
- throw new InvalidOperationException("No value for key [" + key + "] bound to thread [" +
- SystemUtils.ThreadId + "]");
- }
- Object val = resources[key];
- resources.Remove(key);
- if (resources.Count == 0)
- {
- LogicalThreadContext.FreeNamedDataSlot(resourcesDataSlotName);
- }
- if (LOG.IsDebugEnabled)
- {
- LOG.Debug("Removed value [" + Describe(val) + "] for key [" + Describe(key) + "] from thread [" +
- SystemUtils.ThreadId + "]");
- }
- return val;
- }
-
- #endregion
-
- ///
- /// Activate transaction synchronization for the current thread.
- ///
- ///
- /// Called by transaction manager at the beginning of a transaction.
- ///
- ///
- /// If synchronization is already active.
- ///
- public static void InitSynchronization()
- {
- if ( SynchronizationActive )
- {
- throw new InvalidOperationException( "Cannot activate transaction synchronization - already active" );
- }
- if (LOG.IsDebugEnabled)
- {
- LOG.Debug("Initializing transaction synchronization");
- }
- ArrayList syncs = new ArrayList();
- LogicalThreadContext.SetData(syncsDataSlotName, syncs);
- }
-
- ///
- /// Deactivate transaction synchronization for the current thread.
- ///
- ///
- /// Called by transaction manager on transaction cleanup.
- ///
- ///
- /// If synchronization is not active.
- ///
- public static void ClearSynchronization()
- {
- if ( !SynchronizationActive )
- {
- throw new InvalidOperationException( "Cannot deactivate transaction synchronization - not active" );
- }
- if (LOG.IsDebugEnabled)
- {
- LOG.Debug("Clearing transaction synchronization");
- }
- LogicalThreadContext.FreeNamedDataSlot(syncsDataSlotName);
- }
-
- ///
- /// Clears the entire transaction synchronization state for the current thread, registered
- /// synchronizations as well as the various transaction characteristics.
- ///
- public static void Clear()
- {
- ClearSynchronization();
- CurrentTransactionName = null;
- CurrentTransactionReadOnly = false;
- CurrentTransactionIsolationLevel = IsolationLevel.Unspecified;
- ActualTransactionActive = false;
- }
-
- ///
- /// Register a new transaction synchronization for the current thread.
- ///
- ///
- /// Typically called by resource management code.
- ///
- ///
- /// If synchronization is not active.
- ///
- public static void RegisterSynchronization( ITransactionSynchronization synchronization )
- {
- AssertUtils.ArgumentNotNull(synchronization, "TransactionSynchronization must not be null");
- if ( !SynchronizationActive )
- {
- throw new InvalidOperationException( "Transaction synchronization is not active" );
- }
- ArrayList syncs = LogicalThreadContext.GetData(syncsDataSlotName) as ArrayList;
- if (syncs != null)
- {
- object root = syncs.SyncRoot;
- lock (root)
- {
- syncs.Add(synchronization);
- }
- }
- }
-
- private static string Describe(object obj)
- {
- return obj == null ? "" : obj + "@" + obj.GetHashCode().ToString("X");
- }
-
- #region Properties
-
- ///
- /// Return an unmodifiable list of all registered synchronizations
- /// for the current thread.
- ///
- ///
- /// A list of
- /// instances.
- ///
- ///
- /// If synchronization is not active.
- ///
- public static IList Synchronizations
- {
- get
- {
- if ( ! SynchronizationActive )
- {
- throw new InvalidOperationException( "Transaction synchronization is not active" );
- }
- ArrayList syncs = LogicalThreadContext.GetData(syncsDataSlotName) as ArrayList;
- if (syncs != null)
- {
- // Sort lazily here, not in registerSynchronization.
- object root = syncs.SyncRoot;
- lock (root)
- {
- // #SPRNET-1160, tx Ben Rowlands
- CollectionUtils.StableSortInPlace(syncs, syncComparer);
+#region License
+
+/*
+ * Copyright 2002-2010 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 System.Globalization;
+using System.Threading;
+using Spring.Core;
+using Spring.Threading;
+using Spring.Util;
+
+namespace Spring.Transaction.Support
+{
+ ///
+ /// Internal class that manages resources and transaction synchronizations per thread.
+ ///
+ ///
+ /// Supports one resource per key without overwriting, i.e. a resource needs to
+ /// be removed before a new one can be set for the same key.
+ /// Supports a list of transaction synchronizations if synchronization is active.
+ ///
+ /// Resource management code should check for thread-bound resources via GetResource().
+ /// It is normally not supposed
+ /// to bind resources to threads, as this is the responsiblity of transaction managers.
+ /// A further option is to lazily bind on first use if transaction synchronization
+ /// is active, for performing transactions that span an arbitrary number of resources.
+ ///
+ ///
+ /// Transaction synchronization must be activated and deactivated by a transaction
+ /// manager via
+ /// InitSynchronization
+ /// and
+ /// ClearSynchronization.
+ /// This is automatically supported by
+ /// .
+ ///
+ ///
+ /// Resource management code should only register synchronizations when this
+ /// manager is active, and perform resource cleanup immediately else.
+ /// If transaction synchronization isn't active, there is either no current
+ /// transaction, or the transaction manager doesn't support synchronizations.
+ ///
+ /// Note that this class uses following naming convention for the
+ /// named 'data slots' for storage of thread local data, 'Spring.Transaction:Name'
+ /// where Name is either
+ ///
+ /// Juergen Hoeller
+ /// Griffin Caprio (.NET)
+ /// Mark Pollack (.NET)
+ public sealed class TransactionSynchronizationManager
+ {
+ #region Logging
+
+ private static readonly Common.Logging.ILog LOG = Common.Logging.LogManager.GetLogger(typeof (TransactionSynchronizationManager));
+
+ #endregion
+
+ #region Fields
+ private static readonly string syncsDataSlotName = "Spring.Transactions:syncList";
+
+ private static readonly string resourcesDataSlotName = "Spring.Transactions:resources";
+
+ private static readonly string currentTxReadOnlyDataSlotName = "Spring.Transactions:currentTxReadOnly";
+
+ private static readonly string currentTxNameDataSlotName = "Spring.Transactions:currentTxName";
+
+ private static readonly string currentTxIsolationLevelDataSlotName = "Spring.Transactions:currentTxIsolationLevel";
+
+ private static readonly string actualTxActiveDataSlotName = "Spring.Transactions:actualTxActive";
+
+ private static IComparer syncComparer = new OrderComparator();
+
+ #endregion
+
+ #region Management of transaction-associated resource handles
+ ///
+ /// Return all resources that are bound to the current thread.
+ ///
+ /// Main for debugging purposes. Resource manager should always
+ /// invoke HasResource for a specific resource key that they are interested in.
+ ///
+ /// IDictionary with resource keys and resource objects or empty
+ /// dictionary if none is bound.
+ public static IDictionary ResourceDictionary
+ {
+ get
+ {
+ IDictionary resources = LogicalThreadContext.GetData(resourcesDataSlotName) as IDictionary;
+ if (resources != null)
+ {
+ //TODO add readonly wrapper in Spring.Collections.
+ return resources;
+ }
+ else
+ {
+ return new Hashtable();
+ }
+ }
+ }
+
+ ///
+ /// Check if there is a resource for the given key bound to the current thread.
+ ///
+ /// key to check
+ /// if there is a value bound to the current thread
+ public static bool HasResource(Object key)
+ {
+ AssertUtils.ArgumentNotNull(key, "Key must not be null");
+ return ResourceDictionary.Contains(key);
+ }
+
+ ///
+ /// Retrieve a resource for the given key that is bound to the current thread.
+ ///
+ /// key to check
+ /// a value bound to the current thread, or null if none.
+ public static object GetResource(Object key)
+ {
+ AssertUtils.ArgumentNotNull(key, "Key must not be null");
+ IDictionary resources = LogicalThreadContext.GetData(resourcesDataSlotName) as IDictionary;
+ if (resources == null)
+ {
+ return null;
+ }
+ //Check for contains since indexer returning null behavior changes in 2.0
+ if (!resources.Contains(key))
+ {
+ return null;
+ }
+ object val = resources[key];
+
+ if (val != null && LOG.IsDebugEnabled)
+ {
+ LOG.Debug("Retrieved value [" + Describe(val) + "] for key [" + Describe(key) + "] bound to thread [" +
+ SystemUtils.ThreadId + "]");
+ }
+ return val;
+ }
+
+ ///
+ /// Bind the given resource for teh given key to the current thread
+ ///
+ /// key to bind the value to
+ /// value to bind
+ public static void BindResource(Object key, Object value)
+ {
+ AssertUtils.ArgumentNotNull(key, "Key value for thread local storage of transactional resources must not be null");
+ AssertUtils.ArgumentNotNull(value, "Transactional resource to bind to thread local storage must not be null" );
+
+ IDictionary resources = LogicalThreadContext.GetData(resourcesDataSlotName) as IDictionary;
+ //Set thread local resource storage if not found
+ if (resources == null)
+ {
+ resources = new Hashtable();
+ LogicalThreadContext.SetData(resourcesDataSlotName, resources);
+ }
+ if (resources.Contains(key))
+ {
+ throw new InvalidOperationException("Already value [" + resources[key] + "] for key [" + key +
+ "] bound to thread [" + SystemUtils.ThreadId + "]");
+ }
+ resources.Add(key, value);
+ if (LOG.IsDebugEnabled)
+ {
+ LOG.Debug("Bound value [" + Describe(value) + "] for key [" + Describe(key) + "] to thread [" +
+ SystemUtils.ThreadId + "]");
+ }
+ }
+
+
+ ///
+ /// Unbind a resource for the given key from the current thread
+ ///
+ /// key to check
+ /// the previously bound value
+ /// if there is no value bound to the thread
+ public static object UnbindResource(Object key)
+ {
+ AssertUtils.ArgumentNotNull(key, "Key must not be null");
+
+ IDictionary resources = LogicalThreadContext.GetData(resourcesDataSlotName) as IDictionary;
+ if (resources == null || !resources.Contains(key))
+ {
+ throw new InvalidOperationException("No value for key [" + key + "] bound to thread [" +
+ SystemUtils.ThreadId + "]");
+ }
+ Object val = resources[key];
+ resources.Remove(key);
+ if (resources.Count == 0)
+ {
+ LogicalThreadContext.FreeNamedDataSlot(resourcesDataSlotName);
+ }
+ if (LOG.IsDebugEnabled)
+ {
+ LOG.Debug("Removed value [" + Describe(val) + "] for key [" + Describe(key) + "] from thread [" +
+ SystemUtils.ThreadId + "]");
+ }
+ return val;
+ }
+
+ #endregion
+
+ ///
+ /// Activate transaction synchronization for the current thread.
+ ///
+ ///
+ /// Called by transaction manager at the beginning of a transaction.
+ ///
+ ///
+ /// If synchronization is already active.
+ ///
+ public static void InitSynchronization()
+ {
+ if ( SynchronizationActive )
+ {
+ throw new InvalidOperationException( "Cannot activate transaction synchronization - already active" );
+ }
+ if (LOG.IsDebugEnabled)
+ {
+ LOG.Debug("Initializing transaction synchronization");
+ }
+ ArrayList syncs = new ArrayList();
+ LogicalThreadContext.SetData(syncsDataSlotName, syncs);
+ }
+
+ ///
+ /// Deactivate transaction synchronization for the current thread.
+ ///
+ ///
+ /// Called by transaction manager on transaction cleanup.
+ ///
+ ///
+ /// If synchronization is not active.
+ ///
+ public static void ClearSynchronization()
+ {
+ if ( !SynchronizationActive )
+ {
+ throw new InvalidOperationException( "Cannot deactivate transaction synchronization - not active" );
+ }
+ if (LOG.IsDebugEnabled)
+ {
+ LOG.Debug("Clearing transaction synchronization");
+ }
+ LogicalThreadContext.FreeNamedDataSlot(syncsDataSlotName);
+ }
+
+ ///
+ /// Clears the entire transaction synchronization state for the current thread, registered
+ /// synchronizations as well as the various transaction characteristics.
+ ///
+ public static void Clear()
+ {
+ ClearSynchronization();
+ CurrentTransactionName = null;
+ CurrentTransactionReadOnly = false;
+ CurrentTransactionIsolationLevel = IsolationLevel.Unspecified;
+ ActualTransactionActive = false;
+ }
+
+ ///
+ /// Register a new transaction synchronization for the current thread.
+ ///
+ ///
+ /// Typically called by resource management code.
+ ///
+ ///
+ /// If synchronization is not active.
+ ///
+ public static void RegisterSynchronization( ITransactionSynchronization synchronization )
+ {
+ AssertUtils.ArgumentNotNull(synchronization, "TransactionSynchronization must not be null");
+ if ( !SynchronizationActive )
+ {
+ throw new InvalidOperationException( "Transaction synchronization is not active" );
+ }
+ ArrayList syncs = LogicalThreadContext.GetData(syncsDataSlotName) as ArrayList;
+ if (syncs != null)
+ {
+ object root = syncs.SyncRoot;
+ lock (root)
+ {
+ syncs.Add(synchronization);
+ }
+ }
+ }
+
+ private static string Describe(object obj)
+ {
+ return obj == null ? "" : obj + "@" + obj.GetHashCode().ToString("X");
+ }
+
+ #region Properties
+
+ ///
+ /// Return an unmodifiable list of all registered synchronizations
+ /// for the current thread.
+ ///
+ ///
+ /// A list of
+ /// instances.
+ ///
+ ///
+ /// If synchronization is not active.
+ ///
+ public static IList Synchronizations
+ {
+ get
+ {
+ if ( ! SynchronizationActive )
+ {
+ throw new InvalidOperationException( "Transaction synchronization is not active" );
+ }
+ ArrayList syncs = LogicalThreadContext.GetData(syncsDataSlotName) as ArrayList;
+ if (syncs != null)
+ {
+ // Sort lazily here, not in registerSynchronization.
+ object root = syncs.SyncRoot;
+ lock (root)
+ {
+ // #SPRNET-1160, tx Ben Rowlands
+ CollectionUtils.StableSortInPlace(syncs, syncComparer);
}
- // Return unmodifiable snapshot, to avoid exceptions
- // while iterating and invoking synchronization callbacks that in turn
- // might register further synchronizations.
- return ArrayList.ReadOnly(syncs);
- }
- else
- {
- return ArrayList.ReadOnly(new ArrayList());
- }
- }
- }
-
- ///
- /// Return if transaction synchronization is active for the current thread.
- ///
- ///
- /// Can be called before
- /// InitSynchronization
- /// to avoid unnecessary instance creation.
- ///
- public static bool SynchronizationActive
- {
- get
- {
- IList syncs = LogicalThreadContext.GetData(syncsDataSlotName) as IList;
- return syncs != null;
- }
- }
-
- ///
- /// Gets or sets a value indicating whether the
- /// current transaction is read only.
- ///
- ///
- /// Called by transaction manager on transaction begin and on cleanup.
- /// Return whether the current transaction is marked as read-only.
- /// To be called by resource management code when preparing a newly
- /// created resource (for example, a Hibernate Session).
- ///
Note that transaction synchronizations receive the read-only flag
- /// as argument for the beforeCommit callback, to be able
- /// to suppress change detection on commit. The present method is meant
- /// to be used for earlier read-only checks, for example to set the
- /// flush mode of a Hibernate Session to FlushMode.Never upfront.
- ///
- ///
- ///
- /// true if current transaction read only; otherwise, false.
- ///
- public static bool CurrentTransactionReadOnly
- {
- get
- {
- return LogicalThreadContext.GetData(currentTxReadOnlyDataSlotName) != null;
- }
- set
- {
- if (value)
- {
- LogicalThreadContext.SetData(currentTxReadOnlyDataSlotName, true);
- }
- else
- {
- LogicalThreadContext.FreeNamedDataSlot(currentTxReadOnlyDataSlotName);
- }
-
- }
- }
-
- ///
- /// Gets or sets the name of the current transaction, if any.
- ///
- /// Called by the transaction manager on transaction begin and on cleanup.
- /// To be called by resource management code for optimizations per use case, for
- /// example to optimize fetch strategies for specific named transactions.
- /// The name of the current transactio or null if none set.
- public static string CurrentTransactionName
- {
- get
- {
- return LogicalThreadContext.GetData(currentTxNameDataSlotName) as string;
- }
- set
- {
- LogicalThreadContext.SetData(currentTxNameDataSlotName, value);
- }
- }
-
- ///
- /// Gets or sets a value indicating whether there currently is an actual transaction
- /// active.
- ///
- /// This indicates wheter the current thread is associated with an actual
- /// transaction rather than just with active transaction synchronization.
- /// Called by the transaction manager on transaction begin and on cleanup.
- /// To be called by resource management code that wants to discriminate between
- /// active transaction synchronization (with or without backing resource transaction;
- /// also on PROPAGATION_SUPPORTS) and an actual transaction being active; on
- /// PROPAGATION_REQUIRES, PROPAGATION_REQUIRES_NEW, etC)
- ///
- /// true if [actual transaction active]; otherwise, false.
- ///
- public static bool ActualTransactionActive
- {
- get
- {
- return LogicalThreadContext.GetData(actualTxActiveDataSlotName) != null;
- }
- set
- {
- if (value)
- {
- LogicalThreadContext.SetData(actualTxActiveDataSlotName, value);
- }
- else
- {
- LogicalThreadContext.FreeNamedDataSlot(actualTxActiveDataSlotName);
- }
- }
- }
-
-
- ///
- /// Gets or sets the current transaction isolation level, if any.
- ///
- /// Called by the transaction manager on transaction begin and on cleanup.
- /// The current transaction isolation level. If no current transaction is
- /// active, retrun IsolationLevel.Unspecified
- public static IsolationLevel CurrentTransactionIsolationLevel
- {
- get
- {
- object data =
- LogicalThreadContext.GetData(currentTxIsolationLevelDataSlotName);
- if (data != null)
- {
- return (IsolationLevel) data;
- }
- else
- {
- return IsolationLevel.Unspecified;
- }
- }
- set
- {
- LogicalThreadContext.SetData(currentTxIsolationLevelDataSlotName, value);
- }
- }
- #endregion
- }
-}
+ // Return unmodifiable snapshot, to avoid exceptions
+ // while iterating and invoking synchronization callbacks that in turn
+ // might register further synchronizations.
+ return ArrayList.ReadOnly(syncs);
+ }
+ else
+ {
+ return ArrayList.ReadOnly(new ArrayList());
+ }
+ }
+ }
+
+ ///
+ /// Return if transaction synchronization is active for the current thread.
+ ///
+ ///
+ /// Can be called before
+ /// InitSynchronization
+ /// to avoid unnecessary instance creation.
+ ///
+ public static bool SynchronizationActive
+ {
+ get
+ {
+ IList syncs = LogicalThreadContext.GetData(syncsDataSlotName) as IList;
+ return syncs != null;
+ }
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the
+ /// current transaction is read only.
+ ///
+ ///
+ /// Called by transaction manager on transaction begin and on cleanup.
+ /// Return whether the current transaction is marked as read-only.
+ /// To be called by resource management code when preparing a newly
+ /// created resource (for example, a Hibernate Session).
+ ///
Note that transaction synchronizations receive the read-only flag
+ /// as argument for the beforeCommit callback, to be able
+ /// to suppress change detection on commit. The present method is meant
+ /// to be used for earlier read-only checks, for example to set the
+ /// flush mode of a Hibernate Session to FlushMode.Never upfront.
+ ///
+ ///
+ ///
+ /// true if current transaction read only; otherwise, false.
+ ///
+ public static bool CurrentTransactionReadOnly
+ {
+ get
+ {
+ return LogicalThreadContext.GetData(currentTxReadOnlyDataSlotName) != null;
+ }
+ set
+ {
+ if (value)
+ {
+ LogicalThreadContext.SetData(currentTxReadOnlyDataSlotName, true);
+ }
+ else
+ {
+ LogicalThreadContext.FreeNamedDataSlot(currentTxReadOnlyDataSlotName);
+ }
+
+ }
+ }
+
+ ///
+ /// Gets or sets the name of the current transaction, if any.
+ ///
+ /// Called by the transaction manager on transaction begin and on cleanup.
+ /// To be called by resource management code for optimizations per use case, for
+ /// example to optimize fetch strategies for specific named transactions.
+ /// The name of the current transactio or null if none set.
+ public static string CurrentTransactionName
+ {
+ get
+ {
+ return LogicalThreadContext.GetData(currentTxNameDataSlotName) as string;
+ }
+ set
+ {
+ LogicalThreadContext.SetData(currentTxNameDataSlotName, value);
+ }
+ }
+
+ ///
+ /// Gets or sets a value indicating whether there currently is an actual transaction
+ /// active.
+ ///
+ /// This indicates wheter the current thread is associated with an actual
+ /// transaction rather than just with active transaction synchronization.
+ /// Called by the transaction manager on transaction begin and on cleanup.
+ /// To be called by resource management code that wants to discriminate between
+ /// active transaction synchronization (with or without backing resource transaction;
+ /// also on PROPAGATION_SUPPORTS) and an actual transaction being active; on
+ /// PROPAGATION_REQUIRES, PROPAGATION_REQUIRES_NEW, etC)
+ ///
+ /// true if [actual transaction active]; otherwise, false.
+ ///
+ public static bool ActualTransactionActive
+ {
+ get
+ {
+ return LogicalThreadContext.GetData(actualTxActiveDataSlotName) != null;
+ }
+ set
+ {
+ if (value)
+ {
+ LogicalThreadContext.SetData(actualTxActiveDataSlotName, value);
+ }
+ else
+ {
+ LogicalThreadContext.FreeNamedDataSlot(actualTxActiveDataSlotName);
+ }
+ }
+ }
+
+
+ ///
+ /// Gets or sets the current transaction isolation level, if any.
+ ///
+ /// Called by the transaction manager on transaction begin and on cleanup.
+ /// The current transaction isolation level. If no current transaction is
+ /// active, retrun IsolationLevel.Unspecified
+ public static IsolationLevel CurrentTransactionIsolationLevel
+ {
+ get
+ {
+ object data =
+ LogicalThreadContext.GetData(currentTxIsolationLevelDataSlotName);
+ if (data != null)
+ {
+ return (IsolationLevel) data;
+ }
+ else
+ {
+ return IsolationLevel.Unspecified;
+ }
+ }
+ set
+ {
+ LogicalThreadContext.SetData(currentTxIsolationLevelDataSlotName, value);
+ }
+ }
+ #endregion
+ }
+}
diff --git a/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/HibernateTransactionManagerTests.cs b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/HibernateTransactionManagerTests.cs
index 6a30f7ff..ee71e93f 100644
--- a/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/HibernateTransactionManagerTests.cs
+++ b/test/Spring/Spring.Data.NHibernate.Tests/Data/NHibernate/HibernateTransactionManagerTests.cs
@@ -45,6 +45,29 @@ namespace Spring.Data.NHibernate
[TestFixture]
public class HibernateTransactionManagerTests
{
+ private class TestableHibernateTransactionManager : HibernateTransactionManager
+ {
+ private IDbTransaction _stubbedTransactionWithExpectedConnection;
+
+ public TestableHibernateTransactionManager()
+ {
+ }
+
+ public TestableHibernateTransactionManager(ISessionFactory sessionFactory) : base(sessionFactory)
+ {
+ }
+
+ public IDbTransaction StubbedTransactionThatReturnsExpectedConnection
+ {
+ set { _stubbedTransactionWithExpectedConnection = value; }
+ }
+
+ protected override IDbTransaction GetIDbTransaction(ITransaction hibernateTx)
+ {
+ return _stubbedTransactionWithExpectedConnection;
+ }
+ }
+
private MockRepository mocks;
[SetUp]
@@ -88,6 +111,7 @@ namespace Spring.Data.NHibernate
Assert.IsNotNull(sfProxy);
HibernateTransactionManager tm = new HibernateTransactionManager();
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
tm.AdoExceptionTranslator = new FallbackExceptionTranslator();
tm.SessionFactory = sfProxy;
tm.DbProvider = provider;
@@ -95,19 +119,19 @@ namespace Spring.Data.NHibernate
tt.TransactionIsolationLevel = IsolationLevel.Serializable;
- Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sfProxy),"Hasn't thread session");
- Assert.IsTrue(!TransactionSynchronizationManager.HasResource(provider), "Hasn't thread db provider");
- Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active");
- Assert.IsTrue(!TransactionSynchronizationManager.ActualTransactionActive, "Actual transaction not active");
+ Assert.IsFalse(TransactionSynchronizationManager.HasResource(sfProxy),"Hasn't thread session");
+ Assert.IsFalse(TransactionSynchronizationManager.HasResource(provider), "Hasn't thread db provider");
+ Assert.IsFalse(TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active");
+ Assert.IsFalse(TransactionSynchronizationManager.ActualTransactionActive, "Actual transaction not active");
object result = tt.Execute(new TransactionCommitTxCallback(sfProxy, provider));
Assert.IsTrue(result == list, "Incorrect result list");
- Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sfProxy), "Hasn't thread session");
- Assert.IsTrue(!TransactionSynchronizationManager.HasResource(provider), "Hasn't thread db provider");
- Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active");
- Assert.IsTrue(!TransactionSynchronizationManager.ActualTransactionActive, "Actual transaction not active");
+ Assert.IsFalse(TransactionSynchronizationManager.HasResource(sfProxy), "Hasn't thread session");
+ Assert.IsFalse(TransactionSynchronizationManager.HasResource(provider), "Hasn't thread db provider");
+ Assert.IsFalse(TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active");
+ Assert.IsFalse(TransactionSynchronizationManager.ActualTransactionActive, "Actual transaction not active");
mocks.VerifyAll();
@@ -124,6 +148,7 @@ namespace Spring.Data.NHibernate
ISessionFactory sessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory));
ISession session = (ISession)mocks.CreateMock(typeof(ISession));
ITransaction transaction = (ITransaction)mocks.CreateMock(typeof(ITransaction));
+ IDbTransaction adoTransaction = (IDbTransaction)mocks.CreateMock(typeof(IDbTransaction));
using (mocks.Ordered())
{
@@ -132,6 +157,9 @@ namespace Spring.Data.NHibernate
Expect.Call(session.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction);
Expect.Call(session.IsOpen).Return(true);
+ Expect.Call(adoTransaction.Connection).Return(connection);
+ LastCall.On(adoTransaction).Repeat.Once();
+
transaction.Rollback();
LastCall.On(transaction).Repeat.Once();
@@ -139,11 +167,14 @@ namespace Spring.Data.NHibernate
}
mocks.ReplayAll();
- HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory);
+ TestableHibernateTransactionManager tm = new TestableHibernateTransactionManager(sessionFactory);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+ tm.StubbedTransactionThatReturnsExpectedConnection = adoTransaction;
+
TransactionTemplate tt = new TransactionTemplate(tm);
- Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session");
- Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active");
+ Assert.IsFalse(TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session");
+ Assert.IsFalse(TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active");
try
{
@@ -154,8 +185,8 @@ namespace Spring.Data.NHibernate
}
- Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session");
- Assert.IsTrue(!TransactionSynchronizationManager.HasResource(provider), "Hasn't thread db provider");
+ Assert.IsFalse(TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session");
+ Assert.IsFalse(TransactionSynchronizationManager.HasResource(provider), "Hasn't thread db provider");
mocks.VerifyAll();
}
@@ -167,6 +198,7 @@ namespace Spring.Data.NHibernate
ISessionFactory sessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory));
ISession session = (ISession)mocks.CreateMock(typeof(ISession));
ITransaction transaction = (ITransaction)mocks.CreateMock(typeof(ITransaction));
+ IDbTransaction adoTransaction = (IDbTransaction)mocks.CreateMock(typeof(IDbTransaction));
using (mocks.Ordered())
{
@@ -177,6 +209,10 @@ namespace Spring.Data.NHibernate
Expect.Call(session.FlushMode).Return(FlushMode.Auto);
session.Flush();
LastCall.On(session).Repeat.Once();
+
+ Expect.Call(adoTransaction.Connection).Return(connection);
+ LastCall.On(adoTransaction).Repeat.Once();
+
transaction.Rollback();
LastCall.On(transaction).Repeat.Once();
Expect.Call(session.Close()).Return(null);
@@ -184,14 +220,17 @@ namespace Spring.Data.NHibernate
mocks.ReplayAll();
- HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory);
+ TestableHibernateTransactionManager tm = new TestableHibernateTransactionManager(sessionFactory);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+ tm.StubbedTransactionThatReturnsExpectedConnection = adoTransaction;
+
TransactionTemplate tt = new TransactionTemplate(tm);
- Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session");
+ Assert.IsFalse(TransactionSynchronizationManager.HasResource(sessionFactory), "Shouldn't have a thread session");
tt.Execute(new TransactionRollbackOnlyTxCallback(sessionFactory));
- Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session");
+ Assert.IsFalse(TransactionSynchronizationManager.HasResource(sessionFactory), "Shouldn't have a thread session");
mocks.VerifyAll();
@@ -222,6 +261,8 @@ namespace Spring.Data.NHibernate
mocks.ReplayAll();
HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
IList list = new ArrayList();
list.Add("test");
@@ -240,6 +281,7 @@ namespace Spring.Data.NHibernate
ISessionFactory sessionFactory = (ISessionFactory) mocks.CreateMock(typeof (ISessionFactory));
ISession session = (ISession) mocks.CreateMock(typeof (ISession));
ITransaction transaction = (ITransaction) mocks.CreateMock(typeof (ITransaction));
+ IDbTransaction adoTransaction = (IDbTransaction)mocks.CreateMock(typeof(IDbTransaction));
using (mocks.Ordered())
{
@@ -249,6 +291,9 @@ namespace Spring.Data.NHibernate
Expect.Call(session.IsOpen).Return(true);
Expect.Call(session.FlushMode).Return(FlushMode.Auto);
+ Expect.Call(adoTransaction.Connection).Return(connection);
+ LastCall.On(adoTransaction).Repeat.Once();
+
transaction.Rollback();
LastCall.On(transaction).Repeat.Once();
Expect.Call(session.Close()).Return(null);
@@ -256,7 +301,10 @@ namespace Spring.Data.NHibernate
mocks.ReplayAll();
- HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory);
+ TestableHibernateTransactionManager tm = new TestableHibernateTransactionManager(sessionFactory);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+ tm.StubbedTransactionThatReturnsExpectedConnection = adoTransaction;
+
TransactionTemplate tt = new TransactionTemplate(tm);
try
{
@@ -278,6 +326,7 @@ namespace Spring.Data.NHibernate
ISessionFactory sessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory));
ISession session = (ISession)mocks.CreateMock(typeof(ISession));
ITransaction transaction = (ITransaction)mocks.CreateMock(typeof(ITransaction));
+ IDbTransaction adoTransaction = (IDbTransaction)mocks.CreateMock(typeof(IDbTransaction));
using (mocks.Ordered())
{
@@ -286,6 +335,9 @@ namespace Spring.Data.NHibernate
Expect.Call(session.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction);
Expect.Call(session.IsOpen).Return(true);
+ Expect.Call(adoTransaction.Connection).Return(connection);
+ LastCall.On(adoTransaction).Repeat.Once();
+
transaction.Rollback();
LastCall.On(transaction).Repeat.Once();
Expect.Call(session.Close()).Return(null);
@@ -293,7 +345,10 @@ namespace Spring.Data.NHibernate
mocks.ReplayAll();
- HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory);
+ TestableHibernateTransactionManager tm = new TestableHibernateTransactionManager(sessionFactory);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+ tm.StubbedTransactionThatReturnsExpectedConnection = adoTransaction;
+
TransactionTemplate tt = new TransactionTemplate(tm);
IList list = new ArrayList();
list.Add("test");
@@ -345,6 +400,8 @@ namespace Spring.Data.NHibernate
mocks.ReplayAll();
HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
tt.PropagationBehavior = TransactionPropagation.RequiresNew;
@@ -389,6 +446,8 @@ namespace Spring.Data.NHibernate
HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
tt.PropagationBehavior = TransactionPropagation.RequiresNew;
@@ -431,6 +490,8 @@ namespace Spring.Data.NHibernate
Assert.IsNotNull(sfProxy);
HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
tt.PropagationBehavior = TransactionPropagation.Supports;
@@ -485,6 +546,7 @@ namespace Spring.Data.NHibernate
Assert.IsNotNull(sfProxy);
HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
TransactionTemplate tt = new TransactionTemplate(tm);
tt.PropagationBehavior = TransactionPropagation.Supports;
@@ -536,6 +598,8 @@ namespace Spring.Data.NHibernate
ISessionFactory sessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory));
ISession session = (ISession)mocks.CreateMock(typeof(ISession));
ITransaction transaction = (ITransaction)mocks.CreateMock(typeof(ITransaction));
+ IDbTransaction adoTransaction = (IDbTransaction)mocks.CreateMock(typeof(IDbTransaction));
+
Exception rootCause = null;
using (mocks.Ordered())
{
@@ -562,6 +626,9 @@ namespace Spring.Data.NHibernate
LastCall.On(transaction).Throw(rootCause);
}
+ Expect.Call(adoTransaction.Connection).Return(connection);
+ LastCall.On(adoTransaction).Repeat.Once();
+
transaction.Rollback();
LastCall.On(transaction).Repeat.Once();
Expect.Call(session.Close()).Return(null);
@@ -572,8 +639,10 @@ namespace Spring.Data.NHibernate
mocks.ReplayAll();
- HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory);
+ TestableHibernateTransactionManager tm = new TestableHibernateTransactionManager(sessionFactory);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
tm.DbProvider = provider;
+ tm.StubbedTransactionThatReturnsExpectedConnection = adoTransaction;
TransactionTemplate tt = new TransactionTemplate(tm);
Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session");
diff --git a/test/Spring/Spring.Data.NHibernate30.Integration.Tests/Spring.Data.NHibernate30.Integration.Tests.2010.csproj b/test/Spring/Spring.Data.NHibernate30.Integration.Tests/Spring.Data.NHibernate30.Integration.Tests.2010.csproj
new file mode 100644
index 00000000..c800a062
--- /dev/null
+++ b/test/Spring/Spring.Data.NHibernate30.Integration.Tests/Spring.Data.NHibernate30.Integration.Tests.2010.csproj
@@ -0,0 +1,215 @@
+
+
+
+ Debug
+ AnyCPU
+ 9.0.30729
+ 2.0
+ {93FED0CE-0B01-43AF-8CB1-244CC7C3308B}
+ Library
+ Properties
+ Spring
+ Spring.Data.NHibernate30.Integration.Tests
+ v3.5
+
+
+ 3.5
+
+ publish\
+ true
+ Disk
+ false
+ Foreground
+ 7
+ Days
+ false
+ false
+ true
+ 0
+ 1.0.0.%2a
+ false
+ false
+ true
+
+
+
+ true
+ full
+ false
+ ..\..\..\build\VS.Net.2008\Spring.Data.NHibernate30.Integration.Tests\Debug\
+ TRACE;DEBUG;NET_2_0,NH_2_0,NH_2_1
+ prompt
+ 4
+ AllRules.ruleset
+
+
+ pdbonly
+ true
+ ..\..\..\build\VS.Net.2008\Spring.Data.NHibernate30.Integration.Tests\Release\
+ TRACE;NET_2_0,NH_2_0,NH_2_1
+ prompt
+ 4
+ AllRules.ruleset
+
+
+
+ False
+ ..\..\..\lib\Net\2.0\Common.Logging.dll
+
+
+ False
+ ..\..\..\lib\NHibernate30\net\3.5\Iesi.Collections.dll
+
+
+ False
+ ..\..\..\lib\NHibernate30\net\3.5\log4net.dll
+
+
+ False
+ ..\..\..\lib\NHibernate30\net\3.5\NHibernate.dll
+
+
+ False
+ ..\..\..\lib\Net\2.0\nunit.framework.dll
+
+
+
+
+
+
+
+
+ Data\NHibernate\AccountCreditDao.cs
+
+
+ Data\NHibernate\AccountDebitDao.cs
+
+
+ Data\NHibernate\AccountManager.cs
+
+
+ Data\NHibernate\AuditDao.cs
+
+
+ Data\NHibernate\Credit.cs
+
+
+ Data\NHibernate\DbProviderTemplateTests.cs
+
+
+ Data\NHibernate\Debit.cs
+
+
+ Data\NHibernate\IAccountCreditDao.cs
+
+
+ Data\NHibernate\IAccountDebitDao.cs
+
+
+ Data\NHibernate\IAccountManager.cs
+
+
+ Data\NHibernate\IAuditDao.cs
+
+
+ Data\NHibernate\ITestObjectDao.cs
+
+
+ Data\NHibernate\MultipleDbTests.cs
+
+
+ Data\NHibernate\NativeNHTestObjectDao.cs
+
+
+ Data\NHibernate\NativeNHTests.cs
+
+
+ Data\NHibernate\NHDAOTests.cs
+
+
+ Data\NHibernate\NHTestObjectDao.cs
+
+
+ Data\NHibernate\TemplateTests.cs
+
+
+ Data\NHibernate\TestObject.cs
+
+
+ Data\NHibernate\HibernateTxScopeTransactionManagerTests.cs
+
+
+
+
+
+ Data\NHibernate\creditdebit.sql
+
+
+
+
+
+ {3A3A4E65-45A6-4B20-B460-0BEDC302C02C}
+ Spring.Aop.2010
+
+
+ {710961A3-0DF4-49E4-A26E-F5B9C044AC84}
+ Spring.Core.2010
+
+
+ {009247FE-CBAD-40FF-ADC3-D7F28B270071}
+ Spring.Data.NHibernate30.2010
+
+
+ {AE00E5AB-C39A-436F-86D2-33BFE33E2E40}
+ Spring.Data.2010
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ False
+ .NET Framework 3.5 SP1 Client Profile
+ false
+
+
+ False
+ .NET Framework 3.5 SP1
+ true
+
+
+ False
+ Windows Installer 3.1
+ true
+
+
+
+
+ Data\NHibernate\HibernateTxScopeTransactionManagerTests.xml
+
+
+
+
+
+ echo "Copying .xml files for tests"
+xcopy "$(ProjectDir)Data" ..\..\..\..\build\VS.Net.2008\Spring.Data.NHibernate30.Integration.Tests\$(ConfigurationName)\ /y /s /q /d
+xcopy "$(ProjectDir)$(TargetFileName).config" ..\..\..\..\build\VS.Net.2008\Spring.Data.NHibernate30.Integration.Tests\$(ConfigurationName)\ /y /s /q
+
+
\ No newline at end of file
diff --git a/test/Spring/Spring.Data.Tests/Data/AdoPlatformTransactionManagerTests.cs b/test/Spring/Spring.Data.Tests/Data/AdoPlatformTransactionManagerTests.cs
index 04dcb7e5..f4c6755f 100644
--- a/test/Spring/Spring.Data.Tests/Data/AdoPlatformTransactionManagerTests.cs
+++ b/test/Spring/Spring.Data.Tests/Data/AdoPlatformTransactionManagerTests.cs
@@ -86,6 +86,7 @@ namespace Spring.Data
mocks.ReplayAll();
AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
TransactionTemplate tt = new TransactionTemplate(tm);
@@ -127,6 +128,8 @@ namespace Spring.Data
mocks.ReplayAll();
AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider");
@@ -171,9 +174,10 @@ namespace Spring.Data
mocks.ReplayAll();
AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
- Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider");
- Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active");
+ Assert.IsFalse(TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider");
+ Assert.IsFalse(TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active");
ITransactionStatus ts = tm.GetTransaction(new DefaultTransactionDefinition());
TestTransactionSynchronization synch =
@@ -201,7 +205,7 @@ namespace Spring.Data
Assert.IsTrue(outerTransactionBoundaryReached);
}
- Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider");
+ Assert.IsFalse(TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider");
Assert.IsFalse(synch.beforeCommitCalled);
Assert.IsTrue(synch.beforeCompletionCalled);
Assert.IsFalse(synch.afterCommitCalled);
@@ -249,6 +253,8 @@ namespace Spring.Data
mocks.ReplayAll();
AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
tt.PropagationBehavior = TransactionPropagation.RequiresNew;
@@ -293,10 +299,14 @@ namespace Spring.Data
mocks.ReplayAll();
AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
tt.PropagationBehavior = TransactionPropagation.RequiresNew;
AdoPlatformTransactionManager tm2 = new AdoPlatformTransactionManager(dbProvider2);
+ tm2.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt2 = new TransactionTemplate(tm2);
tt2.PropagationBehavior = TransactionPropagation.RequiresNew;
@@ -342,6 +352,8 @@ namespace Spring.Data
mocks.ReplayAll();
AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
tt.PropagationBehavior = TransactionPropagation.RequiresNew;
@@ -393,6 +405,8 @@ namespace Spring.Data
mocks.ReplayAll();
AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
tt.PropagationBehavior = TransactionPropagation.RequiresNew;
Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider");
@@ -428,6 +442,8 @@ namespace Spring.Data
mocks.ReplayAll();
AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
tt.PropagationBehavior = TransactionPropagation.RequiresNew;
Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider");
@@ -475,6 +491,8 @@ namespace Spring.Data
mocks.ReplayAll();
AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
tt.PropagationBehavior = TransactionPropagation.Supports;
Assert.IsTrue(!TransactionSynchronizationManager.HasResource(dbProvider), "Hasn't thread db provider");
@@ -513,6 +531,8 @@ namespace Spring.Data
mocks.ReplayAll();
AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
tt.PropagationBehavior = TransactionPropagation.RequiresNew;
tt.TransactionIsolationLevel = IsolationLevel.Serializable;
@@ -573,6 +593,8 @@ namespace Spring.Data
mocks.ReplayAll();
AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
tt.TransactionTimeout = timeout;
@@ -616,6 +638,8 @@ namespace Spring.Data
mocks.ReplayAll();
AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
try
{
@@ -661,6 +685,8 @@ namespace Spring.Data
mocks.ReplayAll();
AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
try
@@ -703,6 +729,8 @@ namespace Spring.Data
mocks.ReplayAll();
AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
tm.RollbackOnCommitFailure = true;
TransactionTemplate tt = new TransactionTemplate(tm);
@@ -747,6 +775,8 @@ namespace Spring.Data
mocks.ReplayAll();
AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
try
@@ -778,6 +808,8 @@ namespace Spring.Data
mocks.ReplayAll();
AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
tt.PropagationBehavior = TransactionPropagation.Supports;
@@ -797,6 +829,8 @@ namespace Spring.Data
mocks.ReplayAll();
AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
tt.PropagationBehavior = TransactionPropagation.NotSupported;
@@ -816,6 +850,8 @@ namespace Spring.Data
mocks.ReplayAll();
AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
tt.PropagationBehavior = TransactionPropagation.Never;
@@ -852,6 +888,8 @@ namespace Spring.Data
mocks.ReplayAll();
AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
tt.PropagationBehavior = TransactionPropagation.Nested;
@@ -896,6 +934,8 @@ namespace Spring.Data
mocks.ReplayAll();
AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
tt.PropagationBehavior = TransactionPropagation.Nested;
@@ -941,6 +981,8 @@ namespace Spring.Data
mocks.ReplayAll();
AdoPlatformTransactionManager tm = new AdoPlatformTransactionManager(dbProvider);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
tt.PropagationBehavior = TransactionPropagation.Nested;
diff --git a/test/Spring/Spring.Data.Tests/Data/Core/ServiceDomainTransactionManagerTests.cs b/test/Spring/Spring.Data.Tests/Data/Core/ServiceDomainTransactionManagerTests.cs
index e72755c1..15fae3e3 100644
--- a/test/Spring/Spring.Data.Tests/Data/Core/ServiceDomainTransactionManagerTests.cs
+++ b/test/Spring/Spring.Data.Tests/Data/Core/ServiceDomainTransactionManagerTests.cs
@@ -78,8 +78,10 @@ namespace Spring.Data.Core
mocks.ReplayAll();
- IPlatformTransactionManager tm = new ServiceDomainPlatformTransactionManager(txAdapter);
+ ServiceDomainPlatformTransactionManager tm = new ServiceDomainPlatformTransactionManager(txAdapter);
TransactionTemplate tt = new TransactionTemplate(tm);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
tt.Execute(new TransactionDelegate(TransactionCommitMethod));
Assert.IsFalse(TransactionSynchronizationManager.SynchronizationActive);
@@ -117,7 +119,9 @@ namespace Spring.Data.Core
mocks.ReplayAll();
- IPlatformTransactionManager tm = new ServiceDomainPlatformTransactionManager(txAdapter);
+ ServiceDomainPlatformTransactionManager tm = new ServiceDomainPlatformTransactionManager(txAdapter);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active");
@@ -183,7 +187,9 @@ namespace Spring.Data.Core
mocks.ReplayAll();
- IPlatformTransactionManager tm = new ServiceDomainPlatformTransactionManager(txAdapter);
+ ServiceDomainPlatformTransactionManager tm = new ServiceDomainPlatformTransactionManager(txAdapter);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
tt.PropagationBehavior = TransactionPropagation.RequiresNew;
tt.Execute(new PropagationRequiresNewWithExistingTransactionCallbackSD(tt));
diff --git a/test/Spring/Spring.Data.Tests/Data/Core/TxScopeTransactionManagerIntegrationTests.cs b/test/Spring/Spring.Data.Tests/Data/Core/TxScopeTransactionManagerIntegrationTests.cs
index 63763334..9c26fd59 100644
--- a/test/Spring/Spring.Data.Tests/Data/Core/TxScopeTransactionManagerIntegrationTests.cs
+++ b/test/Spring/Spring.Data.Tests/Data/Core/TxScopeTransactionManagerIntegrationTests.cs
@@ -65,6 +65,8 @@ namespace Spring.Data.Core
public void Commit()
{
TxScopeTransactionManager tm = new TxScopeTransactionManager();
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
//tt.Name = "txName";
@@ -91,6 +93,8 @@ namespace Spring.Data.Core
public void TransactionInformation()
{
TxScopeTransactionManager tm = new TxScopeTransactionManager();
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
tt.TransactionIsolationLevel = System.Data.IsolationLevel.ReadUncommitted;
tt.Execute(TransactionInformationTxDelegate);
@@ -121,6 +125,8 @@ namespace Spring.Data.Core
TxScopeTransactionManager tm = new TxScopeTransactionManager();
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
tt.TransactionTimeout = 10;
tt.Name = "txName";
diff --git a/test/Spring/Spring.Data.Tests/Data/Core/TxScopeTransactionManagerTests.cs b/test/Spring/Spring.Data.Tests/Data/Core/TxScopeTransactionManagerTests.cs
index 946d8149..47bab5a3 100644
--- a/test/Spring/Spring.Data.Tests/Data/Core/TxScopeTransactionManagerTests.cs
+++ b/test/Spring/Spring.Data.Tests/Data/Core/TxScopeTransactionManagerTests.cs
@@ -68,7 +68,9 @@ namespace Spring.Data.Core
}
mocks.ReplayAll();
- IPlatformTransactionManager tm = new TxScopeTransactionManager(txAdapter);
+ TxScopeTransactionManager tm = new TxScopeTransactionManager(txAdapter);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
tt.Execute(delegate(ITransactionStatus status)
{
@@ -100,7 +102,9 @@ namespace Spring.Data.Core
}
mocks.ReplayAll();
- IPlatformTransactionManager tm = new TxScopeTransactionManager(txAdapter);
+ TxScopeTransactionManager tm = new TxScopeTransactionManager(txAdapter);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active");
@@ -155,7 +159,9 @@ namespace Spring.Data.Core
}
mocks.ReplayAll();
- IPlatformTransactionManager tm = new TxScopeTransactionManager(txAdapter);
+ TxScopeTransactionManager tm = new TxScopeTransactionManager(txAdapter);
+ tm.TransactionSynchronization = TransactionSynchronizationState.Always;
+
TransactionTemplate tt = new TransactionTemplate(tm);
tt.PropagationBehavior = TransactionPropagation.RequiresNew;
tt.Execute(delegate(ITransactionStatus status)
diff --git a/test/Spring/Spring.Data.Tests/Transaction/Support/AbstractPlatformTransactionManagerTests.cs b/test/Spring/Spring.Data.Tests/Transaction/Support/AbstractPlatformTransactionManagerTests.cs
index 87c49ab5..d04e30fb 100644
--- a/test/Spring/Spring.Data.Tests/Transaction/Support/AbstractPlatformTransactionManagerTests.cs
+++ b/test/Spring/Spring.Data.Tests/Transaction/Support/AbstractPlatformTransactionManagerTests.cs
@@ -3,218 +3,219 @@ using NUnit.Framework;
namespace Spring.Transaction.Support
{
- [TestFixture]
- public class AbstractPlatformTransactionManagerTests
- {
- private MockTxnPlatformMgrAbstract _mockTxnMgr;
-
- [SetUp]
- public void Init()
- {
- _mockTxnMgr = new MockTxnPlatformMgrAbstract();
- if ( TransactionSynchronizationManager.SynchronizationActive )
- {
- TransactionSynchronizationManager.ClearSynchronization();
- }
- }
- [TearDown]
- public void Destroy()
- {
- _mockTxnMgr.Verify();
- _mockTxnMgr = null;
- if ( TransactionSynchronizationManager.SynchronizationActive )
- {
- TransactionSynchronizationManager.ClearSynchronization();
- }
- }
- [Test]
- public void VanillaProperties()
- {
- Assert.AreEqual( TransactionSynchronizationState.Always, _mockTxnMgr.TransactionSynchronization);
- Assert.IsTrue(!_mockTxnMgr.NestedTransactionsAllowed);
- Assert.IsTrue(!_mockTxnMgr.RollbackOnCommitFailure);
-
- _mockTxnMgr.NestedTransactionsAllowed = true;
- _mockTxnMgr.RollbackOnCommitFailure = true;
- _mockTxnMgr.TransactionSynchronization = TransactionSynchronizationState.OnActualTransaction;
-
- Assert.AreEqual( TransactionSynchronizationState.OnActualTransaction, _mockTxnMgr.TransactionSynchronization);
- Assert.IsTrue(_mockTxnMgr.NestedTransactionsAllowed);
- Assert.IsTrue(_mockTxnMgr.RollbackOnCommitFailure);
- }
- [Test]
- [ExpectedException(typeof(InvalidTimeoutException), ExpectedMessage="Invalid transaction timeout")]
- public void DefinitionInvalidTimeoutException()
- {
- MockTxnDefinition def = new MockTxnDefinition();
- def.TransactionTimeout = -1000;
- _mockTxnMgr.GetTransaction( def );
- }
- [Test]
- [ExpectedException(typeof(IllegalTransactionStateException), ExpectedMessage="Transaction propagation 'mandatory' but no existing transaction found")]
- public void DefinitionInvalidPropagationState()
- {
- MockTxnDefinition def = new MockTxnDefinition();
- def.PropagationBehavior = TransactionPropagation.Mandatory;
- _mockTxnMgr.GetTransaction( def );
- }
+ [TestFixture]
+ public class AbstractPlatformTransactionManagerTests
+ {
+ private MockTxnPlatformMgrAbstract _mockTxnMgr;
- [Test]
- [ExpectedException(typeof(IllegalTransactionStateException), ExpectedMessage="Transaction propagation 'never' but existing transaction found.")]
- public void NeverPropagateState()
- {
- MockTxnDefinition def = new MockTxnDefinition();
- def.PropagationBehavior = TransactionPropagation.Never;
- setGeneralGetTransactionExpectations();
- _mockTxnMgr.GetTransaction(def);
- }
- [Test]
- [ExpectedException(typeof(NestedTransactionNotSupportedException), ExpectedMessage="Transaction manager does not allow nested transactions by default - specify 'NestedTransactionsAllowed' property with value 'true'")]
- public void NoNestedTransactionsAllowed()
- {
- MockTxnDefinition def = new MockTxnDefinition();
- def.PropagationBehavior = TransactionPropagation.Nested;
- setGeneralGetTransactionExpectations();
- _mockTxnMgr.GetTransaction(def);
- }
- [Test]
- public void TransactionSuspendedSuccessfully()
- {
- MockTxnDefinition def = new MockTxnDefinition();
- def.PropagationBehavior = TransactionPropagation.NotSupported;
- def.ReadOnly = false;
- setGeneralGetTransactionExpectations();
+ [SetUp]
+ public void Init()
+ {
+ _mockTxnMgr = new MockTxnPlatformMgrAbstract();
+ _mockTxnMgr.TransactionSynchronization = TransactionSynchronizationState.Always;
+ if (TransactionSynchronizationManager.SynchronizationActive)
+ {
+ TransactionSynchronizationManager.ClearSynchronization();
+ }
+ }
+ [TearDown]
+ public void Destroy()
+ {
+ _mockTxnMgr.Verify();
+ _mockTxnMgr = null;
+ if (TransactionSynchronizationManager.SynchronizationActive)
+ {
+ TransactionSynchronizationManager.Clear();
+ }
+ }
+ [Test]
+ public void VanillaProperties()
+ {
+ Assert.AreEqual(TransactionSynchronizationState.Always, _mockTxnMgr.TransactionSynchronization);
+ Assert.IsTrue(!_mockTxnMgr.NestedTransactionsAllowed);
+ Assert.IsTrue(!_mockTxnMgr.RollbackOnCommitFailure);
- DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(def);
- Assert.IsNull( status.Transaction );
- Assert.IsTrue( !status.IsNewTransaction );
- Assert.IsTrue( status.NewSynchronization );
- Assert.IsTrue( !status.ReadOnly );
- Assert.IsNotNull( status.SuspendedResources);
- }
- [Test]
- public void TransactionCreatedSuccessfully()
- {
- MockTxnDefinition def = new MockTxnDefinition();
- def.PropagationBehavior = TransactionPropagation.RequiresNew;
- def.ReadOnly = false;
+ _mockTxnMgr.NestedTransactionsAllowed = true;
+ _mockTxnMgr.RollbackOnCommitFailure = true;
+ _mockTxnMgr.TransactionSynchronization = TransactionSynchronizationState.OnActualTransaction;
- setGeneralGetTransactionExpectations();
+ Assert.AreEqual(TransactionSynchronizationState.OnActualTransaction, _mockTxnMgr.TransactionSynchronization);
+ Assert.IsTrue(_mockTxnMgr.NestedTransactionsAllowed);
+ Assert.IsTrue(_mockTxnMgr.RollbackOnCommitFailure);
+ }
+ [Test]
+ [ExpectedException(typeof(InvalidTimeoutException), ExpectedMessage = "Invalid transaction timeout")]
+ public void DefinitionInvalidTimeoutException()
+ {
+ MockTxnDefinition def = new MockTxnDefinition();
+ def.TransactionTimeout = -1000;
+ _mockTxnMgr.GetTransaction(def);
+ }
+ [Test]
+ [ExpectedException(typeof(IllegalTransactionStateException), ExpectedMessage = "Transaction propagation 'mandatory' but no existing transaction found")]
+ public void DefinitionInvalidPropagationState()
+ {
+ MockTxnDefinition def = new MockTxnDefinition();
+ def.PropagationBehavior = TransactionPropagation.Mandatory;
+ _mockTxnMgr.GetTransaction(def);
+ }
- DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(def);
- Assert.AreEqual( _mockTxnMgr.Transaction, status.Transaction );
- Assert.IsTrue( status.IsNewTransaction );
- Assert.IsTrue( status.NewSynchronization );
- Assert.IsTrue( !status.ReadOnly );
- Assert.IsNotNull( status.SuspendedResources);
- }
- [Test]
- public void NestedTransactionSuccessfully()
- {
- MockTxnDefinition def = new MockTxnDefinition();
- def.PropagationBehavior = TransactionPropagation.Nested;
- def.ReadOnly = false;
+ [Test]
+ [ExpectedException(typeof(IllegalTransactionStateException), ExpectedMessage = "Transaction propagation 'never' but existing transaction found.")]
+ public void NeverPropagateState()
+ {
+ MockTxnDefinition def = new MockTxnDefinition();
+ def.PropagationBehavior = TransactionPropagation.Never;
+ setGeneralGetTransactionExpectations();
+ _mockTxnMgr.GetTransaction(def);
+ }
+ [Test]
+ [ExpectedException(typeof(NestedTransactionNotSupportedException), ExpectedMessage = "Transaction manager does not allow nested transactions by default - specify 'NestedTransactionsAllowed' property with value 'true'")]
+ public void NoNestedTransactionsAllowed()
+ {
+ MockTxnDefinition def = new MockTxnDefinition();
+ def.PropagationBehavior = TransactionPropagation.Nested;
+ setGeneralGetTransactionExpectations();
+ _mockTxnMgr.GetTransaction(def);
+ }
+ [Test]
+ public void TransactionSuspendedSuccessfully()
+ {
+ MockTxnDefinition def = new MockTxnDefinition();
+ def.PropagationBehavior = TransactionPropagation.NotSupported;
+ def.ReadOnly = false;
+ setGeneralGetTransactionExpectations();
- setGeneralGetTransactionExpectations();
- _mockTxnMgr.SetExpectedCalls( "DoBegin", 1 );
- _mockTxnMgr.Savepoints = false;
- _mockTxnMgr.NestedTransactionsAllowed = true;
+ DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(def);
+ Assert.IsNull(status.Transaction);
+ Assert.IsTrue(!status.IsNewTransaction);
+ Assert.IsTrue(status.NewSynchronization);
+ Assert.IsTrue(!status.ReadOnly);
+ Assert.IsNotNull(status.SuspendedResources);
+ }
+ [Test]
+ public void TransactionCreatedSuccessfully()
+ {
+ MockTxnDefinition def = new MockTxnDefinition();
+ def.PropagationBehavior = TransactionPropagation.RequiresNew;
+ def.ReadOnly = false;
- DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(def);
- Assert.AreEqual( _mockTxnMgr.Transaction, status.Transaction );
- Assert.IsTrue( status.IsNewTransaction );
- Assert.AreEqual( true, status.NewSynchronization );
- Assert.IsTrue( !status.ReadOnly );
- Assert.IsNull( status.SuspendedResources);
- }
+ setGeneralGetTransactionExpectations();
- [Test]
- public void NestedTransactionWithSavepoint()
- {
- MockTxnDefinition def = new MockTxnDefinition();
- def.PropagationBehavior = TransactionPropagation.Nested;
- def.ReadOnly = false;
- setVanillaGetTransactionExpectations();
- _mockTxnMgr.SetTransaction( new MyMockTxnObjectSavepointMgr());
- _mockTxnMgr.SetExpectedCalls("DoBegin", 0);
- _mockTxnMgr.Savepoints = true;
- _mockTxnMgr.NestedTransactionsAllowed = true;
+ DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(def);
+ Assert.AreEqual(_mockTxnMgr.Transaction, status.Transaction);
+ Assert.IsTrue(status.IsNewTransaction);
+ Assert.IsTrue(status.NewSynchronization);
+ Assert.IsTrue(!status.ReadOnly);
+ Assert.IsNotNull(status.SuspendedResources);
+ }
+ [Test]
+ public void NestedTransactionSuccessfully()
+ {
+ MockTxnDefinition def = new MockTxnDefinition();
+ def.PropagationBehavior = TransactionPropagation.Nested;
+ def.ReadOnly = false;
- DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(def);
- Assert.AreEqual( _mockTxnMgr.Transaction, status.Transaction );
- Assert.IsFalse( status.IsNewTransaction );
- Assert.IsFalse( status.NewSynchronization );
- Assert.IsTrue( !status.ReadOnly );
- Assert.IsNull( status.SuspendedResources);
- }
- [Test]
- public void DefaultPropagationBehavior()
- {
- MockTxnDefinition def = new MockTxnDefinition();
- def.PropagationBehavior = TransactionPropagation.Required;
- def.ReadOnly = true;
- setGeneralGetTransactionExpectations();
+ setGeneralGetTransactionExpectations();
+ _mockTxnMgr.SetExpectedCalls("DoBegin", 1);
+ _mockTxnMgr.Savepoints = false;
+ _mockTxnMgr.NestedTransactionsAllowed = true;
- DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(def);
- Assert.AreEqual( _mockTxnMgr.Transaction, status.Transaction );
- Assert.IsTrue( !status.IsNewTransaction );
- Assert.IsTrue( status.NewSynchronization );
- Assert.IsTrue( status.ReadOnly );
- Assert.IsNull( status.SuspendedResources);
- }
- [Test]
- public void DefaultPropagationBehaviorWithNullDefinition()
- {
- setGeneralGetTransactionExpectations();
+ DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(def);
+ Assert.AreEqual(_mockTxnMgr.Transaction, status.Transaction);
+ Assert.IsTrue(status.IsNewTransaction);
+ Assert.AreEqual(true, status.NewSynchronization);
+ Assert.IsTrue(!status.ReadOnly);
+ Assert.IsNull(status.SuspendedResources);
+ }
- DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(null);
- Assert.AreEqual( _mockTxnMgr.Transaction, status.Transaction );
- Assert.IsTrue( !status.IsNewTransaction );
- Assert.IsTrue( status.NewSynchronization );
- Assert.IsTrue( !status.ReadOnly );
- Assert.IsNull( status.SuspendedResources);
- }
- [Test]
- public void DefaultNoExistingTransaction()
- {
- setVanillaGetTransactionExpectations();
- _mockTxnMgr.SetExpectedCalls( "DoBegin", 1 );
+ [Test]
+ public void NestedTransactionWithSavepoint()
+ {
+ MockTxnDefinition def = new MockTxnDefinition();
+ def.PropagationBehavior = TransactionPropagation.Nested;
+ def.ReadOnly = false;
+ setVanillaGetTransactionExpectations();
+ _mockTxnMgr.SetTransaction(new MyMockTxnObjectSavepointMgr());
+ _mockTxnMgr.SetExpectedCalls("DoBegin", 0);
+ _mockTxnMgr.Savepoints = true;
+ _mockTxnMgr.NestedTransactionsAllowed = true;
- DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(null);
- Assert.IsNotNull( status.Transaction );
- Assert.IsTrue( status.IsNewTransaction );
- Assert.IsTrue( status.NewSynchronization );
- Assert.IsTrue( !status.ReadOnly );
- Assert.IsNull( status.SuspendedResources);
- }
- [Test]
- public void DefaultBehaviorDefaultPropagationNoExistingTransaction()
- {
- setVanillaGetTransactionExpectations();
- _mockTxnMgr.SetExpectedCalls( "DoBegin", 0 );
-
- MockTxnDefinition def = new MockTxnDefinition();
- def.PropagationBehavior = TransactionPropagation.Never;
- def.ReadOnly = true;
+ DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(def);
+ Assert.AreEqual(_mockTxnMgr.Transaction, status.Transaction);
+ Assert.IsFalse(status.IsNewTransaction);
+ Assert.IsFalse(status.NewSynchronization);
+ Assert.IsTrue(!status.ReadOnly);
+ Assert.IsNull(status.SuspendedResources);
+ }
+ [Test]
+ public void DefaultPropagationBehavior()
+ {
+ MockTxnDefinition def = new MockTxnDefinition();
+ def.PropagationBehavior = TransactionPropagation.Required;
+ def.ReadOnly = true;
+ setGeneralGetTransactionExpectations();
- DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(def);
- Assert.IsNull( status.Transaction );
- Assert.IsTrue( !status.IsNewTransaction );
- Assert.IsTrue( status.NewSynchronization );
- Assert.IsTrue( status.ReadOnly );
- Assert.IsNull( status.SuspendedResources);
- }
- private void setGeneralGetTransactionExpectations()
- {
- _mockTxnMgr.SetTransaction( new object() );
- setVanillaGetTransactionExpectations();
- }
+ DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(def);
+ Assert.AreEqual(_mockTxnMgr.Transaction, status.Transaction);
+ Assert.IsTrue(!status.IsNewTransaction);
+ Assert.IsTrue(status.NewSynchronization);
+ Assert.IsTrue(status.ReadOnly);
+ Assert.IsNull(status.SuspendedResources);
+ }
+ [Test]
+ public void DefaultPropagationBehaviorWithNullDefinition()
+ {
+ setGeneralGetTransactionExpectations();
- private void setVanillaGetTransactionExpectations()
- {
- _mockTxnMgr.SetExpectedCalls( "DoGetTransaction", 1);
- _mockTxnMgr.SetExpectedCalls( "IsExistingTransaction", 1);
- }
+ DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(null);
+ Assert.AreEqual(_mockTxnMgr.Transaction, status.Transaction);
+ Assert.IsFalse(status.IsNewTransaction);
+ Assert.IsTrue(status.NewSynchronization);
+ Assert.IsFalse(status.ReadOnly);
+ Assert.IsNull(status.SuspendedResources);
+ }
+ [Test]
+ public void DefaultNoExistingTransaction()
+ {
+ setVanillaGetTransactionExpectations();
+ _mockTxnMgr.SetExpectedCalls("DoBegin", 1);
- }
+ DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(null);
+ Assert.IsNotNull(status.Transaction);
+ Assert.IsTrue(status.IsNewTransaction);
+ Assert.IsTrue(status.NewSynchronization);
+ Assert.IsTrue(!status.ReadOnly);
+ Assert.IsNull(status.SuspendedResources);
+ }
+ [Test]
+ public void DefaultBehaviorDefaultPropagationNoExistingTransaction()
+ {
+ setVanillaGetTransactionExpectations();
+ _mockTxnMgr.SetExpectedCalls("DoBegin", 0);
+
+ MockTxnDefinition def = new MockTxnDefinition();
+ def.PropagationBehavior = TransactionPropagation.Never;
+ def.ReadOnly = true;
+
+ DefaultTransactionStatus status = (DefaultTransactionStatus)_mockTxnMgr.GetTransaction(def);
+ Assert.IsNull(status.Transaction);
+ Assert.IsTrue(!status.IsNewTransaction);
+ Assert.IsTrue(status.NewSynchronization);
+ Assert.IsTrue(status.ReadOnly);
+ Assert.IsNull(status.SuspendedResources);
+ }
+ private void setGeneralGetTransactionExpectations()
+ {
+ _mockTxnMgr.SetTransaction(new object());
+ setVanillaGetTransactionExpectations();
+ }
+
+ private void setVanillaGetTransactionExpectations()
+ {
+ _mockTxnMgr.SetExpectedCalls("DoGetTransaction", 1);
+ _mockTxnMgr.SetExpectedCalls("IsExistingTransaction", 1);
+ }
+
+ }
}
diff --git a/test/Spring/Spring.Web.Tests/TestSupport/WebApplicationTests.cs b/test/Spring/Spring.Web.Tests/TestSupport/WebApplicationTests.cs
index 0c679664..2802a2a7 100644
--- a/test/Spring/Spring.Web.Tests/TestSupport/WebApplicationTests.cs
+++ b/test/Spring/Spring.Web.Tests/TestSupport/WebApplicationTests.cs
@@ -1,19 +1,19 @@
#region License
-/*
- * Copyright 2002-2010 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.
+/*
+ * Copyright 2002-2010 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