1 /* 2 * Copyright 2003,2004 The Apache Software Foundation 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 package org.mockito.cglib.proxy; 17 18 import java.lang.reflect.InvocationTargetException; 19 import java.lang.reflect.Method; 20 21 import org.mockito.cglib.core.AbstractClassGenerator; 22 import org.mockito.cglib.core.CodeGenerationException; 23 import org.mockito.cglib.core.GeneratorStrategy; 24 import org.mockito.cglib.core.NamingPolicy; 25 import org.mockito.cglib.core.Signature; 26 import org.mockito.cglib.reflect.FastClass; 27 28 29 /** 30 * Classes generated by {@link Enhancer} pass this object to the 31 * registered {@link MethodInterceptor} objects when an intercepted method is invoked. It can 32 * be used to either invoke the original method, or call the same method on a different 33 * object of the same type. 34 * @version $Id: MethodProxy.java,v 1.14 2008/05/26 04:05:50 herbyderby Exp $ 35 */ 36 public class MethodProxy { 37 private Signature sig1; 38 private Signature sig2; 39 private CreateInfo createInfo; 40 41 private final Object initLock = new Object(); 42 private volatile FastClassInfo fastClassInfo; 43 44 /** 45 * For internal use by {@link Enhancer} only; see the {@link org.mockito.cglib.reflect.FastMethod} class 46 * for similar functionality. 47 */ 48 public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) { 49 MethodProxy proxy = new MethodProxy(); 50 proxy.sig1 = new Signature(name1, desc); 51 proxy.sig2 = new Signature(name2, desc); 52 proxy.createInfo = new CreateInfo(c1, c2); 53 return proxy; 54 } 55 56 private void init() 57 { 58 /* 59 * Using a volatile invariant allows us to initialize the FastClass and 60 * method index pairs atomically. 61 * 62 * Double-checked locking is safe with volatile in Java 5. Before 1.5 this 63 * code could allow fastClassInfo to be instantiated more than once, which 64 * appears to be benign. 65 */ 66 if (fastClassInfo == null) 67 { 68 synchronized (initLock) 69 { 70 if (fastClassInfo == null) 71 { 72 CreateInfo ci = createInfo; 73 74 FastClassInfo fci = new FastClassInfo(); 75 fci.f1 = helper(ci, ci.c1); 76 fci.f2 = helper(ci, ci.c2); 77 fci.i1 = fci.f1.getIndex(sig1); 78 fci.i2 = fci.f2.getIndex(sig2); 79 fastClassInfo = fci; 80 } 81 } 82 } 83 } 84 85 private static class FastClassInfo 86 { 87 FastClass f1; 88 FastClass f2; 89 int i1; 90 int i2; 91 } 92 93 private static class CreateInfo 94 { 95 Class c1; 96 Class c2; 97 NamingPolicy namingPolicy; 98 GeneratorStrategy strategy; 99 boolean attemptLoad; 100 101 public CreateInfo(Class c1, Class c2) 102 { 103 this.c1 = c1; 104 this.c2 = c2; 105 AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent(); 106 if (fromEnhancer != null) { 107 namingPolicy = fromEnhancer.getNamingPolicy(); 108 strategy = fromEnhancer.getStrategy(); 109 attemptLoad = fromEnhancer.getAttemptLoad(); 110 } 111 } 112 } 113 114 private static FastClass helper(CreateInfo ci, Class type) { 115 FastClass.Generator g = new FastClass.Generator(); 116 g.setType(type); 117 g.setClassLoader(ci.c2.getClassLoader()); 118 g.setNamingPolicy(ci.namingPolicy); 119 g.setStrategy(ci.strategy); 120 g.setAttemptLoad(ci.attemptLoad); 121 return g.create(); 122 } 123 124 private MethodProxy() { 125 } 126 127 /** 128 * Return the signature of the proxied method. 129 */ 130 public Signature getSignature() { 131 return sig1; 132 } 133 134 /** 135 * Return the name of the synthetic method created by CGLIB which is 136 * used by {@link #invokeSuper} to invoke the superclass 137 * (non-intercepted) method implementation. The parameter types are 138 * the same as the proxied method. 139 */ 140 public String getSuperName() { 141 return sig2.getName(); 142 } 143 144 /** 145 * Return the {@link org.mockito.cglib.reflect.FastClass} method index 146 * for the method used by {@link #invokeSuper}. This index uniquely 147 * identifies the method within the generated proxy, and therefore 148 * can be useful to reference external metadata. 149 * @see #getSuperName 150 */ 151 public int getSuperIndex() { 152 init(); 153 return fastClassInfo.i2; 154 } 155 156 /** 157 * Return the <code>MethodProxy</code> used when intercepting the method 158 * matching the given signature. 159 * @param type the class generated by Enhancer 160 * @param sig the signature to match 161 * @return the MethodProxy instance, or null if no applicable matching method is found 162 * @throws IllegalArgumentException if the Class was not created by Enhancer or does not use a MethodInterceptor 163 */ 164 public static MethodProxy find(Class type, Signature sig) { 165 try { 166 Method m = type.getDeclaredMethod(MethodInterceptorGenerator.FIND_PROXY_NAME, 167 MethodInterceptorGenerator.FIND_PROXY_TYPES); 168 return (MethodProxy)m.invoke(null, new Object[]{ sig }); 169 } catch (NoSuchMethodException e) { 170 throw new IllegalArgumentException("Class " + type + " does not use a MethodInterceptor"); 171 } catch (IllegalAccessException e) { 172 throw new CodeGenerationException(e); 173 } catch (InvocationTargetException e) { 174 throw new CodeGenerationException(e); 175 } 176 } 177 178 /** 179 * Invoke the original method, on a different object of the same type. 180 * @param obj the compatible object; recursion will result if you use the object passed as the first 181 * argument to the MethodInterceptor (usually not what you want) 182 * @param args the arguments passed to the intercepted method; you may substitute a different 183 * argument array as long as the types are compatible 184 * @see MethodInterceptor#intercept 185 * @throws Throwable the bare exceptions thrown by the called method are passed through 186 * without wrapping in an <code>InvocationTargetException</code> 187 */ 188 public Object invoke(Object obj, Object[] args) throws Throwable { 189 try { 190 init(); 191 FastClassInfo fci = fastClassInfo; 192 return fci.f1.invoke(fci.i1, obj, args); 193 } catch (InvocationTargetException e) { 194 throw e.getTargetException(); 195 } catch (IllegalArgumentException e) { 196 if (fastClassInfo.i1 < 0) 197 throw new IllegalArgumentException("Protected method: " + sig1); 198 throw e; 199 } 200 } 201 202 /** 203 * Invoke the original (super) method on the specified object. 204 * @param obj the enhanced object, must be the object passed as the first 205 * argument to the MethodInterceptor 206 * @param args the arguments passed to the intercepted method; you may substitute a different 207 * argument array as long as the types are compatible 208 * @see MethodInterceptor#intercept 209 * @throws Throwable the bare exceptions thrown by the called method are passed through 210 * without wrapping in an <code>InvocationTargetException</code> 211 */ 212 public Object invokeSuper(Object obj, Object[] args) throws Throwable { 213 try { 214 init(); 215 FastClassInfo fci = fastClassInfo; 216 return fci.f2.invoke(fci.i2, obj, args); 217 } catch (InvocationTargetException e) { 218 throw e.getTargetException(); 219 } 220 } 221 } 222