1 /* 2 * Copyright (C) 2006 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.inject.internal; 18 19 import com.google.common.collect.Lists; 20 import java.lang.reflect.AccessibleObject; 21 import java.lang.reflect.Method; 22 import java.util.Arrays; 23 import java.util.HashSet; 24 import java.util.List; 25 import java.util.Set; 26 import net.sf.cglib.proxy.MethodProxy; 27 import org.aopalliance.intercept.MethodInterceptor; 28 import org.aopalliance.intercept.MethodInvocation; 29 30 /** 31 * Intercepts a method with a stack of interceptors. 32 * 33 * @author crazybob@google.com (Bob Lee) 34 */ 35 final class InterceptorStackCallback implements net.sf.cglib.proxy.MethodInterceptor { 36 private static final Set<String> AOP_INTERNAL_CLASSES = 37 new HashSet<String>( 38 Arrays.asList( 39 InterceptorStackCallback.class.getName(), 40 InterceptedMethodInvocation.class.getName(), 41 MethodProxy.class.getName())); 42 43 final MethodInterceptor[] interceptors; 44 final Method method; 45 InterceptorStackCallback(Method method, List<MethodInterceptor> interceptors)46 public InterceptorStackCallback(Method method, List<MethodInterceptor> interceptors) { 47 this.method = method; 48 this.interceptors = interceptors.toArray(new MethodInterceptor[interceptors.size()]); 49 } 50 51 @Override intercept(Object proxy, Method method, Object[] arguments, MethodProxy methodProxy)52 public Object intercept(Object proxy, Method method, Object[] arguments, MethodProxy methodProxy) 53 throws Throwable { 54 return new InterceptedMethodInvocation(proxy, methodProxy, arguments, 0).proceed(); 55 } 56 57 private class InterceptedMethodInvocation implements MethodInvocation { 58 59 final Object proxy; 60 final Object[] arguments; 61 final MethodProxy methodProxy; 62 final int index; 63 InterceptedMethodInvocation( Object proxy, MethodProxy methodProxy, Object[] arguments, int index)64 public InterceptedMethodInvocation( 65 Object proxy, MethodProxy methodProxy, Object[] arguments, int index) { 66 this.proxy = proxy; 67 this.methodProxy = methodProxy; 68 this.arguments = arguments; 69 this.index = index; 70 } 71 72 @Override proceed()73 public Object proceed() throws Throwable { 74 try { 75 return index == interceptors.length 76 ? methodProxy.invokeSuper(proxy, arguments) 77 : interceptors[index] 78 .invoke(new InterceptedMethodInvocation(proxy, methodProxy, arguments, index + 1)); 79 } catch (Throwable t) { 80 pruneStacktrace(t); 81 throw t; 82 } 83 } 84 85 @Override getMethod()86 public Method getMethod() { 87 return method; 88 } 89 90 @Override getArguments()91 public Object[] getArguments() { 92 return arguments; 93 } 94 95 @Override getThis()96 public Object getThis() { 97 return proxy; 98 } 99 100 @Override getStaticPart()101 public AccessibleObject getStaticPart() { 102 return getMethod(); 103 } 104 } 105 106 /** 107 * Removes stacktrace elements related to AOP internal mechanics from the throwable's stack trace 108 * and any causes it may have. 109 */ pruneStacktrace(Throwable throwable)110 private void pruneStacktrace(Throwable throwable) { 111 for (Throwable t = throwable; t != null; t = t.getCause()) { 112 StackTraceElement[] stackTrace = t.getStackTrace(); 113 List<StackTraceElement> pruned = Lists.newArrayList(); 114 for (StackTraceElement element : stackTrace) { 115 String className = element.getClassName(); 116 if (!AOP_INTERNAL_CLASSES.contains(className) && !className.contains("$EnhancerByGuice$")) { 117 pruned.add(element); 118 } 119 } 120 t.setStackTrace(pruned.toArray(new StackTraceElement[pruned.size()])); 121 } 122 } 123 } 124