001: /*
002: * Copyright 2002-2007 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: * http://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:
017: package org.springframework.aop.framework.adapter;
018:
019: import java.lang.reflect.InvocationTargetException;
020: import java.lang.reflect.Method;
021: import java.util.HashMap;
022: import java.util.Map;
023:
024: import org.aopalliance.intercept.MethodInterceptor;
025: import org.aopalliance.intercept.MethodInvocation;
026: import org.apache.commons.logging.Log;
027: import org.apache.commons.logging.LogFactory;
028:
029: import org.springframework.aop.AfterAdvice;
030: import 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</code>
036: * implementation method argument must be of the form:<br>
037: *
038: * <code>void afterThrowing([Method, args, target], ThrowableSubclass);</code>
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: */
054: public class ThrowsAdviceInterceptor implements MethodInterceptor,
055: AfterAdvice {
056:
057: private static final String AFTER_THROWING = "afterThrowing";
058:
059: private static final Log logger = LogFactory
060: .getLog(ThrowsAdviceInterceptor.class);
061:
062: private final Object throwsAdvice;
063:
064: /** Methods on throws advice, keyed by exception class */
065: private final Map exceptionHandlerMap = new HashMap();
066:
067: /**
068: * Create a new ThrowsAdviceInterceptor for the given ThrowsAdvice.
069: * @param throwsAdvice the advice object that defines the exception
070: * handler methods (usually a {@link org.springframework.aop.ThrowsAdvice}
071: * implementation)
072: */
073: public ThrowsAdviceInterceptor(Object throwsAdvice) {
074: Assert.notNull(throwsAdvice, "Advice must not be null");
075: this .throwsAdvice = throwsAdvice;
076:
077: Method[] methods = throwsAdvice.getClass().getMethods();
078: for (int i = 0; i < methods.length; i++) {
079: Method method = methods[i];
080: if (method.getName().equals(AFTER_THROWING)
081: &&
082: //m.getReturnType() == null &&
083: (method.getParameterTypes().length == 1 || method
084: .getParameterTypes().length == 4)
085: && Throwable.class.isAssignableFrom(method
086: .getParameterTypes()[method
087: .getParameterTypes().length - 1])) {
088: // Have an exception handler
089: this .exceptionHandlerMap.put(
090: method.getParameterTypes()[method
091: .getParameterTypes().length - 1],
092: method);
093: if (logger.isDebugEnabled()) {
094: logger.debug("Found exception handler method: "
095: + method);
096: }
097: }
098: }
099:
100: if (this .exceptionHandlerMap.isEmpty()) {
101: throw new IllegalArgumentException(
102: "At least one handler method must be found in class ["
103: + throwsAdvice.getClass() + "]");
104: }
105: }
106:
107: public int getHandlerMethodCount() {
108: return this .exceptionHandlerMap.size();
109: }
110:
111: /**
112: * Determine the exception handle method. Can return null if not found.
113: * @param exception the exception thrown
114: * @return a handler for the given exception type
115: */
116: private Method getExceptionHandler(Throwable exception) {
117: Class exceptionClass = exception.getClass();
118: if (logger.isTraceEnabled()) {
119: logger
120: .trace("Trying to find handler for exception of type ["
121: + exceptionClass.getName() + "]");
122: }
123: Method handler = (Method) this .exceptionHandlerMap
124: .get(exceptionClass);
125: while (handler == null
126: && !exceptionClass.equals(Throwable.class)) {
127: exceptionClass = exceptionClass.getSuperclass();
128: handler = (Method) this .exceptionHandlerMap
129: .get(exceptionClass);
130: }
131: if (handler != null && logger.isDebugEnabled()) {
132: logger.debug("Found handler for exception of type ["
133: + exceptionClass.getName() + "]: " + handler);
134: }
135: return handler;
136: }
137:
138: public Object invoke(MethodInvocation mi) throws Throwable {
139: try {
140: return mi.proceed();
141: } catch (Throwable ex) {
142: Method handlerMethod = getExceptionHandler(ex);
143: if (handlerMethod != null) {
144: invokeHandlerMethod(mi, ex, handlerMethod);
145: }
146: throw ex;
147: }
148: }
149:
150: private void invokeHandlerMethod(MethodInvocation mi, Throwable ex,
151: Method method) throws Throwable {
152: Object[] handlerArgs;
153: if (method.getParameterTypes().length == 1) {
154: handlerArgs = new Object[] { ex };
155: } else {
156: handlerArgs = new Object[] { mi.getMethod(),
157: mi.getArguments(), mi.getThis(), ex };
158: }
159: try {
160: method.invoke(this .throwsAdvice, handlerArgs);
161: } catch (InvocationTargetException targetEx) {
162: throw targetEx.getTargetException();
163: }
164: }
165:
166: }
|