From c79c47f4ea700c7f4d1e637bb30d4d8cf52f2782 Mon Sep 17 00:00:00 2001 From: eeichinger Date: Tue, 4 Nov 2008 16:27:42 +0000 Subject: [PATCH] resolved SPRNET-924 fixed bug in AttributeTypeFilter wrt attribute lookup --- .../AutoProxy/AbstractAutoProxyCreator.cs | 1303 +++++++++-------- .../AbstractFilteringAutoProxyCreator.cs | 81 + .../AutoProxy/AttributeAutoProxyCreator.cs | 115 ++ .../AutoProxy/ObjectNameAutoProxyCreator.cs | 52 +- .../PointcutFilteringAutoProxyCreator.cs | 28 +- .../AutoProxy/TypeNameAutoProxyCreator.cs | 70 + .../Aop/Support/AttributeTypeFilter.cs | 23 +- .../Aop/Support/TypeNameTypeFilter.cs | 75 + src/Spring/Spring.Aop/Spring.Aop.2008.csproj | 4 + .../Spring.Core/Util/PatternMatchUtils.cs | 55 +- .../AttributeAutoProxyCreatorTests.cs | 150 ++ .../PointcutFilteringAutoProxyCreatorTests.cs | 38 +- .../TypeNameAutoProxyCreatorTests.cs | 98 ++ .../Spring.Aop.Tests.2008.csproj | 2 + 14 files changed, 1365 insertions(+), 729 deletions(-) create mode 100644 src/Spring/Spring.Aop/Aop/Framework/AutoProxy/AbstractFilteringAutoProxyCreator.cs create mode 100644 src/Spring/Spring.Aop/Aop/Framework/AutoProxy/AttributeAutoProxyCreator.cs create mode 100644 src/Spring/Spring.Aop/Aop/Framework/AutoProxy/TypeNameAutoProxyCreator.cs create mode 100644 src/Spring/Spring.Aop/Aop/Support/TypeNameTypeFilter.cs create mode 100644 test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/AttributeAutoProxyCreatorTests.cs create mode 100644 test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/TypeNameAutoProxyCreatorTests.cs diff --git a/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/AbstractAutoProxyCreator.cs b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/AbstractAutoProxyCreator.cs index ed1be482..a01dba61 100644 --- a/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/AbstractAutoProxyCreator.cs +++ b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/AbstractAutoProxyCreator.cs @@ -1,5 +1,5 @@ -#region License - +#region License + /* * Copyright © 2002-2005 the original author or authors. * @@ -14,633 +14,674 @@ * 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.Reflection; -using System.Runtime.Remoting; -using AopAlliance.Aop; -using Common.Logging; -using Spring.Aop.Framework.Adapter; -using Spring.Aop.Target; -using Spring.Collections; -using Spring.Core; -using Spring.Objects; -using Spring.Objects.Factory; -using Spring.Objects.Factory.Config; -using Spring.Util; - -#endregion - -namespace Spring.Aop.Framework.AutoProxy -{ - /// - /// ObjectPostProcessor implementation that wraps a group of objects with AOP proxies - /// that delegate to the given interceptors before invoking the object itself. - /// - /// - ///

This class distinguishes between "common" interceptors: shared for all proxies it - /// creates, and "specific" interceptors: unique per object instance. There need not - /// be any common interceptors. If there are, they are set using the interceptorNames - /// property. As with ProxyFactoryObject, interceptors names in the current factory - /// are used rather than object references to allow correct handling of prototype - /// advisors and interceptors: for example, to support stateful mixins. - /// Any advice type is supported for "interceptorNames" entries.

- ///

Such autoproxying is particularly useful if there's a large number of objects that need - /// to be wrapped with similar proxies, i.e. delegating to the same interceptors. - /// Instead of x repetitive proxy definitions for x target objects, you can register - /// one single such post processor with the object factory to achieve the same effect.

- ///

Subclasses can apply any strategy to decide if a object is to be proxied, - /// e.g. by type, by name, by definition details, etc. They can also return - /// additional interceptors that should just be applied to the specific object - /// instance. The default concrete implementation is ObjectNameAutoProxyCreator, - /// identifying the objects to be proxied via a list of object names.

- ///

Any number of TargetSourceCreator implementations can be used with any subclass, - /// to create a custom target source - for example, to pool prototype objects. - /// Autoproxying will occur even if there is no advice if a TargetSourceCreator specifies - /// a custom TargetSource. If there are no TargetSourceCreators set, or if none matches, - /// a SingletonTargetSource will be used by default to wrap the object to be autoproxied.

- ///
- /// Juergen Hoeller - /// Rod Johnson - /// Adhari C Mahendra (.NET) - /// - /// - public abstract class AbstractAutoProxyCreator : ProxyConfig, IInstantiationAwareObjectPostProcessor, IObjectFactoryAware, IOrdered - { - #region Protected Fields - - /// - /// The logger for this class hierarchy. - /// - protected readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - - /// - /// Convenience constant for subclasses: Return value for "do not proxy". - /// - protected static readonly object[] DO_NOT_PROXY = null; - - /// - /// Convenience constant for subclasses: Return value for - /// "proxy without additional interceptors, just the common ones". - /// - protected static readonly object[] PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS = new object[0]; - - #endregion - - #region Private Fields - - /// - /// Default value is same as non-ordered - /// - private int order = int.MaxValue; - - /// - /// Default is global AdvisorAdapterRegistry - /// - private IAdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.Instance; - - - /// - /// - /// - private bool freezeProxy = false; - - /// - /// Names of common interceptors. - /// We must use object name rather than object references - /// to handle prototype advisors/interceptors. - /// Default is the empty array: no common interceptors. - /// - private string[] interceptorNames = new string[0]; - - private bool applyCommonInterceptorsFirst = true; - private IList customTargetSourceCreators = new ArrayList(); - private IObjectFactory owningObjectFactory; - - /// - /// Set of object type + name strings, referring to all objects that this auto-proxy - /// creator created a custom TargetSource for. Used to detect own pre-built proxies - /// (from "PostProcessBeforeInstantiation") in the "PostProcessAfterInitialization" method. - /// - private ISet targetSourcedObjects = new SynchronizedSet(new HashedSet()); - - private ISet advisedObjects = new SynchronizedSet(new HashedSet()); - - private ISet nonAdvisedObjects = new SynchronizedSet(new HashedSet()); - - #endregion - - #region Properties - - /// - /// Sets the AdvisorAdapterRegistry to use. - /// - /// - /// Default is the global AdvisorAdapterRegistry. - /// - public IAdvisorAdapterRegistry AdvisorAdapterRegistry - { - set { advisorAdapterRegistry = value; } - } - - /// - /// Sets custom TargetSourceCreators to be applied in this order. - /// - /// - /// - /// If the list is empty, or they all return null, a SingletonTargetSource - /// will be created. - /// - /// - /// TargetSourceCreators can only be invoked if this post processor is used - /// in a IObjectFactory, and its ObjectFactoryAware callback is used. - /// - /// - public IList CustomTargetSourceCreators - { - set { customTargetSourceCreators = value; } - } - - /// - /// Sets the common interceptors, a list of , - /// and introduction object names. - /// - /// - /// - /// 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. - /// - /// - /// - /// The list of , - /// and introduction object names. - /// - /// - /// - public string[] InterceptorNames - { - set { interceptorNames = value; } - } - - /// - /// Sets whether the common interceptors should be applied before - /// object-specific ones. - /// - /// - /// Default is true; else, object-specific interceptors will get applied first. - /// - public bool ApplyCommonInterceptorsFirst - { - set { applyCommonInterceptorsFirst = value; } - } - - /// - /// Set whether or not the proxy should be frozen, preventing advice - /// from being added to it once it is created. - /// - /// - ///

Overridden from the super class to prevent the proxy configuration - /// from being frozen before the proxy is created. The default is not frozen. - ///

