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 018package org.apache.commons.proxy2.javassist; 019 020import java.lang.reflect.Method; 021 022import javassist.CannotCompileException; 023import javassist.CtClass; 024import javassist.CtConstructor; 025import javassist.CtMethod; 026 027import org.apache.commons.proxy2.Interceptor; 028import org.apache.commons.proxy2.Invoker; 029import org.apache.commons.proxy2.ObjectProvider; 030import org.apache.commons.proxy2.ProxyUtils; 031import org.apache.commons.proxy2.exception.ProxyFactoryException; 032import org.apache.commons.proxy2.impl.AbstractProxyClassGenerator; 033import org.apache.commons.proxy2.impl.AbstractSubclassingProxyFactory; 034import org.apache.commons.proxy2.impl.ProxyClassCache; 035 036public class JavassistProxyFactory extends AbstractSubclassingProxyFactory 037{ 038 //****************************************************************************************************************** 039 // Fields 040 //****************************************************************************************************************** 041 042 private static final String GET_METHOD_METHOD_NAME = "_javassistGetMethod"; 043 044 private static final ProxyClassCache DELEGATING_PROXY_CACHE = new ProxyClassCache( 045 new DelegatingProxyClassGenerator()); 046 private static final ProxyClassCache INTERCEPTOR_PROXY_CACHE = new ProxyClassCache( 047 new InterceptorProxyClassGenerator()); 048 private static final ProxyClassCache INVOKER_PROXY_CACHE = new ProxyClassCache(new InvokerProxyClassGenerator()); 049 050 //****************************************************************************************************************** 051 // Static Methods 052 //****************************************************************************************************************** 053 054 private static void addGetMethodMethod(CtClass proxyClass) throws CannotCompileException 055 { 056 final CtMethod method = new CtMethod(JavassistUtils.resolve(Method.class), GET_METHOD_METHOD_NAME, 057 JavassistUtils.resolve(new Class[] { String.class, String.class, Class[].class }), proxyClass); 058 final String body = "try { return Class.forName($1).getMethod($2, $3); } catch( Exception e ) " 059 + "{ throw new RuntimeException(\"Unable to look up method.\", e); }"; 060 method.setBody(body); 061 proxyClass.addMethod(method); 062 } 063 064 //****************************************************************************************************************** 065 // ProxyFactory Implementation 066 //****************************************************************************************************************** 067 068 /** 069 * {@inheritDoc} 070 */ 071 @Override 072 public <T> T createDelegatorProxy(ClassLoader classLoader, ObjectProvider<?> targetProvider, 073 Class<?>... proxyClasses) 074 { 075 try 076 { 077 @SuppressWarnings("unchecked") // type inference 078 final Class<? extends T> clazz = (Class<? extends T>) DELEGATING_PROXY_CACHE.getProxyClass(classLoader, 079 proxyClasses); 080 return clazz.getConstructor(ObjectProvider.class).newInstance(targetProvider); 081 } 082 catch (Exception e) 083 { 084 throw new ProxyFactoryException("Unable to instantiate proxy2 from generated proxy2 class.", e); 085 } 086 } 087 088 /** 089 * {@inheritDoc} 090 */ 091 @Override 092 public <T> T createInterceptorProxy(ClassLoader classLoader, Object target, Interceptor interceptor, 093 Class<?>... proxyClasses) 094 { 095 try 096 { 097 @SuppressWarnings("unchecked") // type inference 098 final Class<? extends T> clazz = (Class<? extends T>) INTERCEPTOR_PROXY_CACHE.getProxyClass(classLoader, 099 proxyClasses); 100 return clazz.getConstructor(Object.class, Interceptor.class).newInstance(target, interceptor); 101 } 102 catch (Exception e) 103 { 104 throw new ProxyFactoryException("Unable to instantiate proxy2 class instance.", e); 105 } 106 } 107 108 /** 109 * {@inheritDoc} 110 */ 111 @Override 112 public <T> T createInvokerProxy(ClassLoader classLoader, Invoker invoker, Class<?>... proxyClasses) 113 { 114 try 115 { 116 @SuppressWarnings("unchecked") // type inference 117 final Class<? extends T> clazz = (Class<? extends T>) INVOKER_PROXY_CACHE.getProxyClass(classLoader, 118 proxyClasses); 119 return clazz.getConstructor(Invoker.class).newInstance(invoker); 120 } 121 catch (Exception e) 122 { 123 throw new ProxyFactoryException("Unable to instantiate proxy2 from generated proxy2 class.", e); 124 } 125 } 126 127 //****************************************************************************************************************** 128 // Inner Classes 129 //****************************************************************************************************************** 130 131 private static class DelegatingProxyClassGenerator extends AbstractProxyClassGenerator 132 { 133 @Override 134 public Class<?> generateProxyClass(ClassLoader classLoader, Class<?>... proxyClasses) 135 { 136 try 137 { 138 final CtClass proxyClass = JavassistUtils.createClass(getSuperclass(proxyClasses)); 139 JavassistUtils.addField(ObjectProvider.class, "provider", proxyClass); 140 final CtConstructor proxyConstructor = new CtConstructor( 141 JavassistUtils.resolve(new Class[] { ObjectProvider.class }), proxyClass); 142 proxyConstructor.setBody("{ this.provider = $1; }"); 143 proxyClass.addConstructor(proxyConstructor); 144 JavassistUtils.addInterfaces(proxyClass, toInterfaces(proxyClasses)); 145 addHashCodeMethod(proxyClass); 146 addEqualsMethod(proxyClass); 147 final Method[] methods = getImplementationMethods(proxyClasses); 148 for (int i = 0; i < methods.length; ++i) 149 { 150 if (!ProxyUtils.isEqualsMethod(methods[i]) && !ProxyUtils.isHashCode(methods[i])) 151 { 152 final Method method = methods[i]; 153 final CtMethod ctMethod = new CtMethod(JavassistUtils.resolve(method.getReturnType()), 154 method.getName(), JavassistUtils.resolve(method.getParameterTypes()), proxyClass); 155 final String body = "{ return ( $r ) ( ( " + method.getDeclaringClass().getName() 156 + " )provider.getObject() )." + method.getName() + "($$); }"; 157 ctMethod.setBody(body); 158 proxyClass.addMethod(ctMethod); 159 } 160 } 161 return proxyClass.toClass(classLoader, null); 162 } 163 catch (CannotCompileException e) 164 { 165 throw new ProxyFactoryException("Could not compile class.", e); 166 } 167 } 168 } 169 170 private static class InterceptorProxyClassGenerator extends AbstractProxyClassGenerator 171 { 172 @Override 173 public Class<?> generateProxyClass(ClassLoader classLoader, Class<?>... proxyClasses) 174 { 175 try 176 { 177 final CtClass proxyClass = JavassistUtils.createClass(getSuperclass(proxyClasses)); 178 final Method[] methods = getImplementationMethods(proxyClasses); 179 JavassistUtils.addInterfaces(proxyClass, toInterfaces(proxyClasses)); 180 JavassistUtils.addField(Object.class, "target", proxyClass); 181 JavassistUtils.addField(Interceptor.class, "interceptor", proxyClass); 182 addGetMethodMethod(proxyClass); 183 addHashCodeMethod(proxyClass); 184 addEqualsMethod(proxyClass); 185 final CtConstructor proxyConstructor = new CtConstructor(JavassistUtils.resolve(new Class[] { 186 Object.class, Interceptor.class }), proxyClass); 187 proxyConstructor.setBody("{\n\tthis.target = $1;\n\tthis.interceptor = $2; }"); 188 proxyClass.addConstructor(proxyConstructor); 189 for (int i = 0; i < methods.length; ++i) 190 { 191 if (!ProxyUtils.isEqualsMethod(methods[i]) && !ProxyUtils.isHashCode(methods[i])) 192 { 193 final CtMethod method = new CtMethod(JavassistUtils.resolve(methods[i].getReturnType()), 194 methods[i].getName(), JavassistUtils.resolve(methods[i].getParameterTypes()), 195 proxyClass); 196 final Class<?> invocationClass = JavassistInvocation.getMethodInvocationClass(classLoader, 197 methods[i]); 198 199 final String body = "{\n\t return ( $r ) interceptor.intercept( new " 200 + invocationClass.getName() + "( this, target, " + GET_METHOD_METHOD_NAME + "(\"" 201 + methods[i].getDeclaringClass().getName() + "\", \"" + methods[i].getName() 202 + "\", $sig), $args ) );\n }"; 203 method.setBody(body); 204 proxyClass.addMethod(method); 205 } 206 207 } 208 return proxyClass.toClass(classLoader, null); 209 } 210 catch (CannotCompileException e) 211 { 212 throw new ProxyFactoryException("Could not compile class.", e); 213 } 214 } 215 216 } 217 218 private static void addEqualsMethod(CtClass proxyClass) throws CannotCompileException 219 { 220 final CtMethod equalsMethod = new CtMethod(JavassistUtils.resolve(Boolean.TYPE), "equals", 221 JavassistUtils.resolve(new Class[] { Object.class }), proxyClass); 222 final String body = "{\n\treturn this == $1;\n}"; 223 equalsMethod.setBody(body); 224 proxyClass.addMethod(equalsMethod); 225 } 226 227 private static void addHashCodeMethod(CtClass proxyClass) throws CannotCompileException 228 { 229 final CtMethod hashCodeMethod = new CtMethod(JavassistUtils.resolve(Integer.TYPE), "hashCode", new CtClass[0], 230 proxyClass); 231 hashCodeMethod.setBody("{\n\treturn System.identityHashCode(this);\n}"); 232 proxyClass.addMethod(hashCodeMethod); 233 } 234 235 private static class InvokerProxyClassGenerator extends AbstractProxyClassGenerator 236 { 237 @Override 238 public Class<?> generateProxyClass(ClassLoader classLoader, Class<?>... proxyClasses) 239 { 240 try 241 { 242 final CtClass proxyClass = JavassistUtils.createClass(getSuperclass(proxyClasses)); 243 final Method[] methods = getImplementationMethods(proxyClasses); 244 JavassistUtils.addInterfaces(proxyClass, toInterfaces(proxyClasses)); 245 JavassistUtils.addField(Invoker.class, "invoker", proxyClass); 246 final CtConstructor proxyConstructor = new CtConstructor( 247 JavassistUtils.resolve(new Class[] { Invoker.class }), proxyClass); 248 proxyConstructor.setBody("{\n\tthis.invoker = $1; }"); 249 proxyClass.addConstructor(proxyConstructor); 250 addGetMethodMethod(proxyClass); 251 addHashCodeMethod(proxyClass); 252 addEqualsMethod(proxyClass); 253 for (int i = 0; i < methods.length; ++i) 254 { 255 if (!ProxyUtils.isEqualsMethod(methods[i]) && !ProxyUtils.isHashCode(methods[i])) 256 { 257 final CtMethod method = new CtMethod(JavassistUtils.resolve(methods[i].getReturnType()), 258 methods[i].getName(), JavassistUtils.resolve(methods[i].getParameterTypes()), 259 proxyClass); 260 final String body = "{\n\t return ( $r ) invoker.invoke( this, " + GET_METHOD_METHOD_NAME 261 + "(\"" + methods[i].getDeclaringClass().getName() + "\", \"" + methods[i].getName() 262 + "\", $sig), $args );\n }"; 263 method.setBody(body); 264 proxyClass.addMethod(method); 265 } 266 } 267 return proxyClass.toClass(classLoader, null); 268 } 269 catch (CannotCompileException e) 270 { 271 throw new ProxyFactoryException("Could not compile class.", e); 272 } 273 } 274 } 275}