#region License
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
#region Imports
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Globalization;
using System.Reflection;
using System.Runtime.Remoting;
using Common.Logging;
using Spring.Collections;
using Spring.Core;
using Spring.Core.TypeConversion;
using Spring.Core.TypeResolution;
using Spring.Expressions;
using Spring.Objects;
using Spring.Objects.Factory.Config;
using Spring.Util;
#endregion
namespace Spring.Objects.Factory.Support{
/// <summary>
/// Abstract <see cref="Spring.Objects.Factory.IObjectFactory"/> superclass
/// that implements default object creation.
/// </summary>
/// <remarks>
/// <p>
/// Provides object creation, initialization and wiring, supporting
/// autowiring and constructor resolution. Handles runtime object
/// references, managed collections, and object destruction.
/// </p>
/// <p>
/// The main template method to be implemented by subclasses is
/// <see cref="Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory.FindMatchingObjects"/>,
/// used for autowiring by type. Note that this class does not implement object
/// definition registry capabilities
/// (<see cref="Spring.Objects.Factory.Support.DefaultListableObjectFactory"/>
/// does).
/// </p>
/// </remarks>
/// <author>Rod Johnson</author>
/// <author>Juergen Hoeller</author>
/// <author>Rick Evans (.NET)</author>
[Serializable]
public abstract class AbstractAutowireCapableObjectFactory : AbstractObjectFactory, IAutowireCapableObjectFactory
{
#region Constants
/// <summary>
/// The <see cref="System.Reflection.BindingFlags"/> used during the invocation and
/// searching for of methods.
/// </summary>
protected const BindingFlags MethodResolutionFlags =
BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Instance | BindingFlags.IgnoreCase;
#endregion
/// <summary>
/// The <see cref="Common.Logging.ILog"/> instance for this class.
/// </summary>
private readonly ILog log;
#region Constructor (s) / Destructor
/// <summary>
/// Creates a new instance of the
/// <see cref="Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory"/>
/// class.
/// </summary>
/// <remarks>
/// <p>
/// This is an <see langword="abstract"/> class, and as such exposes no public constructors.
/// </p>
/// </remarks>
/// <param name="caseSensitive">Flag specifying whether to make this object factory case sensitive or not.</param>
protected AbstractAutowireCapableObjectFactory(bool caseSensitive)
: this(caseSensitive, null)
{ }
/// <summary>
/// Creates a new instance of the
/// <see cref="Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory"/>
/// class.
/// </summary>
/// <remarks>
/// <p>
/// This is an <see langword="abstract"/> class, and as such exposes no public constructors.
/// </p>
/// </remarks>
/// <param name="caseSensitive">Flag specifying whether to make this object factory case sensitive or not.</param>
/// <param name="parentFactory">The parent object factory, or <see langword="null"/> if none.</param>
protected AbstractAutowireCapableObjectFactory(bool caseSensitive, IObjectFactory parentFactory)
: base(caseSensitive, parentFactory)
{
log = LogManager.GetLogger(this.GetType());
this.IgnoreDependencyInterface(typeof(IObjectFactoryAware));
this.IgnoreDependencyInterface(typeof(IObjectNameAware));
}
#endregion
#region Properties
/// <summary>
/// The <see cref="Spring.Objects.Factory.Support.IInstantiationStrategy"/>
/// implementation to be used to instantiate managed objects.
/// </summary>
protected IInstantiationStrategy InstantiationStrategy
{
get { return instantiationStrategy; }
set { instantiationStrategy = value; }
}
#endregion
#region Methods
/// <summary>
/// Predict the eventual object type (of the processed object instance) for the
/// specified object.
/// </summary>
/// <param name="objectName">Name of the object.</param>
/// <param name="mod">The merged object definition to determine the type for. May be <c>null</c></param>
/// <returns>
/// The type of the object, or <code>null</code> if not predictable
/// </returns>
protected override Type PredictObjectType(string objectName, RootObjectDefinition mod)
{
Type objectType;
if (mod == null)
{
return null;
}
if (StringUtils.HasText(mod.FactoryMethodName))
{
objectType = GetTypeForFactoryMethod(objectName, mod);
}
else
{
objectType = ResolveObjectType(mod, objectName);
}
return objectType;
}
/// <summary>
/// Determines the <see cref="System.Type"/> of the object defined
/// by the supplied object <paramref name="definition"/>.
/// </summary>
/// <param name="objectName">
/// The name associated with the supplied object <paramref name="definition"/>.
/// </param>
/// <param name="definition">
/// The <see cref="Spring.Objects.Factory.Support.RootObjectDefinition"/>
/// that the <see cref="System.Type"/> is to be determined for.
/// </param>
/// <returns>
/// The <see cref="System.Type"/> of the object defined by the supplied
/// object <paramref name="definition"/>; or <see lang="null"/> if the
/// <see cref="System.Type"/> cannot be determined.
/// </returns>
protected override Type GetTypeForFactoryMethod(string objectName, RootObjectDefinition definition)
{
Type factoryType = null;
bool isStatic = true;
if (StringUtils.HasText(definition.FactoryObjectName))
{
// check declared factory method return type on factory type...
factoryType = GetType(definition.FactoryObjectName);
isStatic = false;
}
else
{
factoryType = ResolveObjectType(definition, objectName);
}
if (factoryType == null)
{
return null;
}
// If all factory methods have the same return type, return that type.
// Can't clearly figure out exact method due to type converting / autowiring!
int minNrOfArgs = definition.ConstructorArgumentValues.GenericArgumentValues.Count;
MethodInfo[] candidates = factoryType.GetMethods();
ISet returnTypes = new HybridSet();
foreach (MethodInfo factoryMethod in candidates)
{
#if NET_2_0
GenericArgumentsHolder genericArgsInfo = new GenericArgumentsHolder(definition.FactoryMethodName);
if (factoryMethod.IsStatic == isStatic && factoryMethod.Name.Equals(genericArgsInfo.GenericMethodName)
&& ReflectionUtils.GetParameterTypes(factoryMethod).Length >= minNrOfArgs
&& factoryMethod.GetGenericArguments().Length == genericArgsInfo.GetGenericArguments().Length)
{
if (genericArgsInfo.ContainsGenericArguments)
{
string[] unresolvedGenericArgs = genericArgsInfo.GetGenericArguments();
Type[] genericArgs = new Type[unresolvedGenericArgs.Length];
for (int j = 0; j < unresolvedGenericArgs.Length; j++)
{
genericArgs[j] = TypeResolutionUtils.ResolveType(unresolvedGenericArgs[j]);
}
returnTypes.Add(factoryMethod.MakeGenericMethod(genericArgs).ReturnType);
}
else
{
returnTypes.Add(factoryMethod.ReturnType);
}
}
#else
if (factoryMethod.IsStatic == isStatic && factoryMethod.Name.Equals(definition.FactoryMethodName)
&& ReflectionUtils.GetParameterTypes(factoryMethod).Length >= minNrOfArgs)
{
returnTypes.Add(factoryMethod.ReturnType);
}
#endif
}
if (returnTypes.Count == 1)
{
// clear return type found: all factory methods return same type...
return (Type)ObjectUtils.EnumerateFirstElement(returnTypes);
}
// ambiguous return types found: return null to indicate "not determinable"...
return null;
}
/// <summary>
/// Apply the property values of the object definition with the supplied
/// <paramref name="name"/> to the supplied <paramref name="instance"/>.
/// </summary>
/// <param name="instance">
/// The existing object that the property values for the named object will
/// be applied to.
/// </param>
/// <param name="name">
/// The name of the object definition associated with the property values that are
/// to be applied.
/// </param>
public override void ApplyObjectPropertyValues(object instance, string name)
{
RootObjectDefinition definition = GetMergedObjectDefinition(name, true);
if (definition != null)
{
log.Debug(string.Format("configuring object '{0}' using definition '{1}'", instance, name));
ApplyPropertyValues(name, definition, new ObjectWrapper(instance), definition.PropertyValues);
}
}
/// <summary>
/// Apply the property values of the object definition with the supplied
/// <paramref name="name"/> to the supplied <paramref name="instance"/>.
/// </summary>
/// <param name="instance">
/// The existing object that the property values for the named object will
/// be applied to.
/// </param>
/// <param name="name">
/// The name of the object definition associated with the property values that are
/// to be applied.
/// </param>
/// <param name="definition">
/// An object definition that should be used to apply property values.
/// </param>
public override void ApplyObjectPropertyValues(object instance, string name, IObjectDefinition definition)
{
ApplyPropertyValues(name, new RootObjectDefinition(definition), new ObjectWrapper(instance), definition.PropertyValues);
}
/// <summary>
/// Apply any
/// <see cref="Spring.Objects.Factory.Config.IInstantiationAwareObjectPostProcessor"/>s.
/// </summary>
/// <remarks>
/// <p>
/// The returned instance may be a wrapper around the original.
/// </p>
/// </remarks>
/// <param name="objectType">
/// The <see cref="System.Type"/> of the object that is to be
/// instantiated.
/// </param>
/// <param name="objectName">
/// The name of the object that is to be instantiated.
/// </param>
/// <returns>
/// An instance to use in place of the original instance.
/// </returns>
/// <exception cref="Spring.Objects.ObjectsException">
/// In case of errors.
/// </exception>
protected object ApplyObjectPostProcessorsBeforeInstantiation(Type objectType, string objectName)
{
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug(string.Format("Invoking IInstantiationAwareObjectPostProcessors before " + "the instantiation of '{0}'.", objectName));
}
#endregion
foreach (IObjectPostProcessor processor in ObjectPostProcessors)
{
IInstantiationAwareObjectPostProcessor inProc = processor as IInstantiationAwareObjectPostProcessor;
if (inProc != null)
{
object theObject = inProc.PostProcessBeforeInstantiation(objectType, objectName);
if (theObject != null)
{
return theObject;
}
}
}
return null;
}
/// <summary>
/// Apply the given property values, resolving any runtime references
/// to other objects in this object factory.
/// </summary>
/// <param name="name">
/// The object name passed for better exception information.
/// </param>
/// <param name="definition">
/// The definition of the named object.
/// </param>
/// <param name="wrapper">
/// The <see cref="Spring.Objects.IObjectWrapper"/> wrapping the target object.
/// </param>
/// <param name="properties">
/// The new property values.
/// </param>
/// <remarks>
/// <p>
/// Must use deep copy, so that we don't permanently modify this property.
/// </p>
/// </remarks>
protected void ApplyPropertyValues(string name, RootObjectDefinition definition, IObjectWrapper wrapper, IPropertyValues properties)
{
if (properties == null || properties.PropertyValues.Length == 0)
{
return;
}
ObjectDefinitionValueResolver valueResolver = CreateValueResolver();
MutablePropertyValues deepCopy = new MutablePropertyValues(properties);
PropertyValue[] copiedProperties = deepCopy.PropertyValues;
for (int i = 0; i < copiedProperties.Length; ++i)
{
PropertyValue copiedProperty = copiedProperties[i];
//(string name, RootObjectDefinition definition, string argumentName, object argumentValue)
object value = valueResolver.ResolveValueIfNecessary(name, definition, copiedProperty.Name, copiedProperty.Value);
// object value = ResolveValueIfNecessary(name, definition, copiedProperty.Name, copiedProperty.Value);
PropertyValue propertyValue = new PropertyValue(copiedProperty.Name, value, copiedProperty.Expression);
// update mutable copy...
deepCopy.SetPropertyValueAt(propertyValue, i);
}
// set the (possibly resolved) deep copy properties...
try
{
wrapper.SetPropertyValues(deepCopy);
}
catch (ObjectsException ex)
{
// improve the message by showing the context...
throw new ObjectCreationException(definition.ResourceDescription, name, "Error setting property values: " + ex.Message, ex);
}
}
/// <summary>
/// Create the value resolver strategy to use for resolving raw property values
/// </summary>
protected virtual ObjectDefinitionValueResolver CreateValueResolver()
{
return new ObjectDefinitionValueResolver(this);
}
/// <summary>
/// Return an array of object-type property names that are unsatisfied.
/// </summary>
/// <remarks>
/// <p>
/// These are probably unsatisfied references to other objects in the
/// factory. Does not include simple properties like primitives or
/// <see cref="System.String"/>s.
/// </p>
/// </remarks>
/// <returns>
/// An array of object-type property names that are unsatisfied.
/// </returns>
/// <param name="definition">
/// The definition of the named object.
/// </param>
/// <param name="wrapper">
/// The <see cref="Spring.Objects.IObjectWrapper"/> wrapping the target object.
/// </param>
protected string[] UnsatisfiedNonSimpleProperties(RootObjectDefinition definition, IObjectWrapper wrapper)
{
ListSet results = new ListSet();
IPropertyValues pvs = definition.PropertyValues;
PropertyInfo[] properties = wrapper.GetPropertyInfos();
foreach (PropertyInfo property in properties)
{
string name = property.Name;
if (property.CanWrite
&& !IsExcludedFromDependencyCheck(property)
&& !pvs.Contains(name)
&& !ObjectUtils.IsSimpleProperty(property.PropertyType))
{
results.Add(name);
}
}
return (string[])CollectionUtils.ToArray(results, typeof(string));
}
/// <summary>
/// Destroy all cached singletons in this factory.
/// </summary>
/// <remarks>
/// <p>
/// To be called on shutdown of a factory.
/// </p>
/// </remarks>
public override void Dispose()
{
base.Dispose();
foreach (object o in DisposableInnerObjects)
{
DestroyObject(string.Format(CultureInfo.InvariantCulture, "(Inner object of Type '{0}')", o.GetType().FullName), o);
}
DisposableInnerObjects.Clear();
}
/// <summary>
/// Populate the object instance in the given
/// <see cref="Spring.Objects.IObjectWrapper"/> with the property values from the
/// object definition.
/// </summary>
/// <param name="name">
/// The name of the object.
/// </param>
/// <param name="definition">
/// The definition of the named object.
/// </param>
/// <param name="wrapper">
/// The <see cref="Spring.Objects.IObjectWrapper"/> wrapping the target object.
/// </param>
protected void PopulateObject(string name, RootObjectDefinition definition, IObjectWrapper wrapper)
{
// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
// state of the bean before properties are set. This can be used, for example,
// to support styles of field injection.
bool continueWithPropertyPopulation = true;
if (HasInstantiationAwareBeanPostProcessors)
{
foreach (IObjectPostProcessor processor in ObjectPostProcessors)
{
IInstantiationAwareObjectPostProcessor inProc = processor as IInstantiationAwareObjectPostProcessor;
if (inProc != null)
{
if (!inProc.PostProcessAfterInstantiation(wrapper.WrappedInstance, name))
{
continueWithPropertyPopulation = false;
break;
}
}
}
}
if (!continueWithPropertyPopulation)
{
return;
}
IPropertyValues properties = definition.PropertyValues;
if (wrapper == null)
{
if (properties.PropertyValues.Length > 0)
{
throw new ObjectCreationException(definition.ResourceDescription,
name, "Cannot apply property values to null instance.");
}
// skip property population phase for null instance
return;
}
if (definition.ResolvedAutowireMode == AutoWiringMode.ByName || definition.ResolvedAutowireMode == AutoWiringMode.ByType)
{
MutablePropertyValues mpvs = new MutablePropertyValues(properties);
// add property values based on autowire by name if it's applied
if (definition.ResolvedAutowireMode == AutoWiringMode.ByName)
{
AutowireByName(name, definition, wrapper, mpvs);
}
// add property values based on autowire by type if it's applied
if (definition.ResolvedAutowireMode == AutoWiringMode.ByType)
{
AutowireByType(name, definition, wrapper, mpvs);
}
properties = mpvs;
}
//DependencyCheck(name, definition, wrapper, properties);
bool hasInstAwareOpps = HasInstantiationAwareBeanPostProcessors;
bool needsDepCheck = (definition.DependencyCheck != DependencyCheckingMode.None);
if (hasInstAwareOpps || needsDepCheck)
{
PropertyInfo[] filteredPropInfo = FilterPropertyInfoForDependencyCheck(wrapper);
if (hasInstAwareOpps)
{
foreach (IObjectPostProcessor processor in ObjectPostProcessors)
{
IInstantiationAwareObjectPostProcessor instantiationAwareObjectPostProcessor =
processor as IInstantiationAwareObjectPostProcessor;
if (instantiationAwareObjectPostProcessor != null)
{
properties =
instantiationAwareObjectPostProcessor.PostProcessPropertyValues(properties, filteredPropInfo, wrapper.WrappedInstance,
name);
if (properties == null)
{
return;
}
}
}
}
if (needsDepCheck)
{
CheckDependencies(name, definition, filteredPropInfo, properties);
}
}
ApplyPropertyValues(name, definition, wrapper, properties);
}
/// <summary>
/// Wires up any exposed events in the object instance in the given
/// <see cref="Spring.Objects.IObjectWrapper"/> with any event handler
/// values from the <paramref name="definition"/>.
/// </summary>
/// <param name="name">
/// The name of the object.
/// </param>
/// <param name="definition">
/// The definition of the named object.
/// </param>
/// <param name="wrapper">
/// The <see cref="Spring.Objects.IObjectWrapper"/> wrapping the target object.
/// </param>
protected void WireEvents(string name, IConfigurableObjectDefinition definition, IObjectWrapper wrapper)
{
foreach (string eventName in definition.EventHandlerValues.Events)
{
foreach (IEventHandlerValue handlerValue
in definition.EventHandlerValues[eventName])
{
object handler = null;
if (handlerValue.Source is RuntimeObjectReference)
{
RuntimeObjectReference roref = (RuntimeObjectReference)handlerValue.Source;
handler = ResolveReference(definition, name, eventName, roref);
}
else if (handlerValue.Source is Type)
{
// a static Type event is being wired up; simply pass on the Type
handler = handlerValue.Source;
}
else if (handlerValue.Source is string)
{
// a static Type event is being wired up; we need to resolve the Type
handler = TypeResolutionUtils.ResolveType(handlerValue.Source as string);
}
else
{
throw new FatalObjectException("Currently, only references to other objects and Types are " + "supported as event sources.");
}
handlerValue.Wire(handler, wrapper.WrappedInstance);
}
}
}
/// <summary>
/// Fills in any missing property values with references to
/// other objects in this factory if autowire is set to
/// <see cref="Spring.Objects.Factory.Config.AutoWiringMode.ByName"/>.
/// </summary>
/// <param name="name">
/// The object name to be autowired by <see cref="System.Type"/>.
/// </param>
/// <param name="definition">
/// The definition of the named object to update through autowiring.
/// </param>
/// <param name="wrapper">
/// The <see cref="Spring.Objects.IObjectWrapper"/> wrapping the target object (and
/// from which we can rip out information concerning the object).
/// </param>
/// <param name="properties">
/// The property values to register wired objects with.
/// </param>
protected void AutowireByName(string name, RootObjectDefinition definition, IObjectWrapper wrapper, MutablePropertyValues properties)
{
string[] propertyNames = UnsatisfiedNonSimpleProperties(definition, wrapper);
foreach (string propertyName in propertyNames)
{
// look for a matching type
if (ContainsObject(propertyName))
{
object o = GetObject(propertyName);
properties.Add(propertyName, o);
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug(
string.Format(CultureInfo.InvariantCulture,
"Added autowiring by name from object name '{0}' via " + "property '{1}' to object named '{1}'.", name,
propertyName));
}
#endregion
}
else
{
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug(
string.Format(CultureInfo.InvariantCulture,
"Not autowiring property '{0}' of object '{1}' by name: " + "no matching object found.", propertyName, name));
}
#endregion
}
}
}
/// <summary>
/// Defines "autowire by type" (object properties by type) behavior.
/// </summary>
/// <remarks>
/// <p>
/// This is like PicoContainer default, in which there must be exactly one object
/// of the property type in the object factory. This makes object factories simple
/// to configure for small namespaces, but doesn't work as well as standard Spring
/// behavior for bigger applications.
/// </p>
/// </remarks>
/// <param name="name">
/// The object name to be autowired by <see cref="System.Type"/>.
/// </param>
/// <param name="definition">
/// The definition of the named object to update through autowiring.
/// </param>
/// <param name="wrapper">
/// The <see cref="Spring.Objects.IObjectWrapper"/> wrapping the target object (and
/// from which we can rip out information concerning the object).
/// </param>
/// <param name="properties">
/// The property values to register wired objects with.
/// </param>
protected void AutowireByType(string name, RootObjectDefinition definition, IObjectWrapper wrapper, MutablePropertyValues properties)
{
string[] propertyNames = UnsatisfiedNonSimpleProperties(definition, wrapper);
foreach (string propertyName in propertyNames)
{
// look for a matching type
Type requiredType = wrapper.GetPropertyType(propertyName);
IDictionary matchingObjects = FindMatchingObjects(requiredType);
if (matchingObjects != null && matchingObjects.Count == 1)
{
properties.Add(propertyName, ObjectUtils.EnumerateFirstElement(matchingObjects.Values));
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug(
string.Format(CultureInfo.InvariantCulture,
"Autowiring by type from object name '{0}' via property " + "'{1}' to object named '{2}'.", name,
propertyName, ObjectUtils.EnumerateFirstElement(matchingObjects.Keys)));
}
#endregion
}
else if (matchingObjects != null && matchingObjects.Count > 1)
{
throw new UnsatisfiedDependencyException(string.Empty, name, propertyName,
string.Format(CultureInfo.InvariantCulture,
"There are {0} objects of Type [{1}] for autowire by "
+ "type, when there should have been just 1 to be able to "
+ "autowire property '{2}' of object '{3}'.", matchingObjects.Count,
requiredType, propertyName, name));
}
else
{
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug(
string.Format(CultureInfo.InvariantCulture, "Not autowiring property '{0}' of object '{1}': no matching object found.",
propertyName, name));
}
#endregion
}
}
}
/// <summary>
/// Ignore the given dependency type for autowiring
/// </summary>
/// <remarks>
/// This will typically be used by application contexts to register
/// dependencies that are resolved in other ways, like IOjbectFactory through
/// IObjectFactoryAware or IApplicationContext through IApplicationContextAware.
/// By default, IObjectFactoryAware and IObjectName interfaces are ignored.
/// For further types to ignore, invoke this method for each type.
/// </remarks>
/// <seealso cref="Spring.Objects.Factory.Config.IConfigurableObjectFactory.IgnoreDependencyType"/>.
public void IgnoreDependencyInterface(Type type)
{
ignoredDependencyInterfaces.Add(type);
}
// /// <summary>
// /// Create an object instance for the given object definition.
// /// </summary>
// /// <param name="name">The name of the object.</param>
// /// <param name="definition">
// /// The object definition for the object that is to be instantiated.
// /// </param>
// /// <param name="arguments">
// /// The arguments to use if creating a prototype using explicit arguments to
// /// a static factory method. It is invalid to use a non-<see langword="null"/> arguments value
// /// in any other case.
// /// </param>
// /// <returns>
// /// A new instance of the object.
// /// </returns>
// /// <exception cref="Spring.Objects.ObjectsException">
// /// In case of errors.
// /// </exception>
// /// <remarks>
// /// <p>
// /// Delegates to the
// /// <see cref="Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory.CreateObject (string,RootObjectDefinition,object[],bool)"/>
// /// method version with the <c>allowEagerCaching</c> parameter set to <b>true</b>.
// /// </p>
// /// <p>
// /// The object definition will already have been merged with the parent
// /// definition in case of a child definition.
// /// </p>
// /// <p>
// /// All the other methods in this class invoke this method, although objects
// /// may be cached after being instantiated by this method. All object
// /// instantiation within this class is performed by this method.
// /// </p>
// /// </remarks>
// protected internal override object CreateObject(string name, RootObjectDefinition definition, object[] arguments)
// {
// return CreateObject(name, definition, arguments, true, false);
// }
/// <summary>
/// Create an object instance for the given object definition.
/// </summary>
/// <param name="name">The name of the object.</param>
/// <param name="definition">
/// The object definition for the object that is to be instantiated.
/// </param>
/// <param name="arguments">
/// The arguments to use if creating a prototype using explicit arguments to
/// a static factory method. It is invalid to use a non-<see langword="null"/> arguments value
/// in any other case.
/// </param>
/// <param name="allowEagerCaching">
/// Whether eager caching of singletons is allowed... typically true for
/// singlton objects, but never true for inner object definitions.
/// </param>
/// <param name="suppressConfigure">
/// Suppress injecting dependencies yet.
/// </param>
/// <returns>
/// A new instance of the object.
/// </returns>
/// <exception cref="Spring.Objects.ObjectsException">
/// In case of errors.
/// </exception>
/// <remarks>
/// <p>
/// The object definition will already have been merged with the parent
/// definition in case of a child definition.
/// </p>
/// <p>
/// All the other methods in this class invoke this method, although objects
/// may be cached after being instantiated by this method. All object
/// instantiation within this class is performed by this method.
/// </p>
/// </remarks>
protected internal override object InstantiateObject(string name, RootObjectDefinition definition, object[] arguments, bool allowEagerCaching, bool suppressConfigure)
{
// guarantee the initialization of objects that the current one depends on..
if (definition.DependsOn != null && definition.DependsOn.Length > 0)
{
foreach (string dependant in definition.DependsOn)
{
GetObject(dependant);
}
}
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug(string.Format(CultureInfo.InvariantCulture, "Creating instance of Object '{0}' with merged definition [{1}].", name, definition));
}
#endregion
// Make sure object type is actually resolved at this point.
ResolveObjectType(definition, name);
try
{
definition.PrepareMethodOverrides();
}
catch (ObjectDefinitionValidationException ex)
{
throw new ObjectDefinitionStoreException(definition.ResourceDescription, name,
"Validation of method overrides failed. " + ex.Message, ex);
}
// return IObjectDefinition instance itself for an abstract object-definition
if (definition.IsTemplate)
{
return definition;
}
object instance = null;
IObjectWrapper instanceWrapper = null;
bool eagerlyCached = false;
try
{
// Give IInstantiationAwareObjectPostProcessors a chance to return a proxy instead of the target instance....
if (definition.HasObjectType)
{
instance = ApplyObjectPostProcessorsBeforeInstantiation(definition.ObjectType, name);
if (instance != null)
{
return instance;
}
}
instanceWrapper = CreateObjectInstance(name, definition, arguments);
instance = instanceWrapper.WrappedInstance;
// eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like IObjectFactoryAware.
if (allowEagerCaching && definition.IsSingleton)
{
if (log.IsDebugEnabled)
{
log.Debug("Eagerly caching object '" + name + "' to allow for resolving potential circular references");
}
AddEagerlyCachedSingleton(name, definition, instance);
eagerlyCached = true;
}
if (!suppressConfigure)
{
instance = ConfigureObject(name, definition, instanceWrapper);
}
}
catch (ObjectCreationException)
{
if (eagerlyCached)
{
RemoveEagerlyCachedSingleton(name, definition);
}
throw;
}
catch (Exception ex)
{
if (eagerlyCached)
{
RemoveEagerlyCachedSingleton(name, definition);
}
throw new ObjectCreationException(definition.ResourceDescription, name, "Initialization of object failed : " + ex.Message, ex);
}
return instance;
}
/// <summary>
/// Add the created, but yet unpopulated singleton to the singleton cache
/// to be able to resolve circular references
/// </summary>
/// <param name="objectName">the name of the object to add to the cache.</param>
/// <param name="objectDefinition">the definition used to create and populated the object.</param>
/// <param name="rawSingletonInstance">the raw object instance.</param>
/// <remarks>
/// Derived classes may override this method to select the right cache based on the object definition.
/// </remarks>
protected virtual void AddEagerlyCachedSingleton(string objectName, IObjectDefinition objectDefinition, object rawSingletonInstance)
{
base.AddSingleton(objectName, rawSingletonInstance);
}
/// <summary>
/// Remove the specified singleton from the singleton cache that has
/// been added before by a call to <see cref="AddEagerlyCachedSingleton"/>
/// </summary>
/// <param name="objectName">the name of the object to remove from the cache.</param>
/// <param name="objectDefinition">the definition used to create and populated the object.</param>
/// <remarks>
/// Derived classes may override this method to select the right cache based on the object definition.
/// </remarks>
protected virtual void RemoveEagerlyCachedSingleton(string objectName, IObjectDefinition objectDefinition)
{
base.RemoveSingleton(objectName);
}
/// <summary>
/// Creates an <see cref="IObjectWrapper"/> instance from the <see cref="RootObjectDefinition"/> passed in <paramref name="objectDefinition"/>
/// using constructor <paramref name="arguments"/>
/// </summary>
/// <param name="objectName">The name of the object to create - used for error messages.</param>
/// <param name="objectDefinition">The <see cref="RootObjectDefinition"/> describing the object to be created.</param>
/// <param name="arguments">optional arguments to pass to the constructor</param>
/// <returns>An <see cref="IObjectWrapper"/> wrapping the already instantiated object</returns>
protected IObjectWrapper CreateObjectInstance(string objectName, RootObjectDefinition objectDefinition, object[] arguments)
{
// Make sure object class is actually resolved at this point.
Type objectType = ResolveObjectType(objectDefinition, objectName);
if (StringUtils.HasText(objectDefinition.FactoryMethodName))
{
return InstantiateUsingFactoryMethod(objectName, objectDefinition, arguments);
}
//TODO perf optimization when creating the same object
ConstructorInfo[] ctors = DetermineConstructorsFromObjectPostProcessors(objectType, objectName);
if (ctors != null ||
objectDefinition.ResolvedAutowireMode == AutoWiringMode.Constructor ||
objectDefinition.HasConstructorArgumentValues || !ObjectUtils.IsEmpty(arguments))
{
return AutowireConstructor(objectName, objectDefinition, ctors, arguments);
}
// No special handling: simply use no-arg constructor.
return InstantiateObject(objectName, objectDefinition);
/*
IObjectWrapper instanceWrapper;
if (StringUtils.HasText(definition.FactoryMethodName))
{
instanceWrapper = InstantiateUsingFactoryMethod(name, definition, arguments);
}
//Handle case when arguments are passed in explicitly.
else if (arguments != null && arguments.Length > 0)
{
instanceWrapper = AutowireConstructor(name, definition, arguments);
}
else if (definition.ResolvedAutowireMode == AutoWiringMode.Constructor ||
definition.HasConstructorArgumentValues)
{
instanceWrapper = AutowireConstructor(name, definition);
}
else
{
instanceWrapper = new ObjectWrapper(InstantiationStrategy.Instantiate(definition, name, this));
InitObjectWrapper(instanceWrapper);
}
return instanceWrapper;
*/
}
/// <summary>
/// Instantiates the given object using its default constructor
/// </summary>
/// <param name="objectName">Name of the object.</param>
/// <param name="definition">The definition.</param>
/// <returns>IObjectWrapper for the new instance</returns>
protected virtual IObjectWrapper InstantiateObject(string objectName, RootObjectDefinition definition)
{
return new ObjectWrapper(InstantiationStrategy.Instantiate(definition, objectName, this));
}
/// <summary>
/// Determines candidate constructors to use for the given bean, checking all registered
/// <see cref="SmartInstantiationAwareObjectPostProcessor"/>
/// </summary>
/// <param name="objectType">Raw type of the object.</param>
/// <param name="objectName">Name of the object.</param>
/// <returns>the candidate constructors, or <code>null</code> if none specified</returns>
/// <exception cref="ObjectsException">In case of errors</exception>
/// <seealso cref="SmartInstantiationAwareObjectPostProcessor.DetermineCandidateConstructors"/>
protected virtual ConstructorInfo[] DetermineConstructorsFromObjectPostProcessors(Type objectType, string objectName)
{
if (HasInstantiationAwareBeanPostProcessors)
{
foreach (IObjectPostProcessor objectPostProcessor in ObjectPostProcessors)
{
if (ObjectUtils.IsAssignable(typeof(SmartInstantiationAwareObjectPostProcessor), objectPostProcessor))
{
SmartInstantiationAwareObjectPostProcessor iop =
(SmartInstantiationAwareObjectPostProcessor)objectPostProcessor;
ConstructorInfo[] ctors = iop.DetermineCandidateConstructors(objectType, objectName);
if (ctors != null)
{
return ctors;
}
}
}
}
return null;
}
/// <summary>
/// Instantiate an object instance using a named factory method.
/// </summary>
/// <remarks>
/// <p>
/// The method may be static, if the <paramref name="definition"/>
/// parameter specifies a class, rather than a
/// <see cref="Spring.Objects.Factory.IFactoryObject"/> instance, or an
/// instance variable on a factory object itself configured using Dependency
/// Injection.
/// </p>
/// <p>
/// Implementation requires iterating over the static or instance methods
/// with the name specified in the supplied <paramref name="definition"/>
/// (the method may be overloaded) and trying to match with the parameters.
/// We don't have the types attached to constructor args, so trial and error
/// is the only way to go here.
/// </p>
/// </remarks>
/// <param name="name">
/// The name associated with the supplied <paramref name="definition"/>.
/// </param>
/// <param name="definition">
/// The definition describing the instance that is to be instantiated.
/// </param>
/// <param name="arguments">
/// Any arguments to the factory method that is to be invoked.
/// </param>
/// <returns>
/// The result of the factory method invocation (the instance).
/// </returns>
protected virtual IObjectWrapper InstantiateUsingFactoryMethod(string name, RootObjectDefinition definition, object[] arguments)
{
ConstructorResolver constructorResolver =
new ConstructorResolver(this, this, InstantiationStrategy, CreateValueResolver());
return constructorResolver.InstantiateUsingFactoryMethod(name, definition, arguments);
}
/// <summary>
/// "autowire constructor" (with constructor arguments by type) behaviour.
/// </summary>
/// <param name="name">The name of the object to autowire by type.</param>
/// <param name="definition">The object definition to update through autowiring.</param>
/// <param name="ctors">The chosen candidate constructors.</param>
/// <param name="explicitArgs">The argument values passed in programmatically via the GetObject method,
/// or <code>null</code> if none (-> use constructor argument values from object definition)</param>
/// <returns>
/// An <see cref="Spring.Objects.IObjectWrapper"/> for the new instance.
/// </returns>
/// <remarks>
/// <para>
/// Also applied if explicit constructor argument values are specified,
/// matching all remaining arguments with objects from the object factory.
/// </para>
/// <para>
/// This corresponds to constructor injection: in this mode, a Spring.NET
/// object factory is able to host components that expect constructor-based
/// dependency resolution.
/// </para>
/// </remarks>
protected IObjectWrapper AutowireConstructor(string name, RootObjectDefinition definition, ConstructorInfo[] ctors, object[] explicitArgs)
{
ConstructorResolver constructorResolver =
new ConstructorResolver(this, this, InstantiationStrategy, CreateValueResolver());
return constructorResolver.AutowireConstructor(name, definition, ctors, explicitArgs);
}
/// <summary>
/// Perform a dependency check that all properties exposed have been set, if desired.
/// </summary>
/// <remarks>
/// <p>
/// Dependency checks can be objects (collaborating objects), simple (primitives
/// and <see cref="System.String"/>), or all (both).
/// </p>
/// </remarks>
/// <param name="name">
/// The name of the object.
/// </param>
/// <param name="definition">
/// The definition of the named object.
/// </param>
/// <param name="wrapper">
/// The <see cref="Spring.Objects.IObjectWrapper"/> wrapping the target object.
/// </param>
/// <param name="properties">
/// The property values to be checked.
/// </param>
/// <exception cref="Spring.Objects.Factory.UnsatisfiedDependencyException">
/// If all of the checked dependencies were not satisfied.
/// </exception>
protected void DependencyCheck(string name, IConfigurableObjectDefinition definition, IObjectWrapper wrapper, IPropertyValues properties)
{
DependencyCheckingMode dependencyCheck = definition.DependencyCheck;
if (dependencyCheck == DependencyCheckingMode.None)
{
return;
}
PropertyInfo[] filteredPropInfo = FilterPropertyInfoForDependencyCheck(wrapper);
if (HasInstantiationAwareBeanPostProcessors)
{
foreach (IObjectPostProcessor processor in ObjectPostProcessors)
{
IInstantiationAwareObjectPostProcessor inProc = processor as IInstantiationAwareObjectPostProcessor;
if (inProc != null)
{
properties =
inProc.PostProcessPropertyValues(properties, filteredPropInfo, wrapper.WrappedInstance, name);
if (properties == null)
{
return;
}
}
}
}
CheckDependencies(name, definition, filteredPropInfo, properties);
}
private void CheckDependencies(string name, IConfigurableObjectDefinition definition, PropertyInfo[] filteredPropInfo, IPropertyValues properties)
{
DependencyCheckingMode dependencyCheck = definition.DependencyCheck;
PropertyInfo[] unsatisfiedDependencies = AutowireUtils.GetUnsatisfiedDependencies(filteredPropInfo, properties, dependencyCheck);
if (unsatisfiedDependencies.Length > 0)
{
throw new UnsatisfiedDependencyException(definition.ResourceDescription, name, unsatisfiedDependencies[0].Name,
"Set this property value or disable dependency checking for this object.");
}
}
/// <summary>
/// Extract a filtered set of PropertyInfos from the given IObjectWrapper, excluding
/// ignored dependency types.
/// </summary>
/// <param name="wrapper">The object wrapper the object was created with.</param>
/// <returns>The filtered PropertyInfos</returns>
private PropertyInfo[] FilterPropertyInfoForDependencyCheck(IObjectWrapper wrapper)
{
lock (filteredPropertyDescriptorsCache)
{
PropertyInfo[] filtered = (PropertyInfo[])filteredPropertyDescriptorsCache[wrapper.WrappedType];
if (filtered == null)
{
ArrayList list = new ArrayList(wrapper.GetPropertyInfos());
for (int i = list.Count - 1; i >= 0; i--)
{
PropertyInfo pi = (PropertyInfo)list[i];
if (IsExcludedFromDependencyCheck(pi))
{
list.RemoveAt(i);
}
}
filtered = (PropertyInfo[])list.ToArray(typeof(PropertyInfo));
filteredPropertyDescriptorsCache.Add(wrapper.WrappedType, filtered);
}
return filtered;
}
}
/// <summary>
/// Determine whether the given bean property is excluded from dependency checks.
/// This implementation excludes properties whose type matches an ignored dependency type
/// or which are defined by an ignored dependency interface.
/// </summary>
/// <seealso cref="AbstractObjectFactory.IgnoreDependencyType(Type)"/>
/// <seealso cref="IgnoreDependencyInterface(Type)"/>
/// <param name="property">the <see cref="PropertyInfo"/> of the object property</param>
/// <returns>whether the object property is excluded</returns>
private bool IsExcludedFromDependencyCheck(PropertyInfo property)
{
bool b1 = !property.CanWrite; //AutowireUtils.IsExcludedFromDependencyCheck(pi);
bool b2 = IgnoredDependencyTypes.Contains(property.PropertyType);
bool b3 = AutowireUtils.IsSetterDefinedInInterface(property, ignoredDependencyInterfaces);
return b1 || b2 || b3;
/*
return AutowireUtils.IsExcludedFromDependencyCheck(pi) ||
IgnoredDependencyTypes.Contains(pi.PropertyType) ||
AutowireUtils.IsSetterDefinedInInterface(pi, ignoredDependencyInterfaces);
*/
}
/// <summary>
/// Give an object a chance to react now all its properties are set,
/// and a chance to know about its owning object factory (this object).
/// </summary>
/// <remarks>
/// <p>
/// This means checking whether the object implements
/// <see cref="Spring.Objects.Factory.IInitializingObject"/> and / or
/// <see cref="Spring.Objects.Factory.IObjectFactoryAware"/>, and invoking the
/// necessary callback(s) if it does.
/// </p>
/// <p>
/// Custom init methods are resolved in a <b>case-insensitive</b> manner.
/// </p>
/// </remarks>
/// <param name="target">
/// The new object instance we may need to initialise.
/// </param>
/// <param name="name">
/// The name the object has in the factory. Used for logging output.
/// </param>
/// <param name="definition">
/// The definition of the target object instance.
/// </param>
protected virtual void InvokeInitMethods(object target, string name, IConfigurableObjectDefinition definition)
{
if (ObjectUtils.IsAssignableAndNotTransparentProxy(typeof(IInitializingObject), target))
{
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug(string.Format(CultureInfo.InvariantCulture, "Calling AfterPropertiesSet() on object with name '{0}'.", name));
}
#endregion
((IInitializingObject)target).AfterPropertiesSet();
}
if (StringUtils.HasText(definition.InitMethodName))
{
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug(
string.Format(CultureInfo.InvariantCulture, "Calling custom init method '{0} on object with name '{1}'.",
definition.InitMethodName, name));
}
#endregion
try
{
MethodInfo targetMethod = target.GetType().GetMethod(definition.InitMethodName, MethodResolutionFlags, null, Type.EmptyTypes, null);
if (targetMethod == null)
{
throw new ObjectCreationException(definition.ResourceDescription, name,
"Could not find the named initialization method '" + definition.InitMethodName + "'.");
}
targetMethod.Invoke(target, ObjectUtils.EmptyObjects);
}
catch (TargetInvocationException ex)
{
throw new ObjectCreationException(definition.ResourceDescription, name,
"Initialization method '" + definition.InitMethodName + "' threw exception", ex.GetBaseException());
}
catch (Exception ex)
{
throw new ObjectCreationException(definition.ResourceDescription, name,
"Invocation of initialization method '" + definition.InitMethodName + "' failed", ex);
}
}
}
/// <summary>
/// Invoke the specified custom destroy method on the given object.
/// </summary>
/// <remarks>
/// <p>
/// This implementation invokes a no-arg method if found, else checking
/// for a method with a single boolean argument (passing in "true",
/// assuming a "force" parameter), else logging an error.
/// </p>
/// <p>
/// Can be overridden in subclasses for custom resolution of destroy
/// methods with arguments.
/// </p>
/// <p>
/// Custom destroy methods are resolved in a <b>case-insensitive</b> manner.
/// </p>
/// </remarks>
protected virtual void InvokeCustomDestroyMethod(string name, object target, string destroyMethodName)
{
bool usingForcingVersion = false;
MethodInfo targetMethod = target.GetType().GetMethod(destroyMethodName, MethodResolutionFlags, null, Type.EmptyTypes, null);
if (targetMethod == null)
{
// #%&^! try to find the method with a boolean "force" parameter
targetMethod = target.GetType().GetMethod(destroyMethodName, MethodResolutionFlags, null, new Type[] { typeof(bool) }, null);
if (targetMethod != null)
{
usingForcingVersion = true;
}
}
if (targetMethod == null)
{
#region Instrumentation
log.Error("Couldn't find a method named '" + destroyMethodName + "' on object with name '" + name + "'");
#endregion
}
else
{
object[] args = usingForcingVersion ? new object[] { true } : ObjectUtils.EmptyObjects;
try
{
targetMethod.Invoke(target, args);
}
catch (TargetInvocationException ex)
{
#region Instrumentation
log.Error("Couldn't invoke destroy method '" + destroyMethodName + "' of object with name '" + name + "'", ex.GetBaseException());
#endregion
}
catch (Exception ex)
{
LogExceptionRaisedByCustomDestroyMethodInvocation(destroyMethodName, name, ex);
}
}
}
private void LogExceptionRaisedByCustomDestroyMethodInvocation(string destroyMethodName, string name, Exception ex)
{
log.Error(
string.Format(CultureInfo.InvariantCulture, "Couldn't invoke destroy method '{0}' of object with name '{1}'.", destroyMethodName, name),
ex);
}
/// <summary>
/// Destroy the target object.
/// </summary>
/// <remarks>
/// <p>
/// Must destroy objects that depend on the given object before the object itself.
/// Should not throw any exceptions.
/// </p>
/// </remarks>
/// <param name="name">
/// The name of the object.
/// </param>
/// <param name="target">
/// The target object instance to destroyed.
/// </param>
protected override void DestroyObject(string name, object target)
{
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug("Destroying dependant objects for object '" + name + "'");
}
#endregion
DestroyDependantObjects(name);
if (target is IDisposable)
{
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug(string.Format(CultureInfo.InvariantCulture, "Calling Dispose () on object with name '{0}'.", name));
}
#endregion
try
{
((IDisposable)target).Dispose();
}
catch (Exception ex)
{
#region Instrumentation
log.Error("Destroy() on object with name '" + name + "' threw an exception.", ex);
#endregion
}
}
RootObjectDefinition rootDefinition = GetMergedObjectDefinition(name, false);
if (rootDefinition != null && StringUtils.HasText(rootDefinition.DestroyMethodName))
{
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug("Calling custom destroy method '" + rootDefinition.DestroyMethodName + "' on object with name '" + name + "'.");
}
#endregion
InvokeCustomDestroyMethod(name, target, rootDefinition.DestroyMethodName);
}
}
/// <summary>
/// Destroys all of the objects registered as dependant on the
/// object (definition) identified by the supplied <paramref name="name"/>.
/// </summary>
/// <param name="name">
/// The name of the root object (definition) that is itself being destroyed.
/// </param>
private void DestroyDependantObjects(string name)
{
string[] dependingObjects = GetDependingObjectNames(name);
foreach (string doName in dependingObjects)
{
DestroySingleton(doName);
}
}
///// <summary>
///// Given a property value, return a value, resolving any references to other
///// objects in the factory if necessary.
///// </summary>
///// <remarks>
///// <p>
///// The value could be :
///// <list type="bullet">
///// <item>
///// <p>
///// An <see cref="Spring.Objects.Factory.Config.IObjectDefinition"/>,
///// which leads to the creation of a corresponding new object instance.
///// Singleton flags and names of such "inner objects" are always ignored: inner objects
///// are anonymous prototypes.
///// </p>
///// </item>
///// <item>
///// <p>
///// A <see cref="Spring.Objects.Factory.Config.RuntimeObjectReference"/>, which must
///// be resolved.
///// </p>
///// </item>
///// <item>
///// <p>
///// An <see cref="Spring.Objects.Factory.Support.IManagedCollection"/>. This is a
///// special placeholder collection that may contain
///// <see cref="Spring.Objects.Factory.Config.RuntimeObjectReference"/>s or
///// collections that will need to be resolved.
///// </p>
///// </item>
///// <item>
///// <p>
///// An ordinary object or <see langword="null"/>, in which case it's left alone.
///// </p>
///// </item>
///// </list>
///// </p>
///// </remarks>
///// <param name="name">
///// The name of the object that is having the value of one of its properties resolved.
///// </param>
///// <param name="definition">
///// The definition of the named object.
///// </param>
///// <param name="argumentName">
///// The name of the property the value of which is being resolved.
///// </param>
///// <param name="argumentValue">
///// The value of the property that is being resolved.
///// </param>
// protected object ResolveValueIfNecessary(string name, RootObjectDefinition definition, string argumentName, object argumentValue)
// {
// object resolvedValue = null;
//
// AssertUtils.ArgumentNotNull(resolvedValue, "test");
//
// // we must check the argument value to see whether it requires a runtime
// // reference to another object to be resolved.
// // if it does, we'll attempt to instantiate the object and set the reference.
// if (RemotingServices.IsTransparentProxy(argumentValue))
// {
// resolvedValue = argumentValue;
// }
// else if (argumentValue is ObjectDefinitionHolder)
// {
// // contains an IObjectDefinition with name and aliases...
// ObjectDefinitionHolder holder = (ObjectDefinitionHolder)argumentValue;
// resolvedValue = ResolveInnerObjectDefinition(name, holder.ObjectName, argumentName, holder.ObjectDefinition, definition.IsSingleton);
// }
// else if (argumentValue is IObjectDefinition)
// {
// // resolve plain IObjectDefinition, without contained name: use dummy name...
// IObjectDefinition def = (IObjectDefinition)argumentValue;
// resolvedValue = ResolveInnerObjectDefinition(name, "(inner object)", argumentName, def, definition.IsSingleton);
//
// }
// else if (argumentValue is RuntimeObjectReference)
// {
// RuntimeObjectReference roref = (RuntimeObjectReference)argumentValue;
// resolvedValue = ResolveReference(definition, name, argumentName, roref);
// }
// else if (argumentValue is ExpressionHolder)
// {
// ExpressionHolder expHolder = (ExpressionHolder)argumentValue;
// object context = null;
// IDictionary variables = null;
//
// if (expHolder.Properties != null)
// {
// PropertyValue contextProperty = expHolder.Properties.GetPropertyValue("Context");
// context = contextProperty == null
// ? null
// : ResolveValueIfNecessary2(name, definition, "Context",
// contextProperty.Value);
// PropertyValue variablesProperty = expHolder.Properties.GetPropertyValue("Variables");
// object vars = (variablesProperty == null
// ? null
// : ResolveValueIfNecessary2(name, definition, "Variables",
// variablesProperty.Value));
// if (vars is IDictionary)
// {
// variables = (IDictionary)vars;
// }
// else
// {
// if (vars != null) throw new ArgumentException("'Variables' must resolve to an IDictionary");
// }
// }
//
// if (variables == null) variables = CollectionsUtil.CreateCaseInsensitiveHashtable();
// // add 'this' objectfactory reference to variables
// variables.Add(Expression.ReservedVariableNames.CurrentObjectFactory, this);
//
// resolvedValue = expHolder.Expression.GetValue(context, variables);
// }
// else if (argumentValue is IManagedCollection)
// {
// resolvedValue =
// ((IManagedCollection)argumentValue).Resolve(name, definition, argumentName,
// new ManagedCollectionElementResolver(ResolveValueIfNecessary2));
// }
// else if (argumentValue is TypedStringValue)
// {
// TypedStringValue tsv = (TypedStringValue)argumentValue;
// try
// {
// Type resolvedTargetType = ResolveTargetType(tsv);
// if (resolvedTargetType != null)
// {
// resolvedValue = TypeConversionUtils.ConvertValueIfNecessary(tsv.TargetType, tsv.Value, null);
// }
// else
// {
// resolvedValue = tsv.Value;
// }
// }
// catch (Exception ex)
// {
// throw new ObjectCreationException(definition.ResourceDescription, name,
// "Error converted typed String value for " + argumentName, ex);
// }
//
// }
// else
// {
// // no need to resolve value...
// resolvedValue = argumentValue;
// }
// return resolvedValue;
// }
///// <summary>
///// Resolve the target type of the passed <see cref="TypedStringValue"/>.
///// </summary>
///// <param name="value">The <see cref="TypedStringValue"/> who's target type is to be resolved</param>
///// <returns>The resolved target type, if any. <see lang="null" /> otherwise.</returns>
// protected virtual Type ResolveTargetType(TypedStringValue value)
// {
// if (value.HasTargetType)
// {
// return value.TargetType;
// }
// else
// {
// return null;
// }
// }
///// <summary>
///// Resolves an inner object definition.
///// </summary>
///// <param name="name">
///// The name of the object that surrounds this inner object definition.
///// </param>
///// <param name="innerObjectName">
///// The name of the inner object definition... note: this is a synthetic
///// name assigned by the factory (since it makes no sense for inner object
///// definitions to have names).
///// </param>
///// <param name="argumentName">
///// The name of the property the value of which is being resolved.
///// </param>
///// <param name="definition">
///// The definition of the inner object that is to be resolved.
///// </param>
///// <param name="singletonOwner">
///// <see langword="true"/> if the owner of the property is a singleton.
///// </param>
///// <returns>
///// The resolved object as defined by the inner object definition.
///// </returns>
// protected object ResolveInnerObjectDefinition(string name, string innerObjectName, string argumentName, IObjectDefinition definition,
// bool singletonOwner)
// {
// RootObjectDefinition mod = GetMergedObjectDefinition(innerObjectName, definition);
// mod.IsSingleton = singletonOwner;
// object instance;
// object result;
// try
// {
// instance = InstantiateObject(innerObjectName, mod, ObjectUtils.EmptyObjects, false, false);
// result = GetObjectForInstance(innerObjectName, instance);
// }
// catch (ObjectsException ex)
// {
// throw ObjectCreationException.GetObjectCreationException(ex, name, argumentName, definition.ResourceDescription, innerObjectName);
// }
// if (singletonOwner && instance is IDisposable)
// {
// // keep a reference to the inner object instance, to be able to destroy
// // it on factory shutdown...
// DisposableInnerObjects.Add(instance);
// }
// return result;
// }
/// <summary>
/// Resolve a reference to another object in the factory.
/// </summary>
/// <param name="name">
/// The name of the object that is having the value of one of its properties resolved.
/// </param>
/// <param name="definition">
/// The definition of the named object.
/// </param>
/// <param name="argumentName">
/// The name of the property the value of which is being resolved.
/// </param>
/// <param name="reference">
/// The runtime reference containing the value of the property.
/// </param>
/// <returns>A reference to another object in the factory.</returns>
protected object ResolveReference(IConfigurableObjectDefinition definition, string name, string argumentName, RuntimeObjectReference reference)
{
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug(
string.Format(CultureInfo.InvariantCulture, "Resolving reference from property '{0}' in object '{1}' to object '{2}'.",
argumentName, name, reference.ObjectName));
}
#endregion
try
{
if (reference.IsToParent)
{
if (null == ParentObjectFactory)
{
throw new ObjectCreationException(definition.ResourceDescription, name,
string.Format(
"Can't resolve reference to '{0}' in parent factory: " + "no parent factory available.",
reference.ObjectName));
}
return ParentObjectFactory.GetObject(reference.ObjectName);
}
return GetObject(reference.ObjectName);
}
catch (ObjectsException ex)
{
throw ObjectCreationException.GetObjectCreationException(ex, name, argumentName, definition.ResourceDescription, reference.ObjectName);
}
}
/// <summary>
/// Find object instances that match the required <see cref="System.Type"/>.
/// </summary>
/// <remarks>
/// <p>
/// Called by autowiring. If a subclass cannot obtain information about object
/// names by <see cref="System.Type"/>, a corresponding exception should be thrown.
/// </p>
/// </remarks>
/// <param name="requiredType">
/// The <see cref="System.Type"/> of the objects to look up.
/// </param>
/// <returns>
/// An <see cref="IDictionary"/> of object names and object
/// instances that match the required <see cref="System.Type"/>, or
/// <see langword="null"/> if none are found.
/// </returns>
/// <exception cref="Spring.Objects.ObjectsException">
/// In case of errors.
/// </exception>
protected abstract IDictionary FindMatchingObjects(Type requiredType);
/// <summary>
/// Return the names of the objects that depend on the given object.
/// Called by DestroyObject, to be able to destroy depending objects first.
/// </summary>
/// <param name="name">
/// The name of the object to find depending objects for.
/// </param>
/// <returns>
/// The array of names of depending objects, or the empty string array if none.
/// </returns>
/// <exception cref="Spring.Objects.ObjectsException">
/// In case of errors.
/// </exception>
protected abstract string[] GetDependingObjectNames(string name);
/// <summary>
/// Injects dependencies into the supplied <paramref name="target"/> instance
/// using the named object definition.
/// </summary>
/// <param name="target">
/// The object instance that is to be so configured.
/// </param>
/// <param name="name">
/// The name of the object definition expressing the dependencies that are to
/// be injected into the supplied <parameref name="target"/> instance.
/// </param>
/// <seealso cref="Spring.Objects.Factory.IObjectFactory.ConfigureObject(object, string)"/>
public override object ConfigureObject(object target, string name)
{
RootObjectDefinition definition = GetMergedObjectDefinition(name, true);
if (definition != null)
{
return ConfigureObject(name, definition, new ObjectWrapper(target));
}
return target;
}
/// <summary>
/// Injects dependencies into the supplied <paramref name="target"/> instance
/// using the supplied <paramref name="definition"/>.
/// </summary>
/// <param name="target">
/// The object instance that is to be so configured.
/// </param>
/// <param name="name">
/// The name of the object definition expressing the dependencies that are to
/// be injected into the supplied <parameref name="target"/> instance.
/// </param>
/// <param name="definition">
/// An object definition that should be used to configure object.
/// </param>
/// <seealso cref="Spring.Objects.Factory.IObjectFactory.ConfigureObject(object, string)"/>
public override object ConfigureObject(object target, string name, IObjectDefinition definition)
{
return ConfigureObject(name, new RootObjectDefinition(definition), new ObjectWrapper(target));
}
/// <summary>
/// Configures object instance by injecting dependencies, satisfying Spring lifecycle
/// interfaces and applying object post-processors.
/// </summary>
/// <param name="name">
/// The name of the object definition expressing the dependencies that are to
/// be injected into the supplied <parameref name="target"/> instance.
/// </param>
/// <param name="definition">
/// An object definition that should be used to configure object.
/// </param>
/// <param name="wrapper">
/// A wrapped object instance that is to be so configured.
/// </param>
/// <seealso cref="Spring.Objects.Factory.IObjectFactory.ConfigureObject(object, string)"/>
protected virtual object ConfigureObject(string name, RootObjectDefinition definition, IObjectWrapper wrapper)
{
object instance = wrapper.WrappedInstance;
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug(string.Format("Configuring object using definition '{1}'", instance, name));
}
#endregion
PopulateObject(name, definition, wrapper);
WireEvents(name, definition, wrapper);
if (ObjectUtils.IsAssignableAndNotTransparentProxy(typeof(IObjectNameAware), instance))
{
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug(string.Format(CultureInfo.InvariantCulture, "Setting the name property on the IObjectNameAware object '{0}'.", name));
}
#endregion
((IObjectNameAware)instance).ObjectName = name;
}
if (ObjectUtils.IsAssignableAndNotTransparentProxy(typeof(IObjectFactoryAware), instance))
{
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug(
string.Format(CultureInfo.InvariantCulture, "Setting the ObjectFactory property on the IObjectFactoryAware object '{0}'.",
name));
}
#endregion
((IObjectFactoryAware)instance).ObjectFactory = this;
}
instance = ApplyObjectPostProcessorsBeforeInitialization(instance, name);
InvokeInitMethods(instance, name, definition);
instance = ApplyObjectPostProcessorsAfterInitialization(instance, name);
return instance;
}
/// <summary>
/// Applies the <code>PostProcessAfterInitialization</code> callback of all
/// registered IObjectPostProcessors, giving them a chance to post-process
/// the object obtained from IFactoryObjects (for example, to auto-proxy them)
/// </summary>
/// <param name="instance">The instance obtained from the IFactoryObject.</param>
/// <param name="objectName">Name of the object.</param>
/// <returns>The object instance to expose</returns>
/// <exception cref="ObjectsException">if any post-processing failed.</exception>
protected override object PostProcessObjectFromFactoryObject(object instance, string objectName)
{
return ApplyObjectPostProcessorsAfterInitialization(instance, objectName);
}
#endregion
#region IAutowireCapableObjectFactory Members
/// <summary>
/// Create a new object instance of the given class with the specified
/// autowire strategy.
/// </summary>
/// <param name="type">
/// The <see cref="System.Type"/> of the object to instantiate.
/// </param>
/// <param name="autowireMode">
/// The desired autowiring mode.
/// </param>
/// <param name="dependencyCheck">
/// Whether to perform a dependency check for objects (not applicable to
/// autowiring a constructor, thus ignored there).
/// </param>
/// <returns>The new object instance.</returns>
/// <exception cref="Spring.Objects.ObjectsException">
/// If the wiring fails.
/// </exception>
/// <seealso cref="Spring.Objects.Factory.Config.AutoWiringMode"/>
public virtual object Autowire(Type type, AutoWiringMode autowireMode, bool dependencyCheck)
{
RootObjectDefinition rod = new RootObjectDefinition(type, autowireMode, dependencyCheck);
if (rod.ResolvedAutowireMode == AutoWiringMode.Constructor)
{
return AutowireConstructor(type.Name, rod, null, null).WrappedInstance;
}
object obj = InstantiationStrategy.Instantiate(rod, string.Empty, this);
PopulateObject(obj.GetType().Name, rod, new ObjectWrapper(obj));
return obj;
}
/// <summary>
/// Autowire the object properties of the given object instance by name or
/// <see cref="System.Type"/>.
/// </summary>
/// <param name="instance">
/// The existing object instance.
/// </param>
/// <param name="autowireMode">
/// The desired autowiring mode.
/// </param>
/// <param name="dependencyCheck">
/// Whether to perform a dependency check for the object.
/// </param>
/// <exception cref="Spring.Objects.ObjectsException">
/// If the wiring fails.
/// </exception>
/// <exception cref="System.ArgumentException">
/// If the supplied <paramref name="autowireMode"/> is not one of the
/// <seealso cref="Spring.Objects.Factory.Config.AutoWiringMode.ByName"/> or
/// <seealso cref="Spring.Objects.Factory.Config.AutoWiringMode.ByType"/>
/// values.
/// </exception>
/// <seealso cref="Spring.Objects.Factory.Config.AutoWiringMode"/>
public virtual void AutowireObjectProperties(object instance, AutoWiringMode autowireMode, bool dependencyCheck)
{
if (autowireMode != AutoWiringMode.ByName && autowireMode != AutoWiringMode.ByType)
{
throw new ArgumentException("Just AutoWiringMode.ByName and AutoWiringMode.ByType allowed.");
}
RootObjectDefinition rod = new RootObjectDefinition(instance.GetType(), autowireMode, dependencyCheck);
PopulateObject(instance.GetType().Name, rod, new ObjectWrapper(instance));
}
/// <summary>
/// Apply <see cref="Spring.Objects.Factory.Config.IObjectPostProcessor"/>s
/// to the given existing object instance, invoking their
/// <see cref="Spring.Objects.Factory.Config.IObjectPostProcessor.PostProcessBeforeInitialization"/>
/// methods.
/// </summary>
/// <param name="instance">
/// The existing object instance.
/// </param>
/// <param name="name">
/// The name of the object.
/// </param>
/// <returns>
/// The object instance to use, either the original or a wrapped one.
/// </returns>
/// <exception cref="Spring.Objects.ObjectsException">
/// If any post-processing failed.
/// </exception>
/// <seealso cref="Spring.Objects.Factory.Config.IObjectPostProcessor.PostProcessBeforeInitialization"/>
public virtual object ApplyObjectPostProcessorsBeforeInitialization(object instance, string name)
{
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug("Invoking IObjectPostProcessors before initialization of object '" + name + "'");
}
#endregion
object result = instance;
foreach (IObjectPostProcessor objectProcessor in ObjectPostProcessors)
{
result = objectProcessor.PostProcessBeforeInitialization(result, name);
if (result == null)
{
throw new ObjectCreationException(name,
string.Format(CultureInfo.InvariantCulture,
"PostProcessBeforeInitialization method of IObjectPostProcessor [{0}] "
+ " returned null for object [{1}] with name '{2}'.", objectProcessor, instance, name));
}
}
return result;
}
/// <summary>
/// Apply <see cref="Spring.Objects.Factory.Config.IObjectPostProcessor"/>s
/// to the given existing object instance, invoking their
/// <see cref="Spring.Objects.Factory.Config.IObjectPostProcessor.PostProcessAfterInitialization"/>
/// methods.
/// </summary>
/// <param name="instance">
/// The existing object instance.
/// </param>
/// <param name="name">
/// The name of the object.
/// </param>
/// <returns>
/// The object instance to use, either the original or a wrapped one.
/// </returns>
/// <exception cref="Spring.Objects.ObjectsException">
/// If any post-processing failed.
/// </exception>
/// <seealso cref="Spring.Objects.Factory.Config.IObjectPostProcessor.PostProcessAfterInitialization"/>
public virtual object ApplyObjectPostProcessorsAfterInitialization(object instance, string name)
{
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug("Invoking IObjectPostProcessors after initialization of object '" + name + "'");
}
#endregion
object result = instance;
foreach (IObjectPostProcessor objectProcessor in ObjectPostProcessors)
{
result = objectProcessor.PostProcessAfterInitialization(result, name);
if (result == null)
{
throw new ObjectCreationException(name,
string.Format(CultureInfo.InvariantCulture,
"PostProcessAfterInitialization method of IObjectPostProcessor [{0}] "
+ " returned null for object [{1}] with name [{2}].", objectProcessor, instance, name));
}
}
return result;
}
/// <summary>
/// Resolve the specified dependency against the objects defined in this factory.
/// </summary>
/// <param name="descriptor">The descriptor for the dependency.</param>
/// <param name="objectName">Name of the object which declares the present dependency.</param>
/// <param name="autowiredObjectNames">A list that all names of autowired object (used for
/// resolving the present dependency) are supposed to be added to.</param>
/// <returns>
/// the resolved object, or <code>null</code> if none found
/// </returns>
/// <exception cref="ObjectsException">if dependency resolution failed</exception>
public abstract object ResolveDependency(DependencyDescriptor descriptor, string objectName,
IList autowiredObjectNames);
#endregion
#region Fields
private IInstantiationStrategy instantiationStrategy = new MethodInjectingInstantiationStrategy();
/// <summary>
/// Cache of filtered PropertyInfos: object Type -> PropertyInfo array
/// </summary>
private IDictionary filteredPropertyDescriptorsCache = new Hashtable();
/// <summary>
/// Dependency interfaces to ignore on dependency check and autowire, as Set of
/// Class objects. By default, only the IObjectFactoryAware and IObjectNameAware
/// interfaces are ignored.
/// </summary>
private ISet ignoredDependencyInterfaces = new HybridSet();
#endregion
}
internal class UnsatisfiedDependencyExceptionData
{
private int parameterIndex;
private Type parameterType;
private string errorMessage;
public UnsatisfiedDependencyExceptionData(int parameterIndex, Type parameterType, string errorMessage)
{
this.parameterIndex = parameterIndex;
this.parameterType = parameterType;
this.errorMessage = errorMessage;
}
public int ParameterIndex
{
get { return parameterIndex; }
}
public Type ParameterType
{
get { return parameterType; }
}
public string ErrorMessage
{
get { return errorMessage; }
}
}
}
|