001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.commons.proxy.factory.javassist;
019
020 import javassist.CannotCompileException;
021 import javassist.CtClass;
022 import javassist.CtConstructor;
023 import javassist.CtMethod;
024 import org.apache.commons.proxy.Interceptor;
025 import org.apache.commons.proxy.Invoker;
026 import org.apache.commons.proxy.ObjectProvider;
027 import org.apache.commons.proxy.exception.ProxyFactoryException;
028 import org.apache.commons.proxy.factory.util.AbstractProxyClassGenerator;
029 import org.apache.commons.proxy.factory.util.AbstractSubclassingProxyFactory;
030 import org.apache.commons.proxy.factory.util.ProxyClassCache;
031
032 import java.lang.reflect.Method;
033
034 /**
035 * A <a href="http://www.jboss.org/products/javassist">Javassist</a>-based {@link org.apache.commons.proxy.ProxyFactory}
036 * implementation.
037 * <p/>
038 * <b>Dependencies</b>: <ul> <li>Javassist version 3.0 or greater</li> </ul> </p>
039 *
040 * @author James Carman
041 * @since 1.0
042 */
043 public class JavassistProxyFactory extends AbstractSubclassingProxyFactory
044 {
045 //**********************************************************************************************************************
046 // Fields
047 //**********************************************************************************************************************
048
049 private static final String GET_METHOD_METHOD_NAME = "_javassistGetMethod";
050
051 private static final ProxyClassCache delegatingProxyClassCache = new ProxyClassCache(
052 new DelegatingProxyClassGenerator());
053 private static final ProxyClassCache interceptorProxyClassCache = new ProxyClassCache(
054 new InterceptorProxyClassGenerator());
055 private static final ProxyClassCache invocationHandlerProxyClassCache = new ProxyClassCache(
056 new InvokerProxyClassGenerator());
057
058 //**********************************************************************************************************************
059 // Static Methods
060 //**********************************************************************************************************************
061
062 private static void addGetMethodMethod(CtClass proxyClass) throws CannotCompileException
063 {
064 final CtMethod method = new CtMethod(JavassistUtils.resolve(Method.class), GET_METHOD_METHOD_NAME,
065 JavassistUtils.resolve(new Class[]{String.class, String.class, Class[].class}), proxyClass);
066 final String body = "try { return Class.forName($1).getMethod($2, $3); } catch( Exception e ) " +
067 "{ throw new RuntimeException(\"Unable to look up method.\", e); }";
068 method.setBody(body);
069 proxyClass.addMethod(method);
070 }
071
072 //**********************************************************************************************************************
073 // Other Methods
074 //**********************************************************************************************************************
075
076 public Object createDelegatorProxy(ClassLoader classLoader, ObjectProvider targetProvider,
077 Class[] proxyClasses)
078 {
079 try
080 {
081 final Class clazz = delegatingProxyClassCache.getProxyClass(classLoader, proxyClasses);
082 return clazz.getConstructor(new Class[]{ObjectProvider.class})
083 .newInstance(new Object[]{targetProvider});
084 }
085 catch (Exception e)
086 {
087 throw new ProxyFactoryException("Unable to instantiate proxy from generated proxy class.", e);
088 }
089 }
090
091 public Object createInterceptorProxy(ClassLoader classLoader, Object target, Interceptor interceptor,
092 Class[] proxyClasses)
093 {
094 try
095 {
096 final Class clazz = interceptorProxyClassCache.getProxyClass(classLoader, proxyClasses);
097 return clazz.getConstructor(new Class[]{Object.class, Interceptor.class})
098 .newInstance(new Object[]{target, interceptor});
099 }
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