- ///
- public override bool IsFrozen - { - get { return freezeProxy; } - set { this.freezeProxy = value; } - } - - #endregion - - #region IObjectPostProcessor Members - - /// - /// Create a proxy with the configured interceptors if the object is - /// identified as one to proxy by the subclass. - /// - public virtual object PostProcessAfterInitialization(object obj, string objectName) - { - if (targetSourcedObjects.Contains(objectName)) - { - return obj; - } - - object cacheKey = GetCacheKey(obj.GetType(), objectName); - if (nonAdvisedObjects.Contains(cacheKey)) - { - return obj; - } - if (IsInfrastructureType(obj.GetType(), objectName) || ShouldSkip(obj.GetType(), objectName)) - { - #region Instrumentation - - if (logger.IsDebugEnabled) - { - logger.Debug(string.Format("Did not attempt to autoproxy infrastructure type [{0}]", obj.GetType().ToString())); - } - - #endregion - - nonAdvisedObjects.Add(cacheKey); - return obj; - } - - //ITargetSource targetSource = GetCustomTargetSource(obj.GetType(), objectName); - object[] specificInterceptors; - if (RemotingServices.IsTransparentProxy(obj)) - { - specificInterceptors = GetAdvicesAndAdvisorsForObject(ObjectFactory.GetType(objectName), objectName, null); - } - else - { - specificInterceptors = GetAdvicesAndAdvisorsForObject(obj.GetType(), objectName, null); - } - - - // proxy if we have advice or if a TargetSourceCreator wants to do some - // fancy stuff such as pooling - if (specificInterceptors != DO_NOT_PROXY) - { - advisedObjects.Add(cacheKey); - return CreateProxy(obj.GetType(), objectName, specificInterceptors, new SingletonTargetSource(obj)); - } - nonAdvisedObjects.Add(cacheKey); - return obj; - } - - /// - /// No-op for before initialization. - /// - /// The obj. - /// The name. - /// - public virtual object PostProcessBeforeInitialization(object obj, string name) - { - return obj; - } - - #endregion - - #region IObjectFactoryAware Members - - /// - /// Callback that supplies the owning factory to an object instance. - /// - /// - /// Owning - /// (may not be ). The object can immediately - /// call methods on the factory. - /// - /// - ///

- /// Invoked after population of normal object properties but before an init - /// callback like 's - /// - /// method or a custom init-method. - ///

- ///
- /// - /// In case of initialization errors. - /// - public virtual IObjectFactory ObjectFactory - { - get { return owningObjectFactory; } - set { owningObjectFactory = value; } - } - - #endregion - - #region IOrdered Members - - /// - /// Propery Order - /// - /// - /// 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. - /// - public virtual int Order - { - get { return order; } - set { order = value; } - } - - #endregion - - #region Protected Methods - - /// - /// Subclasses should override this method to return true if this - /// object should not be considered for autoproxying by this post processor. - /// Sometimes we need to be able to avoid this happening if it will lead to - /// a circular reference. This implementation returns false. - /// - /// the type of the object - /// the name of the object - /// if remarkable to skip - protected virtual bool ShouldSkip(Type objectType, string objectName) - { - return false; - } - - /// - /// Subclasses may choose to implement this: for example, - /// to change the interfaces exposed - /// - /// - /// ProxyFactory that will be used to create the proxy immediably after this method returns. - /// - protected virtual void CustomizeProxyFactory(ProxyFactory pf) - { - // This implementation does nothing - } - - /// - /// Determines whether the object is an infrastructure type, - /// IAdvisor, IAdvice, IAdvisors or AbstractAutoProxyCreator - /// - /// The object type to compare - /// The name of the object - /// - /// true if [is infrastructure type] [the specified obj]; otherwise, false. - /// - 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); - } - - - /// - /// Create a target source for object instances. Uses any - /// TargetSourceCreators if set. Returns null if no Custom TargetSource - /// should be used. - /// This implementation uses the customTargetSourceCreators property. - /// Subclasses can override this method to use a different mechanism. - /// - /// the type of the object to create a TargetSource for - /// the name of the object - /// a TargetSource for this object - protected virtual ITargetSource GetCustomTargetSource(Type objectType, string name) - { - // We can't create fancy target sources for directly registered singletons. - if (customTargetSourceCreators != null && - owningObjectFactory != null && owningObjectFactory.ContainsObject(name)) - { - for (int i = 0; i < customTargetSourceCreators.Count; i++) - { - ITargetSourceCreator tsc = (ITargetSourceCreator) customTargetSourceCreators[i]; - ITargetSource ts = tsc.GetTargetSource(objectType, name, owningObjectFactory); - if (ts != null) - { - // found a match - if (logger.IsInfoEnabled) - { - logger.Info(string.Format("TargetSourceCreator [{0} found custom TargetSource for object with objectName '{1}'", tsc, name)); - } - return ts; - } - } - } - - // no custom TargetSource found - return null; - } - - /// - /// Return whether the given object is to be proxied, what additional - /// advices (e.g. AOP Alliance interceptors) and advisors to apply. - /// - /// - ///

The previous name of this method was "GetInterceptorAndAdvisorForObject". - /// It has been renamed in the course of general terminology clarification - /// in Spring 1.1. An AOP Alliance Interceptor is just a special form of - /// Advice, so the generic Advice term is preferred now.

- ///

The third parameter, customTargetSource, is new in Spring 1.1; - /// add it to existing implementations of this method.

