From 085464919fa44fe43d9748d7a56692ac1b0ca6fe Mon Sep 17 00:00:00 2001 From: eeichinger Date: Tue, 2 Dec 2008 20:19:45 +0000 Subject: [PATCH] fixed SPRNET-1128 --- .../Support/AbstractApplicationContext.cs | 3 + .../Context/Support/ContextRegistry.cs | 93 +++++++++++++++---- .../Context/Support/ContextRegistryTests.cs | 11 +++ 3 files changed, 91 insertions(+), 16 deletions(-) diff --git a/src/Spring/Spring.Core/Context/Support/AbstractApplicationContext.cs b/src/Spring/Spring.Core/Context/Support/AbstractApplicationContext.cs index f8602d26..e12a575e 100644 --- a/src/Spring/Spring.Core/Context/Support/AbstractApplicationContext.cs +++ b/src/Spring/Spring.Core/Context/Support/AbstractApplicationContext.cs @@ -227,6 +227,8 @@ namespace Spring.Context.Support /// public virtual void Dispose() { + GC.SuppressFinalize(this); + #region Instrumentation if (log.IsDebugEnabled) @@ -239,6 +241,7 @@ 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)); diff --git a/src/Spring/Spring.Core/Context/Support/ContextRegistry.cs b/src/Spring/Spring.Core/Context/Support/ContextRegistry.cs index 81bf9198..8af8cb27 100644 --- a/src/Spring/Spring.Core/Context/Support/ContextRegistry.cs +++ b/src/Spring/Spring.Core/Context/Support/ContextRegistry.cs @@ -16,7 +16,7 @@ * limitations under the License. */ -#endregion +#endregion #region Imports @@ -25,7 +25,7 @@ using System.Collections; using System.Collections.Specialized; using System.Configuration; using Common.Logging; - +using Spring.Context.Events; using Spring.Util; #endregion @@ -77,21 +77,21 @@ namespace Spring.Context.Support ///

/// static ContextRegistry() - {} + { } // CLOVER:ON #endregion /// - /// This event is fired, if ContextRegistry.Clear() is called.
- /// Clients may register to get informed - ///
- /// - /// This event is fired while still holding a lock on the Registry.
- /// 'sender' parameter is sent as typeof(ContextRegistry), EventArgs are not used - ///
- public static event EventHandler Cleared; + /// This event is fired, if ContextRegistry.Clear() is called.
+ /// Clients may register to get informed + /// + /// + /// This event is fired while still holding a lock on the Registry.
+ /// 'sender' parameter is sent as typeof(ContextRegistry), EventArgs are not used + ///
+ public static event EventHandler Cleared; /// /// Gets an object that should be used to synchronize access to ContextRegistry @@ -129,6 +129,7 @@ namespace Spring.Context.Support ctx, context.Name)); } instance.contextMap[context.Name] = context; + context.ContextEvent += new ApplicationEventHandler(OnContextEvent); #region Instrumentation @@ -147,6 +148,46 @@ namespace Spring.Context.Support } } + /// + /// Handles events raised by an application context. + /// + /// + /// + private static void OnContextEvent(object sender, ApplicationEventArgs e) + { + ContextEventArgs cea = e as ContextEventArgs; + if (cea != null + && cea.Event == ContextEventArgs.ContextEvent.Closed + && sender is IApplicationContext) + { + // we know the context is registered! + UnregisterContext((IApplicationContext)sender); + } + } + + /// + /// Removes the context from the registry + /// + /// + /// Has no effect if the context wasn't registered + /// + /// īthe context to remove from the registry + private static void UnregisterContext(IApplicationContext context) + { + AssertUtils.ArgumentNotNull(context, "context"); + lock (syncRoot) + { + if (IsContextRegistered(context.Name)) + { + instance.contextMap.Remove(context.Name); + if (rootContextName == context.Name) + { + rootContextName = null; + } + } + } + } + /// /// Returns the root application context. /// @@ -203,7 +244,7 @@ namespace Spring.Context.Support if (ctx == null) { throw new ApplicationContextException(String.Format( - "No context registered under name '{0}'. Use the 'RegisterContext' method or the 'spring/context' section from your configuration file.", + "No context registered under name '{0}'. Use the 'RegisterContext' method or the 'spring/context' section from your configuration file.", name)); } @@ -234,12 +275,32 @@ namespace Spring.Context.Support { lock (syncRoot) { - foreach (IApplicationContext ctx in instance.contextMap.Values) + ArrayList contexts = new ArrayList(instance.contextMap.Values); + foreach (IApplicationContext ctx in contexts) { ctx.Dispose(); } + + #region Instrumentation + + // contexts will be removed from contextMap during OnContextEvent handler + // but someone might choose to override AbstractApplicationContext.Dispose() without + // calling base.Dispose() ... + if (log.IsWarnEnabled) + { + if (instance.contextMap.Count > 0) + { + log.Warn( + String.Format( + "Not all contexts were removed from registry during cleanup - did you forget to call base.Dispose() when overriding AbstractApplicationContext.Dispose()?")); + } + } + + #endregion + instance.contextMap.Clear(); rootContextName = null; + // mark section dirty - force re-read from disk next time ConfigurationUtils.RefreshSection(AbstractApplicationContext.ContextSectionName); DynamicCodeManager.Clear(); if (Cleared != null) @@ -254,7 +315,7 @@ namespace Spring.Context.Support /// /// The context name. /// true, if the context is already registered. false otherwise - public static bool IsContextRegistered( string name ) + public static bool IsContextRegistered(string name) { lock (instance) { @@ -270,7 +331,7 @@ namespace Spring.Context.Support { if (rootContextCurrentlyInCreation) { - throw new InvalidOperationException("root context is currently in creation. You must not call ContextRegistry.GetContext() from e.g. constructors of your singleton objects"); + throw new InvalidOperationException("root context is currently in creation. You must not call ContextRegistry.GetContext() from e.g. constructors of your singleton objects"); } rootContextCurrentlyInCreation = true; @@ -284,6 +345,6 @@ namespace Spring.Context.Support } } } - } + } } diff --git a/test/Spring/Spring.Core.Tests/Context/Support/ContextRegistryTests.cs b/test/Spring/Spring.Core.Tests/Context/Support/ContextRegistryTests.cs index 714aacb3..d5111d25 100644 --- a/test/Spring/Spring.Core.Tests/Context/Support/ContextRegistryTests.cs +++ b/test/Spring/Spring.Core.Tests/Context/Support/ContextRegistryTests.cs @@ -229,5 +229,16 @@ namespace Spring.Context.Support MockApplicationContext duplicate = new MockApplicationContext("original"); ContextRegistry.RegisterContext(duplicate); } + + [Test] + public void RemovesContextFromRegistryWhenContextCloses() + { + StaticApplicationContext appCtx = new StaticApplicationContext(); + appCtx.Name = "myCtx"; + ContextRegistry.RegisterContext(appCtx); + Assert.IsTrue(ContextRegistry.IsContextRegistered(appCtx.Name)); + appCtx.Dispose(); + Assert.IsFalse(ContextRegistry.IsContextRegistered(appCtx.Name)); + } } }