From c161b030caff104a02619d96b9cd6fda78a467d0 Mon Sep 17 00:00:00 2001 From: eeichinger Date: Sun, 8 Mar 2009 12:58:06 +0000 Subject: [PATCH] Enabled support for nhibernate dynamic entities fixed SPRNET-1174: Proxies dont implement methods of base interfaces of interfaces fixed SPRNET-1182: defer target==null checking until actual joinpoint invocation in proxy method implementations --- .../Aop/Framework/AbstractMethodInvocation.cs | 28 +- .../Aop/Framework/DynamicMethodInvocation.cs | 208 ++--- .../Framework/ReflectiveMethodInvocation.cs | 16 +- .../Proxy/AbstractProxyMethodBuilder.cs | 45 +- .../Proxy/AbstractProxyTypeBuilder.cs | 17 +- src/Spring/Spring.Core/Util/AssertUtils.cs | 124 ++- .../DynamicProxy/AbstractAopProxyTests.cs | 734 +++++++++++------- .../DynamicProxy/CompositionAopProxyTests.cs | 3 + .../Util/AssertUtilsTests.cs | 199 +++-- 9 files changed, 941 insertions(+), 433 deletions(-) diff --git a/src/Spring/Spring.Aop/Aop/Framework/AbstractMethodInvocation.cs b/src/Spring/Spring.Aop/Aop/Framework/AbstractMethodInvocation.cs index 945a450d..f793712f 100644 --- a/src/Spring/Spring.Aop/Aop/Framework/AbstractMethodInvocation.cs +++ b/src/Spring/Spring.Aop/Aop/Framework/AbstractMethodInvocation.cs @@ -130,7 +130,9 @@ namespace Spring.Aop.Framework { #region Sanity Check - AssertUtils.ArgumentNotNull(target, "target"); + // EE: There is not necessarily always a target - e.g. for DynamicEntities + // moved this check to InvokeJoinpoint() +// AssertUtils.ArgumentNotNull(target, "target"); AssertUtils.ArgumentNotNull(method, "method"); #endregion @@ -252,7 +254,8 @@ namespace Spring.Aop.Framework { if (this.interceptors == null || this.currentInterceptorIndex == this.interceptors.Count) - { + { + AssertJoinpoint(); return InvokeJoinpoint(); } object interceptor = this.interceptors[this.currentInterceptorIndex]; @@ -296,6 +299,27 @@ namespace Spring.Aop.Framework protected abstract IMethodInvocation PrepareMethodInvocationForProceed( IMethodInvocation invocation); + /// + /// Performs sanity checks, whether the actual joinpoint may be invoked + /// + /// + /// By default checks that the underlying target is not null and the called method is implemented + /// by the target's type. + /// + /// if is null. + /// if the 's type does not implement . + protected virtual void AssertJoinpoint() + { + AssertUtils.ArgumentNotNull(target, "target"); +// if (this.method != null +// && !this.method.DeclaringType.IsAssignableFrom(target.GetType())) +// { +// // This means the target type doesn't implement the interface. +// // Since no interceptor has handled the call, we throw a sensible exception here. +// throw new NotSupportedException(string.Format("Interface method '{0}.{1}()' was not handled by any interceptor and the underlying target type '{2}' does not implement this method.", method.DeclaringType.FullName, method.Name, target.GetType().FullName)); +// } + } + /// /// Invokes the joinpoint. /// diff --git a/src/Spring/Spring.Aop/Aop/Framework/DynamicMethodInvocation.cs b/src/Spring/Spring.Aop/Aop/Framework/DynamicMethodInvocation.cs index 9021f9ac..4e9f6fce 100644 --- a/src/Spring/Spring.Aop/Aop/Framework/DynamicMethodInvocation.cs +++ b/src/Spring/Spring.Aop/Aop/Framework/DynamicMethodInvocation.cs @@ -1,5 +1,5 @@ -#region License - +#region License + /* * Copyright © 2002-2005 the original author or authors. * @@ -14,85 +14,89 @@ * 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 Spring.Util; -using Spring.Reflection.Dynamic; -using AopAlliance.Intercept; - -#endregion - -namespace Spring.Aop.Framework -{ - /// - /// Invokes a target method using dynamic reflection. - /// - /// - /// Aleksandar Seovic - /// Bruno Baia - [Serializable] - public class DynamicMethodInvocation : AbstractMethodInvocation - { - /// - /// The method invocation that is to be invoked on the proxy. - /// - protected MethodInfo proxyMethod; - - /// - /// Creates a new instance of the - /// class. - /// - /// The AOP proxy. - /// The target object. - /// The target method proxied. - /// The method to invoke on proxy. - /// The target method's arguments. - /// - /// The of the target object. - /// - /// The list of interceptors that are to be applied. May be - /// . - /// - /// - /// If any of the or - /// parameters is . - /// - public DynamicMethodInvocation( - object proxy, object target, MethodInfo method, MethodInfo proxyMethod, - object[] arguments, Type targetType, IList interceptors) - : base(proxy, target, method, arguments, targetType, interceptors) - { - this.proxyMethod = proxyMethod; - } - - /// - /// Invokes the joinpoint using dynamic reflection. - /// - /// - ///

- /// Subclasses can override this to use custom invocation. - ///

- ///
- /// - /// The return value of the invocation of the joinpoint. - /// - /// - /// If invoking the joinpoint resulted in an exception. - /// - /// + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; + +using Spring.Util; +using Spring.Reflection.Dynamic; +using AopAlliance.Intercept; + +#endregion + +namespace Spring.Aop.Framework +{ + /// + /// Invokes a target method using dynamic reflection. + /// + /// + /// Aleksandar Seovic + /// Bruno Baia + [Serializable] + public class DynamicMethodInvocation : AbstractMethodInvocation + { + /// + /// The method invocation that is to be invoked on the proxy. + /// + protected MethodInfo proxyMethod; + + /// + /// Creates a new instance of the + /// class. + /// + /// The AOP proxy. + /// The target object. + /// The target method proxied. + /// The method to invoke on proxy. + /// The target method's arguments. + /// + /// The of the target object. + /// + /// The list of interceptors that are to be applied. May be + /// . + /// + /// + /// If any of the or + /// parameters is . + /// + public DynamicMethodInvocation( + object proxy, object target, MethodInfo method, MethodInfo proxyMethod, + object[] arguments, Type targetType, IList interceptors) + : base(proxy, target, method, arguments, targetType, interceptors) + { + this.proxyMethod = proxyMethod; + } + + /// + /// Invokes the joinpoint using dynamic reflection. + /// + /// + ///

+ /// Subclasses can override this to use custom invocation. + ///

+ ///
+ /// + /// The return value of the invocation of the joinpoint. + /// + /// + /// If invoking the joinpoint resulted in an exception. + /// + /// protected override object InvokeJoinpoint() - { - IDynamicMethod targetMethod = (this.proxyMethod == null) ? new SafeMethod(method) : new SafeMethod(proxyMethod); - try - { + { + MethodInfo targetMethodInfo = ((this.proxyMethod == null)) ? method : this.proxyMethod; + + IDynamicMethod targetMethod = new SafeMethod(targetMethodInfo); + + try + { + AssertUtils.Understands(target, "target", targetMethodInfo); return targetMethod.Invoke(target, arguments); } // Only happens if fallback to standard reflection. @@ -100,26 +104,26 @@ namespace Spring.Aop.Framework { throw ReflectionUtils.UnwrapTargetInvocationException(ex); } - } - - /// - /// Creates a new instance - /// from the specified and - /// increments the interceptor index. - /// - /// - /// The current instance. - /// - /// - /// The new instance to use. - /// - protected override IMethodInvocation PrepareMethodInvocationForProceed(IMethodInvocation invocation) - { - DynamicMethodInvocation rmi = new DynamicMethodInvocation( - this.proxy, this.target, this.method, this.proxyMethod, this.arguments, this.targetType, this.interceptors); - rmi.currentInterceptorIndex = this.currentInterceptorIndex + 1; - - return rmi; - } - } + } + + /// + /// Creates a new instance + /// from the specified and + /// increments the interceptor index. + /// + /// + /// The current instance. + /// + /// + /// The new instance to use. + /// + protected override IMethodInvocation PrepareMethodInvocationForProceed(IMethodInvocation invocation) + { + DynamicMethodInvocation rmi = new DynamicMethodInvocation( + this.proxy, this.target, this.method, this.proxyMethod, this.arguments, this.targetType, this.interceptors); + rmi.currentInterceptorIndex = this.currentInterceptorIndex + 1; + + return rmi; + } + } } \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aop/Framework/ReflectiveMethodInvocation.cs b/src/Spring/Spring.Aop/Aop/Framework/ReflectiveMethodInvocation.cs index 152359f3..899244c8 100644 --- a/src/Spring/Spring.Aop/Aop/Framework/ReflectiveMethodInvocation.cs +++ b/src/Spring/Spring.Aop/Aop/Framework/ReflectiveMethodInvocation.cs @@ -91,21 +91,17 @@ namespace Spring.Aop.Framework protected override object InvokeJoinpoint() { try - { - if (proxyMethod == null) - { - return method.Invoke(target, arguments); - } - else - { - return proxyMethod.Invoke(target, arguments); - } + { + MethodInfo targetMethodInfo = ((this.proxyMethod == null)) ? method : this.proxyMethod; + + AssertUtils.Understands(target, "target", targetMethodInfo); + return targetMethodInfo.Invoke(target, arguments); } catch (TargetInvocationException ex) { throw ReflectionUtils.UnwrapTargetInvocationException(ex); } - } + } /// /// Creates a new instance diff --git a/src/Spring/Spring.Core/Proxy/AbstractProxyMethodBuilder.cs b/src/Spring/Spring.Core/Proxy/AbstractProxyMethodBuilder.cs index 2fc128f9..391a2d88 100644 --- a/src/Spring/Spring.Core/Proxy/AbstractProxyMethodBuilder.cs +++ b/src/Spring/Spring.Core/Proxy/AbstractProxyMethodBuilder.cs @@ -275,7 +275,14 @@ namespace Spring.Proxy // setup target object for call PushTarget(il); - // cast to type method is on + // TODO (EE): check for null and interface type and throw NotSupportedException + LocalBuilder targetRef = il.DeclareLocal(typeof(object)); + il.Emit(OpCodes.Stloc, targetRef); + + CallAssertUnderstands(il, interfaceMethod, targetRef, "target"); + + // setup target and cast to type method is on + il.Emit(OpCodes.Ldloc, targetRef); il.Emit(OpCodes.Castclass, interfaceMethod.DeclaringType); // setup parameters for call @@ -289,6 +296,17 @@ namespace Spring.Proxy il.EmitCall(OpCodes.Callvirt, interfaceMethod, null); } + private void CallAssertUnderstands(ILGenerator il, MethodInfo method, LocalBuilder targetRef, string targetName) + { + // AssertArgumentType + il.Emit(OpCodes.Ldloc, targetRef); + il.Emit(OpCodes.Ldstr, targetName); + il.Emit(OpCodes.Ldtoken, method.DeclaringType); + il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) })); +// il.Emit(OpCodes.Ldstr, string.Format("Interface method '{0}.{1}()' was not handled by any interceptor and the target does not implement this method.", method.DeclaringType.FullName, method.Name)); + il.Emit(OpCodes.Call, typeof(AssertUtils).GetMethod("Understands", new Type[] { typeof(object), typeof(string), typeof(Type) })); + } + /// /// Calls base method directly. /// @@ -299,6 +317,16 @@ namespace Spring.Proxy // setup proxy instance for call PushProxy(il); + // TODO (EE): check for null and interface type and throw NotSupportedException + LocalBuilder targetRef = il.DeclareLocal(typeof(object)); + il.Emit(OpCodes.Stloc, targetRef); + + CallAssertUnderstands(il, method, targetRef, "base"); + + // setup target and cast to type method is on + il.Emit(OpCodes.Ldloc, targetRef); + il.Emit(OpCodes.Castclass, method.DeclaringType); + // setup parameters for call ParameterInfo[] paramArray = method.GetParameters(); for (int i = 0; i < paramArray.Length; i++) @@ -343,6 +371,21 @@ namespace Spring.Proxy il.MarkLabel(jmpMethodReturn); } + /// + /// Generates code that throws . + /// + /// IL generator to use. + /// the type of the exception to throw + /// Error message to use. + protected static void EmitThrowException(ILGenerator il, Type exceptionType, string message) + { + ConstructorInfo NewException = exceptionType.GetConstructor(new Type[] { typeof(string) }); + + il.Emit(OpCodes.Ldstr, message); + il.Emit(OpCodes.Newobj, NewException); + il.Emit(OpCodes.Throw); + } + #endregion } } diff --git a/src/Spring/Spring.Core/Proxy/AbstractProxyTypeBuilder.cs b/src/Spring/Spring.Core/Proxy/AbstractProxyTypeBuilder.cs index d9206928..c3797747 100644 --- a/src/Spring/Spring.Core/Proxy/AbstractProxyTypeBuilder.cs +++ b/src/Spring/Spring.Core/Proxy/AbstractProxyTypeBuilder.cs @@ -27,6 +27,7 @@ using System.Reflection.Emit; using System.Runtime.Serialization; using Common.Logging; +using Spring.Collections; using Spring.Core.TypeResolution; using Spring.Util; @@ -964,7 +965,7 @@ namespace Spring.Proxy /// protected virtual Type[] GetProxiableInterfaces(Type[] interfaces) { - ArrayList proxiableInterfaces = new ArrayList(); + ArrayList proxiableInterfaces = new ArrayList(); foreach(Type intf in interfaces) { @@ -972,7 +973,19 @@ namespace Spring.Proxy !IsSpecialInterface(intf) && ReflectionUtils.IsTypeVisible(intf, DynamicProxyManager.ASSEMBLY_NAME)) { - proxiableInterfaces.Add(intf); + if (!proxiableInterfaces.Contains(intf)) + { + proxiableInterfaces.Add(intf); + } + + Type[] baseInterfaces = intf.GetInterfaces(); + foreach (Type baseInterface in baseInterfaces) + { + if (!proxiableInterfaces.Contains(baseInterface)) + { + proxiableInterfaces.Add(baseInterface); + } + } } } diff --git a/src/Spring/Spring.Core/Util/AssertUtils.cs b/src/Spring/Spring.Core/Util/AssertUtils.cs index 6b252d51..e1baebd6 100644 --- a/src/Spring/Spring.Core/Util/AssertUtils.cs +++ b/src/Spring/Spring.Core/Util/AssertUtils.cs @@ -22,7 +22,11 @@ using System; using System.Collections; -using System.Globalization; +using System.Globalization; +using System.Reflection; +using System.Runtime.Remoting; +using System.Runtime.Remoting.Proxies; +using System.Runtime.Serialization; #endregion @@ -37,9 +41,125 @@ namespace Spring.Util ///

/// /// Aleksandar Seovic + /// Erich Eichinger public sealed class AssertUtils { - /// + /// + /// Checks, whether may be invoked on . + /// Supports testing transparent proxies. + /// + ///the target instance or null + ///the name of the target to be used in error messages + ///the method to test for + /// + /// if is null + /// + /// + /// if it is not possible to invoke on + /// + public static void Understands(object target, string targetName, MethodBase method) + { + ArgumentNotNull(method, "method"); + + if (target==null ) + { + if (method.IsStatic) + { + return; + } + throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "Target '{0}' is null and target method '{1}.{2}' is not static.", targetName, method.DeclaringType.FullName, method.Name)); + } + + Understands(target, targetName, method.DeclaringType); + } + + /// + /// checks, whether supports the methods of . + /// Supports testing transparent proxies. + /// + ///the target instance or null + ///the name of the target to be used in error messages + ///the type to test for + /// + /// if is null + /// + /// + /// if it is not possible to invoke methods of + /// type on + /// + public static void Understands(object target, string targetName, Type requiredType) + { + ArgumentNotNull(requiredType, "requiredType"); + + if (target == null) + { + throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "Target '{0}' is null.", targetName)); + } + + Type targetType; + if (RemotingServices.IsTransparentProxy(target)) + { + RealProxy rp = RemotingServices.GetRealProxy(target); + IRemotingTypeInfo rti = rp as IRemotingTypeInfo; + if (rti != null) + { + if (rti.CanCastTo(requiredType, target)) + { + return; + } + throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "Target '{0}' is a transparent proxy that does not support methods of '{1}'.", targetName, requiredType.FullName)); + } + targetType = rp.GetProxiedType(); + } + else + { + targetType = target.GetType(); + } + + if (!requiredType.IsAssignableFrom(targetType)) + { + throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "Target '{0}' of type '{1}' does not support methods of '{2}'.", targetName, targetType, requiredType.FullName)); + } + } + + #region checking casts on transparent proxies (From BCL via Reflector) + // private static bool CheckCast(RealProxy rp, Type castType) +// { +// bool flag = false; +// if (castType == typeof(object)) +// { +// return true; +// } +// if (!castType.IsInterface && !castType.IsMarshalByRef) +// { +// return false; +// } +// if (castType != typeof(IObjectReference)) +// { +// IRemotingTypeInfo typeInfo = rp as IRemotingTypeInfo; +// if (typeInfo != null) +// { +// return typeInfo.CanCastTo(castType, rp.GetTransparentProxy()); +// } +// Identity identityObject = rp.IdentityObject; +// if (identityObject != null) +// { +// ObjRef objectRef = identityObject.ObjectRef; +// if (objectRef != null) +// { +// typeInfo = objectRef.TypeInfo; +// if (typeInfo != null) +// { +// flag = typeInfo.CanCastTo(castType, rp.GetTransparentProxy()); +// } +// } +// } +// } +// return flag; + // } + #endregion + + /// /// Checks the value of the supplied and throws an /// if it is . /// diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/AbstractAopProxyTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/AbstractAopProxyTests.cs index 49c4dcbb..db84f77c 100644 --- a/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/AbstractAopProxyTests.cs +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/AbstractAopProxyTests.cs @@ -34,6 +34,7 @@ using System.Runtime.Remoting; using AopAlliance.Aop; using AopAlliance.Intercept; +using Rhino.Mocks; using Spring.Aop.Target; using Spring.Aop.Framework; using Spring.Aop.Framework.Adapter; @@ -71,7 +72,7 @@ namespace Spring.Aop.Framework.DynamicProxy [TestFixtureSetUp] public void FixtureSetUp() { -// SystemUtils.RegisterLoadedAssemblyResolver(); + // SystemUtils.RegisterLoadedAssemblyResolver(); } [SetUp] @@ -249,8 +250,136 @@ namespace Spring.Aop.Framework.DynamicProxy Assert.AreEqual(name, to.Name); } + #region ImplementsInterfaceHierarchy Types + + public interface ITestPerson + { + long Id { get; set; } + string Name { get; set; } + } + + public interface ITestCustomer : ITestPerson + { + string Company { get; set; } + } + + #endregion + + [Test(Description = "http://jira.springframework.org/browse/SPRNET-1174")] + public void ImplementsInterfaceHierarchy() + { + MockRepository mocks = new MockRepository(); + + IMethodInterceptor mi = (IMethodInterceptor)mocks.DynamicMock(typeof(IMethodInterceptor)); + + Expect.Call(mi.Invoke(null)).IgnoreArguments().Return((long)5); + Expect.Call(mi.Invoke(null)).IgnoreArguments().Return("Customer Name"); + Expect.Call(mi.Invoke(null)).IgnoreArguments().Return("Customer Company"); + mocks.ReplayAll(); + + AdvisedSupport advised = new AdvisedSupport(); + advised.AddAdvice(mi); + advised.Interfaces = new Type[] { typeof(ITestCustomer) }; + + ITestCustomer to = CreateProxy(advised) as ITestCustomer; + Assert.IsNotNull(to); + Assert.AreEqual((long)5, to.Id, "Incorrect Id"); + Assert.AreEqual("Customer Name", to.Name, "Incorrect Name"); + Assert.AreEqual("Customer Company", to.Company, "Incorrect Company"); + + mocks.VerifyAll(); + } + [Test] - public void InterceptorInvokedWithNoTarget() + [ExpectedException(typeof(NotSupportedException) + , ExpectedMessage = "Target 'target' is null.")] + public void Does_proxy_interfacemethods_without_implementation_and_by_default_throws_NotSupportedException() + { + AdvisedSupport advised = new AdvisedSupport(); + advised.TargetSource = new DynamicTargetSource(typeof(object), null); + advised.Interfaces = new Type[] { typeof(ITestObject) }; + + ITestObject proxy = CreateProxy(advised) as ITestObject; + Assert.IsNotNull(proxy); + + proxy.GetDescription(); + } + + [Test] + public void Does_proxy_interfacemethods_without_implementation_and_delegates_to_interceptors() + { + DynamicInvocationTestInterceptor invocationInterceptor = new DynamicInvocationTestInterceptor(); + DynamicTargetSource targetSource = new DynamicTargetSource(typeof(object), null); + + AdvisedSupport advised = new AdvisedSupport(); + advised.TargetSource = targetSource; + advised.Interfaces = new Type[] { typeof(ITestObject) }; + advised.AddAdvice(invocationInterceptor); + ITestObject proxy = CreateProxy(advised) as ITestObject; + Assert.IsNotNull(proxy); + + // target null, call handled by interceptor + targetSource.Target = null; + invocationInterceptor.CallProceed = false; + proxy.GetDescription(); + Assert.AreEqual("GetDescription", invocationInterceptor.LastMethodInvocation.Method.Name); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException), MatchType=MessageMatch.Contains, UserMessage="'target'")] + public void Does_proxy_interfacemethods_without_implementation_and_throws_ArgumentNullException_On_NullTarget() + { + DynamicInvocationTestInterceptor invocationInterceptor = new DynamicInvocationTestInterceptor(); + DynamicTargetSource targetSource = new DynamicTargetSource(typeof(object), null); + + AdvisedSupport advised = new AdvisedSupport(); + advised.TargetSource = targetSource; + advised.Interfaces = new Type[] { typeof(ITestObject) }; + advised.AddAdvice(invocationInterceptor); + ITestObject proxy = CreateProxy(advised) as ITestObject; + Assert.IsNotNull(proxy); + + // target null, call not handled by interceptor + targetSource.Target = null; + invocationInterceptor.CallProceed = true; + proxy.GetDescription(); + } + + [Test] + [ExpectedException(typeof(NotSupportedException) + , ExpectedMessage = "Target 'target' of type 'System.Object' does not support methods of 'Spring.Objects.ITestObject'.")] + public void Does_proxy_interfacemethods_without_implementation_and_throws_NotSupportedException_On_Incompatible_Target() + { + DynamicInvocationTestInterceptor invocationInterceptor = new DynamicInvocationTestInterceptor(); + DynamicTargetSource targetSource = new DynamicTargetSource(typeof(object), null); + + AdvisedSupport advised = new AdvisedSupport(); + advised.TargetSource = targetSource; + advised.Interfaces = new Type[] { typeof(ITestObject) }; + advised.AddAdvice(invocationInterceptor); + ITestObject proxy = CreateProxy(advised) as ITestObject; + Assert.IsNotNull(proxy); + + // target incompatible, call not handled by interceptor + targetSource.Target = new object(); + invocationInterceptor.CallProceed = true; + proxy.GetDescription(); + } + + [Test] + [ExpectedException(typeof(NotSupportedException) + , ExpectedMessage = "Target 'target' of type 'System.Object' does not support methods of 'Spring.Objects.ITestObject'.")] + public void NoInterceptorWithNoTarget() + { + AdvisedSupport advised = new AdvisedSupport(); + advised.Interfaces = new Type[] { typeof(ITestObject) }; + + ITestObject to = CreateProxy(advised) as ITestObject; + to.GetDescription(); + } + + [Test] + public void InterceptorHandledCallWithNoTarget() { int age = 26; DynamicMock mock = new DynamicMock(typeof(IMethodInterceptor)); @@ -267,6 +396,20 @@ namespace Spring.Aop.Framework.DynamicProxy mock.Verify(); } + [Test] + [ExpectedException(typeof(NotSupportedException) + , ExpectedMessage = "Target 'target' of type 'System.Object' does not support methods of 'Spring.Objects.ITestObject'.")] + public void InterceptorUnhandledCallWithNoTarget() + { + AdvisedSupport advised = new AdvisedSupport(); + advised.AddAdvice(new NopInterceptor()); + advised.Interfaces = new Type[] { typeof(ITestObject) }; + + ITestObject to = CreateProxy(advised) as ITestObject; + Assert.IsNotNull(to); + to.GetDescription(); + } + [Test] public void ProxyAProxy() { @@ -492,10 +635,10 @@ namespace Spring.Aop.Framework.DynamicProxy public interface IRefOutTestObject { - int DoIt(int valueType, TestObject obj, EnumValue enumValue, + int DoIt(int valueType, TestObject obj, EnumValue enumValue, ref bool refValueType, out int outValueType, ref String refObj, out TestObject outObj, - ref EnumValue refEnum, out EnumValue outEnum, + ref EnumValue refEnum, out EnumValue outEnum, ref Guid refGuid, out Guid outGuid); } @@ -504,7 +647,7 @@ namespace Spring.Aop.Framework.DynamicProxy public int DoIt(int valueType, TestObject obj, EnumValue enumValue, ref bool refValueType, out int outValueType, ref String refObj, out TestObject outObj, - ref EnumValue refEnum, out EnumValue outEnum, + ref EnumValue refEnum, out EnumValue outEnum, ref Guid refGuid, out Guid outGuid) { valueType++; @@ -652,7 +795,7 @@ namespace Spring.Aop.Framework.DynamicProxy [Test] public void InterceptGenericMethod() { - AbstractProxyTypeBuilderTests.ClassWithGenericMethod target = + AbstractProxyTypeBuilderTests.ClassWithGenericMethod target = new AbstractProxyTypeBuilderTests.ClassWithGenericMethod(); mockTargetSource.SetTarget(target); @@ -692,7 +835,7 @@ namespace Spring.Aop.Framework.DynamicProxy [Test] public void InterceptGenericInterface() { - AbstractProxyTypeBuilderTests.ClassThatImplementsGenericInterface target = + AbstractProxyTypeBuilderTests.ClassThatImplementsGenericInterface target = new AbstractProxyTypeBuilderTests.ClassThatImplementsGenericInterface(); mockTargetSource.SetTarget(target); @@ -702,7 +845,7 @@ namespace Spring.Aop.Framework.DynamicProxy advised.TargetSource = mockTargetSource; advised.AddAdvice(ni); - AbstractProxyTypeBuilderTests.GenericInterface proxy = + AbstractProxyTypeBuilderTests.GenericInterface proxy = CreateProxy(advised) as AbstractProxyTypeBuilderTests.GenericInterface; Assert.IsNotNull(proxy); @@ -1034,21 +1177,21 @@ namespace Spring.Aop.Framework.DynamicProxy pf.AddAdvice(new CountingBeforeAdvice()); pf.AddAdvice(new CountingAfterReturningAdvice()); pf.AddAdvice(cta); - IPerson p = (IPerson) CreateAopProxy(pf).GetProxy(); + IPerson p = (IPerson)CreateAopProxy(pf).GetProxy(); p.Echo(null); Assert.AreEqual(0, cta.GetCalls()); - try + try { p.Echo(new Exception()); } - catch (Exception) + catch (Exception) { } Assert.AreEqual(1, cta.GetCalls()); // Will throw exception if it fails - IPerson p2 = (IPerson) SerializationTestUtils.SerializeAndDeserialize(p); + IPerson p2 = (IPerson)SerializationTestUtils.SerializeAndDeserialize(p); Assert.AreNotSame(p, p2); Assert.AreEqual(p.GetName(), p2.GetName()); Assert.AreEqual(p.GetAge(), p2.GetAge()); @@ -1070,10 +1213,10 @@ namespace Spring.Aop.Framework.DynamicProxy p2.GetAge(); Assert.AreEqual(1, ni.Count); - cta = (CountingThrowsAdvice) a2.Advisors[3].Advice; + cta = (CountingThrowsAdvice)a2.Advisors[3].Advice; p2.Echo(null); Assert.AreEqual(1, cta.GetCalls()); - try + try { p2.Echo(new Exception()); } @@ -1091,7 +1234,7 @@ namespace Spring.Aop.Framework.DynamicProxy /// and don't conflict. /// [Test] - public void OneAdvisedObjectCallsAnother() + public void OneAdvisedObjectCallsAnother() { int age1 = 33; int age2 = 37; @@ -1104,7 +1247,7 @@ namespace Spring.Aop.Framework.DynamicProxy pf1.AddAdvice(0, di1); pf1.AddAdvice(1, new ProxyMatcherInterceptor()); pf1.AddAdvice(2, new MethodInvocationMatcherInterceptor()); - ITestObject advised1 = (ITestObject) pf1.GetProxy(); + ITestObject advised1 = (ITestObject)pf1.GetProxy(); advised1.Age = age1; // = 1 invocation TestObject target2 = new TestObject(); @@ -1114,7 +1257,7 @@ namespace Spring.Aop.Framework.DynamicProxy pf2.AddAdvice(0, di2); pf2.AddAdvice(1, new ProxyMatcherInterceptor()); pf2.AddAdvice(2, new MethodInvocationMatcherInterceptor()); - ITestObject advised2 = (ITestObject) CreateProxy(pf2); + ITestObject advised2 = (ITestObject)CreateProxy(pf2); advised2.Age = age2; advised1.Spouse = advised2; // = 2 invocations @@ -1244,7 +1387,7 @@ namespace Spring.Aop.Framework.DynamicProxy AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(ITestObject) }); advised.Target = raw; - ITestObject to = (ITestObject) CreateProxy(advised); + ITestObject to = (ITestObject)CreateProxy(advised); Assert.IsTrue(to.Spouse == to, "this return is wrapped in proxy"); } @@ -1256,10 +1399,10 @@ namespace Spring.Aop.Framework.DynamicProxy } } - #endregion + #endregion [Test] - public void TargetThrowsException() + public void TargetThrowsException() { Exception expectedException = new ApplicationException(); @@ -1268,13 +1411,13 @@ namespace Spring.Aop.Framework.DynamicProxy advised.Target = new TestObject(); IAopProxy aop = CreateAopProxy(advised); - try + try { - ITestObject to = (ITestObject) aop.GetProxy(); + ITestObject to = (ITestObject)aop.GetProxy(); to.Exceptional(expectedException); Assert.Fail("Should have thrown exception raised by target"); } - catch (Exception ex) + catch (Exception ex) { Assert.AreEqual(expectedException, ex, "exception matches"); } @@ -1324,60 +1467,60 @@ namespace Spring.Aop.Framework.DynamicProxy #endregion // TODO : Introduction tests -/* - [Test(Description = "Test stateful interceptor")] - public void MixinWithIntroductionAdvisor() - { - TestObject to = new TestObject(); - AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(ITestObject) }); - advised.AddAdvisor(new LockMixinAdvisor()); - advised.Target = to; + /* + [Test(Description = "Test stateful interceptor")] + public void MixinWithIntroductionAdvisor() + { + TestObject to = new TestObject(); + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(ITestObject) }); + advised.AddAdvisor(new LockMixinAdvisor()); + advised.Target = to; - CheckTestObjectIntroduction(advised); - } + CheckTestObjectIntroduction(advised); + } - [Test] - public void MixinWithIntroductionInfo() - { - TestObject to = new TestObject(); - AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(ITestObject) }); - advised.AddAdvice(new LockMixin()); - advised.Target = to; + [Test] + public void MixinWithIntroductionInfo() + { + TestObject to = new TestObject(); + AdvisedSupport advised = new AdvisedSupport(new Type[] { typeof(ITestObject) }); + advised.AddAdvice(new LockMixin()); + advised.Target = to; - CheckTestObjectIntroduction(advised); - } + CheckTestObjectIntroduction(advised); + } - private void CheckTestObjectIntroduction(AdvisedSupport advised) - { - int newAge = 65; + private void CheckTestObjectIntroduction(AdvisedSupport advised) + { + int newAge = 65; - ITestObject ito = (ITestObject) CreateProxy(advised); - ito.Age = newAge; - Assert.IsTrue(ito.Age == newAge); + ITestObject ito = (ITestObject) CreateProxy(advised); + ito.Age = newAge; + Assert.IsTrue(ito.Age == newAge); - ILockable lockable = (ILockable) ito; - Assert.IsFalse(lockable.Locked()); - lockable.DoLock(); + ILockable lockable = (ILockable) ito; + Assert.IsFalse(lockable.Locked()); + lockable.DoLock(); - Assert.IsTrue(ito.Age == newAge); - try - { - ito.Age = 1; - Assert.Fail("Setters should fail when locked"); - } - catch (LockedException) - { - // ok - } - Assert.IsTrue(ito.Age == newAge); + Assert.IsTrue(ito.Age == newAge); + try + { + ito.Age = 1; + Assert.Fail("Setters should fail when locked"); + } + catch (LockedException) + { + // ok + } + Assert.IsTrue(ito.Age == newAge); - // Unlock - Assert.IsTrue(lockable.Locked()); - lockable.Unlock(); - ito.Age = 1; - Assert.IsTrue(ito.Age == 1); - } -*/ + // Unlock + Assert.IsTrue(lockable.Locked()); + lockable.Unlock(); + ito.Age = 1; + Assert.IsTrue(ito.Age == 1); + } + */ #region MultipleProceedCalls @@ -1668,33 +1811,33 @@ namespace Spring.Aop.Framework.DynamicProxy } // TODO : Opaque can be implemented if really usefull (To increase performance) -/* - public void testCanPreventCastToAdvisedUsingOpaque() - { - TestObject target = new TestObject(); - ProxyFactory pf = new ProxyFactory(target); - pf.Interfaces = new Type[] { typeof(ITestObject) }; - pf.AddAdvice(new NopInterceptor()); - CountingBeforeAdvice mba = new CountingBeforeAdvice(); - NameMatchMethodPointcut nmmp = new NameMatchMethodPointcut(); - nmmp.MappedName = "set_Age"; - IAdvisor advisor = new DefaultPointcutAdvisor(nmmp, mba); - pf.AddAdvisor(advisor); - Assert.IsFalse(pf.Opaque, "Opaque defaults to false"); - pf.Opaque = true; - Assert.IsTrue(pf.Opaque, "Opaque now true for this config"); - ITestObject proxied = (ITestObject) CreateProxy(pf); - proxied.Age = 10; - Assert.AreEqual(10, proxied.Age); - Assert.AreEqual(1, mba.GetCalls()); + /* + public void testCanPreventCastToAdvisedUsingOpaque() + { + TestObject target = new TestObject(); + ProxyFactory pf = new ProxyFactory(target); + pf.Interfaces = new Type[] { typeof(ITestObject) }; + pf.AddAdvice(new NopInterceptor()); + CountingBeforeAdvice mba = new CountingBeforeAdvice(); + NameMatchMethodPointcut nmmp = new NameMatchMethodPointcut(); + nmmp.MappedName = "set_Age"; + IAdvisor advisor = new DefaultPointcutAdvisor(nmmp, mba); + pf.AddAdvisor(advisor); + Assert.IsFalse(pf.Opaque, "Opaque defaults to false"); + pf.Opaque = true; + Assert.IsTrue(pf.Opaque, "Opaque now true for this config"); + ITestObject proxied = (ITestObject) CreateProxy(pf); + proxied.Age = 10; + Assert.AreEqual(10, proxied.Age); + Assert.AreEqual(1, mba.GetCalls()); - Assert.IsFalse(proxied is IAdvised, "Cannot be cast to Advised", ); - } - */ + Assert.IsFalse(proxied is IAdvised, "Cannot be cast to Advised", ); + } + */ // TODO AdviceSupportListeners test #region AdviceSupportListeners -/* + /* [Test] public void AdviceSupportListeners() { @@ -1791,7 +1934,7 @@ namespace Spring.Aop.Framework.DynamicProxy TestDynamicPointcutAdvisor dp = new TestDynamicPointcutAdvisor(new NopInterceptor(), "get_Age"); pf.AddAdvisor(dp); pf.Target = to; - ITestObject it = (ITestObject) CreateProxy(pf); + ITestObject it = (ITestObject)CreateProxy(pf); Assert.AreEqual(dp.count, 0); int age = it.Age; Assert.IsNotNull(age); // avoid mono mcs CS0218 @@ -1872,7 +2015,7 @@ namespace Spring.Aop.Framework.DynamicProxy TestStaticPointcutAdvisor sp = new TestStaticPointcutAdvisor(ni, "get_Age"); pf.AddAdvisor(sp); pf.Target = to; - ITestObject it = (ITestObject) CreateProxy(pf); + ITestObject it = (ITestObject)CreateProxy(pf); Assert.AreEqual(ni.Count, 0); int age = it.Age; Assert.IsNotNull(age); // avoid mono mcs error CS0219 @@ -1907,106 +2050,106 @@ namespace Spring.Aop.Framework.DynamicProxy #region CloneInvocationToProceedThreeTimes // TODO ? ReflectiveMethodInvocation is not Cloneable -/* - [Test(Description="There are times when we want to call proceed() twice.")] - public void CloneInvocationToProceedThreeTimes() - { - //We can do this if we clone the invocation. + /* + [Test(Description="There are times when we want to call proceed() twice.")] + public void CloneInvocationToProceedThreeTimes() + { + //We can do this if we clone the invocation. - TestObject to = new TestObject(); - ProxyFactory pf = new ProxyFactory(to); - pf.AddInterface(typeof(ITestObject)); + TestObject to = new TestObject(); + ProxyFactory pf = new ProxyFactory(to); + pf.AddInterface(typeof(ITestObject)); - TwoBirthdayAdvice twoBirthdayAdvice = new TwoBirthdayAdvice(); + TwoBirthdayAdvice twoBirthdayAdvice = new TwoBirthdayAdvice(); - TwoBirthdayPointcutAdvisor sp = new TwoBirthdayPointcutAdvisor(twoBirthdayAdvice); - pf.AddAdvisor(sp); - ITestObject ito = (ITestObject)CreateProxy(pf); + TwoBirthdayPointcutAdvisor sp = new TwoBirthdayPointcutAdvisor(twoBirthdayAdvice); + pf.AddAdvisor(sp); + ITestObject ito = (ITestObject)CreateProxy(pf); - int age = 20; - ito.Age = age; - Assert.AreEqual(age, ito.Age); - // Should return the age before the third, AOP-induced birthday - Assert.AreEqual(age + 2, ito.haveBirthday()); - // Return the final age produced by 3 birthdays - Assert.AreEqual(age + 3, ito.Age); - } + int age = 20; + ito.Age = age; + Assert.AreEqual(age, ito.Age); + // Should return the age before the third, AOP-induced birthday + Assert.AreEqual(age + 2, ito.haveBirthday()); + // Return the final age produced by 3 birthdays + Assert.AreEqual(age + 3, ito.Age); + } - private class TwoBirthdayPointcutAdvisor : StaticMethodMatcherPointcutAdvisor - { - public TwoBirthdayPointcutAdvisor(IAdvice advice) - : base(advice) - { - } + private class TwoBirthdayPointcutAdvisor : StaticMethodMatcherPointcutAdvisor + { + public TwoBirthdayPointcutAdvisor(IAdvice advice) + : base(advice) + { + } - public override bool Matches(MethodInfo method, Type targetType) - { - return "haveBirthday".Equals(method.Name); - } - } + public override bool Matches(MethodInfo method, Type targetType) + { + return "haveBirthday".Equals(method.Name); + } + } - private class TwoBirthdayAdvice : IMethodInterceptor - { - public object Invoke(IMethodInvocation invocation) - { - // Clone the invocation to proceed three times - // "The Moor's Last Sigh": this technology can cause premature aging - IMethodInvocation clone1 = ((ReflectiveMethodInvocation)invocation).InvocableClone(); - IMethodInvocation clone2 = ((ReflectiveMethodInvocation)invocation).InvocableClone(); - clone1.Proceed(); - clone2.Proceed(); - return invocation.Proceed(); - } - } + private class TwoBirthdayAdvice : IMethodInterceptor + { + public object Invoke(IMethodInvocation invocation) + { + // Clone the invocation to proceed three times + // "The Moor's Last Sigh": this technology can cause premature aging + IMethodInvocation clone1 = ((ReflectiveMethodInvocation)invocation).InvocableClone(); + IMethodInvocation clone2 = ((ReflectiveMethodInvocation)invocation).InvocableClone(); + clone1.Proceed(); + clone2.Proceed(); + return invocation.Proceed(); + } + } -// // We want to change the arguments on a clone: it shouldn't affect the original. -// public void testCanChangeArgumentsIndependentlyOnClonedInvocation() throws Throwable -// { -// TestObject to = new TestObject(); -// ProxyFactory pc = new ProxyFactory(to); -// pc.addInterface(typeof(ITestObject)); + // // We want to change the arguments on a clone: it shouldn't affect the original. + // public void testCanChangeArgumentsIndependentlyOnClonedInvocation() throws Throwable + // { + // TestObject to = new TestObject(); + // ProxyFactory pc = new ProxyFactory(to); + // pc.addInterface(typeof(ITestObject)); -// // Changes the name, then changes it back. -// MethodInterceptor nameReverter = new MethodInterceptor() { -// public Object invoke(MethodInvocation mi) throws Throwable { -// MethodInvocation clone = ((ReflectiveMethodInvocation) mi).invocableClone(); -// String oldName = ((ITestObject) mi.getThis()).Name; -// clone.getArguments()[0] = oldName; -// // Original method invocation should be unaffected by changes to argument list of clone -// mi.proceed(); -// return clone.proceed(); -// } -// }; + // // Changes the name, then changes it back. + // MethodInterceptor nameReverter = new MethodInterceptor() { + // public Object invoke(MethodInvocation mi) throws Throwable { + // MethodInvocation clone = ((ReflectiveMethodInvocation) mi).invocableClone(); + // String oldName = ((ITestObject) mi.getThis()).Name; + // clone.getArguments()[0] = oldName; + // // Original method invocation should be unaffected by changes to argument list of clone + // mi.proceed(); + // return clone.proceed(); + // } + // }; -// class NameSaver implements MethodInterceptor { -// private List names = new LinkedList(); + // class NameSaver implements MethodInterceptor { + // private List names = new LinkedList(); -// public Object invoke(MethodInvocation mi) throws Throwable { -// names.add(mi.getArguments()[0]); -// return mi.proceed(); -// } -// } + // public Object invoke(MethodInvocation mi) throws Throwable { + // names.add(mi.getArguments()[0]); + // return mi.proceed(); + // } + // } -// NameSaver saver = new NameSaver(); + // NameSaver saver = new NameSaver(); -// pc.addAdvisor(new DefaultPointcutAdvisor(Pointcuts.SETTERS, nameReverter)); -// pc.addAdvisor(new DefaultPointcutAdvisor(Pointcuts.SETTERS, saver)); -// ITestObject it = (ITestObject) createProxy(pc); + // pc.addAdvisor(new DefaultPointcutAdvisor(Pointcuts.SETTERS, nameReverter)); + // pc.addAdvisor(new DefaultPointcutAdvisor(Pointcuts.SETTERS, saver)); + // ITestObject it = (ITestObject) createProxy(pc); -// String name1 = "tony"; -// String name2 = "gordon"; + // String name1 = "tony"; + // String name2 = "gordon"; -// to.setName(name1); -// Assert.AreEqual(name1, to.Name); + // to.setName(name1); + // Assert.AreEqual(name1, to.Name); -// it.setName(name2); -// // NameReverter saved it back -// Assert.AreEqual(name1, it.Name); -// Assert.AreEqual(2, saver.names.size()); -// Assert.AreEqual(name2, saver.names.get(0)); -// Assert.AreEqual(name1, saver.names.get(1)); -// } -*/ + // it.setName(name2); + // // NameReverter saved it back + // Assert.AreEqual(name1, it.Name); + // Assert.AreEqual(2, saver.names.size()); + // Assert.AreEqual(name2, saver.names.get(0)); + // Assert.AreEqual(name1, saver.names.get(1)); + // } + */ #endregion #region OverloadedMethodsWithDifferentAdvice @@ -2021,7 +2164,7 @@ namespace Spring.Aop.Framework.DynamicProxy NopInterceptor overloadInt = new NopInterceptor(); pf.AddAdvisor(new OverloadIntPointcutAdvisor(overloadInt)); - IOverloads proxy = (IOverloads) CreateProxy(pf); + IOverloads proxy = (IOverloads)CreateProxy(pf); Assert.AreEqual(0, overloadInt.Count); Assert.AreEqual(0, overloadVoid.Count); @@ -2036,7 +2179,7 @@ namespace Spring.Aop.Framework.DynamicProxy Assert.AreEqual(1, overloadVoid.Count); } - public interface IOverloads + public interface IOverloads { void Overload(); int Overload(int i); @@ -2044,23 +2187,23 @@ namespace Spring.Aop.Framework.DynamicProxy void NoAdvice(); } - public class Overloads : IOverloads + public class Overloads : IOverloads { - public void Overload() + public void Overload() { } - public int Overload(int i) + public int Overload(int i) { return i; } - public string Overload(string foo) + public string Overload(string foo) { return foo; } - public void NoAdvice() + public void NoAdvice() { } } @@ -2095,88 +2238,88 @@ namespace Spring.Aop.Framework.DynamicProxy #endregion // TODO : IAdvised.TargetSource is read only (no setter) -/* - [Test] - public void ExistingProxyChangesTarget() - { - TestObject to1 = new TestObject(); - to1.Age = 33; + /* + [Test] + public void ExistingProxyChangesTarget() + { + TestObject to1 = new TestObject(); + to1.Age = 33; - TestObject to2 = new TestObject(); - to2.Age = 26; - to2.Name = "Juergen"; - TestObject to3 = new TestObject(); - to3.Age = 37; - ProxyFactory pf = new ProxyFactory(to1); - NopInterceptor nop = new NopInterceptor(); - pf.AddAdvice(nop); - ITestObject proxy = (ITestObject)CreateProxy(pf); - Assert.AreEqual(nop.Count, 0); - Assert.AreEqual(to1.Age, proxy.Age); - Assert.AreEqual(nop.Count, 1); - // Change to a new static target - pf.Target = to2; - Assert.AreEqual(to2.Age, proxy.Age); - Assert.AreEqual(nop.Count, 2); + TestObject to2 = new TestObject(); + to2.Age = 26; + to2.Name = "Juergen"; + TestObject to3 = new TestObject(); + to3.Age = 37; + ProxyFactory pf = new ProxyFactory(to1); + NopInterceptor nop = new NopInterceptor(); + pf.AddAdvice(nop); + ITestObject proxy = (ITestObject)CreateProxy(pf); + Assert.AreEqual(nop.Count, 0); + Assert.AreEqual(to1.Age, proxy.Age); + Assert.AreEqual(nop.Count, 1); + // Change to a new static target + pf.Target = to2; + Assert.AreEqual(to2.Age, proxy.Age); + Assert.AreEqual(nop.Count, 2); - // Change to a new dynamic target - HotSwappableTargetSource hts = new HotSwappableTargetSource(to3); - pf.TargetSource = hts; - Assert.AreEqual(to3.Age, proxy.Age); - Assert.AreEqual(nop.Count, 3); - hts.Swap(to1); - Assert.AreEqual(to1.Age, proxy.Age); - to1.Name = "Colin"; - Assert.AreEqual(to1.Name, proxy.Name); - Assert.AreEqual(nop.Count, 5); + // Change to a new dynamic target + HotSwappableTargetSource hts = new HotSwappableTargetSource(to3); + pf.TargetSource = hts; + Assert.AreEqual(to3.Age, proxy.Age); + Assert.AreEqual(nop.Count, 3); + hts.Swap(to1); + Assert.AreEqual(to1.Age, proxy.Age); + to1.Name = "Colin"; + Assert.AreEqual(to1.Name, proxy.Name); + Assert.AreEqual(nop.Count, 5); - // Change back, relying on casting to Advised - IAdvised advised = (IAdvised)proxy; - Assert.AreSame(hts, advised.TargetSource); - SingletonTargetSource sts = new SingletonTargetSource(to2); - advised.TargetSource = sts; - Assert.AreEqual(to2.Name, proxy.Name); - Assert.AreSame(sts, advised.TargetSource); - Assert.AreEqual(to2.Age, proxy.Age); - } - - [Test] - public void ProxyIsBoundBeforeTargetSourceInvoked() - { - TestObject target = new TestObject(); - ProxyFactory pf = new ProxyFactory(target); - pf.AddAdvice(new DebugInterceptor()); - pf.ExposeProxy = true; - ITestObject proxy = (ITestObject) CreateProxy(pf); - IAdvised config = (IAdvised) proxy; - // This class just checks proxy is bound before getTarget() call - config.setTargetSource(new TargetSource() { - public Class getTargetClass() { - return TestObject.class; + // Change back, relying on casting to Advised + IAdvised advised = (IAdvised)proxy; + Assert.AreSame(hts, advised.TargetSource); + SingletonTargetSource sts = new SingletonTargetSource(to2); + advised.TargetSource = sts; + Assert.AreEqual(to2.Name, proxy.Name); + Assert.AreSame(sts, advised.TargetSource); + Assert.AreEqual(to2.Age, proxy.Age); } - public boolean isStatic() { - return false; - } + [Test] + public void ProxyIsBoundBeforeTargetSourceInvoked() + { + TestObject target = new TestObject(); + ProxyFactory pf = new ProxyFactory(target); + pf.AddAdvice(new DebugInterceptor()); + pf.ExposeProxy = true; + ITestObject proxy = (ITestObject) CreateProxy(pf); + IAdvised config = (IAdvised) proxy; + // This class just checks proxy is bound before getTarget() call + config.setTargetSource(new TargetSource() { + public Class getTargetClass() { + return TestObject.class; + } - public Object getTarget() throws Exception { - Assert.AreEqual(proxy, AopContext.currentProxy()); - return target; - } + public boolean isStatic() { + return false; + } - public void releaseTarget(Object target) throws Exception { - } - }); + public Object getTarget() throws Exception { + Assert.AreEqual(proxy, AopContext.currentProxy()); + return target; + } + + public void releaseTarget(Object target) throws Exception { + } + }); - // Just test anything: it will fail if context wasn't found - Assert.AreEqual(0, proxy.Age); - } -*/ + // Just test anything: it will fail if context wasn't found + Assert.AreEqual(0, proxy.Age); + } + */ #region BeforeAdvisorIsInvoked [Test] - public void BeforeAdvisorIsInvoked() + public void BeforeAdvisorIsInvoked() { CountingBeforeAdvice cba = new CountingBeforeAdvice(); IAdvisor matchesNoArgsAdvisor = new NoArgsMethodPointcutAdvisor(cba); @@ -2272,7 +2415,7 @@ namespace Spring.Aop.Framework.DynamicProxy #region BeforeAdviceThrowsException [Test] - public void BeforeAdviceThrowsException() + public void BeforeAdviceThrowsException() { ApplicationException aex = new ApplicationException(); CountingBeforeNonSetterAdvice ba = new CountingBeforeNonSetterAdvice(aex); @@ -2293,12 +2436,12 @@ namespace Spring.Aop.Framework.DynamicProxy Assert.AreEqual(1, nop1.Count); Assert.AreEqual(1, nop2.Count); // Will fail, after invoking Nop1 - try + try { proxied.Age = 26; Assert.Fail("before advice should have ended chain"); } - catch (ApplicationException ex) + catch (ApplicationException ex) { Assert.AreEqual(aex, ex); } @@ -2319,11 +2462,11 @@ namespace Spring.Aop.Framework.DynamicProxy _exception = ex; } - public override void Before(MethodInfo method, object[] args, object target) + public override void Before(MethodInfo method, object[] args, object target) { - base.Before(method, args, target); + base.Before(method, args, target); - if (method.Name.StartsWith("set_")) + if (method.Name.StartsWith("set_")) throw _exception; } } @@ -2333,7 +2476,7 @@ namespace Spring.Aop.Framework.DynamicProxy #region AfterReturningAdvisorIsInvoked [Test] - public void AfterReturningAdvisorIsInvoked() + public void AfterReturningAdvisorIsInvoked() { SummingAfterAdvice aa = new SummingAfterAdvice(); IAdvisor matchesIntAdvisor = new ReturnsIntPointcutAdvisor(aa); @@ -2440,14 +2583,14 @@ namespace Spring.Aop.Framework.DynamicProxy { Assert.AreEqual(ex, caught); } - + ex = new HttpException(); - try + try { proxied.EchoException(1, ex); Assert.Fail("Should have thrown HttpException"); } - catch (HttpException caught) + catch (HttpException caught) { Assert.AreEqual(ex, caught); } @@ -2486,19 +2629,19 @@ namespace Spring.Aop.Framework.DynamicProxy Assert.AreEqual(0, th.GetCalls()); Exception ex = new Exception(); // Will be advised but doesn't match - try + try { proxied.EchoException(1, ex); Assert.Fail("Should have thrown Exception"); } - catch (Exception caught) + catch (Exception caught) { Assert.AreEqual(ex, caught); } // Subclass of RemoteException ex = new RemotingTimeoutException(); - try + try { proxied.EchoException(1, ex); Assert.Fail("Should have thrown RemotingTimeoutException"); @@ -2563,7 +2706,7 @@ namespace Spring.Aop.Framework.DynamicProxy #endregion } -#endregion + #endregion #region Helper classes definitions @@ -2619,6 +2762,59 @@ namespace Spring.Aop.Framework.DynamicProxy } } + public class DynamicTargetSource : ITargetSource + { + private object target; + private Type targetType; + + public DynamicTargetSource(Type targetType, object target) + { + this.targetType = targetType; + this.target = target; + } + + public object Target + { + get { return target; } + set { target = value; } + } + + public Type TargetType + { + get { return targetType; } + set { targetType = value; } + } + + public bool IsStatic + { + get { return false; } + } + + public virtual object GetTarget() + { + return target; + } + + public void ReleaseTarget(object target) + { } + } + + public class DynamicInvocationTestInterceptor : IMethodInterceptor + { + public bool CallProceed = false; + public IMethodInvocation LastMethodInvocation; + + public object Invoke(IMethodInvocation invocation) + { + LastMethodInvocation = invocation; + if (CallProceed) + { + return invocation.Proceed(); + } + return null; + } + } + #endregion } } diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/CompositionAopProxyTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/CompositionAopProxyTests.cs index de17d2c9..31982a31 100644 --- a/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/CompositionAopProxyTests.cs +++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/DynamicProxy/CompositionAopProxyTests.cs @@ -27,7 +27,10 @@ using System.Reflection; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters; using System.Runtime.Serialization.Formatters.Binary; +using AopAlliance.Intercept; +using DotNetMock.Dynamic; using NUnit.Framework; +using Rhino.Mocks; using Spring.Aop.Interceptor; using Spring.Aop.Support; using Spring.Objects; diff --git a/test/Spring/Spring.Core.Tests/Util/AssertUtilsTests.cs b/test/Spring/Spring.Core.Tests/Util/AssertUtilsTests.cs index 731210e8..2d4e952a 100644 --- a/test/Spring/Spring.Core.Tests/Util/AssertUtilsTests.cs +++ b/test/Spring/Spring.Core.Tests/Util/AssertUtilsTests.cs @@ -21,25 +21,29 @@ #region Imports using System; - +using System.Reflection; +using System.Runtime.Remoting; +using System.Runtime.Remoting.Messaging; +using System.Runtime.Remoting.Proxies; using NUnit.Framework; +using Spring.Objects; #endregion namespace Spring.Util { - /// - /// Unit tests for the AssertUtils class. - /// - /// Rick Evans - [TestFixture] - public sealed class AssertUtilsTests - { + /// + /// Unit tests for the AssertUtils class. + /// + /// Rick Evans + [TestFixture] + public sealed class AssertUtilsTests + { [Test] - [ExpectedException(typeof(ArgumentException),ExpectedMessage = "foo")] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "foo")] public void IsTrueWithMesssage() { - AssertUtils.IsTrue(false,"foo"); + AssertUtils.IsTrue(false, "foo"); } [Test] @@ -65,48 +69,48 @@ namespace Spring.Util [ExpectedException(typeof(InvalidOperationException))] public void StateTrue() { - AssertUtils.State(false,"foo"); + AssertUtils.State(false, "foo"); } - [Test] - [ExpectedException(typeof(ArgumentNullException))] - public void ArgumentNotNull () - { - AssertUtils.ArgumentNotNull(null, "foo"); - } - - [Test] + [Test] [ExpectedException(typeof(ArgumentNullException))] - public void ArgumentNotNullWithMessage () - { - AssertUtils.ArgumentNotNull(null, "foo", "Bang!"); - } + public void ArgumentNotNull() + { + AssertUtils.ArgumentNotNull(null, "foo"); + } - [Test] - public void ArgumentHasTextWithValidText() - { - AssertUtils.ArgumentHasText("... and no-one's getting fat 'cept Mama Cas!", "foo"); - } + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ArgumentNotNullWithMessage() + { + AssertUtils.ArgumentNotNull(null, "foo", "Bang!"); + } - [Test] - public void ArgumentHasTextWithValidTextAndMessage() - { - AssertUtils.ArgumentHasText("... and no-one's getting fat 'cept Mama Cas!", "foo", "Bang!"); - } + [Test] + public void ArgumentHasTextWithValidText() + { + AssertUtils.ArgumentHasText("... and no-one's getting fat 'cept Mama Cas!", "foo"); + } - [Test] - [ExpectedException(typeof(ArgumentNullException))] - public void ArgumentHasText () - { - AssertUtils.ArgumentHasText(null, "foo"); - } + [Test] + public void ArgumentHasTextWithValidTextAndMessage() + { + AssertUtils.ArgumentHasText("... and no-one's getting fat 'cept Mama Cas!", "foo", "Bang!"); + } - [Test] - [ExpectedException(typeof(ArgumentNullException))] - public void ArgumentHasTextWithMessage () - { - AssertUtils.ArgumentHasText(null, "foo", "Bang!"); - } + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ArgumentHasText() + { + AssertUtils.ArgumentHasText(null, "foo"); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void ArgumentHasTextWithMessage() + { + AssertUtils.ArgumentHasText(null, "foo", "Bang!"); + } [Test] [ExpectedException(typeof(ArgumentNullException))] @@ -174,5 +178,110 @@ namespace Spring.Util { AssertUtils.ArgumentHasElements(new object[] { new object(), new object(), new object() }, "foo"); } + + [Test] + public void UnderstandsType() + { + MethodInfo getDescriptionMethod = typeof(ITestObject).GetMethod("GetDescription", new Type[0]); + MethodInfo understandsMethod = typeof(AssertUtils).GetMethod("Understands", BindingFlags.Public|BindingFlags.Static, null, new Type[] {typeof (object), typeof(string), typeof (MethodBase)}, null); + + // null target, any type + AssertNotUnderstandsType(null, "target", typeof(object), typeof(NotSupportedException), "Target 'target' is null."); + // any target, null type + AssertNotUnderstandsType(new object(), "target", null, typeof(ArgumentNullException), "Argument 'requiredType' cannot be null."); + } + + [Test] + public void UnderstandsMethod() + { + MethodInfo getDescriptionMethod = typeof(ITestObject).GetMethod("GetDescription", new Type[0]); + MethodInfo understandsMethod = typeof(AssertUtils).GetMethod("Understands", BindingFlags.Public|BindingFlags.Static, null, new Type[] {typeof (object), typeof(string), typeof (MethodBase)}, null); + + // null target, static method + AssertUtils.Understands(null, "target", understandsMethod); + // null target, instance method + AssertNotUnderstandsMethod(null, "target", getDescriptionMethod, typeof(NotSupportedException), "Target 'target' is null and target method 'Spring.Objects.ITestObject.GetDescription' is not static."); + // compatible target, instance method + AssertUtils.Understands(new TestObject(), "target", getDescriptionMethod); + // incompatible target, instance method + AssertNotUnderstandsMethod(new object(), "target", getDescriptionMethod, typeof(NotSupportedException), "Target 'target' of type 'System.Object' does not support methods of 'Spring.Objects.ITestObject'."); + // compatible transparent proxy, instance method + object compatibleProxy = new TestProxy(new TestObject()).GetTransparentProxy(); + AssertUtils.Understands(compatibleProxy, "compatibleProxy", getDescriptionMethod); + // incompatible transparent proxy, instance method + object incompatibleProxy = new TestProxy(new object()).GetTransparentProxy(); + AssertNotUnderstandsMethod(incompatibleProxy, "incompatibleProxy", getDescriptionMethod, typeof(NotSupportedException), "Target 'incompatibleProxy' is a transparent proxy that does not support methods of 'Spring.Objects.ITestObject'."); + } + + private void AssertNotUnderstandsType(object target, string targetName, Type requiredType, Type exceptionType, string partialMessage) + { + try + { + AssertUtils.Understands(target, targetName, requiredType); + Assert.Fail(); + } + catch(Exception ex) + { + if (ex.GetType() != exceptionType) + { + Assert.Fail("Expected Exception of type {0}, but was {1}", exceptionType, ex.GetType()); + } + + if (-1 == ex.Message.IndexOf(partialMessage)) + { + Assert.Fail("Expected Message '{0}', but got '{1}'", partialMessage, ex.Message); + } + } + } + + private void AssertNotUnderstandsMethod(object target, string targetName, MethodBase method, Type exceptionType, string partialMessage) + { + try + { + AssertUtils.Understands(target, targetName, method); + Assert.Fail(); + } + catch(Exception ex) + { + if (ex.GetType() != exceptionType) + { + Assert.Fail("Expected Exception of type {0}, but was {1}", exceptionType, ex.GetType()); + } + + if (-1 == ex.Message.IndexOf(partialMessage)) + { + Assert.Fail("Expected Message '{0}', but got '{1}'", partialMessage, ex.Message); + } + } + } + + private class TestProxy : RealProxy, IRemotingTypeInfo + { + private readonly object targetInstance; + + public TestProxy(object targetInstance) + : base(typeof(MarshalByRefObject)) + { + this.targetInstance = targetInstance; + } + + public override IMessage Invoke(IMessage msg) + { + // return new ReturnMessage(result, null, 0, null, callMsg); + throw new NotSupportedException(); + } + + public bool CanCastTo(Type fromType, object o) + { + bool res = fromType.IsAssignableFrom(targetInstance.GetType()); + return res; + } + + public string TypeName + { + get { return targetInstance.GetType().AssemblyQualifiedName; } + set { throw new System.NotSupportedException(); } + } + } } }