fix for SPRNET-1260. DefensiveEventRaiser now rethrows first caught exception, if any
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
src/Spring/Spring.Core/Util/IEventExceptionsCollector.cs
Normal file
12
src/Spring/Spring.Core/Util/IEventExceptionsCollector.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user