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