Open Source Repository

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



org/springframework/aop/framework/adapter/ThrowsAdviceInterceptor.java
/*
 * 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.
 */

package org.springframework.aop.framework.adapter;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.aop.AfterAdvice;
import org.springframework.util.Assert;

/**
 * Interceptor to wrap an after-throwing advice.
 *
 <p>The signatures on handler methods on the <code>ThrowsAdvice</code>
 * implementation method argument must be of the form:<br>
 *
 <code>void afterThrowing([Method, args, target], ThrowableSubclass);</code>
 *
 <p>Only the last argument is required.
 *
 <p>Some examples of valid methods would be:
 *
 <pre class="code">public void afterThrowing(Exception ex)</pre>
 <pre class="code">public void afterThrowing(RemoteException)</pre>
 <pre class="code">public void afterThrowing(Method method, Object[] args, Object target, Exception ex)</pre>
 <pre class="code">public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)</pre>
 *
 <p>This is a framework class that need not be used directly by Spring users.
 *
 @author Rod Johnson
 @author Juergen Hoeller
 */
public class ThrowsAdviceInterceptor implements MethodInterceptor, AfterAdvice {

  private static final String AFTER_THROWING = "afterThrowing";

  private static final Log logger = LogFactory.getLog(ThrowsAdviceInterceptor.class);


  private final Object throwsAdvice;

  /** Methods on throws advice, keyed by exception class */
  private final Map<Class, Method> exceptionHandlerMap = new HashMap<Class, Method>();


  /**
   * Create a new ThrowsAdviceInterceptor for the given ThrowsAdvice.
   @param throwsAdvice the advice object that defines the exception
   * handler methods (usually a {@link org.springframework.aop.ThrowsAdvice}
   * implementation)
   */
  public ThrowsAdviceInterceptor(Object throwsAdvice) {
    Assert.notNull(throwsAdvice, "Advice must not be null");
    this.throwsAdvice = throwsAdvice;

    Method[] methods = throwsAdvice.getClass().getMethods();
    for (Method method : methods) {
      if (method.getName().equals(AFTER_THROWING&&
          (method.getParameterTypes().length == || method.getParameterTypes().length == 4&&
          Throwable.class.isAssignableFrom(method.getParameterTypes()[method.getParameterTypes().length - 1])
        ) {
        // Have an exception handler
        this.exceptionHandlerMap.put(method.getParameterTypes()[method.getParameterTypes().length - 1], method);
        if (logger.isDebugEnabled()) {
          logger.debug("Found exception handler method: " + method);
        }
      }
    }
    
    if (this.exceptionHandlerMap.isEmpty()) {
      throw new IllegalArgumentException(
          "At least one handler method must be found in class [" + throwsAdvice.getClass() "]");
    }
  }
  
  public int getHandlerMethodCount() {
    return this.exceptionHandlerMap.size();
  }

  /**
   * Determine the exception handle method. Can return null if not found.
   @param exception the exception thrown
   @return a handler for the given exception type
   */
  private Method getExceptionHandler(Throwable exception) {
    Class exceptionClass = exception.getClass();
    if (logger.isTraceEnabled()) {
      logger.trace("Trying to find handler for exception of type [" + exceptionClass.getName() "]");
    }
    Method handler = this.exceptionHandlerMap.get(exceptionClass);
    while (handler == null && !exceptionClass.equals(Throwable.class)) {
      exceptionClass = exceptionClass.getSuperclass();
      handler = this.exceptionHandlerMap.get(exceptionClass);
    }
    if (handler != null && logger.isDebugEnabled()) {
      logger.debug("Found handler for exception of type [" + exceptionClass.getName() "]: " + handler);
    }
    return handler;
  }

  public Object invoke(MethodInvocation mithrows Throwable {
    try {
      return mi.proceed();
    }
    catch (Throwable ex) {
      Method handlerMethod = getExceptionHandler(ex);
      if (handlerMethod != null) {
        invokeHandlerMethod(mi, ex, handlerMethod);
      }
      throw ex;
    }
  }
  
  private void invokeHandlerMethod(MethodInvocation mi, Throwable ex, Method methodthrows Throwable {
    Object[] handlerArgs;
    if (method.getParameterTypes().length == 1) {
      handlerArgs = new Object[] { ex };
    }
    else {
      handlerArgs = new Object[] {mi.getMethod(), mi.getArguments(), mi.getThis(), ex};
    }
    try {
      method.invoke(this.throwsAdvice, handlerArgs);
    }
    catch (InvocationTargetException targetEx) {
      throw targetEx.getTargetException();
    }
  }

}