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.Invocation; 025 import org.apache.commons.proxy.ProxyUtils; 026 027 import java.lang.ref.WeakReference; 028 import java.lang.reflect.Method; 029 import java.util.HashMap; 030 import java.util.Map; 031 import java.util.WeakHashMap; 032 033 /** 034 * A <a href="http://www.jboss.org/products/javassist">Javassist</a>-based {@link Invocation} implementation. This 035 * class actually serves as the superclass for all <a href="http://www.jboss.org/products/javassist">Javassist</a>-based 036 * method invocations. Subclasses are dynamically created to deal with specific interface methods (they're hard-wired). 037 * 038 * @author James Carman 039 * @since 1.0 040 */ 041 public abstract class JavassistInvocation implements Invocation 042 { 043 //---------------------------------------------------------------------------------------------------------------------- 044 // Fields 045 //---------------------------------------------------------------------------------------------------------------------- 046 private static WeakHashMap loaderToClassCache = new WeakHashMap(); 047 protected final Method method; 048 protected final Object target; 049 protected final Object[] arguments; 050 051 //---------------------------------------------------------------------------------------------------------------------- 052 // Static Methods 053 //---------------------------------------------------------------------------------------------------------------------- 054 055 private static String createCastExpression( Class type, String objectToCast ) 056 { 057 if( !type.isPrimitive() ) 058 { 059 return "( " + ProxyUtils.getJavaClassName( type ) + " )" + objectToCast; 060 } 061 else 062 { 063 return "( ( " + ProxyUtils.getWrapperClass( type ).getName() + " )" + objectToCast + " )." + 064 type.getName() + "Value()"; 065 } 066 } 067 068 private static Class createInvocationClass( ClassLoader classLoader, Method interfaceMethod ) 069 throws CannotCompileException 070 { 071 Class invocationClass; 072 final CtClass ctClass = JavassistUtils.createClass( 073 getSimpleName( interfaceMethod.getDeclaringClass() ) + "_" + interfaceMethod.getName() + 074 "_invocation", 075 JavassistInvocation.class ); 076 final CtConstructor constructor = new CtConstructor( 077 JavassistUtils.resolve( new Class[]{ Method.class, Object.class, Object[].class } ), 078 ctClass ); 079 constructor.setBody( "{\n\tsuper($$);\n}" ); 080 ctClass.addConstructor( constructor ); 081 final CtMethod proceedMethod = new CtMethod( JavassistUtils.resolve( Object.class ), "proceed", 082 JavassistUtils.resolve( new Class[0] ), ctClass ); 083 final Class[] argumentTypes = interfaceMethod.getParameterTypes(); 084 final StringBuffer proceedBody = new StringBuffer( "{\n" ); 085 if( !Void.TYPE.equals( interfaceMethod.getReturnType() ) ) 086 { 087 proceedBody.append( "\treturn " ); 088 if( interfaceMethod.getReturnType().isPrimitive() ) 089 { 090 proceedBody.append( "new " ); 091 proceedBody.append( ProxyUtils.getWrapperClass( interfaceMethod.getReturnType() ).getName() ); 092 proceedBody.append( "( " ); 093 } 094 } 095 else 096 { 097 proceedBody.append( "\t" ); 098 } 099 proceedBody.append( "( (" ); 100 proceedBody.append( ProxyUtils.getJavaClassName( interfaceMethod.getDeclaringClass() ) ); 101 proceedBody.append( " )target )." ); 102 proceedBody.append( interfaceMethod.getName() ); 103 proceedBody.append( "(" ); 104 for( int i = 0; i < argumentTypes.length; ++i ) 105 { 106 final Class argumentType = argumentTypes[i]; 107 proceedBody.append( createCastExpression( argumentType, "arguments[" + i + "]" ) ); 108 if( i != argumentTypes.length - 1 ) 109 { 110 proceedBody.append( ", " ); 111 } 112 } 113 if( !Void.TYPE.equals( interfaceMethod.getReturnType() ) && interfaceMethod.getReturnType().isPrimitive() ) 114 { 115 proceedBody.append( ") );\n" ); 116 } 117 else 118 { 119 proceedBody.append( ");\n" ); 120 } 121 if( Void.TYPE.equals( interfaceMethod.getReturnType() ) ) 122 { 123 proceedBody.append( "\treturn null;\n" ); 124 } 125 proceedBody.append( "}" ); 126 final String body = proceedBody.toString(); 127 proceedMethod.setBody( body ); 128 ctClass.addMethod( proceedMethod ); 129 invocationClass = ctClass.toClass( classLoader ); 130 return invocationClass; 131 } 132 133 private static Map getClassCache( ClassLoader classLoader ) 134 { 135 Map cache = ( Map ) loaderToClassCache.get( classLoader ); 136 if( cache == null ) 137 { 138 cache = new HashMap(); 139 loaderToClassCache.put( classLoader, cache ); 140 } 141 return cache; 142 } 143 144 /** 145 * Returns a method invocation class specifically coded to invoke the supplied interface method. 146 * 147 * @param classLoader the classloader to use 148 * @param interfaceMethod the interface method 149 * @return a method invocation class specifically coded to invoke the supplied interface method 150 * @throws CannotCompileException if a compilation error occurs 151 */ 152 synchronized static Class getMethodInvocationClass( ClassLoader classLoader, 153 Method interfaceMethod ) 154 throws CannotCompileException 155 { 156 final Map classCache = getClassCache( classLoader ); 157 final String key = toClassCacheKey( interfaceMethod ); 158 final WeakReference invocationClassRef = ( WeakReference ) classCache.get( key ); 159 Class invocationClass; 160 if( invocationClassRef == null ) 161 { 162 invocationClass = createInvocationClass( classLoader, interfaceMethod ); 163 classCache.put( key, new WeakReference( invocationClass ) ); 164 } 165 else 166 { 167 synchronized( invocationClassRef ) 168 { 169 invocationClass = ( Class ) invocationClassRef.get(); 170 if( invocationClass == null ) 171 { 172 invocationClass = createInvocationClass( classLoader, interfaceMethod ); 173 classCache.put( key, new WeakReference( invocationClass ) ); 174 } 175 } 176 } 177 return invocationClass; 178 } 179 180 private static String getSimpleName( Class c ) 181 { 182 final String name = c.getName(); 183 final int ndx = name.lastIndexOf( '.' ); 184 return ndx == -1 ? name : name.substring( ndx + 1 ); 185 } 186 187 private static String toClassCacheKey( Method method ) 188 { 189 return String.valueOf( method ); 190 } 191 192 //---------------------------------------------------------------------------------------------------------------------- 193 // Constructors 194 //---------------------------------------------------------------------------------------------------------------------- 195 196 public JavassistInvocation( Method method, Object target, Object[] arguments ) 197 { 198 this.method = method; 199 this.target = target; 200 this.arguments = arguments; 201 } 202 203 //---------------------------------------------------------------------------------------------------------------------- 204 // Invocation Implementation 205 //---------------------------------------------------------------------------------------------------------------------- 206 207 public Object[] getArguments() 208 { 209 return arguments; 210 } 211 212 public Method getMethod() 213 { 214 return method; 215 } 216 217 public Object getProxy() 218 { 219 return target; 220 } 221 } 222