- ///
- /// the new object instance - /// the name of the object - /// targetSource returned by TargetSource property: - /// may be ignored. Will be null unless a custom target source is in use. - /// an array of additional interceptors for the particular object; - /// or an empty array if no additional interceptors but just the common ones; - /// or null if no proxy at all, not even with the common interceptors. - protected abstract object[] GetAdvicesAndAdvisorsForObject(Type objType, string name, ITargetSource customTargetSource); - - /// - /// Create an AOP proxy for the given object. - /// - /// Type of the object. - /// The name of the object. - /// The set of interceptors that is specific to this - /// object (may be empty but not null) - /// The target source for the proxy, already pre-configured to access the object. - /// The AOP Proxy for the object. - protected virtual object CreateProxy(Type objectType, string objectName, object[] specificInterceptors, ITargetSource targetSource) - { - ProxyFactory proxyFactory = CreateProxyFactory(); - // copy our properties (proxyTargetClass) inherited from ProxyConfig - proxyFactory.CopyFrom(this); - - object target = targetSource.GetTarget(); - - - if(!ProxyTargetType) - { - // Must allow for introductions; can't just set interfaces to - // the target's interfaces only. - Type[] targetInterfaceTypes = AopUtils.GetAllInterfaces(target); - foreach (Type interfaceType in targetInterfaceTypes) - { - proxyFactory.AddInterface(interfaceType); - } - } - - - IAdvisor[] advisors = BuildAdvisors(objectName, specificInterceptors); - - foreach (IAdvisor advisor in advisors) - { - if (advisor is IIntroductionAdvisor) - { - proxyFactory.AddIntroduction((IIntroductionAdvisor)advisor); - } - else - { - proxyFactory.AddAdvisor(advisor); - } - } - proxyFactory.TargetSource = targetSource; - CustomizeProxyFactory(proxyFactory); - - proxyFactory.IsFrozen = freezeProxy; - return proxyFactory.GetProxy(); - } - - /// - /// Obtain a new proxy factory instance to be used for proxying a particular object - /// - /// A proxy factory instance for proxying a particular object - protected virtual ProxyFactory CreateProxyFactory() - { - return new ProxyFactory(); - } - - /// - /// Determines the advisors for the given object, including the specific interceptors - /// as well as the common interceptor, all adapted to the Advisor interface. - /// - /// The name of the object. - /// The set of interceptors that is specific to this - /// object (may be empty, but not null) - /// The list of Advisors for the given object - protected virtual IAdvisor[] BuildAdvisors(string objectName, object[] specificInterceptors) - { - // handle prototypes correctly - IAdvisor[] commonInterceptors = ResolveInterceptorNames(); - - ArrayList allInterceptors = new ArrayList(); - if (specificInterceptors != null) - { - allInterceptors.AddRange(specificInterceptors); - if (commonInterceptors != null) - { - if (applyCommonInterceptorsFirst) - { - allInterceptors.InsertRange(0, commonInterceptors); - } - else - { - allInterceptors.AddRange(commonInterceptors); - } - } - } - if (logger.IsInfoEnabled) - { - int nrOfCommonInterceptors = commonInterceptors != null ? commonInterceptors.Length : 0; - int nrOfSpecificInterceptors = specificInterceptors != null ? specificInterceptors.Length : 0; - logger.Info(string.Format("Creating implicit proxy for object '{0}' with {1} common interceptors and {2} specific interceptors", objectName, nrOfCommonInterceptors, nrOfSpecificInterceptors)); - } - - - IAdvisor[] advisors = new IAdvisor[allInterceptors.Count]; - for (int i = 0; i < allInterceptors.Count; i++) - { - advisors[i] = advisorAdapterRegistry.Wrap(allInterceptors[i]); - } - return advisors; - } - - /// - /// Build a cache key for the given object type and object name - /// - /// The object type. - /// The object name. - /// The cache key for the given type and name - protected virtual object GetCacheKey(Type objectType, string objectName) - { - return objectType.FullName + "_" + objectName; - } - - - #endregion - - #region Private Methods - - private IAdvisor[] ResolveInterceptorNames() - { - ArrayList advisors = new ArrayList(); - foreach(string name in interceptorNames) - { - object next = owningObjectFactory.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 IInstantiationAwareObjectPostProcessor Members - - /// - /// Create the proxy if have a custom TargetSource - /// - /// The object type - /// The object name - /// null if not creating a proxy, otherwise return the proxy. - public object PostProcessBeforeInstantiation(Type objectType, string objectName) - { - object cacheKey = GetCacheKey(objectType, objectName); - if (!targetSourcedObjects.Contains(cacheKey)) - { - if (advisedObjects.Contains(cacheKey) || nonAdvisedObjects.Contains(cacheKey)) - { - return null; - } - if (IsInfrastructureType(objectType, objectName) || ShouldSkip(objectType, objectName)) - { - nonAdvisedObjects.Add(cacheKey); - return null; - } - } - // Create proxy here if we have a custom TargetSource. - // Suppresses unnecessary default instantiation of the target object: - // The TargetSource will handle target instances in a custom fashion. - ITargetSource targetSource = GetCustomTargetSource(objectType, objectName); - if (targetSource != null) - { - targetSourcedObjects.Add(objectName); - object[] specificInterceptors = GetAdvicesAndAdvisorsForObject(objectType, objectName, targetSource); - return CreateProxy(objectType, objectName, specificInterceptors, targetSource); - } - return null; - } - - - /// - /// Default behavior, return true and continue processing. - /// - /// The object instance - /// The object name. - /// true - public bool PostProcessAfterInstantiation(object objectInstance, string objectName) - { - return true; - } - - /// - /// Default behavior, return passed in PropertyValues - /// - /// The property values that the factory is about to apply (never null). - /// he relevant property infos for the target object (with ignored - /// dependency types - which the factory handles specifically - already filtered out) - /// The object instance created, but whose properties have not yet - /// been set. - /// Name of the object. - /// The passed in PropertyValues - public IPropertyValues PostProcessPropertyValues(IPropertyValues pvs, PropertyInfo[] pis, object objectInstance, - string objectName) - { - return pvs; - } - - #endregion - } + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using System.Runtime.Remoting; +using AopAlliance.Aop; +using Common.Logging; +using Spring.Aop.Framework.Adapter; +using Spring.Aop.Target; +using Spring.Collections; +using Spring.Core; +using Spring.Objects; +using Spring.Objects.Factory; +using Spring.Objects.Factory.Config; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework.AutoProxy +{ + /// + /// ObjectPostProcessor implementation that wraps a group of objects with AOP proxies + /// that delegate to the given interceptors before invoking the object itself. + /// + /// + ///

This class distinguishes between "common" interceptors: shared for all proxies it + /// creates, and "specific" interceptors: unique per object instance. There need not + /// be any common interceptors. If there are, they are set using the interceptorNames + /// property. As with ProxyFactoryObject, interceptors names in the current factory + /// are used rather than object references to allow correct handling of prototype + /// advisors and interceptors: for example, to support stateful mixins. + /// Any advice type is supported for "interceptorNames" entries.

+ ///

Such autoproxying is particularly useful if there's a large number of objects that need + /// to be wrapped with similar proxies, i.e. delegating to the same interceptors. + /// Instead of x repetitive proxy definitions for x target objects, you can register + /// one single such post processor with the object factory to achieve the same effect.

+ ///

Subclasses can apply any strategy to decide if a object is to be proxied, + /// e.g. by type, by name, by definition details, etc. They can also return + /// additional interceptors that should just be applied to the specific object + /// instance. The default concrete implementation is ObjectNameAutoProxyCreator, + /// identifying the objects to be proxied via a list of object names.

+ ///

Any number of TargetSourceCreator implementations can be used with any subclass, + /// to create a custom target source - for example, to pool prototype objects. + /// Autoproxying will occur even if there is no advice if a TargetSourceCreator specifies + /// a custom TargetSource. If there are no TargetSourceCreators set, or if none matches, + /// a SingletonTargetSource will be used by default to wrap the object to be autoproxied.

+ ///
+ /// Juergen Hoeller + /// Rod Johnson + /// Adhari C Mahendra (.NET) + /// + /// + public abstract class AbstractAutoProxyCreator : ProxyConfig, IInstantiationAwareObjectPostProcessor, IObjectFactoryAware, IOrdered + { + #region Protected Fields + + /// + /// The logger for this class hierarchy. + /// + protected readonly ILog logger = LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod().DeclaringType ); + + /// + /// Convenience constant for subclasses: Return value for "do not proxy". + /// + protected static readonly object[] DO_NOT_PROXY = null; + + /// + /// Convenience constant for subclasses: Return value for + /// "proxy without additional interceptors, just the common ones". + /// + protected static readonly object[] PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS = new object[0]; + + #endregion + + #region Private Fields + + /// + /// Default value is same as non-ordered + /// + private int order = int.MaxValue; + + /// + /// Default is global AdvisorAdapterRegistry + /// + private IAdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.Instance; + + + /// + /// + /// + private bool freezeProxy = false; + + /// + /// Names of common interceptors. + /// We must use object name rather than object references + /// to handle prototype advisors/interceptors. + /// Default is the empty array: no common interceptors. + /// + private string[] interceptorNames = new string[0]; + + private bool applyCommonInterceptorsFirst = true; + private IList customTargetSourceCreators = new ArrayList(); + private IObjectFactory owningObjectFactory; + + /// + /// Set of object type + name strings, referring to all objects that this auto-proxy + /// creator created a custom TargetSource for. Used to detect own pre-built proxies + /// (from "PostProcessBeforeInstantiation") in the "PostProcessAfterInitialization" method. + /// + private ISet targetSourcedObjects = new SynchronizedSet( new HashedSet() ); + + private ISet advisedObjects = new SynchronizedSet( new HashedSet() ); + + private ISet nonAdvisedObjects = new SynchronizedSet( new HashedSet() ); + + #endregion + + #region Properties + + /// + /// Sets the AdvisorAdapterRegistry to use. + /// + /// + /// Default is the global AdvisorAdapterRegistry. + /// + public IAdvisorAdapterRegistry AdvisorAdapterRegistry + { + set { advisorAdapterRegistry = value; } + } + + /// + /// Sets custom TargetSourceCreators to be applied in this order. + /// + /// + /// + /// If the list is empty, or they all return null, a SingletonTargetSource + /// will be created. + /// + /// + /// TargetSourceCreators can only be invoked if this post processor is used + /// in a IObjectFactory, and its ObjectFactoryAware callback is used. + /// + /// + public IList CustomTargetSourceCreators + { + set { customTargetSourceCreators = value; } + } + + /// + /// Sets the common interceptors, a list of , + /// and introduction object names. + /// + /// + /// + /// 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. + /// + /// + /// + /// The list of , + /// and introduction object names. + /// + /// + /// + public string[] InterceptorNames + { + set { interceptorNames = value; } + } + + /// + /// Sets whether the common interceptors should be applied before + /// object-specific ones. + /// + /// + /// Default is true; else, object-specific interceptors will get applied first. + /// + public bool ApplyCommonInterceptorsFirst + { + set { applyCommonInterceptorsFirst = value; } + } + + /// + /// Set whether or not the proxy should be frozen, preventing advice + /// from being added to it once it is created. + /// + /// + ///

