001/* 002 * Copyright 2003,2004 The Apache Software Foundation 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.cglib.proxy; 018 019import java.lang.reflect.InvocationTargetException; 020import java.lang.reflect.Method; 021 022import org.springframework.cglib.core.AbstractClassGenerator; 023import org.springframework.cglib.core.CodeGenerationException; 024import org.springframework.cglib.core.GeneratorStrategy; 025import org.springframework.cglib.core.NamingPolicy; 026import org.springframework.cglib.core.Signature; 027import org.springframework.cglib.reflect.FastClass; 028 029/** 030 * Classes generated by {@link Enhancer} pass this object to the 031 * registered {@link MethodInterceptor} objects when an intercepted method is invoked. It can 032 * be used to either invoke the original method, or call the same method on a different 033 * object of the same type. 034 * @version $Id: MethodProxy.java,v 1.16 2009/01/11 20:09:48 herbyderby Exp $ 035 */ 036@SuppressWarnings({"rawtypes", "unchecked"}) 037public class MethodProxy { 038 039 private Signature sig1; 040 041 private Signature sig2; 042 043 private CreateInfo createInfo; 044 045 private final Object initLock = new Object(); 046 047 private volatile FastClassInfo fastClassInfo; 048 049 /** 050 * For internal use by {@link Enhancer} only; see the {@link org.springframework.cglib.reflect.FastMethod} class 051 * for similar functionality. 052 */ 053 public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) { 054 MethodProxy proxy = new MethodProxy(); 055 proxy.sig1 = new Signature(name1, desc); 056 proxy.sig2 = new Signature(name2, desc); 057 proxy.createInfo = new CreateInfo(c1, c2); 058 return proxy; 059 } 060 061 private void init() { 062 /* 063 * Using a volatile invariant allows us to initialize the FastClass and 064 * method index pairs atomically. 065 * 066 * Double-checked locking is safe with volatile in Java 5. Before 1.5 this 067 * code could allow fastClassInfo to be instantiated more than once, which 068 * appears to be benign. 069 */ 070 if (fastClassInfo == null) { 071 synchronized (initLock) { 072 if (fastClassInfo == null) { 073 CreateInfo ci = createInfo; 074 075 FastClassInfo fci = new FastClassInfo(); 076 fci.f1 = helper(ci, ci.c1); 077 fci.f2 = helper(ci, ci.c2); 078 fci.i1 = fci.f1.getIndex(sig1); 079 fci.i2 = fci.f2.getIndex(sig2); 080 fastClassInfo = fci; 081 createInfo = null; 082 } 083 } 084 } 085 } 086 087 088 private static class FastClassInfo { 089 090 FastClass f1; 091 092 FastClass f2; 093 094 int i1; 095 096 int i2; 097 } 098 099 100 private static class CreateInfo { 101 102 Class c1; 103 104 Class c2; 105 106 NamingPolicy namingPolicy; 107 108 GeneratorStrategy strategy; 109 110 boolean attemptLoad; 111 112 public CreateInfo(Class c1, Class c2) { 113 this.c1 = c1; 114 this.c2 = c2; 115 AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent(); 116 if (fromEnhancer != null) { 117 namingPolicy = fromEnhancer.getNamingPolicy(); 118 strategy = fromEnhancer.getStrategy(); 119 attemptLoad = fromEnhancer.getAttemptLoad(); 120 } 121 } 122 } 123 124 125 private static FastClass helper(CreateInfo ci, Class type) { 126 FastClass.Generator g = new FastClass.Generator(); 127 g.setType(type); 128 // SPRING PATCH BEGIN 129 g.setContextClass(type); 130 // SPRING PATCH END 131 g.setClassLoader(ci.c2.getClassLoader()); 132 g.setNamingPolicy(ci.namingPolicy); 133 g.setStrategy(ci.strategy); 134 g.setAttemptLoad(ci.attemptLoad); 135 return g.create(); 136 } 137 138 private MethodProxy() { 139 } 140 141 /** 142 * Return the signature of the proxied method. 143 */ 144 public Signature getSignature() { 145 return sig1; 146 } 147 148 /** 149 * Return the name of the synthetic method created by CGLIB which is 150 * used by {@link #invokeSuper} to invoke the superclass 151 * (non-intercepted) method implementation. The parameter types are 152 * the same as the proxied method. 153 */ 154 public String getSuperName() { 155 return sig2.getName(); 156 } 157 158 /** 159 * Return the {@link org.springframework.cglib.reflect.FastClass} method index 160 * for the method used by {@link #invokeSuper}. This index uniquely 161 * identifies the method within the generated proxy, and therefore 162 * can be useful to reference external metadata. 163 * @see #getSuperName 164 */ 165 public int getSuperIndex() { 166 init(); 167 return fastClassInfo.i2; 168 } 169 170 // For testing 171 FastClass getFastClass() { 172 init(); 173 return fastClassInfo.f1; 174 } 175 176 // For testing 177 FastClass getSuperFastClass() { 178 init(); 179 return fastClassInfo.f2; 180 } 181 182 /** 183 * Return the <code>MethodProxy</code> used when intercepting the method 184 * matching the given signature. 185 * @param type the class generated by Enhancer 186 * @param sig the signature to match 187 * @return the MethodProxy instance, or null if no applicable matching method is found 188 * @throws IllegalArgumentException if the Class was not created by Enhancer or does not use a MethodInterceptor 189 */ 190 public static MethodProxy find(Class type, Signature sig) { 191 try { 192 Method m = type.getDeclaredMethod(MethodInterceptorGenerator.FIND_PROXY_NAME, 193 MethodInterceptorGenerator.FIND_PROXY_TYPES); 194 return (MethodProxy) m.invoke(null, new Object[]{sig}); 195 } 196 catch (NoSuchMethodException ex) { 197 throw new IllegalArgumentException("Class " + type + " does not use a MethodInterceptor"); 198 } 199 catch (IllegalAccessException | InvocationTargetException ex) { 200 throw new CodeGenerationException(ex); 201 } 202 } 203 204 /** 205 * Invoke the original method, on a different object of the same type. 206 * @param obj the compatible object; recursion will result if you use the object passed as the first 207 * argument to the MethodInterceptor (usually not what you want) 208 * @param args the arguments passed to the intercepted method; you may substitute a different 209 * argument array as long as the types are compatible 210 * @throws Throwable the bare exceptions thrown by the called method are passed through 211 * without wrapping in an <code>InvocationTargetException</code> 212 * @see MethodInterceptor#intercept 213 */ 214 public Object invoke(Object obj, Object[] args) throws Throwable { 215 try { 216 init(); 217 FastClassInfo fci = fastClassInfo; 218 return fci.f1.invoke(fci.i1, obj, args); 219 } 220 catch (InvocationTargetException ex) { 221 throw ex.getTargetException(); 222 } 223 catch (IllegalArgumentException ex) { 224 if (fastClassInfo.i1 < 0) 225 throw new IllegalArgumentException("Protected method: " + sig1); 226 throw ex; 227 } 228 } 229 230 /** 231 * Invoke the original (super) method on the specified object. 232 * @param obj the enhanced object, must be the object passed as the first 233 * argument to the MethodInterceptor 234 * @param args the arguments passed to the intercepted method; you may substitute a different 235 * argument array as long as the types are compatible 236 * @throws Throwable the bare exceptions thrown by the called method are passed through 237 * without wrapping in an <code>InvocationTargetException</code> 238 * @see MethodInterceptor#intercept 239 */ 240 public Object invokeSuper(Object obj, Object[] args) throws Throwable { 241 try { 242 init(); 243 FastClassInfo fci = fastClassInfo; 244 return fci.f2.invoke(fci.i2, obj, args); 245 } 246 catch (InvocationTargetException e) { 247 throw e.getTargetException(); 248 } 249 } 250 251}