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      private static final ProxyClassCache delegatingProxyClassCache = new ProxyClassCache(
49              new DelegatingProxyClassGenerator() );
50      private static final ProxyClassCache interceptorProxyClassCache = new ProxyClassCache(
51              new InterceptorProxyClassGenerator() );
52      private static final ProxyClassCache invocationHandlerProxyClassCache = new ProxyClassCache(
53              new InvokerProxyClassGenerator() );
54  
55  //----------------------------------------------------------------------------------------------------------------------
56  // ProxyFactory Implementation
57  //----------------------------------------------------------------------------------------------------------------------
58  
59      public Object createDelegatorProxy( ClassLoader classLoader, ObjectProvider targetProvider,
60                                          Class[] proxyClasses )
61      {
62          try
63          {
64              final Class clazz = delegatingProxyClassCache.getProxyClass( classLoader, proxyClasses );
65              return clazz.getConstructor( new Class[]{ ObjectProvider.class } )
66                      .newInstance( new Object[]{ targetProvider } );
67          }
68          catch( Exception e )
69          {
70              throw new ProxyFactoryException( "Unable to instantiate proxy from generated proxy class.", e );
71          }
72      }
73  
74      public Object createInterceptorProxy( ClassLoader classLoader, Object target, Interceptor interceptor,
75                                            Class[] proxyClasses )
76      {
77          try
78          {
79              final Class clazz = interceptorProxyClassCache.getProxyClass( classLoader, proxyClasses );
80              final Method[] methods = AbstractProxyClassGenerator.getImplementationMethods( proxyClasses );
81              return clazz.getConstructor( new Class[]{ Method[].class, Object.class, Interceptor.class } )
82                      .newInstance( new Object[]{ methods, target, interceptor } );
83          }
84          catch( Exception e )
85          {
86              throw new ProxyFactoryException( "Unable to instantiate proxy class instance.", e );
87          }
88      }
89  
90      public Object createInvokerProxy( ClassLoader classLoader, Invoker invoker,
91                                        Class[] proxyClasses )
92      {
93          try
94          {
95              final Class clazz = invocationHandlerProxyClassCache.getProxyClass( classLoader, proxyClasses );
96              final Method[] methods = AbstractProxyClassGenerator.getImplementationMethods( proxyClasses );
97              return clazz.getConstructor( new Class[]{ Method[].class, Invoker.class } )
98                      .newInstance( new Object[]{ methods, invoker } );
99          }
100         catch( Exception e )
101         {
102             throw new ProxyFactoryException( "Unable to instantiate proxy from generated proxy class.", e );
103         }
104     }
105 
106 //----------------------------------------------------------------------------------------------------------------------
107 // Inner Classes
108 //----------------------------------------------------------------------------------------------------------------------
109 
110     private static class InvokerProxyClassGenerator extends AbstractProxyClassGenerator
111     {
112         public Class generateProxyClass( ClassLoader classLoader, Class[] proxyClasses )
113         {
114             try
115             {
116                 final CtClass proxyClass = JavassistUtils.createClass( getSuperclass( proxyClasses ) );
117                 final Method[] methods = getImplementationMethods( proxyClasses );
118                 JavassistUtils.addInterfaces( proxyClass, toInterfaces( proxyClasses ) );
119                 JavassistUtils.addField( Method[].class, "methods", proxyClass );
120                 JavassistUtils.addField( Invoker.class, "invoker", proxyClass );
121                 final CtConstructor proxyConstructor = new CtConstructor(
122                         JavassistUtils.resolve(
123                                 new Class[]{ Method[].class, Invoker.class } ),
124                         proxyClass );
125                 proxyConstructor
126                         .setBody( "{\n\tthis.methods = $1;\n\tthis.invoker = $2; }" );
127                 proxyClass.addConstructor( proxyConstructor );
128                 for( int i = 0; i < methods.length; ++i )
129                 {
130                     final CtMethod method = new CtMethod( JavassistUtils.resolve( methods[i].getReturnType() ),
131                                                           methods[i].getName(),
132                                                           JavassistUtils.resolve( methods[i].getParameterTypes() ),
133                                                           proxyClass );
134                     final String body = "{\n\t return ( $r ) invoker.invoke( this, methods[" + i +
135                                         "], $args );\n }";
136                     method.setBody( body );
137                     proxyClass.addMethod( method );
138                 }
139                 return proxyClass.toClass( classLoader );
140             }
141             catch( CannotCompileException e )
142             {
143                 throw new ProxyFactoryException( "Could not compile class.", e );
144             }
145         }
146     }
147 
148     private static class InterceptorProxyClassGenerator extends AbstractProxyClassGenerator
149     {
150         public Class generateProxyClass( ClassLoader classLoader, Class[] proxyClasses )
151         {
152             try
153             {
154                 final CtClass proxyClass = JavassistUtils.createClass( getSuperclass( proxyClasses ) );
155                 final Method[] methods = getImplementationMethods( proxyClasses );
156                 JavassistUtils.addInterfaces( proxyClass, toInterfaces( proxyClasses ) );
157                 JavassistUtils.addField( Method[].class, "methods", proxyClass );
158                 JavassistUtils.addField( Object.class, "target", proxyClass );
159                 JavassistUtils.addField( Interceptor.class, "interceptor", proxyClass );
160                 final CtConstructor proxyConstructor = new CtConstructor(
161                         JavassistUtils.resolve(
162                                 new Class[]{ Method[].class, Object.class, Interceptor.class } ),
163                         proxyClass );
164                 proxyConstructor
165                         .setBody(
166                                 "{\n\tthis.methods = $1;\n\tthis.target = $2;\n\tthis.interceptor = $3; }" );
167                 proxyClass.addConstructor( proxyConstructor );
168                 for( int i = 0; i < methods.length; ++i )
169                 {
170                     final CtMethod method = new CtMethod( JavassistUtils.resolve( methods[i].getReturnType() ),
171                                                           methods[i].getName(),
172                                                           JavassistUtils.resolve( methods[i].getParameterTypes() ),
173                                                           proxyClass );
174                     final Class invocationClass = JavassistInvocation
175                             .getMethodInvocationClass( classLoader, methods[i] );
176                     final String body = "{\n\t return ( $r ) interceptor.intercept( new " + invocationClass.getName() +
177                                         "( methods[" + i + "], target, $args ) );\n }";
178                     method.setBody( body );
179                     proxyClass.addMethod( method );
180 
181                 }
182                 return proxyClass.toClass( classLoader );
183             }
184             catch( CannotCompileException e )
185             {
186                 throw new ProxyFactoryException( "Could not compile class.", e );
187             }
188         }
189     }
190 
191     private static class DelegatingProxyClassGenerator extends AbstractProxyClassGenerator
192     {
193         public Class generateProxyClass( ClassLoader classLoader, Class[] proxyClasses )
194         {
195             try
196             {
197                 final CtClass proxyClass = JavassistUtils.createClass( getSuperclass( proxyClasses ) );
198                 JavassistUtils.addField( ObjectProvider.class, "provider", proxyClass );
199                 final CtConstructor proxyConstructor = new CtConstructor(
200                         JavassistUtils.resolve( new Class[]{ ObjectProvider.class } ),
201                         proxyClass );
202                 proxyConstructor.setBody( "{ this.provider = $1; }" );
203                 proxyClass.addConstructor( proxyConstructor );
204                 JavassistUtils.addInterfaces( proxyClass, toInterfaces( proxyClasses ) );
205                 final Method[] methods = getImplementationMethods( proxyClasses );
206                 for( int i = 0; i < methods.length; ++i )
207                 {
208                     final Method method = methods[i];
209                     final CtMethod ctMethod = new CtMethod( JavassistUtils.resolve( method.getReturnType() ),
210                                                             method.getName(),
211                                                             JavassistUtils.resolve( method.getParameterTypes() ),
212                                                             proxyClass );
213                     final String body = "{ return ( $r ) ( ( " + method.getDeclaringClass().getName() +
214                                         " )provider.getObject() )." +
215                                         method.getName() + "($$); }";
216                     ctMethod.setBody( body );
217                     proxyClass.addMethod( ctMethod );
218 
219                 }
220                 return proxyClass.toClass( classLoader );
221             }
222             catch( CannotCompileException e )
223             {
224                 throw new ProxyFactoryException( "Could not compile class.", e );
225             }
226         }
227     }
228 }
229