Overridden from the super class to prevent the proxy configuration + /// from being frozen before the proxy is created. The default is not frozen. + ///

+ ///
+ public override bool IsFrozen + { + get { return freezeProxy; } + set { this.freezeProxy = value; } + } + + #endregion + + #region IObjectPostProcessor Members + + /// + /// Create a proxy with the configured interceptors if the object is + /// identified as one to proxy by the subclass. + /// + public virtual object PostProcessAfterInitialization( object obj, string objectName ) + { + if (targetSourcedObjects.Contains( objectName )) + { + return obj; + } + + object cacheKey = GetCacheKey( obj.GetType(), objectName ); + if (nonAdvisedObjects.Contains( cacheKey )) + { + return obj; + } + + if (IsInfrastructureType( obj.GetType(), objectName )) + { + #region Instrumentation + + if (logger.IsDebugEnabled) + { + logger.Debug( string.Format( "Did not attempt to autoproxy infrastructure type [{0}]", obj.GetType() ) ); + } + + #endregion + + nonAdvisedObjects.Add( cacheKey ); + return obj; + } + + if (ShouldSkip( obj.GetType(), objectName )) + { + #region Instrumentation + + if (logger.IsDebugEnabled) + { + logger.Debug( string.Format( "Skipping type [{0}]", obj.GetType() ) ); + } + + #endregion + + nonAdvisedObjects.Add( cacheKey ); + return obj; + } + + //ITargetSource targetSource = GetCustomTargetSource(obj.GetType(), objectName); + object[] specificInterceptors; + if (RemotingServices.IsTransparentProxy( obj )) + { + specificInterceptors = GetAdvicesAndAdvisorsForObject( ObjectFactory.GetType( objectName ), objectName, null ); + } + else + { + specificInterceptors = GetAdvicesAndAdvisorsForObject( obj.GetType(), objectName, null ); + } + + + // proxy if we have advice or if a TargetSourceCreator wants to do some + // fancy stuff such as pooling + if (specificInterceptors != DO_NOT_PROXY) + { + advisedObjects.Add( cacheKey ); + return CreateProxy( obj.GetType(), objectName, specificInterceptors, new SingletonTargetSource( obj ) ); + } + nonAdvisedObjects.Add( cacheKey ); + return obj; + } + + /// + /// No-op for before initialization. + /// + /// The obj. + /// The name. + /// + public virtual object PostProcessBeforeInitialization( object obj, string name ) + { + return obj; + } + + #endregion + + #region IObjectFactoryAware Members + + /// + /// Callback that supplies the owning factory to an object instance. + /// + /// + /// Owning + /// (may not be ). The object can immediately + /// call methods on the factory. + /// + /// + ///

+ /// Invoked after population of normal object properties but before an init + /// callback like 's + /// + /// method or a custom init-method. + ///

+ ///
+ /// + /// In case of initialization errors. + /// + public virtual IObjectFactory ObjectFactory + { + get { return owningObjectFactory; } + set { owningObjectFactory = value; } + } + + #endregion + + #region IOrdered Members + + /// + /// Propery Order + /// + /// + /// 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. + /// + public virtual int Order + { + get { return order; } + set { order = value; } + } + + #endregion + + #region Protected Methods + + /// + /// Subclasses should override this method to return true if this + /// object should not be considered for autoproxying by this post processor. + /// Sometimes we need to be able to avoid this happening if it will lead to + /// a circular reference. This implementation returns false. + /// + /// the type of the object + /// the name of the object + /// if remarkable to skip + protected virtual bool ShouldSkip( Type objectType, string objectName ) + { + return false; + } + + /// + /// Subclasses may choose to implement this: for example, + /// to change the interfaces exposed + /// + /// + /// ProxyFactory that will be used to create the proxy immediably after this method returns. + /// + protected virtual void CustomizeProxyFactory( ProxyFactory pf ) + { + // This implementation does nothing + } + + /// + /// Determines whether the object is an infrastructure type, + /// IAdvisor, IAdvice, IAdvisors or AbstractAutoProxyCreator + /// + /// The object type to compare + /// The name of the object + /// + /// true if [is infrastructure type] [the specified obj]; otherwise, false. + /// + 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 ); + } + + + /// + /// Create a target source for object instances. Uses any + /// TargetSourceCreators if set. Returns null if no Custom TargetSource + /// should be used. + /// This implementation uses the customTargetSourceCreators property. + /// Subclasses can override this method to use a different mechanism. + /// + /// the type of the object to create a TargetSource for + /// the name of the object + /// a TargetSource for this object + protected virtual ITargetSource GetCustomTargetSource( Type objectType, string name ) + { + // We can't create fancy target sources for directly registered singletons. + if (customTargetSourceCreators != null && + owningObjectFactory != null && owningObjectFactory.ContainsObject( name )) + { + for (int i = 0; i < customTargetSourceCreators.Count; i++) + { + ITargetSourceCreator tsc = (ITargetSourceCreator)customTargetSourceCreators[i]; + ITargetSource ts = tsc.GetTargetSource( objectType, name, owningObjectFactory ); + if (ts != null) + { + // found a match + if (logger.IsInfoEnabled) + { + logger.Info( string.Format( "TargetSourceCreator [{0} found custom TargetSource for object with objectName '{1}'", tsc, name ) ); + } + return ts; + } + } + } + + // no custom TargetSource found + return null; + } + + /// + /// Return whether the given object is to be proxied, what additional + /// advices (e.g. AOP Alliance interceptors) and advisors to apply. + /// + /// + ///

The previous name of this method was "GetInterceptorAndAdvisorForObject". + /// It has been renamed in the course of general terminology clarification + /// in Spring 1.1. An AOP Alliance Interceptor is just a special form of + /// Advice, so the generic Advice term is preferred now.

+ ///

The third parameter, customTargetSource, is new in Spring 1.1; + /// add it to existing implementations of this method.

