Open Source Repository

Home /spring/spring-aop-3.0.5 | Repository Home



org/springframework/aop/aspectj/MethodInvocationProceedingJoinPoint.java
/*
 * Copyright 2002-2009 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.
 */

package org.springframework.aop.aspectj;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.aspectj.lang.reflect.SourceLocation;
import org.aspectj.runtime.internal.AroundClosure;

import org.springframework.aop.ProxyMethodInvocation;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.util.Assert;

/**
 * Implementation of AspectJ ProceedingJoinPoint interface
 * wrapping an AOP Alliance MethodInvocation.
 *
 <p><b>Note</b>: the <code>getThis()</code> method returns the current Spring AOP proxy.
 * The <code>getTarget()</code> method returns the current Spring AOP target (which may be
 <code>null</code> if there is no target), and is a plain POJO without any advice.
 <b>If you want to call the object and have the advice take effect, use
 <code>getThis()</code>.</b> A common example is casting the object to an
 * introduced interface in the implementation of an introduction.
 *
 <p>Of course there is no such distinction between target and proxy in AspectJ.
 *
 @author Rod Johnson
 @author Juergen Hoeller
 @author Adrian Colyer
 @author Ramnivas Laddad
 @since 2.0
 */
public class MethodInvocationProceedingJoinPoint implements ProceedingJoinPoint, JoinPoint.StaticPart {
  
  private final ProxyMethodInvocation methodInvocation;

  private Object[] defensiveCopyOfArgs;

  /** Lazily initialized signature object */
  private Signature signature;

  /** Lazily initialized source location object */
  private SourceLocation sourceLocation;


  /**
   * Create a new MethodInvocationProceedingJoinPoint, wrapping the given
   * Spring ProxyMethodInvocation object.
   @param methodInvocation the Spring ProxyMethodInvocation object
   */
  public MethodInvocationProceedingJoinPoint(ProxyMethodInvocation methodInvocation) {
    Assert.notNull(methodInvocation, "MethodInvocation must not be null");
    this.methodInvocation = methodInvocation;
  }

  public void set$AroundClosure(AroundClosure aroundClosure) {
    throw new UnsupportedOperationException();
  }

  public Object proceed() throws Throwable {
    return this.methodInvocation.invocableClone().proceed();
  }

