| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.commons.proxy.factory.javassist; |
| |
| import javassist.CannotCompileException; |
| import javassist.CtClass; |
| import javassist.CtConstructor; |
| import javassist.CtMethod; |
| import org.apache.commons.proxy.Interceptor; |
| import org.apache.commons.proxy.Invoker; |
| import org.apache.commons.proxy.ObjectProvider; |
| import org.apache.commons.proxy.exception.ProxyFactoryException; |
| import org.apache.commons.proxy.factory.util.AbstractProxyClassGenerator; |
| import org.apache.commons.proxy.factory.util.AbstractSubclassingProxyFactory; |
| import org.apache.commons.proxy.factory.util.ProxyClassCache; |
| |
| import java.lang.reflect.Method; |
| |
| /** |
| * A <a href="http://www.jboss.org/products/javassist">Javassist</a>-based {@link org.apache.commons.proxy.ProxyFactory} |
| * implementation. |
| * <p/> |
| * <b>Dependencies</b>: <ul> <li>Javassist version 3.0 or greater</li> </ul> </p> |
| * |
| * @author James Carman |
| * @since 1.0 |
| */ |
| public class JavassistProxyFactory extends AbstractSubclassingProxyFactory |
| { |
| //---------------------------------------------------------------------------------------------------------------------- |
| // Fields |
| //---------------------------------------------------------------------------------------------------------------------- |
| private static final ProxyClassCache delegatingProxyClassCache = new ProxyClassCache( |
| new DelegatingProxyClassGenerator() ); |
| private static final ProxyClassCache interceptorProxyClassCache = new ProxyClassCache( |
| new InterceptorProxyClassGenerator() ); |
| private static final ProxyClassCache invocationHandlerProxyClassCache = new ProxyClassCache( |
| new InvokerProxyClassGenerator() ); |
| |
| //---------------------------------------------------------------------------------------------------------------------- |
| // ProxyFactory Implementation |
| //---------------------------------------------------------------------------------------------------------------------- |
| |
| public Object createDelegatorProxy( ClassLoader classLoader, ObjectProvider targetProvider, |
| Class[] proxyClasses ) |
| { |
| try |
| { |
| final Class clazz = delegatingProxyClassCache.getProxyClass( classLoader, proxyClasses ); |
| return clazz.getConstructor( new Class[]{ ObjectProvider.class } ) |
| .newInstance( new Object[]{ targetProvider } ); |
| } |
| catch( Exception e ) |
| { |
| throw new ProxyFactoryException( "Unable to instantiate proxy from generated proxy class.", e ); |
| } |
| } |
| |
| public Object createInterceptorProxy( ClassLoader classLoader, Object target, Interceptor interceptor, |
| Class[] proxyClasses ) |
| { |
| try |
| { |
| final Class clazz = interceptorProxyClassCache.getProxyClass( classLoader, proxyClasses ); |
| final Method[] methods = AbstractProxyClassGenerator.getImplementationMethods( proxyClasses ); |
| return clazz.getConstructor( new Class[]{ Method[].class, Object.class, Interceptor.class } ) |
| .newInstance( new Object[]{ methods, target, interceptor } ); |
| } |
| catch( Exception e ) |
| { |
| throw new ProxyFactoryException( "Unable to instantiate proxy class instance.", e ); |
| } |
| } |
| |
| public Object createInvokerProxy( ClassLoader classLoader, Invoker invoker, |
| Class[] proxyClasses ) |
| { |
| try |
| { |
| final Class clazz = invocationHandlerProxyClassCache.getProxyClass( classLoader, proxyClasses ); |
| final Method[] methods = AbstractProxyClassGenerator.getImplementationMethods( proxyClasses ); |
| return clazz.getConstructor( new Class[]{ Method[].class, Invoker.class } ) |
| .newInstance( new Object[]{ methods, invoker } ); |
| } |
| catch( Exception e ) |
| { |
| throw new ProxyFactoryException( "Unable to instantiate proxy from generated proxy class.", e ); |
| } |
| } |
| |
| //---------------------------------------------------------------------------------------------------------------------- |
| // Inner Classes |
| //---------------------------------------------------------------------------------------------------------------------- |
| |
| private static class InvokerProxyClassGenerator extends AbstractProxyClassGenerator |
| { |
| public Class generateProxyClass( ClassLoader classLoader, Class[] proxyClasses ) |
| { |
| try |
| { |
| final CtClass proxyClass = JavassistUtils.createClass( getSuperclass( proxyClasses ) ); |
| final Method[] methods = getImplementationMethods( proxyClasses ); |
| JavassistUtils.addInterfaces( proxyClass, toInterfaces( proxyClasses ) ); |
| JavassistUtils.addField( Method[].class, "methods", proxyClass ); |
| JavassistUtils.addField( Invoker.class, "invoker", proxyClass ); |
| final CtConstructor proxyConstructor = new CtConstructor( |
| JavassistUtils.resolve( |
| new Class[]{ Method[].class, Invoker.class } ), |
| proxyClass ); |
| proxyConstructor |
| .setBody( "{\n\tthis.methods = $1;\n\tthis.invoker = $2; }" ); |
| proxyClass.addConstructor( proxyConstructor ); |
| for( int i = 0; i < methods.length; ++i ) |
| { |
| final CtMethod method = new CtMethod( JavassistUtils.resolve( methods[i].getReturnType() ), |
| methods[i].getName(), |
| JavassistUtils.resolve( methods[i].getParameterTypes() ), |
| proxyClass ); |
| final String body = "{\n\t return ( $r ) invoker.invoke( this, methods[" + i + |
| "], $args );\n }"; |
| method.setBody( body ); |
| proxyClass.addMethod( method ); |
| } |
| return proxyClass.toClass( classLoader ); |
| } |
| catch( CannotCompileException e ) |
| { |
| throw new ProxyFactoryException( "Could not compile class.", e ); |
| } |
| } |
| } |
| |
| private static class InterceptorProxyClassGenerator extends AbstractProxyClassGenerator |
| { |
| public Class generateProxyClass( ClassLoader classLoader, Class[] proxyClasses ) |
| { |
| try |
| { |
| final CtClass proxyClass = JavassistUtils.createClass( getSuperclass( proxyClasses ) ); |
| final Method[] methods = getImplementationMethods( proxyClasses ); |
| JavassistUtils.addInterfaces( proxyClass, toInterfaces( proxyClasses ) ); |
| JavassistUtils.addField( Method[].class, "methods", proxyClass ); |
| JavassistUtils.addField( Object.class, "target", proxyClass ); |
| JavassistUtils.addField( Interceptor.class, "interceptor", proxyClass ); |
| final CtConstructor proxyConstructor = new CtConstructor( |
| JavassistUtils.resolve( |
| new Class[]{ Method[].class, Object.class, Interceptor.class } ), |
| proxyClass ); |
| proxyConstructor |
| .setBody( |
| "{\n\tthis.methods = $1;\n\tthis.target = $2;\n\tthis.interceptor = $3; }" ); |
| proxyClass.addConstructor( proxyConstructor ); |
| for( int i = 0; i < methods.length; ++i ) |
| { |
| final CtMethod method = new CtMethod( JavassistUtils.resolve( methods[i].getReturnType() ), |
| methods[i].getName(), |
| JavassistUtils.resolve( methods[i].getParameterTypes() ), |
| proxyClass ); |
| final Class invocationClass = JavassistInvocation |
| .getMethodInvocationClass( classLoader, methods[i] ); |
| final String body = "{\n\t return ( $r ) interceptor.intercept( new " + invocationClass.getName() + |
| "( methods[" + i + "], target, $args ) );\n }"; |
| method.setBody( body ); |
| proxyClass.addMethod( method ); |
| |
| } |
| return proxyClass.toClass( classLoader ); |
| } |
| catch( CannotCompileException e ) |
| { |
| throw new ProxyFactoryException( "Could not compile class.", e ); |
| } |
| } |
| } |
| |
| private static class DelegatingProxyClassGenerator extends AbstractProxyClassGenerator |
| { |
| public Class generateProxyClass( ClassLoader classLoader, Class[] proxyClasses ) |
| { |
| try |
| { |
| final CtClass proxyClass = JavassistUtils.createClass( getSuperclass( proxyClasses ) ); |
| JavassistUtils.addField( ObjectProvider.class, "provider", proxyClass ); |
| final CtConstructor proxyConstructor = new CtConstructor( |
| JavassistUtils.resolve( new Class[]{ ObjectProvider.class } ), |
| proxyClass ); |
| proxyConstructor.setBody( "{ this.provider = $1; }" ); |
| proxyClass.addConstructor( proxyConstructor ); |
| JavassistUtils.addInterfaces( proxyClass, toInterfaces( proxyClasses ) ); |
| final Method[] methods = getImplementationMethods( proxyClasses ); |
| for( int i = 0; i < methods.length; ++i ) |
| { |
| final Method method = methods[i]; |
| final CtMethod ctMethod = new CtMethod( JavassistUtils.resolve( method.getReturnType() ), |
| method.getName(), |
| JavassistUtils.resolve( method.getParameterTypes() ), |
| proxyClass ); |
| final String body = "{ return ( $r ) ( ( " + method.getDeclaringClass().getName() + |
| " )provider.getObject() )." + |
| method.getName() + "($$); }"; |
| ctMethod.setBody( body ); |
| proxyClass.addMethod( ctMethod ); |
| |
| } |
| return proxyClass.toClass( classLoader ); |
| } |
| catch( CannotCompileException e ) |
| { |
| throw new ProxyFactoryException( "Could not compile class.", e ); |
| } |
| } |
| } |
| } |
| |