SPRNET-1334

clone dictionary before iterating through it to avoid iterator reentrancy
This commit is contained in:
sbohlen
2011-04-04 14:30:01 +00:00
parent 768f26559b
commit 6fd34b7664
2 changed files with 112 additions and 1 deletions

View File

@@ -446,11 +446,23 @@ namespace Spring.Objects.Factory.Support
/// </remarks>
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();
}

View File

@@ -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(@"<?xml version='1.0' encoding='UTF-8' ?>
<objects xmlns='http://www.springframework.net'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xsi:schemaLocation='http://www.springframework.net http://www.springframework.net/xsd/spring-objects.xsd'>
<object id='Parent' type='Spring.Objects.Factory.SPRNET_1334+ParentClass, Spring.Core.Tests'>
<property name='Name' value='Foo!'/>
<property name='InnerObject'>
<object type='Spring.Objects.Factory.SPRNET_1334+DisposableClass, Spring.Core.Tests'/>
</property>
</object>
<!--
<object id='Parent' type='Spring.Objects.Factory.SPRNET_1334+ParentClass, Spring.Core.Tests'>
<property name='Name' value='Foo!'/>
<property name='InnerObject' ref='Inner'/>
</object>
<object id='Inner' type='Spring.Objects.Factory.SPRNET_1334+DisposableClass, Spring.Core.Tests'/>
-->
</objects>
"));
}
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