SPRNET-1334
clone dictionary before iterating through it to avoid iterator reentrancy
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user