From 6fd34b7664c99a0a0419d6f703cf9190ff9222cc Mon Sep 17 00:00:00 2001 From: sbohlen Date: Mon, 4 Apr 2011 14:30:01 +0000 Subject: [PATCH] SPRNET-1334 clone dictionary before iterating through it to avoid iterator reentrancy --- .../AbstractAutowireCapableObjectFactory.cs | 14 ++- .../Factory/AbstractObjectFactoryTests.cs | 99 +++++++++++++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/AbstractAutowireCapableObjectFactory.cs b/src/Spring/Spring.Core/Objects/Factory/Support/AbstractAutowireCapableObjectFactory.cs index b4d6dadb..ac187561 100644 --- a/src/Spring/Spring.Core/Objects/Factory/Support/AbstractAutowireCapableObjectFactory.cs +++ b/src/Spring/Spring.Core/Objects/Factory/Support/AbstractAutowireCapableObjectFactory.cs @@ -446,11 +446,23 @@ namespace Spring.Objects.Factory.Support /// public override void Dispose() { + //TODO: fix the calls to GetObject(...) etc. so that they are invalid during container shutdown rather than attempting to resolve + // objects and permitting calling code to even get this far; considered too invasive a breaking change for 1.3.2; recommend impl + // of this change for the 2.0 release + base.Dispose(); - foreach (object o in DisposableInnerObjects) + + //have to clone the collection before iterating it to avoid arbitrary code in the objects' Dispose() + // that might permit re-entering the DisposableInnerObjects collections during the iteration to destroy them + // see https://jira.springframework.org/browse/SPRNET-1334 + + ISet clone = (ISet) DisposableInnerObjects.Clone(); + + foreach (object o in clone) { DestroyObject(string.Format(CultureInfo.InvariantCulture, "(Inner object of Type '{0}')", o.GetType().FullName), o); } + DisposableInnerObjects.Clear(); } diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/AbstractObjectFactoryTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/AbstractObjectFactoryTests.cs index 12000c3b..9bd018ac 100644 --- a/test/Spring/Spring.Core.Tests/Objects/Factory/AbstractObjectFactoryTests.cs +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/AbstractObjectFactoryTests.cs @@ -22,9 +22,12 @@ using System; using NUnit.Framework; +using Spring.Context; +using Spring.Core.IO; using Spring.Expressions; using Spring.Objects.Factory.Config; using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; using Spring.Threading; using System.Threading; using System.Diagnostics; @@ -576,6 +579,102 @@ namespace Spring.Objects.Factory } } + + [TestFixture] + public class SPRNET_1334 + { + public static AbstractObjectFactory CreateObjectFactory(bool caseSensitive) + { + return new DefaultListableObjectFactory(caseSensitive); + } + + [Test] + public void CanDisposeFactoryWhenDependentObjectCallsFactoryInDispose() + { + AbstractObjectFactory factory = CreateObjectFactory(false); + ConfigureObjectFactory(factory as IObjectDefinitionRegistry); + + ParentClass parent = (ParentClass)factory.GetObject("Parent"); + Assert.That(parent, Is.Not.Null); + + DisposableClass innerObject = (DisposableClass)parent.InnerObject; + innerObject.ObjectFactory = factory; + + factory.Dispose(); + + Assert.Pass("Test concluded successfully."); + } + + private void ConfigureObjectFactory(IObjectDefinitionRegistry factory) + { + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(factory); + reader.LoadObjectDefinitions(new StringResource(@" + + + + + + + + + + + + ")); + } + + public class ParentClass + { + private string _name; + + public string Name + { + get { return _name; } + set { _name = value; } + } + + private IDisposable _innerObject; + + public IDisposable InnerObject + { + get { return _innerObject; } + set { _innerObject = value; } + } + } + + public class DisposableClass : IDisposable + { + private AbstractObjectFactory _objectFactory; + public AbstractObjectFactory ObjectFactory + { + get { return _objectFactory; } + set { _objectFactory = value; } + } + + public void Dispose() + { + Console.WriteLine("DisposableClass.Dispose()"); + if (ObjectFactory == null) + return; + + object parent = ObjectFactory.GetObject("Parent"); + if (parent == null) + Console.WriteLine("parent == null"); + } + + } + } + + #if NET_2_0 [TestFixture] public class SPRNET_1338