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