001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.aop.framework.adapter; 018 019import java.lang.reflect.InvocationTargetException; 020import java.lang.reflect.Method; 021import java.util.HashMap; 022import java.util.Map; 023 024import org.aopalliance.intercept.MethodInterceptor; 025import org.aopalliance.intercept.MethodInvocation; 026import org.apache.commons.logging.Log; 027import org.apache.commons.logging.LogFactory; 028 029import org.springframework.aop.AfterAdvice; 030import org.springframework.util.Assert; 031 032/** 033 * Interceptor to wrap an after-throwing advice. 034 * 035 * <p>The signatures on handler methods on the {@code ThrowsAdvice} 036 * implementation method argument must be of the form:<br> 037 * 038 * {@code void afterThrowing([Method, args, target], ThrowableSubclass);} 039 * 040 * <p>Only the last argument is required. 041 * 042 * <p>Some examples of valid methods would be: 043 * 044 * <pre class="code">public void afterThrowing(Exception ex)</pre> 045 * <pre class="code">public void afterThrowing(RemoteException)</pre> 046 * <pre class="code">public void afterThrowing(Method method, Object[] args, Object target, Exception ex)</pre> 047 * <pre class="code">public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)</pre> 048 * 049 * <p>This is a framework class that need not be used directly by Spring users. 050 * 051 * @author Rod Johnson 052 * @author Juergen Hoeller 053 * @see MethodBeforeAdviceInterceptor 054 * @see AfterReturningAdviceInterceptor 055 */ 056public class ThrowsAdviceInterceptor implements MethodInterceptor, AfterAdvice { 057 058 private static final String AFTER_THROWING = "afterThrowing"; 059 060 private static final Log logger = LogFactory.getLog(ThrowsAdviceInterceptor.class); 061 062 063 private final Object throwsAdvice; 064 065 /** Methods on throws advice, keyed by exception class */ 066 private final Map<Class<?>, Method> exceptionHandlerMap = new HashMap<Class<?>, Method>(); 067 068 069 /** 070 * Create a new ThrowsAdviceInterceptor for the given ThrowsAdvice. 071 * @param throwsAdvice the advice object that defines the exception handler methods 072 * (usually a {@link org.springframework.aop.ThrowsAdvice} implementation) 073 */ 074 public ThrowsAdviceInterceptor(Object throwsAdvice) { 075 Assert.notNull(throwsAdvice, "Advice must not be null"); 076 this.throwsAdvice = throwsAdvice; 077 078 Method[] methods = throwsAdvice.getClass().getMethods(); 079 for (Method method : methods) { 080 if (method.getName().equals(AFTER_THROWING)) { 081 Class<?>[] paramTypes = method.getParameterTypes(); 082 if (paramTypes.length == 1 || paramTypes.length == 4) { 083 Class<?> throwableParam = paramTypes[paramTypes.length - 1]; 084 if (Throwable.class.isAssignableFrom(throwableParam)) { 085 // An exception handler to register... 086 this.exceptionHandlerMap.put(throwableParam, method); 087 if (logger.isDebugEnabled()) { 088 logger.debug("Found exception handler method on throws advice: " + method); 089 } 090 } 091 } 092 } 093 } 094 095 if (this.exceptionHandlerMap.isEmpty()) { 096 throw new IllegalArgumentException( 097 "At least one handler method must be found in class [" + throwsAdvice.getClass() + "]"); 098 } 099 } 100 101 102 /** 103 * Return the number of handler methods in this advice. 104 */ 105 public int getHandlerMethodCount() { 106 return this.exceptionHandlerMap.size(); 107 } 108 109 110 @Override 111 public Object invoke(MethodInvocation mi) throws Throwable { 112 try { 113 return mi.proceed(); 114 } 115 catch (Throwable ex) { 116 Method handlerMethod = getExceptionHandler(ex); 117 if (handlerMethod != null) { 118 invokeHandlerMethod(mi, ex, handlerMethod); 119 } 120 throw ex; 121 } 122 } 123 124 /** 125 * Determine the exception handle method for the given exception. 126 * @param exception the exception thrown 127 * @return a handler for the given exception type, or {@code null} if none found 128 */ 129 private Method getExceptionHandler(Throwable exception) { 130 Class<?> exceptionClass = exception.getClass(); 131 if (logger.isTraceEnabled()) { 132 logger.trace("Trying to find handler for exception of type [" + exceptionClass.getName() + "]"); 133 } 134 Method handler = this.exceptionHandlerMap.get(exceptionClass); 135 while (handler == null && exceptionClass != Throwable.class) { 136 exceptionClass = exceptionClass.getSuperclass(); 137 handler = this.exceptionHandlerMap.get(exceptionClass); 138 } 139 if (handler != null && logger.isDebugEnabled()) { 140 logger.debug("Found handler for exception of type [" + exceptionClass.getName() + "]: " + handler); 141 } 142 return handler; 143 } 144 145 private void invokeHandlerMethod(MethodInvocation mi, Throwable ex, Method method) throws Throwable { 146 Object[] handlerArgs; 147 if (method.getParameterTypes().length == 1) { 148 handlerArgs = new Object[] {ex}; 149 } 150 else { 151 handlerArgs = new Object[] {mi.getMethod(), mi.getArguments(), mi.getThis(), ex}; 152 } 153 try { 154 method.invoke(this.throwsAdvice, handlerArgs); 155 } 156 catch (InvocationTargetException targetEx) { 157 throw targetEx.getTargetException(); 158 } 159 } 160 161}