+ ///
+ /// the new object instance + /// the name of the object + /// targetSource returned by TargetSource property: + /// may be ignored. Will be null unless a custom target source is in use. + /// an array of additional interceptors for the particular object; + /// or an empty array if no additional interceptors but just the common ones; + /// or null if no proxy at all, not even with the common interceptors. + protected abstract object[] GetAdvicesAndAdvisorsForObject( Type objType, string name, ITargetSource customTargetSource ); + + /// + /// Create an AOP proxy for the given object. + /// + /// Type of the object. + /// The name of the object. + /// The set of interceptors that is specific to this + /// object (may be empty but not null) + /// The target source for the proxy, already pre-configured to access the object. + /// The AOP Proxy for the object. + protected virtual object CreateProxy( Type objectType, string objectName, object[] specificInterceptors, ITargetSource targetSource ) + { + ProxyFactory proxyFactory = CreateProxyFactory(); + // copy our properties (proxyTargetClass) inherited from ProxyConfig + proxyFactory.CopyFrom( this ); + + object target = targetSource.GetTarget(); + + + if (!ProxyTargetType) + { + // Must allow for introductions; can't just set interfaces to + // the target's interfaces only. + Type[] targetInterfaceTypes = AopUtils.GetAllInterfaces( target ); + foreach (Type interfaceType in targetInterfaceTypes) + { + proxyFactory.AddInterface( interfaceType ); + } + } + + + IAdvisor[] advisors = BuildAdvisors( objectName, specificInterceptors ); + + foreach (IAdvisor advisor in advisors) + { + if (advisor is IIntroductionAdvisor) + { + proxyFactory.AddIntroduction( (IIntroductionAdvisor)advisor ); + } + else + { + proxyFactory.AddAdvisor( advisor ); + } + } + proxyFactory.TargetSource = targetSource; + CustomizeProxyFactory( proxyFactory ); + + proxyFactory.IsFrozen = freezeProxy; + return proxyFactory.GetProxy(); + } + + /// + /// Obtain a new proxy factory instance to be used for proxying a particular object + /// + /// A proxy factory instance for proxying a particular object + protected virtual ProxyFactory CreateProxyFactory() + { + return new ProxyFactory(); + } + + /// + /// Determines the advisors for the given object, including the specific interceptors + /// as well as the common interceptor, all adapted to the Advisor interface. + /// + /// The name of the object. + /// The set of interceptors that is specific to this + /// object (may be empty, but not null) + /// The list of Advisors for the given object + protected virtual IAdvisor[] BuildAdvisors( string objectName, object[] specificInterceptors ) + { + // handle prototypes correctly + IAdvisor[] commonInterceptors = ResolveInterceptorNames(); + + ArrayList allInterceptors = new ArrayList(); + if (specificInterceptors != null) + { + allInterceptors.AddRange( specificInterceptors ); + if (commonInterceptors != null) + { + if (applyCommonInterceptorsFirst) + { + allInterceptors.InsertRange( 0, commonInterceptors ); + } + else + { + allInterceptors.AddRange( commonInterceptors ); + } + } + } + if (logger.IsInfoEnabled) + { + int nrOfCommonInterceptors = commonInterceptors != null ? commonInterceptors.Length : 0; + int nrOfSpecificInterceptors = specificInterceptors != null ? specificInterceptors.Length : 0; + logger.Info( string.Format( "Creating implicit proxy for object '{0}' with {1} common interceptors and {2} specific interceptors", objectName, nrOfCommonInterceptors, nrOfSpecificInterceptors ) ); + } + + + IAdvisor[] advisors = new IAdvisor[allInterceptors.Count]; + for (int i = 0; i < allInterceptors.Count; i++) + { + advisors[i] = advisorAdapterRegistry.Wrap( allInterceptors[i] ); + } + return advisors; + } + + /// + /// Build a cache key for the given object type and object name + /// + /// The object type. + /// The object name. + /// The cache key for the given type and name + protected virtual object GetCacheKey( Type objectType, string objectName ) + { + return objectType.FullName + "_" + objectName; + } + + + #endregion + + #region Private Methods + + private IAdvisor[] ResolveInterceptorNames() + { + ArrayList advisors = new ArrayList(); + foreach (string name in interceptorNames) + { + object next = owningObjectFactory.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 IInstantiationAwareObjectPostProcessor Members + + /// + /// Create the proxy if have a custom TargetSource + /// + /// The object type + /// The object name + /// null if not creating a proxy, otherwise return the proxy. + public object PostProcessBeforeInstantiation( Type objectType, string objectName ) + { + object cacheKey = GetCacheKey( objectType, objectName ); + if (!targetSourcedObjects.Contains( cacheKey )) + { + if (advisedObjects.Contains( cacheKey ) || nonAdvisedObjects.Contains( cacheKey )) + { + return null; + } + + if (IsInfrastructureType( objectType, objectName )) + { + #region Instrumentation + + if (logger.IsDebugEnabled) + { + logger.Debug( string.Format( "Did not attempt to autoproxy infrastructure type [{0}]", objectType ) ); + } + + #endregion + + nonAdvisedObjects.Add( cacheKey ); + return null; + } + + if (ShouldSkip( objectType, objectName )) + { + #region Instrumentation + + if (logger.IsDebugEnabled) + { + logger.Debug( string.Format( "Skipping type [{0}]", objectType ) ); + } + + #endregion + + nonAdvisedObjects.Add( cacheKey ); + return null; + } + } + // Create proxy here if we have a custom TargetSource. + // Suppresses unnecessary default instantiation of the target object: + // The TargetSource will handle target instances in a custom fashion. + ITargetSource targetSource = GetCustomTargetSource( objectType, objectName ); + if (targetSource != null) + { + targetSourcedObjects.Add( objectName ); + object[] specificInterceptors = GetAdvicesAndAdvisorsForObject( objectType, objectName, targetSource ); + return CreateProxy( objectType, objectName, specificInterceptors, targetSource ); + } + return null; + } + + + /// + /// Default behavior, return true and continue processing. + /// + /// The object instance + /// The object name. + /// true + public bool PostProcessAfterInstantiation( object objectInstance, string objectName ) + { + return true; + } + + /// + /// Default behavior, return passed in PropertyValues + /// + /// The property values that the factory is about to apply (never null). + /// he relevant property infos for the target object (with ignored + /// dependency types - which the factory handles specifically - already filtered out) + /// The object instance created, but whose properties have not yet + /// been set. + /// Name of the object. + /// The passed in PropertyValues + public IPropertyValues PostProcessPropertyValues( IPropertyValues pvs, PropertyInfo[] pis, object objectInstance, + string objectName ) + { + return pvs; + } + + #endregion + } } \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/AbstractFilteringAutoProxyCreator.cs b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/AbstractFilteringAutoProxyCreator.cs new file mode 100644 index 00000000..5462b463 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/AbstractFilteringAutoProxyCreator.cs @@ -0,0 +1,81 @@ +#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 Spring.Objects.Factory; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework.AutoProxy +{ + /// + /// The base class for AutoProxyCreator implementations that mark objects + /// eligible for proxying based on arbitrary criteria. + /// + /// Erich Eichinger + public abstract class AbstractFilteringAutoProxyCreator : AbstractAutoProxyCreator + { + /// + ///Overridden to call . + /// + /// the type of the object + /// the name of the object + /// if remarkable to skip + protected override bool ShouldSkip( Type objectType, string objectName ) + { + bool shouldSkip = !IsEligibleForProxying( objectType, objectName ); + return shouldSkip; + } + + /// + /// Override to always return . + /// + /// + /// Whether an object shall be proxied or not is determined by the result of . + /// + /// ingored + /// ignored + /// ignored + /// + /// Always to indicate, that the object shall be proxied. + /// + /// + protected override object[] GetAdvicesAndAdvisorsForObject( Type objType, string name, ITargetSource customTargetSource ) + { + return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS; + } + + /// + /// Decide, whether the given object is eligible for proxying. + /// + /// + /// Override this method to allow or reject proxying for the given object. + /// + /// the object's type + /// the name of the object + /// + /// whether the given object shall be proxied. + protected abstract bool IsEligibleForProxying( Type objType, string name ); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/AttributeAutoProxyCreator.cs b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/AttributeAutoProxyCreator.cs new file mode 100644 index 00000000..66d6de06 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/AttributeAutoProxyCreator.cs @@ -0,0 +1,115 @@ +#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 Spring.Aop.Support; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework.AutoProxy +{ + /// + /// An AutoProxyCreator, that identifies objects to be proxied by checking s defined on their type. + /// + /// Erich Eichinger + public class AttributeAutoProxyCreator : AbstractFilteringAutoProxyCreator + { + private bool _checkInherited = false; + private Type[] _attributeTypes = null; + + /// + /// Indicates, whether to consider base types for filtering when checking declared attributes. Defaults to false. + /// + public bool CheckInherited + { + get { return _checkInherited; } + set { _checkInherited = value; } + } + + /// + /// The list of attribute types marking object types as eligible for auto-proxying by this AutoProxyCreator. Must not be null. + /// + public Type[] AttributeTypes + { + get { return _attributeTypes; } + set + { + AssertUtils.ArgumentNotNull( value, "AttributeTypes" ); + _attributeTypes = value; + } + } + + /// + /// Determines, whether the given object shall be proxied by matching against . + /// + /// the object's type + /// the name of the object + protected override bool IsEligibleForProxying( Type objType, string name ) + { + AssertUtils.ArgumentNotNull(this.AttributeTypes, "AttributeTypes"); + + bool shallProxy = IsAnnotatedWithAnyOfAttribute( objType, this.AttributeTypes, this.CheckInherited ); + return shallProxy; + } + + /// + /// Checks if is annotated with any of the attributes within the given list of . + /// + /// the object's type + /// the list of types to match agains. + /// whether to check base classes and intefaces for any of the given attributes. + /// if any of the attributes is found + protected virtual bool IsAnnotatedWithAnyOfAttribute( Type objectType, Type[] attributeTypes, bool checkInherited ) + { + foreach(Type attributeType in attributeTypes) + { + if (IsAnnotatedWithAttribute(objectType, attributeType, checkInherited)) + { + return true; + } + } + return false; + } + + /// + /// Checks if is annotated with the specified . + /// + /// the object's type + /// the type to match agains. + /// whether to check base classes and intefaces for the specified attribute. + /// if the attributes is found + protected virtual bool IsAnnotatedWithAttribute( Type objectType, Type attributeType, bool checkInherited ) + { + if (checkInherited) + { + return AttributeUtils.FindAttribute( objectType, attributeType ) != null; + } + else + { + object[] atts = objectType.GetCustomAttributes( attributeType, false ); + return ArrayUtils.HasLength( atts ); + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/ObjectNameAutoProxyCreator.cs b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/ObjectNameAutoProxyCreator.cs index 5d63b4b9..137ad39a 100644 --- a/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/ObjectNameAutoProxyCreator.cs +++ b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/ObjectNameAutoProxyCreator.cs @@ -30,7 +30,7 @@ using Spring.Util; namespace Spring.Aop.Framework.AutoProxy { /// - /// Object Auto Proxy Creator + /// AutoProxyCreator that identifies objects to proxy via a list of names. /// /// /// @@ -46,10 +46,16 @@ namespace Spring.Aop.Framework.AutoProxy /// /// Juergen Hoeller /// Adhari C Mahendra (.NET) - public class ObjectNameAutoProxyCreator : AbstractAutoProxyCreator + public class ObjectNameAutoProxyCreator : AbstractFilteringAutoProxyCreator { private IList objectNames; + /// + /// Initializes a new instance of . + /// + public ObjectNameAutoProxyCreator() + {} + /// /// Set the names of the objects in IList fashioned way that should automatically /// get wrapped with proxies. @@ -62,47 +68,15 @@ namespace Spring.Aop.Framework.AutoProxy get { return objectNames; } } - /// - /// Determines, whether the given object shall be proxied. - /// - /// - /// if the object shall be proxied.
- /// otherwise. - ///
- protected override object[] GetAdvicesAndAdvisorsForObject( Type objType, string name, ITargetSource customTargetSource ) - { - if (ShallProxy( objType, name, customTargetSource )) - { - return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS; - } - return DO_NOT_PROXY; - } - /// /// Identify as object to proxy if the object name is in the configured list of names. /// - protected virtual bool ShallProxy( Type objType, string name, ITargetSource customTargetSource ) + protected override bool IsEligibleForProxying( Type objType, string name ) { - if (objectNames != null) - { - for (int i = 0; i < objectNames.Count; i++) - { - string mappedName = String.Copy( (string)objectNames[i] ); - if (typeof( IFactoryObject ).IsAssignableFrom( objType )) - { - if (!name.StartsWith( ObjectFactoryUtils.FactoryObjectPrefix )) - { - continue; - } - mappedName = mappedName.Substring( ObjectFactoryUtils.FactoryObjectPrefix.Length ); - } - if (IsMatch( name, mappedName )) - { - return true; - } - } - } - return false; + AssertUtils.ArgumentNotNull(this.ObjectNames, "ObjectNames"); + + bool shallProxy = PatternMatchUtils.IsObjectNameMatch(objType, name, this.ObjectNames, new PatternMatchUtils.ObjectNameMatchPredicate(IsMatch), ObjectFactoryUtils.FactoryObjectPrefix); + return shallProxy; } /// diff --git a/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/PointcutFilteringAutoProxyCreator.cs b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/PointcutFilteringAutoProxyCreator.cs index 18b09d7a..38df5772 100644 --- a/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/PointcutFilteringAutoProxyCreator.cs +++ b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/PointcutFilteringAutoProxyCreator.cs @@ -34,7 +34,7 @@ namespace Spring.Aop.Framework.AutoProxy /// be further restricted by specifying object name patterns like with . /// /// Erich Eichinger - public class PointcutFilteringAutoProxyCreator : ObjectNameAutoProxyCreator + public class PointcutFilteringAutoProxyCreator : AbstractFilteringAutoProxyCreator { private IPointcut _pointcut; @@ -44,34 +44,18 @@ namespace Spring.Aop.Framework.AutoProxy public IPointcut Pointcut { set { _pointcut = value; } + get { return _pointcut; } } /// /// Determines, whether the given object shall be proxied. /// - protected override bool ShallProxy( Type objType, string name, ITargetSource customTargetSource ) + protected override bool IsEligibleForProxying( Type objType, string name ) { - if (CollectionUtils.IsEmpty( ObjectNames ) && _pointcut == null) - { - throw new ArgumentException("At least one of ObjectNames and Pointcut criteria are required"); - } + AssertUtils.ArgumentNotNull(_pointcut, "Pointcut"); - bool isObjectNameMatch = base.ShallProxy( objType, name, customTargetSource ); - - // we have a name match, but empty pointcut -> ok - if (isObjectNameMatch && _pointcut==null) - { - return true; - } - - // positive name match or no names specified -> get the pointcut match - if ( (isObjectNameMatch || CollectionUtils.IsEmpty(ObjectNames) ) - && _pointcut != null) - { - return AopUtils.CanApply( _pointcut, objType, null ); - } - - return false; + bool shallProxy = AopUtils.CanApply( _pointcut, objType, null ); + return shallProxy; } } } \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/TypeNameAutoProxyCreator.cs b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/TypeNameAutoProxyCreator.cs new file mode 100644 index 00000000..777c03a4 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/TypeNameAutoProxyCreator.cs @@ -0,0 +1,70 @@ +#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 Spring.Aop.Support; +using Spring.Util; + +#endregion + +namespace Spring.Aop.Framework.AutoProxy +{ + /// + /// AutoProxyCreator, that identifies objects to proxy by matching their against a list of patterns. + /// + /// Erich Eichinger + public class TypeNameAutoProxyCreator : AbstractFilteringAutoProxyCreator + { + private TypeNameTypeFilter _typeNameFilter = null; + + /// + /// The list of patterns to match against. For pattern syntax, see + /// + public string[] TypeNames + { + get { return _typeNameFilter.TypeNamePatterns; } + set + { + AssertUtils.ArgumentNotNull(value, "TypeNames"); + _typeNameFilter = new TypeNameTypeFilter(value); + } + } + + /// + /// Decide, whether the given object is eligible for proxying. + /// + /// + /// Override this method to allow or reject proxying for the given object. + /// + /// the object's type + /// the name of the object + /// + /// whether the given object shall be proxied. + protected override bool IsEligibleForProxying(Type objType, string name) + { + AssertUtils.ArgumentNotNull(_typeNameFilter, "TypeNames"); + + bool shallProxy = _typeNameFilter.Matches(objType); + return shallProxy; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Support/AttributeTypeFilter.cs b/src/Spring/Spring.Aop/Aop/Support/AttributeTypeFilter.cs index 84fbeb14..3633df19 100644 --- a/src/Spring/Spring.Aop/Aop/Support/AttributeTypeFilter.cs +++ b/src/Spring/Spring.Aop/Aop/Support/AttributeTypeFilter.cs @@ -31,9 +31,24 @@ namespace Spring.Aop.Support public class AttributeTypeFilter : ITypeFilter { private readonly Type attributeType; - private readonly bool checkInherited; + /// + /// The attribute for this filter. + /// + public Type AttributeType + { + get { return attributeType; } + } + + /// + /// Indicates, whether this filter considers base types for filtering. + /// + public bool CheckInherited + { + get { return checkInherited; } + } + /// /// Initializes a new instance of the class for the /// given attribute type. @@ -80,9 +95,11 @@ namespace Spring.Aop.Support if (checkInherited) { return AttributeUtils.FindAttribute(type, attributeType) != null; - } else + } + else { - return Attribute.GetCustomAttributes(type, attributeType, false) != null; + object[] atts = type.GetCustomAttributes(attributeType, false); + return ArrayUtils.HasLength(atts); } } diff --git a/src/Spring/Spring.Aop/Aop/Support/TypeNameTypeFilter.cs b/src/Spring/Spring.Aop/Aop/Support/TypeNameTypeFilter.cs new file mode 100644 index 00000000..49688927 --- /dev/null +++ b/src/Spring/Spring.Aop/Aop/Support/TypeNameTypeFilter.cs @@ -0,0 +1,75 @@ +#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 Spring.Util; + +#endregion + +namespace Spring.Aop.Support +{ + /// + /// Simple implementation that matches + /// a given 's against . + /// For a list of supported pattern syntax see . + /// + /// Erich Eichinger + /// + public class TypeNameTypeFilter : ITypeFilter + { + private string[] _typeNamePatterns; + + /// + /// Returns the list of type name patterns for this filter. + /// + /// + public string[] TypeNamePatterns + { + get { return _typeNamePatterns; } + } + + /// + ///Creates a new instance of using a list of given . + /// + ///the list patterns to match typenames against. Must not be null. + /// + public TypeNameTypeFilter(string[] patterns) + { + AssertUtils.ArgumentNotNull(patterns, "patterns"); + _typeNamePatterns = patterns; + } + + /// + /// Does the supplied type's match any of the ? + /// + /// + /// The candidate . + /// + /// + /// if the matches any of the . + /// + public bool Matches(Type type) + { + return PatternMatchUtils.SimpleMatch(_typeNamePatterns, type.FullName); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Spring.Aop.2008.csproj b/src/Spring/Spring.Aop/Spring.Aop.2008.csproj index 57c87c8c..13bd5343 100644 --- a/src/Spring/Spring.Aop/Spring.Aop.2008.csproj +++ b/src/Spring/Spring.Aop/Spring.Aop.2008.csproj @@ -136,8 +136,11 @@ + + + @@ -359,6 +362,7 @@ Code + Code diff --git a/src/Spring/Spring.Core/Util/PatternMatchUtils.cs b/src/Spring/Spring.Core/Util/PatternMatchUtils.cs index c577ce0e..8feae635 100644 --- a/src/Spring/Spring.Core/Util/PatternMatchUtils.cs +++ b/src/Spring/Spring.Core/Util/PatternMatchUtils.cs @@ -21,8 +21,10 @@ #region Imports using System; +using System.Collections; using System.Reflection; -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; +using Spring.Objects.Factory; #endregion @@ -94,5 +96,56 @@ namespace Spring.Util } return false; } + + /// + /// Signature of callbacks that may be used for matching object names. + /// + /// the object name to check. + /// the pattern to match against. + /// true, if the matches + /// + public delegate bool ObjectNameMatchPredicate(string objectName, string namePattern); + + /// + /// Convenience method that may be used by derived classes. Iterates over the list of to match against. + /// + /// the object's type. Must not be null. + /// the name of the object Must not be null. + /// the list of patterns, that shall be matched against. Must not be null. + /// + /// the used for + /// matching against each pattern in . Must not be null. + /// + /// the prefix to be used for dereferencing factory object names. + /// + /// If is null, will always return true, otherwise + /// if matches any of the patterns specified in . + /// + public static bool IsObjectNameMatch(Type objType, string objectName, IList objectNamePatterns, ObjectNameMatchPredicate isMatchPredicate, string factoryObjectPrefix) + { + AssertUtils.ArgumentNotNull(objType, "objType"); + AssertUtils.ArgumentNotNull(objectName, "objectName"); + AssertUtils.ArgumentNotNull(objectNamePatterns, "objectNamePatterns"); + AssertUtils.ArgumentNotNull(isMatchPredicate, "isMatchPredicate"); + AssertUtils.ArgumentNotNull(factoryObjectPrefix, "factoryObjectPrefix"); + + for (int i = 0; i < objectNamePatterns.Count; i++) + { + string mappedName = (string)objectNamePatterns[i]; + if (typeof( IFactoryObject ).IsAssignableFrom( objType )) + { + if (!objectName.StartsWith( factoryObjectPrefix )) + { + continue; + } + mappedName = mappedName.Substring( factoryObjectPrefix.Length ); + } + if (isMatchPredicate( objectName, mappedName )) + { + return true; + } + } + return false; + } } } \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/AttributeAutoProxyCreatorTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/AttributeAutoProxyCreatorTests.cs new file mode 100644 index 00000000..4ea9f5ba --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/AttributeAutoProxyCreatorTests.cs @@ -0,0 +1,150 @@ +#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 NUnit.Framework; +using Spring.Objects; +using Spring.Stereotype; + +#endregion + +namespace Spring.Aop.Framework.AutoProxy +{ + /// + /// + /// + /// Erich Eichinger + [TestFixture] + public class AttributeAutoProxyCreatorTests + { + public interface IEmptyInterface + {} + + [AttributeUsage( AttributeTargets.Class|AttributeTargets.Method, Inherited=false )] + private class ApcTestAttribute : Attribute {} + + private class ApcTestObject: IEmptyInterface {} + + [ApcTest] + private class AttributedApcTestObject : ApcTestObject + {} + + private class DerivedAttributedApcTestObject : AttributedApcTestObject + { + [ApcTest] + public void SomeMethod() {} + } + + [Test] + [ExpectedException( typeof( ArgumentNullException ) )] + public void ThrowsOnMissingAttributeTypeList() + { + AttributeAutoProxyCreator apc = new AttributeAutoProxyCreator(); + apc.PostProcessAfterInitialization( new ApcTestObject(), "testObject" ); + } + + [Test] + [ExpectedException( typeof( ArgumentNullException ) )] + public void ThrowsOnAssigningNullAttributeList() + { + AttributeAutoProxyCreator apc = new AttributeAutoProxyCreator(); + apc.AttributeTypes = null; + } + + [Test] + public void AllowsEmptyAttributeList() + { + AttributeAutoProxyCreator apc = new AttributeAutoProxyCreator(); + apc.AttributeTypes = new Type[0]; + apc.PostProcessAfterInitialization( new ApcTestObject(), "testObject" ); + } + + [Test] + public void DefaultsToNotCheckInherited() + { + AttributeAutoProxyCreator apc = new AttributeAutoProxyCreator(); + Assert.IsFalse(apc.CheckInherited); + } + + [Test] + public void CreatesProxyOnAttributeMatch() + { + AttributeAutoProxyCreator apc = new AttributeAutoProxyCreator(); + apc.AttributeTypes = new Type[] { typeof(ApcTestAttribute) }; + + object result = apc.PostProcessAfterInitialization( new AttributedApcTestObject(), "testObject" ); + Assert.IsTrue( AopUtils.IsAopProxy( result ) ); + } + + [Test] + public void CreatesProxyOnInheritedAttributeMatchWhenCheckInherited() + { + AttributeAutoProxyCreator apc = new AttributeAutoProxyCreator(); + apc.AttributeTypes = new Type[] { typeof(ApcTestAttribute) }; + apc.CheckInherited = true; + object result = apc.PostProcessAfterInitialization( new DerivedAttributedApcTestObject(), "testObject" ); + Assert.IsTrue( AopUtils.IsAopProxy( result ) ); + } + + [Test] + public void DoesNotCreateProxyOnInheritedAttributeMatchWhenNotCheckInherited() + { + AttributeAutoProxyCreator apc = new AttributeAutoProxyCreator(); + apc.AttributeTypes = new Type[] { typeof(ApcTestAttribute) }; + apc.CheckInherited = false; + object result = apc.PostProcessAfterInitialization( new DerivedAttributedApcTestObject(), "testObject" ); + Assert.IsFalse( AopUtils.IsAopProxy( result ) ); + } + + [Test] + public void DoesNotCreateProxyIfNoAttributeMatch() + { + AttributeAutoProxyCreator apc = new AttributeAutoProxyCreator(); + apc.AttributeTypes = new Type[] { typeof(ApcTestAttribute) }; + + object result = apc.PostProcessAfterInitialization( new TestObject(), "testObject" ); + Assert.IsFalse( AopUtils.IsAopProxy( result ) ); + } + + [Test] + public void DoesNotCheckMethodLevelAttributes() + { + AttributeAutoProxyCreator apc = new AttributeAutoProxyCreator(); + apc.AttributeTypes = new Type[] { typeof(ApcTestAttribute) }; + apc.CheckInherited = false; // (!) + + // does not check method level attributes! + object result = apc.PostProcessAfterInitialization( new DerivedAttributedApcTestObject(), "testObject" ); + Assert.IsFalse( AopUtils.IsAopProxy( result ) ); + } + + [Test] + public void DoesNotCreateProxyIfEmptyAtributeList() + { + AttributeAutoProxyCreator apc = new AttributeAutoProxyCreator(); + apc.AttributeTypes = new Type[0]; + + object result = apc.PostProcessAfterInitialization( new ApcTestObject(), "testObject" ); + Assert.IsFalse( AopUtils.IsAopProxy( result ) ); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/PointcutFilteringAutoProxyCreatorTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/PointcutFilteringAutoProxyCreatorTests.cs index 549d3ec8..3554c421 100644 --- a/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/PointcutFilteringAutoProxyCreatorTests.cs +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/PointcutFilteringAutoProxyCreatorTests.cs @@ -36,58 +36,30 @@ namespace Spring.Aop.Framework.AutoProxy [TestFixture] public class PointcutFilteringAutoProxyCreatorTests { - [Test] - public void CreatesProxyOnlyIfPointcutAndObjectNameMatch() - { - // is match - PointcutFilteringAutoProxyCreator apc = new PointcutFilteringAutoProxyCreator(); - apc.ObjectNames = new string[] { "test*" } ;; - apc.Pointcut = new SdkRegularExpressionMethodPointcut(".*\\.GetHashCode"); - object result = apc.PostProcessAfterInitialization( new TestObject(), "testObject" ); - Assert.IsTrue(AopUtils.IsAopProxy(result)); - - apc = new PointcutFilteringAutoProxyCreator(); - apc.ObjectNames = new string[] { "test*" } ;; - apc.Pointcut = new SdkRegularExpressionMethodPointcut(".*\\.GetHashCODE"); - result = apc.PostProcessAfterInitialization( new TestObject(), "testObject" ); - Assert.IsFalse(AopUtils.IsAopProxy(result)); - - apc = new PointcutFilteringAutoProxyCreator(); - apc.ObjectNames = new string[] { "tesT*" } ;; - apc.Pointcut = new SdkRegularExpressionMethodPointcut(".*\\.GetHashCode"); - result = apc.PostProcessAfterInitialization( new TestObject(), "testObject" ); - Assert.IsFalse(AopUtils.IsAopProxy(result)); - } - [Test] public void CreatesProxyOnPointcutMatch() { PointcutFilteringAutoProxyCreator apc = new PointcutFilteringAutoProxyCreator(); - apc.ObjectNames = null; apc.Pointcut = new SdkRegularExpressionMethodPointcut(".*\\.GetHashCode"); object result = apc.PostProcessAfterInitialization( new TestObject(), "testObject" ); Assert.IsTrue(AopUtils.IsAopProxy(result)); } [Test] - public void CreatesProxyOnNameMatch() + public void DoesNotCreateProxyIfNoPointcutMatch() { PointcutFilteringAutoProxyCreator apc = new PointcutFilteringAutoProxyCreator(); - apc.ObjectNames = new string[] { "test*" } ; - apc.Pointcut = null; + apc.Pointcut = new SdkRegularExpressionMethodPointcut(".*\\.DOEsNOTExist"); object result = apc.PostProcessAfterInitialization( new TestObject(), "testObject" ); - Assert.IsTrue(AopUtils.IsAopProxy(result)); + Assert.IsFalse(AopUtils.IsAopProxy(result)); } [Test] - [ExpectedException(typeof(ArgumentException))] + [ExpectedException(typeof(ArgumentNullException))] public void ThrowsArgumentExceptionIfNoCriteriaSpecified() { PointcutFilteringAutoProxyCreator apc = new PointcutFilteringAutoProxyCreator(); - apc.ObjectNames = new string[] {} ; - apc.Pointcut = null; - object result = apc.PostProcessAfterInitialization( new TestObject(), "testObject" ); - Assert.IsTrue(AopUtils.IsAopProxy(result)); + apc.PostProcessAfterInitialization( new TestObject(), "testObject" ); } } } \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/TypeNameAutoProxyCreatorTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/TypeNameAutoProxyCreatorTests.cs new file mode 100644 index 00000000..818bdd9f --- /dev/null +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/AutoProxy/TypeNameAutoProxyCreatorTests.cs @@ -0,0 +1,98 @@ +#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 NUnit.Framework; +using Spring.Objects; + +#endregion + +namespace Spring.Aop.Framework.AutoProxy +{ + /// + /// + /// + /// Erich Eichinger + [TestFixture] + public class TypeNameAutoProxyCreatorTests + { + private class MyLocalTestObject: TestObject + {} + + [Test] + [ExpectedException( typeof( ArgumentNullException ) )] + public void ThrowsOnMissingTypeNames() + { + TypeNameAutoProxyCreator apc = new TypeNameAutoProxyCreator(); + apc.PostProcessAfterInitialization( new TestObject(), "testObject" ); + } + + [Test] + [ExpectedException( typeof( ArgumentNullException ) )] + public void ThrowsOnAssigningNullTypeNames() + { + TypeNameAutoProxyCreator apc = new TypeNameAutoProxyCreator(); + apc.TypeNames = null; + } + + [Test] + public void AllowsEmptyTypeNameList() + { + TypeNameAutoProxyCreator apc = new TypeNameAutoProxyCreator(); + apc.TypeNames = new string[] {}; + + apc.PostProcessAfterInitialization( new TestObject(), "testObject" ); + } + + [Test] + public void CreatesProxyOnTypeNameMatch() + { + TypeNameAutoProxyCreator apc = new TypeNameAutoProxyCreator(); + apc.TypeNames = new string[] { "Spring.Objects.Test*", "*MyLocal*" }; + + object result = apc.PostProcessAfterInitialization( new TestObject(), "testObject" ); + Assert.IsTrue( AopUtils.IsAopProxy( result ) ); + result = apc.PostProcessAfterInitialization( new MyLocalTestObject(), "myLocalTestObject" ); + Assert.IsTrue( AopUtils.IsAopProxy( result ) ); + } + + [Test] + public void DoesNotCreateProxyIfNoTypeNameMatch() + { + TypeNameAutoProxyCreator apc = new TypeNameAutoProxyCreator(); + apc.TypeNames = new string[] { "Foo*" }; + + object result = apc.PostProcessAfterInitialization( new TestObject(), "testObject" ); + Assert.IsFalse( AopUtils.IsAopProxy( result ) ); + } + + [Test] + public void DoesNotCreateProxyIfEmptyTypeNameList() + { + TypeNameAutoProxyCreator apc = new TypeNameAutoProxyCreator(); + apc.TypeNames = new string[] {}; + + object result = apc.PostProcessAfterInitialization( new TestObject(), "testObject" ); + Assert.IsFalse( AopUtils.IsAopProxy( result ) ); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.2008.csproj b/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.2008.csproj index 10e9d433..153b74f4 100644 --- a/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.2008.csproj +++ b/test/Spring/Spring.Aop.Tests/Spring.Aop.Tests.2008.csproj @@ -125,6 +125,7 @@ + @@ -132,6 +133,7 @@ +