  public Object proceed(Object[] argumentsthrows Throwable {
    Assert.notNull(arguments, "Argument array passed to proceed cannot be null");
    if (arguments.length != this.methodInvocation.getArguments().length) {
      throw new IllegalArgumentException("Expecting " +
          this.methodInvocation.getArguments().length + " arguments to proceed, " +
          "but was passed " + arguments.length + " arguments");
    }
    this.methodInvocation.setArguments(arguments);
    return this.methodInvocation.invocableClone(arguments).proceed();
  }

  /**
   * Returns the Spring AOP proxy. Cannot be <code>null</code>.
   */
  public Object getThis() {
    return this.methodInvocation.getProxy();
  }

  /**
   * Returns the Spring AOP target. May be <code>null</code> if there is no target.
   */
  public Object getTarget() {
    return this.methodInvocation.getThis();
  }

  public Object[] getArgs() {
    if (this.defensiveCopyOfArgs == null) {
      Object[] argsSource = this.methodInvocation.getArguments();
      this.defensiveCopyOfArgs = new Object[argsSource.length];
      System.arraycopy(argsSource, 0this.defensiveCopyOfArgs, 0, argsSource.length);
    }
    return this.defensiveCopyOfArgs;
  }

  public Signature getSignature() {
    if (this.signature == null) {
      this.signature = new MethodSignatureImpl();
    }
    return signature;
  }

  public SourceLocation getSourceLocation() {
    if (this.sourceLocation == null) {
      this.sourceLocation = new SourceLocationImpl();
    }
    return this.sourceLocation;
  }

  public String getKind() {
    return ProceedingJoinPoint.METHOD_EXECUTION;
  }

  public int getId() {
    // TODO: It's just an adapter but returning 0 might still have side effects...
    return 0;
  }

  public JoinPoint.StaticPart getStaticPart() {
    return this;
  }

  public String toShortString() {
    return "execution(" + getSignature().toShortString() ")";
  }

  public String toLongString() {
    return "execution(" + getSignature().toLongString() ")";
  }

  public String toString() {
    return "execution(" + getSignature().toString() ")";
  }


  /**
   * Lazily initialized MethodSignature.
   */
  private class MethodSignatureImpl implements MethodSignature {

    private volatile String[] parameterNames;

    public String getName() {
      return methodInvocation.getMethod().getName();
    }

    public int getModifiers() {
      return methodInvocation.getMethod().getModifiers();
    }

    public Class getDeclaringType() {
      return methodInvocation.getMethod().getDeclaringClass();
    }

    public String getDeclaringTypeName() {
      return methodInvocation.getMethod().getDeclaringClass().getName();
    }

    public Class getReturnType() {
      return methodInvocation.getMethod().getReturnType();
    }

    public Method getMethod() {
      return methodInvocation.getMethod();
    }

    public Class[] getParameterTypes() {
      return methodInvocation.getMethod().getParameterTypes();
    }

    public String[] getParameterNames() {
      if (this.parameterNames == null) {
        this.parameterNames = (new LocalVariableTableParameterNameDiscoverer()).getParameterNames(getMethod());
      }
      return this.parameterNames;
    }

    public Class[] getExceptionTypes() {
      return methodInvocation.getMethod().getExceptionTypes();
    }

    public String toShortString() {
      return toString(false, false, false, false);
    }

    public String toLongString() {
      return toString(true, true, true, true);
    }

    public String toString() {
      return toString(false, true, false, true);
    }

    private String toString(boolean includeModifier, boolean includeReturnTypeAndArgs,
        boolean useLongReturnAndArgumentTypeName, boolean useLongTypeName) {
      StringBuilder sb = new StringBuilder();
      if (includeModifier) {
        sb.append(Modifier.toString(getModifiers()));
        sb.append(" ");
      }
      if (includeReturnTypeAndArgs) {
        appendType(sb, getReturnType(), useLongReturnAndArgumentTypeName);
        sb.append(" ");
      }
      appendType(sb, getDeclaringType(), useLongTypeName);
      sb.append(".");
      sb.append(getMethod().getName());
      sb.append("(");
      Class[] parametersTypes = getParameterTypes();
      appendTypes(sb, parametersTypes, includeReturnTypeAndArgs, useLongReturnAndArgumentTypeName);
      sb.append(")");
      return sb.toString();
    }

    private void appendTypes(StringBuilder sb, Class<?>[] types,
        boolean includeArgs, boolean useLongReturnAndArgumentTypeName) {
      if (includeArgs) {
        for (int size = types.length, i = 0; i < size; i++) {
          appendType(sb, types[i], useLongReturnAndArgumentTypeName);
          if (i < size - 1) {
            sb.append(",");
          }
        }
      }
      else {
        if (types.length != 0) {
          sb.append("..");
        }
      }
    }

    private void appendType(StringBuilder sb, Class<?> type, boolean useLongTypeName) {
      if (type.isArray()) {
        appendType(sb, type.getComponentType(), useLongTypeName);
        sb.append("[]");
      }
      else {
        sb.append(useLongTypeName ? type.getName() : type.getSimpleName());
      }
    }
  }


  /**
   * Lazily initialized SourceLocation.
   */
  private class SourceLocationImpl implements SourceLocation {

    public Class getWithinType() {
      if (methodInvocation.getThis() == null) {
        throw new UnsupportedOperationException("No source location joinpoint available: target is null");
      }
      return methodInvocation.getThis().getClass();
    }

    public String getFileName() {
      throw new UnsupportedOperationException();
    }

    public int getLine() {
      throw new UnsupportedOperationException();
    }

    public int getColumn() {
      throw new UnsupportedOperationException();
    }
  }

}