1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.proxy.factory.javassist;
19
20 import javassist.CannotCompileException;
21 import javassist.CtClass;
22 import javassist.CtConstructor;
23 import javassist.CtMethod;
24 import org.apache.commons.proxy.Interceptor;
25 import org.apache.commons.proxy.Invoker;
26 import org.apache.commons.proxy.ObjectProvider;
27 import org.apache.commons.proxy.exception.ProxyFactoryException;
28 import org.apache.commons.proxy.factory.util.AbstractProxyClassGenerator;
29 import org.apache.commons.proxy.factory.util.AbstractSubclassingProxyFactory;
30 import org.apache.commons.proxy.factory.util.ProxyClassCache;
31
32 import java.lang.reflect.Method;
33
34 /**
35 * A <a href="http://www.jboss.org/products/javassist">Javassist</a>-based {@link org.apache.commons.proxy.ProxyFactory}
36 * implementation.
37 * <p/>
38 * <b>Dependencies</b>: <ul> <li>Javassist version 3.0 or greater</li> </ul> </p>
39 *
40 * @author James Carman
41 * @since 1.0
42 */
43 public class JavassistProxyFactory extends AbstractSubclassingProxyFactory
44 {
45 //**********************************************************************************************************************
46 // Fields
47 //**********************************************************************************************************************
48
49 private static final String GET_METHOD_METHOD_NAME = "_javassistGetMethod";
50
51 private static final ProxyClassCache delegatingProxyClassCache = new ProxyClassCache(
52 new DelegatingProxyClassGenerator());
53 private static final ProxyClassCache interceptorProxyClassCache = new ProxyClassCache(
54 new InterceptorProxyClassGenerator());
55 private static final ProxyClassCache invocationHandlerProxyClassCache = new ProxyClassCache(
56 new InvokerProxyClassGenerator());
57
58 //**********************************************************************************************************************
59 // Static Methods
60 //**********************************************************************************************************************
61
62 private static void addGetMethodMethod(CtClass proxyClass) throws CannotCompileException
63 {
64 final CtMethod method = new CtMethod(JavassistUtils.resolve(Method.class), GET_METHOD_METHOD_NAME,
65 JavassistUtils.resolve(new Class[]{String.class, String.class, Class[].class}), proxyClass);
66 final String body = "try { return Class.forName($1).getMethod($2, $3); } catch( Exception e ) " +
67 "{ throw new RuntimeException(\"Unable to look up method.\", e); }";
68 method.setBody(body);
69 proxyClass.addMethod(method);
70 }
71
72 //**********************************************************************************************************************
73 // Other Methods
74 //**********************************************************************************************************************
75
76 public Object createDelegatorProxy(ClassLoader classLoader, ObjectProvider targetProvider,
77 Class[] proxyClasses)
78 {
79 try
80 {
81 final Class clazz = delegatingProxyClassCache.getProxyClass(classLoader, proxyClasses);
82 return clazz.getConstructor(new Class[]{ObjectProvider.class})
83 .newInstance(new Object[]{targetProvider});
84 }
85 catch (Exception e)
86 {
87 throw new ProxyFactoryException("Unable to instantiate proxy from generated proxy class.", e);
88 }
89 }
90
91 public Object createInterceptorProxy(ClassLoader classLoader, Object target, Interceptor interceptor,
92 Class[] proxyClasses)
93 {
94 try
95 {
96 final Class clazz = interceptorProxyClassCache.getProxyClass(classLoader, proxyClasses);
97 return clazz.getConstructor(new Class[]{Object.class, Interceptor.class})
98 .newInstance(new Object[]{target, interceptor});
99 }
100 catch (Exception e)
101 {
102 throw new ProxyFactoryException("Unable to instantiate proxy class instance.", e);
103 }
104 }
105
106 public Object createInvokerProxy(ClassLoader classLoader, Invoker invoker,
107 Class[] proxyClasses)
108 {
109 try
110 {
111 final Class clazz = invocationHandlerProxyClassCache.getProxyClass(classLoader, proxyClasses);
112 return clazz.getConstructor(new Class[]{Invoker.class})
113 .newInstance(new Object[]{invoker});
114 }
115 catch (Exception e)
116 {
117 throw new ProxyFactoryException("Unable to instantiate proxy from generated proxy class.", e);
118 }
119 }
120
121 //**********************************************************************************************************************
122 // Inner Classes
123 //**********************************************************************************************************************
124
125 private static class DelegatingProxyClassGenerator extends AbstractProxyClassGenerator
126 {
127 public Class generateProxyClass(ClassLoader classLoader, Class[] proxyClasses)
128 {
129 try
130 {
131 final CtClass proxyClass = JavassistUtils.createClass(getSuperclass(proxyClasses));
132 JavassistUtils.addField(ObjectProvider.class, "provider", proxyClass);
133 final CtConstructor proxyConstructor = new CtConstructor(
134 JavassistUtils.resolve(new Class[]{ObjectProvider.class}),
135 proxyClass);
136 proxyConstructor.setBody("{ this.provider = $1; }");
137 proxyClass.addConstructor(proxyConstructor);
138 JavassistUtils.addInterfaces(proxyClass, toInterfaces(proxyClasses));
139 addHashCodeMethod(proxyClass);
140 addEqualsMethod(proxyClass);
141 final Method[] methods = getImplementationMethods(proxyClasses);
142 for (int i = 0; i < methods.length; ++i)
143 {
144 if (!isEqualsMethod(methods[i]) && !isHashCode(methods[i]))
145 {
146 final Method method = methods[i];
147 final CtMethod ctMethod = new CtMethod(JavassistUtils.resolve(method.getReturnType()),
148 method.getName(),
149 JavassistUtils.resolve(method.getParameterTypes()),
150 proxyClass);
151 final String body = "{ return ( $r ) ( ( " + method.getDeclaringClass().getName() +
152 " )provider.getObject() )." +
153 method.getName() + "($$); }";
154 ctMethod.setBody(body);
155 proxyClass.addMethod(ctMethod);
156 }
157 }
158 return proxyClass.toClass(classLoader);
159 }
160 catch (CannotCompileException e)
161 {
162 throw new ProxyFactoryException("Could not compile class.", e);
163 }
164 }
165 }
166
167 private static class InterceptorProxyClassGenerator extends AbstractProxyClassGenerator
168 {
169 public Class generateProxyClass(ClassLoader classLoader, Class[] proxyClasses)
170 {
171 try
172 {
173 final CtClass proxyClass = JavassistUtils.createClass(getSuperclass(proxyClasses));
174 final Method[] methods = getImplementationMethods(proxyClasses);
175 JavassistUtils.addInterfaces(proxyClass, toInterfaces(proxyClasses));
176 JavassistUtils.addField(Object.class, "target", proxyClass);
177 JavassistUtils.addField(Interceptor.class, "interceptor", proxyClass);
178 addGetMethodMethod(proxyClass);
179 addHashCodeMethod(proxyClass);
180 addEqualsMethod(proxyClass);
181 final CtConstructor proxyConstructor = new CtConstructor(
182 JavassistUtils.resolve(
183 new Class[]{Object.class, Interceptor.class}),
184 proxyClass);
185 proxyConstructor
186 .setBody(
187 "{\n\tthis.target = $1;\n\tthis.interceptor = $2; }");
188 proxyClass.addConstructor(proxyConstructor);
189 for (int i = 0; i < methods.length; ++i)
190 {
191 if (!isEqualsMethod(methods[i]) && !isHashCode(methods[i]))
192 {
193 final CtMethod method = new CtMethod(JavassistUtils.resolve(methods[i].getReturnType()),
194 methods[i].getName(),
195 JavassistUtils.resolve(methods[i].getParameterTypes()),
196 proxyClass);
197 final Class invocationClass = JavassistInvocation
198 .getMethodInvocationClass(classLoader, methods[i]);
199
200 final String body = "{\n\t return ( $r ) interceptor.intercept( new " + invocationClass.getName() +
201 "( " + GET_METHOD_METHOD_NAME + "(\"" + methods[i].getDeclaringClass().getName() +
202 "\", \"" + methods[i].getName() + "\", $sig), target, $args ) );\n }";
203 method.setBody(body);
204 proxyClass.addMethod(method);
205 }
206
207 }
208 return proxyClass.toClass(classLoader);
209 }
210 catch (CannotCompileException e)
211 {
212 throw new ProxyFactoryException("Could not compile class.", e);
213 }
214 }
215
216
217 }
218
219 private static void addEqualsMethod(CtClass proxyClass)
220 throws CannotCompileException
221 {
222 final CtMethod equalsMethod = new CtMethod(JavassistUtils.resolve(Boolean.TYPE), "equals",
223 JavassistUtils.resolve(new Class[]{Object.class}), proxyClass);
224 final String body = "{\n\treturn this == $1;\n}";
225 equalsMethod.setBody(body);
226 proxyClass.addMethod(equalsMethod);
227 }
228
229 private static void addHashCodeMethod(CtClass proxyClass)
230 throws CannotCompileException
231 {
232 final CtMethod hashCodeMethod = new CtMethod(JavassistUtils.resolve(Integer.TYPE), "hashCode",
233 new CtClass[0], proxyClass);
234 hashCodeMethod.setBody("{\n\treturn System.identityHashCode(this);\n}");
235 proxyClass.addMethod(hashCodeMethod);
236 }
237
238 private static class InvokerProxyClassGenerator extends AbstractProxyClassGenerator
239 {
240 public Class generateProxyClass(ClassLoader classLoader, Class[] proxyClasses)
241 {
242 try
243 {
244 final CtClass proxyClass = JavassistUtils.createClass(getSuperclass(proxyClasses));
245 final Method[] methods = getImplementationMethods(proxyClasses);
246 JavassistUtils.addInterfaces(proxyClass, toInterfaces(proxyClasses));
247 JavassistUtils.addField(Invoker.class, "invoker", proxyClass);
248 final CtConstructor proxyConstructor = new CtConstructor(
249 JavassistUtils.resolve(
250 new Class[]{Invoker.class}),
251 proxyClass);
252 proxyConstructor
253 .setBody("{\n\tthis.invoker = $1; }");
254 proxyClass.addConstructor(proxyConstructor);
255 addGetMethodMethod(proxyClass);
256 addHashCodeMethod(proxyClass);
257 addEqualsMethod(proxyClass);
258 for (int i = 0; i < methods.length; ++i)
259 {
260 if (!isEqualsMethod(methods[i]) && !isHashCode(methods[i]))
261 {
262 final CtMethod method = new CtMethod(JavassistUtils.resolve(methods[i].getReturnType()),
263 methods[i].getName(),
264 JavassistUtils.resolve(methods[i].getParameterTypes()),
265 proxyClass);
266 final String body = "{\n\t return ( $r ) invoker.invoke( this, " + GET_METHOD_METHOD_NAME + "(\"" +
267 methods[i].getDeclaringClass().getName() +
268 "\", \"" + methods[i].getName() + "\", $sig), $args );\n }";
269 method.setBody(body);
270 proxyClass.addMethod(method);
271 }
272 }
273 return proxyClass.toClass(classLoader);
274 }
275 catch (CannotCompileException e)
276 {
277 throw new ProxyFactoryException("Could not compile class.", e);
278 }
279 }
280 }
281 }
282