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