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.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      // Fields
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      // Static Methods
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      // ProxyFactory Implementation
66      //******************************************************************************************************************
67  
68      /**
69       * {@inheritDoc}
70       */
71      @Override
72      public <T> T createDelegatorProxy(ClassLoader classLoader, ObjectProvider<?> targetProvider,
73              Class<?>... proxyClasses)
74      {
75          try
76          {
77              @SuppressWarnings("unchecked") // type inference
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       * {@inheritDoc}
90       */
91      @Override
92      public <T> T createInterceptorProxy(ClassLoader classLoader, Object target, Interceptor interceptor,
93              Class<?>... proxyClasses)
94      {
95          try
96          {
97              @SuppressWarnings("unchecked") // type inference
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      * {@inheritDoc}
110      */
111     @Override
112     public <T> T createInvokerProxy(ClassLoader classLoader, Invoker invoker, Class<?>... proxyClasses)
113     {
114         try
115         {
116             @SuppressWarnings("unchecked") // type inference
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     // Inner Classes
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 }