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