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        private static final ProxyClassCache delegatingProxyClassCache = new ProxyClassCache(
049                new DelegatingProxyClassGenerator() );
050        private static final ProxyClassCache interceptorProxyClassCache = new ProxyClassCache(
051                new InterceptorProxyClassGenerator() );
052        private static final ProxyClassCache invocationHandlerProxyClassCache = new ProxyClassCache(
053                new InvokerProxyClassGenerator() );
054    
055    //----------------------------------------------------------------------------------------------------------------------
056    // ProxyFactory Implementation
057    //----------------------------------------------------------------------------------------------------------------------
058    
059        public Object createDelegatorProxy( ClassLoader classLoader, ObjectProvider targetProvider,
060                                            Class[] proxyClasses )
061        {
062            try
063            {
064                final Class clazz = delegatingProxyClassCache.getProxyClass( classLoader, proxyClasses );
065                return clazz.getConstructor( new Class[]{ ObjectProvider.class } )
066                        .newInstance( new Object[]{ targetProvider } );
067            }
068            catch( Exception e )
069            {
070                throw new ProxyFactoryException( "Unable to instantiate proxy from generated proxy class.", e );
071            }
072        }
073    
074        public Object createInterceptorProxy( ClassLoader classLoader, Object target, Interceptor interceptor,
075                                              Class[] proxyClasses )
076        {
077            try
078            {
079                final Class clazz = interceptorProxyClassCache.getProxyClass( classLoader, proxyClasses );
080                final Method[] methods = AbstractProxyClassGenerator.getImplementationMethods( proxyClasses );
081                return clazz.getConstructor( new Class[]{ Method[].class, Object.class, Interceptor.class } )
082                        .newInstance( new Object[]{ methods, target, interceptor } );
083            }
084            catch( Exception e )
085            {
086                throw new ProxyFactoryException( "Unable to instantiate proxy class instance.", e );
087            }
088        }
089    
090        public Object createInvokerProxy( ClassLoader classLoader, Invoker invoker,
091                                          Class[] proxyClasses )
092        {
093            try
094            {
095                final Class clazz = invocationHandlerProxyClassCache.getProxyClass( classLoader, proxyClasses );
096                final Method[] methods = AbstractProxyClassGenerator.getImplementationMethods( proxyClasses );
097                return clazz.getConstructor( new Class[]{ Method[].class, Invoker.class } )
098                        .newInstance( new Object[]{ methods, invoker } );
099            }
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