View Javadoc

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