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));
+ }
}
}