diff --git a/src/Spring/Spring.Core/Context/Events/ContextEventArgs.cs b/src/Spring/Spring.Core/Context/Events/ContextEventArgs.cs index b8fea4ac..8621d73f 100644 --- a/src/Spring/Spring.Core/Context/Events/ContextEventArgs.cs +++ b/src/Spring/Spring.Core/Context/Events/ContextEventArgs.cs @@ -49,7 +49,7 @@ namespace Spring.Context.Events Closed } ; - private ContextEvent _contextEvent; + private readonly ContextEvent _contextEvent; /// /// Creates a new instance of the ContextEventArgs class to represent the @@ -79,5 +79,29 @@ namespace Spring.Context.Events CultureInfo.InvariantCulture, "{0} [{1}]", GetType().Name, Event); } - } + } + + /// + /// Event object sent to listeners registered with an + /// to inform them of + /// context lifecycle event. + /// + public class ContextRefreshedEventArgs : ContextEventArgs + { + public ContextRefreshedEventArgs() : base(ContextEvent.Refreshed) + { + } + } + + /// + /// Event object sent to listeners registered with an + /// to inform them of + /// context lifecycle event. + /// + public class ContextClosedEventArgs : ContextEventArgs + { + public ContextClosedEventArgs() : base(ContextEvent.Closed) + { + } + } } \ No newline at end of file diff --git a/src/Spring/Spring.Core/Context/Support/AbstractApplicationContext.cs b/src/Spring/Spring.Core/Context/Support/AbstractApplicationContext.cs index 21cf2e6e..4b14379c 100644 --- a/src/Spring/Spring.Core/Context/Support/AbstractApplicationContext.cs +++ b/src/Spring/Spring.Core/Context/Support/AbstractApplicationContext.cs @@ -145,9 +145,11 @@ namespace Spring.Context.Support private string _name; private DateTime _startupDate; private readonly bool _isCaseSensitive; + private EventRaiser _eventRaiser; #endregion + #region Constructor (s) / Destructor /// @@ -202,6 +204,7 @@ namespace Spring.Context.Support _name = (StringUtils.IsNullOrEmpty(name)) ? DefaultRootContextName : name; _isCaseSensitive = caseSensitive; _parentApplicationContext = parentApplicationContext; + EventRaiser = CreateEventRaiser(); _objectFactoryPostProcessors = new ArrayList(); _defaultObjectPostProcessors = new ArrayList(); AddDefaultObjectPostProcessor(new ObjectPostProcessorChecker()); @@ -244,10 +247,10 @@ namespace Spring.Context.Support #endregion - // TODO: any reason, why Closed event is raised before destroying objectfactory? - new DefensiveEventRaiser().Raise( - ContextEvent, this, - new ContextEventArgs(ContextEventArgs.ContextEvent.Closed)); + // Closed event is raised before destroying objectfactory to enable registered IApplicationEventListeners + // to handle the event before they get disposed. + PublishEvent(this, new ContextClosedEventArgs()); + ObjectFactory.Dispose(); } @@ -281,6 +284,18 @@ namespace Spring.Context.Support get { return this; } } + /// + /// Set the to be used by this context. + /// + public EventRaiser EventRaiser + { + set + { + AssertUtils.ArgumentNotNull(value, "EventRaiser"); + _eventRaiser = value; + } + } + /// /// The timestamp when this context was first loaded. /// @@ -387,7 +402,15 @@ namespace Spring.Context.Support /// protected virtual void OnContextEvent(object source, ApplicationEventArgs e) { - new DefensiveEventRaiser().Raise(ContextEvent, source, e); + _eventRaiser.Raise(ContextEvent, source, e); + } + + /// + /// Create the strategy to be used + /// + protected virtual EventRaiser CreateEventRaiser() + { + return new DefensiveEventRaiser(); } /// @@ -444,6 +467,7 @@ namespace Spring.Context.Support /// protected virtual void OnPostRefresh() { + PublishEvent(this, new ContextRefreshedEventArgs()); } /// @@ -864,10 +888,11 @@ namespace Spring.Context.Support RegisterObjectPostProcessors(objectFactory); InitEventRegistry(); InitMessageSource(); - OnRefresh(); RefreshApplicationEventListeners(); + OnRefresh(); + #region Instrumentation if (log.IsDebugEnabled) @@ -881,10 +906,6 @@ namespace Spring.Context.Support OnPostRefresh(); - new DefensiveEventRaiser().Raise( - ContextEvent, this, - new ContextEventArgs(ContextEventArgs.ContextEvent.Refreshed)); - #region Instrumentation if (log.IsInfoEnabled) diff --git a/src/Spring/Spring.Core/Objects/Events/Support/EventRegistry.cs b/src/Spring/Spring.Core/Objects/Events/Support/EventRegistry.cs index 53450e95..d05f2704 100644 --- a/src/Spring/Spring.Core/Objects/Events/Support/EventRegistry.cs +++ b/src/Spring/Spring.Core/Objects/Events/Support/EventRegistry.cs @@ -35,7 +35,7 @@ namespace Spring.Objects.Events.Support /// Griffin Caprio public class EventRegistry : IEventRegistry { - private IList _publishers; + private readonly IList _publishers; /// /// Creates a new instance of the EventRegistry class. diff --git a/src/Spring/Spring.Core/Util/EventUtils.cs b/src/Spring/Spring.Core/Util/EventUtils.cs index 2cee7b0e..c92b6755 100644 --- a/src/Spring/Spring.Core/Util/EventUtils.cs +++ b/src/Spring/Spring.Core/Util/EventUtils.cs @@ -21,7 +21,10 @@ #region Imports using System; +using System.Collections; +using System.Diagnostics; using System.Reflection; +using Common.Logging; #endregion @@ -33,6 +36,16 @@ namespace Spring.Util /// Rick Evans public class EventRaiser { + protected readonly ILog Log; + + /// + /// Create a new EventRaiser instance + /// + public EventRaiser() + { + Log = LogManager.GetLogger(this.GetType()); + } + /// /// Raises the event encapsulated by the supplied /// , passing the supplied @@ -40,17 +53,23 @@ namespace Spring.Util /// /// The event to be raised. /// The arguments to the event. - public virtual void Raise (Delegate source, params object [] arguments) + /// a map of sink/exception entries that occurred during event raising + public virtual IDictionary Raise (Delegate source, params object [] arguments) { if (source == null) { - return; + return null; } + + IDictionary exceptions = null; + Delegate [] delegates = source.GetInvocationList (); foreach (Delegate sink in delegates) { - Invoke (sink, arguments); + exceptions = Invoke (sink, arguments, exceptions); } + + return exceptions; } /// @@ -59,11 +78,13 @@ namespace Spring.Util /// /// The sink to be invoked. /// The arguments to the sink. - protected virtual void Invoke (Delegate sink, object [] arguments) + /// the map of sink/exception entries to add any exception to + protected virtual IDictionary Invoke (Delegate sink, object [] arguments, IDictionary exceptions) { try { sink.DynamicInvoke (arguments); + return exceptions; } catch (TargetInvocationException ex) { @@ -92,15 +113,20 @@ namespace Spring.Util /// /// The sink to be invoked. /// The arguments to the sink. - protected override void Invoke (Delegate sink, object [] arguments) + /// the map of sink/exception entries to add any exception to + protected override IDictionary Invoke(Delegate sink, object[] arguments, IDictionary exceptions) { try { sink.DynamicInvoke (arguments); } - catch + 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; } } } diff --git a/test/Spring/Spring.Core.Tests/Util/DefensiveEventRaiserTests.cs b/test/Spring/Spring.Core.Tests/Util/DefensiveEventRaiserTests.cs index 72df72ae..fd04c9cd 100644 --- a/test/Spring/Spring.Core.Tests/Util/DefensiveEventRaiserTests.cs +++ b/test/Spring/Spring.Core.Tests/Util/DefensiveEventRaiserTests.cs @@ -21,28 +21,53 @@ #region Imports using System; - +using System.Collections; using NUnit.Framework; #endregion namespace Spring.Util { - /// - /// Unit tests for the DefensiveEventRaiser class. + /// + /// Unit tests for the DefensiveEventRaiser class. /// /// Rick Evans - [TestFixture] + [TestFixture] public sealed class DefensiveEventRaiserTests { [Test] - public void RaiseSwallowsExceptionRaisedByHandlers () + public void RaiseSwallowsExceptionRaisedByHandlers() { - OneThirstyDude dude = new OneThirstyDude (); - Soda bru = new Soda (); - bru.Pop += new PopHandler (dude.HandlePopWithException); - bru.OnPop ("Iron Brew", new DefensiveEventRaiser ()); - Assert.AreEqual ("Iron Brew", dude.Soda); // should have got through before exception was thrown + OneThirstyDude dude = new OneThirstyDude(); + Soda bru = new Soda(); + bru.Pop += new PopHandler(dude.HandlePopWithException); + bru.OnPop("Iron Brew", new DefensiveEventRaiser()); + Assert.AreEqual("Iron Brew", dude.Soda); // should have got through before exception was thrown } - } + +#if NET_2_0 + [Test] + public void RaiseSwallowsExceptionRaisedByHandlerButCallsAllOtherHandlers() + { + bool firstCall = false; + bool secondCall = false; + bool thirdCall = false; + + OneThirstyDude dude = new OneThirstyDude(); + Soda bru = new Soda(); + bru.Pop += new PopHandler(delegate { firstCall = true; }); + bru.Pop += new PopHandler(delegate { secondCall = true; throw new Exception(); }); + bru.Pop += new PopHandler(delegate { thirdCall = true; }); + + DefensiveEventRaiser eventRaiser = new DefensiveEventRaiser(); + + IDictionary exceptions = bru.OnPop( "Iron Brew", eventRaiser ); + + Assert.AreEqual(1, exceptions.Count); + Assert.IsTrue(firstCall); + Assert.IsTrue(secondCall); + Assert.IsTrue(thirdCall); + } +#endif + } } diff --git a/test/Spring/Spring.Core.Tests/Util/EventRaiserTests.cs b/test/Spring/Spring.Core.Tests/Util/EventRaiserTests.cs index b80840e7..b1732df4 100644 --- a/test/Spring/Spring.Core.Tests/Util/EventRaiserTests.cs +++ b/test/Spring/Spring.Core.Tests/Util/EventRaiserTests.cs @@ -21,7 +21,7 @@ #region Imports using System; - +using System.Collections; using NUnit.Framework; #endregion @@ -81,9 +81,9 @@ namespace Spring.Util { public event PopHandler Pop; - public void OnPop (string soda, EventRaiser raiser) + public IDictionary OnPop (string soda, EventRaiser raiser) { - raiser.Raise (Pop, this, soda); + return raiser.Raise (Pop, this, soda); } public void OnPopWithBadNumberOfArguments (string soda, EventRaiser raiser)