#region License
/*
* Copyright 2002-2008 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 AopAlliance.Aop;
using Spring.Util;
using Spring.Objects.Factory;
using Spring.Objects.Factory.Config;
using Spring.Objects.Factory.Support;
using Spring.Aop;
using Spring.Aop.Framework;
using Spring.Aop.Framework.Adapter;
using Spring.Aop.Framework.DynamicProxy;
using Spring.Core;
#endregion
namespace Spring.Aop.Framework.AutoProxy{
/// <summary>
/// <see cref="IObjectFactoryPostProcessor"/> implementation that replaces a group of objects
/// with a 'true' inheritance based AOP mechanism that delegates
/// to the given interceptors before invoking the object itself.
/// </summary>
/// <author>Bruno Baia</author>
public class InheritanceBasedAopConfigurer : IObjectFactoryPostProcessor, IObjectFactoryAware, IOrdered
{
#region Fields
private IList objectNames;
private string[] interceptorNames = new string[0];
private bool proxyTargetAttributes = true;
private bool proxyDeclaredMembersOnly = true;
private bool proxyInterfaces = false;
private IObjectFactory objectFactory;
private int order = int.MaxValue;
private IAdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.Instance;
#endregion
#region Properties
/// <summary>
/// Set the names of the objects in IList fashioned way
/// that should automatically get intercepted.
/// </summary>
/// <remarks>
/// A name can specify a prefix to match by ending with "*", e.g. "myObject,tx*"
/// will match the object named "myObject" and all objects whose name start with "tx".
/// </remarks>
public virtual IList ObjectNames
{
set { objectNames = value; }
}
/// <summary>
/// Sets the common interceptors, a list of <see cref="AopAlliance.Aop.IAdvice"/>,
/// <see cref="Spring.Aop.IAdvisor"/> and introduction object names.
/// </summary>
/// <remarks>
/// <para>
/// If this property isn't set, there will be zero common interceptors.
/// This is perfectly valid, if "specific" interceptors such as
/// matching Advisors are all we want.
/// </para>
/// </remarks>
/// <value>
/// The list of <see cref="AopAlliance.Aop.IAdvice"/>,
/// <see cref="Spring.Aop.IAdvisor"/> and introduction object names.
/// </value>
/// <seealso cref="AopAlliance.Aop.IAdvice"/>
/// <seealso cref="Spring.Aop.IAdvisor"/>
public virtual string[] InterceptorNames
{
set { interceptorNames = value; }
}
/// <summary>
/// Is target type attributes, method attributes, method's return type attributes
/// and method's parameter attributes to be proxied in addition
/// to any interfaces declared on the proxied <see cref="System.Type"/>?
/// </summary>
public virtual bool ProxyTargetAttributes
{
get { return this.proxyTargetAttributes; }
set { this.proxyTargetAttributes = value; }
}
/// <summary>
/// Gets or sets a value indicating whether inherited members should be proxied.
/// </summary>
/// <value>
/// <see langword="true"/> if inherited members should be proxied;
/// otherwise, <see langword="false"/>.
/// </value>
public bool ProxyDeclaredMembersOnly
{
get { return proxyDeclaredMembersOnly; }
set { proxyDeclaredMembersOnly = value; }
}
/// <summary>
/// Gets or sets a value indicating whether interfaces members should be proxied.
/// </summary>
/// <value>
/// <see langword="true"/> if interfaces members should be proxied;
/// otherwise, <see langword="false"/>.
/// </value>
public bool ProxyInterfaces
{
get { return proxyInterfaces; }
set { proxyInterfaces = value; }
}
#endregion
#region IObjectFactoryAware Members
/// <summary>
/// Callback that supplies the owning factory to an object instance.
/// </summary>
/// <value>
/// Owning <see cref="Spring.Objects.Factory.IObjectFactory"/>
/// (may not be <see langword="null"/>). The object can immediately
/// call methods on the factory.
/// </value>
/// <remarks>
/// <p>
/// Invoked after population of normal object properties but before an init
/// callback like <see cref="Spring.Objects.Factory.IInitializingObject"/>'s
/// <see cref="Spring.Objects.Factory.IInitializingObject.AfterPropertiesSet"/>
/// method or a custom init-method.
/// </p>
/// </remarks>
/// <exception cref="Spring.Objects.ObjectsException">
/// In case of initialization errors.
/// </exception>
public IObjectFactory ObjectFactory
{
set { objectFactory = value; }
}
#endregion
#region IObjectFactoryPostProcessor Members
/// <summary>
/// Allows for custom modification of an application context's object definitions.
/// </summary>
/// <remarks>
/// <p>
/// If the object name matches, replaces the type by a AOP proxied type based on inheritance.
/// </p>
/// </remarks>
public void PostProcessObjectFactory(IConfigurableListableObjectFactory factory)
{
string[] objectDefinitionNames = factory.GetObjectDefinitionNames();
for (int i = 0; i < objectDefinitionNames.Length; ++i)
{
string name = objectDefinitionNames[i];
if (IsObjectNameMatch(name))
{
IConfigurableObjectDefinition definition =
factory.GetObjectDefinition(name) as IConfigurableObjectDefinition;
if (definition == null || IsInfrastructureType(definition.ObjectType, name))
{
continue;
}
ProxyFactory pf = CreateProxyFactory(definition.ObjectType, name);
InheritanceAopProxyTypeBuilder iaptb = new InheritanceAopProxyTypeBuilder(pf);
iaptb.ProxyDeclaredMembersOnly = this.ProxyDeclaredMembersOnly;
Type type = iaptb.BuildProxyType();
definition.ObjectType = type;
definition.ConstructorArgumentValues.AddIndexedArgumentValue(definition.ConstructorArgumentValues.ArgumentCount, pf);
}
}
}
#endregion
#region IOrdered Members
/// <summary>
/// Return the order value of this object, where a higher value means greater in
/// terms of sorting.
/// </summary>
/// <remarks>
/// Ordering which will apply to this class's implementation
/// of Ordered, used when applying multiple ObjectPostProcessors.
/// Default value is int.MaxValue, meaning that it's non-ordered.
/// </remarks>
/// <returns>The order value.</returns>
public virtual int Order
{
get { return order; }
set { order = value; }
}
#endregion
#region Protected methods
/// <summary>
/// Determines whether the object is an infrastructure type,
/// IAdvisor, IAdvice, IAdvisors, AbstractAutoProxyCreator or InheritanceBasedAopConfigurer.
/// </summary>
/// <param name="type">The object type to compare</param>
/// <param name="name">The name of the object</param>
/// <returns>
/// <c>true</c> if [is infrastructure type] [the specified obj]; otherwise, <c>false</c>.
/// </returns>
protected virtual bool IsInfrastructureType(Type type, String name)
{
return typeof(IAdvisor).IsAssignableFrom(type)
|| typeof(IAdvice).IsAssignableFrom(type)
|| typeof(IAdvisors).IsAssignableFrom(type)
|| typeof(AbstractAutoProxyCreator).IsAssignableFrom(type)
|| typeof(InheritanceBasedAopConfigurer).IsAssignableFrom(type);
}
/// <summary>
/// Create an AOP proxy for the given object.
/// </summary>
/// <param name="objectType">Type of the object.</param>
/// <param name="objectName">The name of the object.</param>
/// <returns>The AOP Proxy for the object.</returns>
protected virtual ProxyFactory CreateProxyFactory(Type objectType, string objectName)
{
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.ProxyTargetAttributes = this.ProxyTargetAttributes;
proxyFactory.ProxyTargetAttributes = this.ProxyDeclaredMembersOnly;
proxyFactory.TargetSource = new InheritanceBasedAopTargetSource(objectType);
if (!ProxyInterfaces)
{
proxyFactory.Interfaces = Type.EmptyTypes;
}
IAdvisor[] advisors = ResolveInterceptorNames();
foreach (IAdvisor advisor in advisors)
{
if (advisor is IIntroductionAdvisor)
{
proxyFactory.AddIntroduction((IIntroductionAdvisor)advisor);
}
else
{
proxyFactory.AddAdvisor(advisor);
}
}
return proxyFactory;
}
/// <summary>
/// Return if the given object name matches one of the object names specified.
/// </summary>
/// <remarks>
/// <p>
/// The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches,
/// as well as direct equality. Can be overridden in subclasses.
/// </p>
/// </remarks>
/// <param name="objectName">the object name to check</param>
/// <returns>if the names match</returns>
protected virtual bool IsObjectNameMatch(string objectName)
{
if (objectNames != null)
{
for (int i = 0; i < objectNames.Count; i++)
{
string mappedName = String.Copy((string)objectNames[i]);
if (PatternMatchUtils.SimpleMatch(mappedName, objectName))
{
return true;
}
}
}
return false;
}
#endregion
#region Private Methods
private IAdvisor[] ResolveInterceptorNames()
{
ArrayList advisors = new ArrayList();
foreach (string name in interceptorNames)
{
object next = objectFactory.GetObject(name);
if (next is IAdvisors)
{
advisors.AddRange(((IAdvisors)next).Advisors);
}
else
{
advisors.Add(advisorAdapterRegistry.Wrap(next));
}
}
return (IAdvisor[])advisors.ToArray(typeof(IAdvisor));
}
#endregion
#region InheritanceBasedAopTargetSource inner class definition
[Serializable]
private class InheritanceBasedAopTargetSource : ITargetSource
{
private readonly Type _targetType;
public InheritanceBasedAopTargetSource(Type targetType)
{
_targetType = targetType;
}
#region ITargetSource Members
public object GetTarget()
{
return null;
}
public bool IsStatic
{
get { return true; }
}
public void ReleaseTarget(object target)
{
}
public Type TargetType
{
get { return _targetType; }
}
#endregion
}
#endregion
}
}
|