#region License
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
#region Imports
using System;
using System.Collections;
using System.Reflection;
using System.Text;
using AopAlliance.Intercept;
using Spring.Util;
#endregion
namespace Spring.Aop.Framework{
/// <summary>
/// Convenience base class for <see cref="AopAlliance.Intercept.IMethodInvocation"/>
/// implementations.
/// </summary>
/// <remarks>
/// <p>
/// Subclasses can override the
/// <see cref="Spring.Aop.Framework.AbstractMethodInvocation.InvokeJoinpoint()"/>
/// method to change this behavior, so this is a useful/ base class for
/// <see cref="AopAlliance.Intercept.IMethodInvocation"/> implementations.
/// </p>
/// </remarks>
/// <author>Rod Johnson</author>
/// <author>Aleksandar Seovic (.NET)</author>
/// <author>Rick Evans (.NET)</author>
/// <author>Bruno Baia (.NET)</author>
[Serializable]
public abstract class AbstractMethodInvocation : IMethodInvocation
{
/// <summary>
/// The arguments (if any = may be <see lang="null"/>) to the method
/// that is to be invoked.
/// </summary>
protected object[] arguments;
/// <summary>
/// The target object that the method is to be invoked on.
/// </summary>
protected object target;
/// <summary>
/// The AOP proxy for the target object.
/// </summary>
protected object proxy;
/// <summary>
/// The method invocation that is to be invoked.
/// </summary>
protected MethodInfo method;
/// <summary>
/// The list of <see cref="AopAlliance.Intercept.IMethodInterceptor"/> and
/// <cref see="Spring.Aop.Framework.InterceptorAndDynamicMethodMatcher"/>
/// that need dynamic checks.
/// </summary>
protected IList interceptors;
/// <summary>
/// The declaring type of the method that is to be invoked.
/// </summary>
protected Type targetType;
/// <summary>
/// The index from 0 of the current interceptor we're invoking.
/// </summary>
protected int currentInterceptorIndex;
/// <summary>
/// Creates a new instance of the
/// <see cref="Spring.Aop.Framework.AbstractMethodInvocation"/> class.
/// </summary>
/// <remarks>
/// <p>
/// This is an abstract class, and as such exposes no publicly visible
/// constructors.
/// </p>
/// <p>
/// <note type="implementnotes">
/// The <paramref name="interceptors"/> list can also contain any
/// <see cref="Spring.Aop.Framework.InterceptorAndDynamicMethodMatcher"/>s
/// that need evaluation at runtime.
/// <see cref="Spring.Aop.IMethodMatcher"/>s included in an
/// <see cref="Spring.Aop.Framework.InterceptorAndDynamicMethodMatcher"/>
/// must already have been found to have matched as far as was possible
/// <b>statically</b>. Passing an array might be about 10% faster, but
/// would complicate the code, and it would work only for static
/// pointcuts.
/// </note>
/// </p>
/// </remarks>
/// <param name="proxy">The AOP proxy.</param>
/// <param name="target">The target object.</param>
/// <param name="method">the target method.</param>
/// <param name="arguments">The target method's arguments.</param>
/// <param name="targetType">
/// The <see cref="System.Type"/> of the target object.</param>
/// <param name="interceptors">
/// The list of interceptors that are to be applied. May be
/// <cref lang="null"/>.
/// </param>
/// <exception cref="System.ArgumentNullException">
/// If the <paramref name="target"/> is <see lang="null"/>.
/// </exception>
protected AbstractMethodInvocation(object proxy, object target,
MethodInfo method, object[] arguments, Type targetType, IList interceptors)
{
#region Sanity Check
// 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
this.proxy = proxy;
this.target = target;
this.method = method;
this.targetType = targetType;
this.arguments = arguments;
this.interceptors = interceptors;
}
/// <summary>
/// Gets the method invocation that is to be invoked.
/// </summary>
/// <remarks>
/// <p>
/// May or may not correspond with a method invoked on an underlying
/// implementation of that interface.
/// </p>
/// </remarks>
/// <see cref="AopAlliance.Intercept.IMethodInvocation.Method"/>
public virtual MethodInfo Method
{
get { return method; }
}
/// <summary>
/// Gets the static part of this joinpoint.
/// </summary>
/// <value>
/// The proxied member's information.
/// </value>
/// <see cref="AopAlliance.Intercept.IJoinpoint.StaticPart"/>
public virtual MemberInfo StaticPart
{
get { return Method; }
}
/// <summary>
/// Gets the proxy that this interception was made through.
/// </summary>
/// <value>
/// The proxy that this interception was made through.
/// </value>
public virtual object Proxy
{
get { return this.proxy; }
}
/// <summary>
/// Gets the target object for the invocation.
/// </summary>
/// <value>
/// The target object for this method invocation.
/// </value>
public virtual object Target
{
get { return this.target; }
}
/// <summary>
/// Gets the type of the target object.
/// </summary>
/// <value>
/// The type of the target object.
/// </value>
public virtual Type TargetType
{
get { return this.targetType; }
}
/// <summary>
/// Gets and sets the arguments (if any - may be <cref lang="null"/>)
/// to the method that is to be invoked.
/// </summary>
/// <value>
/// The arguments (if any - may be <cref lang="null"/>) to the
/// method that is to be invoked.
/// </value>
/// <see cref="AopAlliance.Intercept.IInvocation.Arguments"/>
public virtual object[] Arguments
{
get { return this.arguments; }
set { this.arguments = value; }
}
/// <summary>
/// The list of method interceptors.
/// </summary>
/// <remarks>
/// <p>
/// May be <see lang="null"/>.
/// </p>
/// </remarks>
public virtual IList Interceptors
{
get { return this.interceptors; }
set { this.interceptors = value; }
}
/// <summary>
/// Gets the target object.
/// </summary>
public virtual object This
{
get { return this.target; }
}
/// <summary>
/// Proceeds to the next interceptor in the chain.
/// </summary>
/// <returns>
/// The return value of the method invocation.
/// </returns>
/// <exception cref="System.Exception">
/// If any of the interceptors at the joinpoint throws an exception.
/// </exception>
/// <see cref="AopAlliance.Intercept.IJoinpoint.Proceed"/>
public virtual object Proceed()
{
if (this.interceptors == null ||
this.currentInterceptorIndex == this.interceptors.Count)
{
AssertJoinpoint();
return InvokeJoinpoint();
}
object interceptor = this.interceptors[this.currentInterceptorIndex];
InterceptorAndDynamicMethodMatcher dynamicMatcher
= interceptor as InterceptorAndDynamicMethodMatcher;
IMethodInvocation nextInvocation = PrepareMethodInvocationForProceed(this);
if (dynamicMatcher != null)
{
// evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match...
if (dynamicMatcher.MethodMatcher.Matches(
nextInvocation.Method, nextInvocation.TargetType, nextInvocation.Arguments))
{
return dynamicMatcher.Interceptor.Invoke(nextInvocation);
}
else
{
// dynamic match failed; skip this interceptor and invoke the next in the chain...
return nextInvocation.Proceed();
}
}
else
{
// it's an interceptor so we just invoke it: the pointcut will have
// been evaluated statically before this object was constructed...
return ((IMethodInterceptor)interceptor).Invoke(nextInvocation);
}
}
/// <summary>
/// Retrieves a new <see cref="AopAlliance.Intercept.IMethodInvocation"/> instance
/// for the next Proceed method call.
/// </summary>
/// <param name="invocation">
/// The current <see cref="AopAlliance.Intercept.IMethodInvocation"/> instance.
/// </param>
/// <returns>
/// The new <see cref="AopAlliance.Intercept.IMethodInvocation"/> instance to use.
/// </returns>
/// <see cref="Spring.Aop.Framework.ReflectiveMethodInvocation.PrepareMethodInvocationForProceed"/>
protected abstract IMethodInvocation PrepareMethodInvocationForProceed(
IMethodInvocation invocation);
/// <summary>
/// Performs sanity checks, whether the actual joinpoint may be invoked
/// </summary>
/// <remarks>
/// By default checks that the underlying target is not null and the called method is implemented
/// by the target's type.
/// </remarks>
/// <exception cref="ArgumentNullException">if <see cref="target"/> is <c>null</c>.</exception>
/// <exception cref="NotSupportedException">if the <see cref="target"/> 's type does not implement <see cref="method"/>.</exception>
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));
// }
}
/// <summary>
/// Invokes the joinpoint.
/// </summary>
/// <remarks>
/// <p>
/// Subclasses can override this to use custom invocation.
/// </p>
/// </remarks>
/// <returns>
/// The return value of the invocation of the joinpoint.
/// </returns>
/// <exception cref="System.Exception">
/// If invoking the joinpoint resulted in an exception.
/// </exception>
/// <see cref="Spring.Aop.Framework.ReflectiveMethodInvocation.InvokeJoinpoint"/>
protected abstract object InvokeJoinpoint();
/// <summary>
/// A <see cref="System.String"/> that represents the current
/// invocation.
/// </summary>
/// <remarks>
/// <p>
/// <note type="implementnotes">
/// Does <b>not</b> invoke <see cref="System.Object.ToString()"/> on the
/// <see cref="Spring.Aop.Framework.AbstractMethodInvocation.This"/> target
/// object, as that too may be proxied.
/// </note>
/// </p>
/// </remarks>
/// <returns>
/// A <see cref="System.String"/> that represents the current invocation.
/// </returns>
public override string ToString()
{
StringBuilder buffer = new StringBuilder("Invocation: method '");
buffer.Append(Method.Name).Append("', ").Append("arguments ");
buffer.Append(this.arguments != null ? StringUtils.ArrayToCommaDelimitedString(this.arguments) : "[none]");
buffer.Append("; ");
if (this.target == null)
{
buffer.Append("target is null.");
}
else
{
buffer.Append("target is of Type [").Append(this.targetType.FullName).Append(']');
}
return buffer.ToString();
}
}
}
|