1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.proxy2.javassist;
19
20 import java.lang.reflect.Method;
21
22 import javassist.CannotCompileException;
23 import javassist.CtClass;
24 import javassist.CtConstructor;
25 import javassist.CtMethod;
26
27 import org.apache.commons.proxy2.Interceptor;
28 import org.apache.commons.proxy2.Invoker;
29 import org.apache.commons.proxy2.ObjectProvider;
30 import org.apache.commons.proxy2.ProxyUtils;
31 import org.apache.commons.proxy2.exception.ProxyFactoryException;
32 import org.apache.commons.proxy2.impl.AbstractProxyClassGenerator;
33 import org.apache.commons.proxy2.impl.AbstractSubclassingProxyFactory;
34 import org.apache.commons.proxy2.impl.ProxyClassCache;
35
36 public class JavassistProxyFactory extends AbstractSubclassingProxyFactory
37 {
38
39
40
41
42 private static final String GET_METHOD_METHOD_NAME = "_javassistGetMethod";
43
44 private static final ProxyClassCache DELEGATING_PROXY_CACHE = new ProxyClassCache(
45 new DelegatingProxyClassGenerator());
46 private static final ProxyClassCache INTERCEPTOR_PROXY_CACHE = new ProxyClassCache(
47 new InterceptorProxyClassGenerator());
48 private static final ProxyClassCache INVOKER_PROXY_CACHE = new ProxyClassCache(new InvokerProxyClassGenerator());
49
50
51
52
53
54 private static void addGetMethodMethod(CtClass proxyClass) throws CannotCompileException
55 {
56 final CtMethod method = new CtMethod(JavassistUtils.resolve(Method.class), GET_METHOD_METHOD_NAME,
57 JavassistUtils.resolve(new Class[] { String.class, String.class, Class[].class }), proxyClass);
58 final String body = "try { return Class.forName($1).getMethod($2, $3); } catch( Exception e ) "
59 + "{ throw new RuntimeException(\"Unable to look up method.\", e); }";
60 method.setBody(body);
61 proxyClass.addMethod(method);
62 }
63
64
65
66
67
68
69
70
71 @Override
72 public <T> T createDelegatorProxy(ClassLoader classLoader, ObjectProvider<?> targetProvider,
73 Class<?>... proxyClasses)
74 {
75 try
76 {
77 @SuppressWarnings("unchecked")
78 final Class<? extends T> clazz = (Class<? extends T>) DELEGATING_PROXY_CACHE.getProxyClass(classLoader,
79 proxyClasses);
80 return clazz.getConstructor(ObjectProvider.class).newInstance(targetProvider);
81 }
82 catch (Exception e)
83 {
84 throw new ProxyFactoryException("Unable to instantiate proxy2 from generated proxy2 class.", e);
85 }
86 }
87
88
89
90
91 @Override
92 public <T> T createInterceptorProxy(ClassLoader classLoader, Object target, Interceptor interceptor,
93 Class<?>... proxyClasses)
94 {
95 try
96 {
97 @SuppressWarnings("unchecked")
98 final Class<? extends T> clazz = (Class<? extends T>) INTERCEPTOR_PROXY_CACHE.getProxyClass(classLoader,
99 proxyClasses);
100 return clazz.getConstructor(Object.class, Interceptor.class).newInstance(target, interceptor);
101 }
102 catch (Exception e)
103 {
104 throw new ProxyFactoryException("Unable to instantiate proxy2 class instance.", e);
105 }
106 }
107
108
109
110
111 @Override
112 public <T> T createInvokerProxy(ClassLoader classLoader, Invoker invoker, Class<?>... proxyClasses)
113 {
114 try
115 {
116 @SuppressWarnings("unchecked")
117 final Class<? extends T> clazz = (Class<? extends T>) INVOKER_PROXY_CACHE.getProxyClass(classLoader,
118 proxyClasses);
119 return clazz.getConstructor(Invoker.class).newInstance(invoker);
120 }
121 catch (Exception e)
122 {
123 throw new ProxyFactoryException("Unable to instantiate proxy2 from generated proxy2 class.", e);
124 }
125 }
126
127
128
129
130
131 private static class DelegatingProxyClassGenerator extends AbstractProxyClassGenerator
132 {
133 @Override
134 public Class<?> generateProxyClass(ClassLoader classLoader, Class<?>... proxyClasses)
135 {
136 try
137 {
138 final CtClass proxyClass = JavassistUtils.createClass(getSuperclass(proxyClasses));
139 JavassistUtils.addField(ObjectProvider.class, "provider", proxyClass);
140 final CtConstructor proxyConstructor = new CtConstructor(
141 JavassistUtils.resolve(new Class[] { ObjectProvider.class }), proxyClass);
142 proxyConstructor.setBody("{ this.provider = $1; }");
143 proxyClass.addConstructor(proxyConstructor);
144 JavassistUtils.addInterfaces(proxyClass, toInterfaces(proxyClasses));
145 addHashCodeMethod(proxyClass);
146 addEqualsMethod(proxyClass);
147 final Method[] methods = getImplementationMethods(proxyClasses);
148 for (int i = 0; i < methods.length; ++i)
149 {
150 if (!ProxyUtils.isEqualsMethod(methods[i]) && !ProxyUtils.isHashCode(methods[i]))
151 {
152 final Method method = methods[i];
153 final CtMethod ctMethod = new CtMethod(JavassistUtils.resolve(method.getReturnType()),
154 method.getName(), JavassistUtils.resolve(method.getParameterTypes()), proxyClass);
155 final String body = "{ return ( $r ) ( ( " + method.getDeclaringClass().getName()
156 + " )provider.getObject() )." + method.getName() + "($$); }";
157 ctMethod.setBody(body);
158 proxyClass.addMethod(ctMethod);
159 }
160 }
161 return proxyClass.toClass(classLoader, null);
162 }
163 catch (CannotCompileException e)
164 {
165 throw new ProxyFactoryException("Could not compile class.", e);
166 }
167 }
168 }
169
170 private static class InterceptorProxyClassGenerator extends AbstractProxyClassGenerator
171 {
172 @Override
173 public Class<?> generateProxyClass(ClassLoader classLoader, Class<?>... proxyClasses)
174 {
175 try
176 {
177 final CtClass proxyClass = JavassistUtils.createClass(getSuperclass(proxyClasses));
178 final Method[] methods = getImplementationMethods(proxyClasses);
179 JavassistUtils.addInterfaces(proxyClass, toInterfaces(proxyClasses));
180 JavassistUtils.addField(Object.class, "target", proxyClass);
181 JavassistUtils.addField(Interceptor.class, "interceptor", proxyClass);
182 addGetMethodMethod(proxyClass);
183 addHashCodeMethod(proxyClass);
184 addEqualsMethod(proxyClass);
185 final CtConstructor proxyConstructor = new CtConstructor(JavassistUtils.resolve(new Class[] {
186 Object.class, Interceptor.class }), proxyClass);
187 proxyConstructor.setBody("{\n\tthis.target = $1;\n\tthis.interceptor = $2; }");
188 proxyClass.addConstructor(proxyConstructor);
189 for (int i = 0; i < methods.length; ++i)
190 {
191 if (!ProxyUtils.isEqualsMethod(methods[i]) && !ProxyUtils.isHashCode(methods[i]))
192 {
193 final CtMethod method = new CtMethod(JavassistUtils.resolve(methods[i].getReturnType()),
194 methods[i].getName(), JavassistUtils.resolve(methods[i].getParameterTypes()),
195 proxyClass);
196 final Class<?> invocationClass = JavassistInvocation.getMethodInvocationClass(classLoader,
197 methods[i]);
198
199 final String body = "{\n\t return ( $r ) interceptor.intercept( new "
200 + invocationClass.getName() + "( this, target, " + GET_METHOD_METHOD_NAME + "(\""
201 + methods[i].getDeclaringClass().getName() + "\", \"" + methods[i].getName()
202 + "\", $sig), $args ) );\n }";
203 method.setBody(body);
204 proxyClass.addMethod(method);
205 }
206
207 }
208 return proxyClass.toClass(classLoader, null);
209 }
210 catch (CannotCompileException e)
211 {
212 throw new ProxyFactoryException("Could not compile class.", e);
213 }
214 }
215
216 }
217
218 private static void addEqualsMethod(CtClass proxyClass) throws CannotCompileException
219 {
220 final CtMethod equalsMethod = new CtMethod(JavassistUtils.resolve(Boolean.TYPE), "equals",
221 JavassistUtils.resolve(new Class[] { Object.class }), proxyClass);
222 final String body = "{\n\treturn this == $1;\n}";
223 equalsMethod.setBody(body);
224 proxyClass.addMethod(equalsMethod);
225 }
226
227 private static void addHashCodeMethod(CtClass proxyClass) throws CannotCompileException
228 {
229 final CtMethod hashCodeMethod = new CtMethod(JavassistUtils.resolve(Integer.TYPE), "hashCode", new CtClass[0],
230 proxyClass);
231 hashCodeMethod.setBody("{\n\treturn System.identityHashCode(this);\n}");
232 proxyClass.addMethod(hashCodeMethod);
233 }
234
235 private static class InvokerProxyClassGenerator extends AbstractProxyClassGenerator
236 {
237 @Override
238 public Class<?> generateProxyClass(ClassLoader classLoader, Class<?>... proxyClasses)
239 {
240 try
241 {
242 final CtClass proxyClass = JavassistUtils.createClass(getSuperclass(proxyClasses));
243 final Method[] methods = getImplementationMethods(proxyClasses);
244 JavassistUtils.addInterfaces(proxyClass, toInterfaces(proxyClasses));
245 JavassistUtils.addField(Invoker.class, "invoker", proxyClass);
246 final CtConstructor proxyConstructor = new CtConstructor(
247 JavassistUtils.resolve(new Class[] { Invoker.class }), proxyClass);
248 proxyConstructor.setBody("{\n\tthis.invoker = $1; }");
249 proxyClass.addConstructor(proxyConstructor);
250 addGetMethodMethod(proxyClass);
251 addHashCodeMethod(proxyClass);
252 addEqualsMethod(proxyClass);
253 for (int i = 0; i < methods.length; ++i)
254 {
255 if (!ProxyUtils.isEqualsMethod(methods[i]) && !ProxyUtils.isHashCode(methods[i]))
256 {
257 final CtMethod method = new CtMethod(JavassistUtils.resolve(methods[i].getReturnType()),
258 methods[i].getName(), JavassistUtils.resolve(methods[i].getParameterTypes()),
259 proxyClass);
260 final String body = "{\n\t return ( $r ) invoker.invoke( this, " + GET_METHOD_METHOD_NAME
261 + "(\"" + methods[i].getDeclaringClass().getName() + "\", \"" + methods[i].getName()
262 + "\", $sig), $args );\n }";
263 method.setBody(body);
264 proxyClass.addMethod(method);
265 }
266 }
267 return proxyClass.toClass(classLoader, null);
268 }
269 catch (CannotCompileException e)
270 {
271 throw new ProxyFactoryException("Could not compile class.", e);
272 }
273 }
274 }
275 }