From 31c006ed4414c8ff70fbc8900ce53095843eec6b Mon Sep 17 00:00:00 2001 From: "josh.robb" Date: Thu, 15 Nov 2007 12:46:49 +0000 Subject: [PATCH] Applied patch from Stiiifff adding a TransactionFailed event on ITransactionManager service. git-svn-id: https://svn.castleproject.org/svn/castle/trunk@4489 73e77b4c-caa6-f847-a29a-24ab75ae54b6 --- .../ResourceImpl.cs | 6 +- .../ThrowsExceptionResource.cs | 6 +- .../TransactionManagerTestCase.cs | 204 +++++++++++++++++++++ .../DefaultTransactionManager.cs | 21 ++- .../ITransactionManager.cs | 9 +- .../StandardTransaction.cs | 47 ++++- Services/Transaction/Changes.txt | 3 + 7 files changed, 285 insertions(+), 11 deletions(-) diff --git a/Services/Transaction/Castle.Services.Transaction.Tests/ResourceImpl.cs b/Services/Transaction/Castle.Services.Transaction.Tests/ResourceImpl.cs index f0c53dbfe..70b370197 100644 --- a/Services/Transaction/Castle.Services.Transaction.Tests/ResourceImpl.cs +++ b/Services/Transaction/Castle.Services.Transaction.Tests/ResourceImpl.cs @@ -44,21 +44,21 @@ namespace Castle.Services.Transaction.Tests #region IResource Members - public void Start() + public virtual void Start() { if (_started) throw new ApplicationException("Start called before"); _started = true; } - public void Rollback() + public virtual void Rollback() { if (_rolledback) throw new ApplicationException("Rollback called before"); _rolledback = true; } - public void Commit() + public virtual void Commit() { if (_committed) throw new ApplicationException("Commit called before"); diff --git a/Services/Transaction/Castle.Services.Transaction.Tests/ThrowsExceptionResource.cs b/Services/Transaction/Castle.Services.Transaction.Tests/ThrowsExceptionResource.cs index 54b3962ec..700570a89 100644 --- a/Services/Transaction/Castle.Services.Transaction.Tests/ThrowsExceptionResource.cs +++ b/Services/Transaction/Castle.Services.Transaction.Tests/ThrowsExceptionResource.cs @@ -27,7 +27,7 @@ namespace Castle.Services.Transaction.Tests this.throwOnRollback = throwOnRollback; } - public new void Rollback() + public override void Rollback() { if (throwOnRollback) { @@ -37,7 +37,7 @@ namespace Castle.Services.Transaction.Tests base.Rollback(); } - public new void Commit() + public override void Commit() { if (throwOnCommit) { @@ -47,4 +47,4 @@ namespace Castle.Services.Transaction.Tests base.Commit(); } } -} \ No newline at end of file +} diff --git a/Services/Transaction/Castle.Services.Transaction.Tests/TransactionManagerTestCase.cs b/Services/Transaction/Castle.Services.Transaction.Tests/TransactionManagerTestCase.cs index 67542c8ed..9fef9d056 100644 --- a/Services/Transaction/Castle.Services.Transaction.Tests/TransactionManagerTestCase.cs +++ b/Services/Transaction/Castle.Services.Transaction.Tests/TransactionManagerTestCase.cs @@ -148,5 +148,209 @@ namespace Castle.Services.Transaction.Tests transaction.Commit(); } + + [Test] + public void TransactionCreatedEvent() + { + bool transactionCreatedEventTriggered = false; + + tm.TransactionCreated += delegate { transactionCreatedEventTriggered = true; }; + + Assert.IsFalse(transactionCreatedEventTriggered); + + ITransaction transaction = tm.CreateTransaction( + TransactionMode.Requires, IsolationMode.Unspecified); + + Assert.IsTrue(transactionCreatedEventTriggered); + } + + [Test] + public void TransactionDisposedEvent() + { + bool transactionDisposedEventTriggered = false; + + tm.TransactionDisposed += delegate { transactionDisposedEventTriggered = true; }; + + ITransaction transaction = tm.CreateTransaction( + TransactionMode.Requires, IsolationMode.Unspecified); + + Assert.IsFalse(transactionDisposedEventTriggered); + + transaction.Begin(); + + Assert.IsFalse(transactionDisposedEventTriggered); + + transaction.Commit(); + + Assert.IsFalse(transactionDisposedEventTriggered); + + tm.Dispose(transaction); + + Assert.IsTrue(transactionDisposedEventTriggered); + } + + [Test] + public void TransactionCommittedEvent() + { + bool transactionCommittedEventTriggered = false; + bool transactionRolledBackEventTriggered = false; + bool transactionFailedEventTriggered = false; + + tm.TransactionCommitted += delegate { transactionCommittedEventTriggered = true; }; + tm.TransactionRolledback += delegate { transactionRolledBackEventTriggered = true; }; + tm.TransactionFailed += delegate { transactionFailedEventTriggered = true; }; + + ITransaction transaction = tm.CreateTransaction( + TransactionMode.Requires, IsolationMode.Unspecified); + + ResourceImpl resource = new ResourceImpl(); + + transaction.Enlist(resource); + + Assert.IsFalse(transactionCommittedEventTriggered); + Assert.IsFalse(transactionRolledBackEventTriggered); + Assert.IsFalse(transactionFailedEventTriggered); + + transaction.Begin(); + + Assert.IsFalse(transactionCommittedEventTriggered); + Assert.IsFalse(transactionRolledBackEventTriggered); + Assert.IsFalse(transactionFailedEventTriggered); + + transaction.Commit(); + + Assert.IsTrue(transactionCommittedEventTriggered); + Assert.IsFalse(transactionRolledBackEventTriggered); + Assert.IsFalse(transactionFailedEventTriggered); + } + + [Test] + public void TransactionRolledBackEvent() + { + bool transactionCommittedEventTriggered = false; + bool transactionRolledBackEventTriggered = false; + bool transactionFailedEventTriggered = false; + + tm.TransactionCommitted += delegate { transactionCommittedEventTriggered = true; }; + tm.TransactionRolledback += delegate { transactionRolledBackEventTriggered = true; }; + tm.TransactionFailed += delegate { transactionFailedEventTriggered = true; }; + + ITransaction transaction = tm.CreateTransaction( + TransactionMode.Requires, IsolationMode.Unspecified); + + ResourceImpl resource = new ResourceImpl(); + + transaction.Enlist(resource); + + Assert.IsFalse(transactionCommittedEventTriggered); + Assert.IsFalse(transactionRolledBackEventTriggered); + Assert.IsFalse(transactionFailedEventTriggered); + + transaction.Begin(); + + Assert.IsFalse(transactionCommittedEventTriggered); + Assert.IsFalse(transactionRolledBackEventTriggered); + Assert.IsFalse(transactionFailedEventTriggered); + + transaction.Rollback(); + + Assert.IsFalse(transactionCommittedEventTriggered); + Assert.IsTrue(transactionRolledBackEventTriggered); + Assert.IsFalse(transactionFailedEventTriggered); + } + + [Test] + public void TransactionFailedOnCommitEvent() + { + bool transactionCommittedEventTriggered = false; + bool transactionRolledBackEventTriggered = false; + bool transactionFailedEventTriggered = false; + + tm.TransactionCommitted += delegate { transactionCommittedEventTriggered = true; }; + tm.TransactionRolledback += delegate { transactionRolledBackEventTriggered = true; }; + tm.TransactionFailed += delegate { transactionFailedEventTriggered = true; }; + + ITransaction transaction = tm.CreateTransaction( + TransactionMode.Requires, IsolationMode.Unspecified); + + ResourceImpl resource = new ThrowsExceptionResourceImpl(true, false); + + transaction.Enlist(resource); + + Assert.IsFalse(transactionCommittedEventTriggered); + Assert.IsFalse(transactionRolledBackEventTriggered); + Assert.IsFalse(transactionFailedEventTriggered); + + transaction.Begin(); + + Assert.IsFalse(transactionCommittedEventTriggered); + Assert.IsFalse(transactionRolledBackEventTriggered); + Assert.IsFalse(transactionFailedEventTriggered); + + TransactionException exception = null; + + try + { + transaction.Commit(); + } + catch (TransactionException transactionError) + { + exception = transactionError; + } + + Assert.IsFalse(transactionCommittedEventTriggered); + Assert.IsFalse(transactionRolledBackEventTriggered); + Assert.IsTrue(transactionFailedEventTriggered); + + Assert.IsNotNull(exception); + Assert.IsInstanceOfType(typeof(CommitResourceException), exception); + } + + [Test] + public void TransactionFailedOnRollbackEvent() + { + bool transactionCommittedEventTriggered = false; + bool transactionRolledBackEventTriggered = false; + bool transactionFailedEventTriggered = false; + + tm.TransactionCommitted += delegate { transactionCommittedEventTriggered = true; }; + tm.TransactionRolledback += delegate { transactionRolledBackEventTriggered = true; }; + tm.TransactionFailed += delegate { transactionFailedEventTriggered = true; }; + + ITransaction transaction = tm.CreateTransaction( + TransactionMode.Requires, IsolationMode.Unspecified); + + ResourceImpl resource = new ThrowsExceptionResourceImpl(false, true); + + transaction.Enlist(resource); + + Assert.IsFalse(transactionCommittedEventTriggered); + Assert.IsFalse(transactionRolledBackEventTriggered); + Assert.IsFalse(transactionFailedEventTriggered); + + transaction.Begin(); + + Assert.IsFalse(transactionCommittedEventTriggered); + Assert.IsFalse(transactionRolledBackEventTriggered); + Assert.IsFalse(transactionFailedEventTriggered); + + TransactionException exception = null; + + try + { + transaction.Rollback(); + } + catch (TransactionException transactionError) + { + exception = transactionError; + } + + Assert.IsFalse(transactionCommittedEventTriggered); + Assert.IsFalse(transactionRolledBackEventTriggered); + Assert.IsTrue(transactionFailedEventTriggered); + + Assert.IsNotNull(exception); + Assert.IsInstanceOfType(typeof(RollbackResourceException), exception); + } } } diff --git a/Services/Transaction/Castle.Services.Transaction/DefaultTransactionManager.cs b/Services/Transaction/Castle.Services.Transaction/DefaultTransactionManager.cs index a8d26683a..9fbf6cac9 100644 --- a/Services/Transaction/Castle.Services.Transaction/DefaultTransactionManager.cs +++ b/Services/Transaction/Castle.Services.Transaction/DefaultTransactionManager.cs @@ -26,6 +26,7 @@ namespace Castle.Services.Transaction private static readonly object TransactionCreatedEvent = new object(); private static readonly object TransactionCommittedEvent = new object(); private static readonly object TransactionRolledbackEvent = new object(); + private static readonly object TransactionFailedEvent = new object(); private static readonly object TransactionDisposedEvent = new object(); private static readonly object ChildTransactionCreatedEvent = new object(); @@ -164,7 +165,9 @@ namespace Castle.Services.Transaction { return new StandardTransaction( new TransactionDelegate(RaiseTransactionCommitted), - new TransactionDelegate(RaiseTransactionRolledback), transactionMode, isolationMode, distributedTransaction); + new TransactionDelegate(RaiseTransactionRolledback), + new TransactionErrorDelegate(RaiseTransactionFailed), + transactionMode, isolationMode, distributedTransaction); } public virtual ITransaction CurrentTransaction @@ -198,6 +201,12 @@ namespace Castle.Services.Transaction remove { events.RemoveHandler(TransactionRolledbackEvent, value); } } + public event TransactionErrorDelegate TransactionFailed + { + add { events.AddHandler(TransactionFailedEvent, value); } + remove { events.RemoveHandler(TransactionFailedEvent, value); } + } + public event TransactionDelegate TransactionDisposed { add { events.AddHandler(TransactionDisposedEvent, value); } @@ -227,6 +236,16 @@ namespace Castle.Services.Transaction } } + protected void RaiseTransactionFailed(ITransaction transaction, TransactionException exception) + { + TransactionErrorDelegate eventDelegate = (TransactionErrorDelegate)events[TransactionFailedEvent]; + + if (eventDelegate != null) + { + eventDelegate(transaction, exception); + } + } + protected void RaiseTransactionDisposed(ITransaction transaction) { TransactionDelegate eventDelegate = (TransactionDelegate) events[TransactionDisposedEvent]; diff --git a/Services/Transaction/Castle.Services.Transaction/ITransactionManager.cs b/Services/Transaction/Castle.Services.Transaction/ITransactionManager.cs index c26670028..94f7c6b0d 100644 --- a/Services/Transaction/Castle.Services.Transaction/ITransactionManager.cs +++ b/Services/Transaction/Castle.Services.Transaction/ITransactionManager.cs @@ -17,6 +17,8 @@ namespace Castle.Services.Transaction public delegate void TransactionCreationInfoDelegate(ITransaction transaction, TransactionMode transactionMode, IsolationMode isolationMode, bool distributedTransaction); public delegate void TransactionDelegate(ITransaction transaction); + + public delegate void TransactionErrorDelegate(ITransaction transaction, TransactionException transactionError); /// /// Manages the creation and disposal of instances. @@ -47,6 +49,11 @@ namespace Castle.Services.Transaction /// Raised when the transaction was disposed /// event TransactionDelegate TransactionDisposed; + + /// + /// Raised when the transaction has failed on commit/rollback + /// + event TransactionErrorDelegate TransactionFailed; /// /// Creates a transaction. @@ -80,4 +87,4 @@ namespace Castle.Services.Transaction /// void Dispose(ITransaction transaction); } -} \ No newline at end of file +} diff --git a/Services/Transaction/Castle.Services.Transaction/StandardTransaction.cs b/Services/Transaction/Castle.Services.Transaction/StandardTransaction.cs index 881655d98..e879c36fe 100644 --- a/Services/Transaction/Castle.Services.Transaction/StandardTransaction.cs +++ b/Services/Transaction/Castle.Services.Transaction/StandardTransaction.cs @@ -14,6 +14,7 @@ namespace Castle.Services.Transaction { + using System; using System.Collections; /// @@ -23,6 +24,7 @@ namespace Castle.Services.Transaction { private readonly TransactionDelegate onTransactionCommitted; private readonly TransactionDelegate onTransactionRolledback; + private readonly TransactionErrorDelegate onTransactionFailed; private IList children = ArrayList.Synchronized( new ArrayList() ); private bool rollbackOnly; @@ -32,11 +34,12 @@ namespace Castle.Services.Transaction } public StandardTransaction(TransactionDelegate onTransactionCommitted, TransactionDelegate onTransactionRolledback, - TransactionMode transactionMode, IsolationMode isolationMode, bool distributedTransaction) : + TransactionErrorDelegate onTransactionFailed, TransactionMode transactionMode, IsolationMode isolationMode, bool distributedTransaction) : this(transactionMode, isolationMode, distributedTransaction) { this.onTransactionCommitted = onTransactionCommitted; this.onTransactionRolledback = onTransactionRolledback; + this.onTransactionFailed = onTransactionFailed; } public StandardTransaction(TransactionMode transactionMode, IsolationMode isolationMode, bool distributedTransaction) : @@ -65,14 +68,52 @@ namespace Castle.Services.Transaction throw new TransactionException("Can't commit as transaction was marked as 'rollback only'"); } - base.Commit(); + try + { + base.Commit(); + } + catch (TransactionException transactionError) + { + if (onTransactionFailed != null) + { + try + { + onTransactionFailed(this, transactionError); + } + catch (Exception e) + { + // Log exception & swallow it + Logger.Warn("An error occured while notifying caller about transaction commit failure.", e); + } + } + throw; + } if (onTransactionCommitted != null) onTransactionCommitted(this); } public override void Rollback() { - base.Rollback(); + try + { + base.Rollback(); + } + catch (TransactionException transactionError) + { + if (onTransactionFailed != null) + { + try + { + onTransactionFailed(this, transactionError); + } + catch (Exception e) + { + // Log exception & swallow it + Logger.Warn("An error occured while notifying caller about transaction rollback failure.", e); + } + } + throw; + } if (onTransactionRolledback != null) onTransactionRolledback(this); } diff --git a/Services/Transaction/Changes.txt b/Services/Transaction/Changes.txt index 5a3bfe87e..97f333a60 100644 --- a/Services/Transaction/Changes.txt +++ b/Services/Transaction/Changes.txt @@ -1,3 +1,6 @@ +November, 2007 +- Applied patch from Stiiifff adding a TransactionFailed event on ITransactionManager service + May, 2007 - Applied Ron Grabowski's patch fixing SERVICES-9 -- 2.11.4.GIT