fix for SPRNET-1260. DefensiveEventRaiser now rethrows first caught exception, if any

This commit is contained in:
eeichinger
2009-11-16 13:09:16 +00:00
parent c9d328441f
commit c04397943c
7 changed files with 98 additions and 17 deletions

View File

@@ -402,7 +402,13 @@ namespace Spring.Context.Support
/// </param>
protected virtual void OnContextEvent(object source, ApplicationEventArgs e)
{
_eventRaiser.Raise(ContextEvent, source, e);
IEventExceptionsCollector exceptions = _eventRaiser.Raise(ContextEvent, source, e);
if (exceptions.HasExceptions)
{
Delegate target = ContextEvent.GetInvocationList()[0];
Exception exception = (Exception) exceptions[target];
throw new ApplicationContextException(string.Format("An unhandled exception occured during processing application event {0} in handler {1}", e.GetType(), target.Method), exception);
}
}
/// <summary>

View File

@@ -1112,6 +1112,7 @@
<Compile Include="Util\ConfigXmlElement.cs" />
<Compile Include="Util\FatalReflectionException.cs" />
<Compile Include="Util\IChainableConfigSystem.cs" />
<Compile Include="Util\IEventExceptionsCollector.cs" />
<Compile Include="Util\IoUtils.cs" />
<Compile Include="Util\ITextPosition.cs" />
<Compile Include="Util\ObjectUtils.cs" />

View File

@@ -18,16 +18,12 @@
#endregion
#region Imports
using System;
using System.Collections;
using System.Diagnostics;
using System.Reflection;
using Common.Logging;
#endregion
namespace Spring.Util
{
/// <summary>
@@ -36,6 +32,41 @@ namespace Spring.Util
/// <author>Rick Evans</author>
public class EventRaiser
{
protected class EventExceptionsCollector : IEventExceptionsCollector
{
private readonly Hashtable _eventExceptions;
public EventExceptionsCollector()
{
_eventExceptions = new Hashtable();
}
public bool HasExceptions
{
get { return _eventExceptions.Count > 0; }
}
public Delegate[] Sources
{
get { return (Delegate[]) CollectionUtils.ToArray(_eventExceptions.Keys, typeof(Delegate)); }
}
public Exception[] Exceptions
{
get { return (Exception[]) CollectionUtils.ToArray(_eventExceptions.Values, typeof (Exception)); }
}
public Exception this[Delegate source]
{
get { return (Exception) _eventExceptions[source]; }
}
public void Add(Delegate source, Exception exception)
{
_eventExceptions.Add(source, exception);
}
}
protected readonly ILog Log;
/// <summary>
@@ -54,19 +85,19 @@ namespace Spring.Util
/// <param name="source">The event to be raised.</param>
/// <param name="arguments">The arguments to the event.</param>
/// <returns>a map of sink/exception entries that occurred during event raising</returns>
public virtual IDictionary Raise (Delegate source, params object [] arguments)
public virtual IEventExceptionsCollector Raise(Delegate source, params object[] arguments)
{
if (source == null)
{
return null;
}
IDictionary exceptions = null;
EventExceptionsCollector exceptions = new EventExceptionsCollector();
Delegate [] delegates = source.GetInvocationList ();
foreach (Delegate sink in delegates)
{
exceptions = Invoke (sink, arguments, exceptions);
Invoke (sink, arguments, exceptions);
}
return exceptions;
@@ -79,17 +110,18 @@ namespace Spring.Util
/// <param name="sink">The sink to be invoked.</param>
/// <param name="arguments">The arguments to the sink.</param>
/// <param name="exceptions">the map of sink/exception entries to add any exception to</param>
protected virtual IDictionary Invoke (Delegate sink, object [] arguments, IDictionary exceptions)
protected virtual void Invoke(Delegate sink, object[] arguments, EventExceptionsCollector exceptions)
{
try
{
sink.DynamicInvoke (arguments);
return exceptions;
}
catch (TargetInvocationException ex)
{
// unwrap the exception that actually caused the TargetInvocationException and throw that...
throw ReflectionUtils.UnwrapTargetInvocationException(ex);
Exception cause = ReflectionUtils.UnwrapTargetInvocationException(ex);
exceptions.Add(sink, cause);
throw cause;
}
}
}
@@ -114,7 +146,7 @@ namespace Spring.Util
/// <param name="sink">The sink to be invoked.</param>
/// <param name="arguments">The arguments to the sink.</param>
/// <param name="exceptions">the map of sink/exception entries to add any exception to</param>
protected override IDictionary Invoke(Delegate sink, object[] arguments, IDictionary exceptions)
protected override void Invoke(Delegate sink, object[] arguments, EventExceptionsCollector exceptions)
{
try
{
@@ -123,10 +155,8 @@ namespace Spring.Util
catch(Exception ex)
{
Log.Warn("Error during raising an event from " + new StackTrace(), ex);
if (exceptions == null) exceptions = new Hashtable();
exceptions.Add(sink, ex);
}
return exceptions;
}
}
}

View File

@@ -0,0 +1,12 @@
using System;
namespace Spring.Util
{
public interface IEventExceptionsCollector
{
bool HasExceptions { get; }
Delegate[] Sources { get;}
Exception[] Exceptions { get; }
Exception this[Delegate source] { get; }
}
}

View File

@@ -63,6 +63,38 @@ namespace Spring.Context.Support
}
#if NET_2_0
[Test]
public void ExecutesAllContextEventHandlersAndRethrowsExceptionsThrownDuringContextEventHandlingByDefault()
{
MockApplicationContext appCtx = new MockApplicationContext();
bool secondHandlerExecuted = false;
appCtx.ContextEvent += new ApplicationEventHandler(delegate(object sender, ApplicationEventArgs e)
{
throw new ApplicationException("dummy");
} );
appCtx.ContextEvent += new ApplicationEventHandler(delegate(object sender, ApplicationEventArgs e)
{
secondHandlerExecuted = true;
} );
ApplicationException resultException = null;
try
{
appCtx.PublishEvent(this, new ApplicationEventArgs());
Assert.Fail();
}
catch (ApplicationContextException e)
{
resultException = (ApplicationException) e.GetBaseException();
}
Assert.AreEqual("dummy", resultException.Message);
Assert.IsTrue(secondHandlerExecuted);
}
#endif
[Test]
public void DoesNotSearchParentContextForMessageSource()
{

View File

@@ -61,9 +61,9 @@ namespace Spring.Util
DefensiveEventRaiser eventRaiser = new DefensiveEventRaiser();
IDictionary exceptions = bru.OnPop( "Iron Brew", eventRaiser );
IEventExceptionsCollector exceptions = bru.OnPop( "Iron Brew", eventRaiser );
Assert.AreEqual(1, exceptions.Count);
Assert.AreEqual(1, exceptions.Exceptions.Length);
Assert.IsTrue(firstCall);
Assert.IsTrue(secondCall);
Assert.IsTrue(thirdCall);

View File

@@ -81,7 +81,7 @@ namespace Spring.Util
{
public event PopHandler Pop;
public IDictionary OnPop (string soda, EventRaiser raiser)
public IEventExceptionsCollector OnPop (string soda, EventRaiser raiser)
{
return raiser.Raise (Pop, this, soda);
}