View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.proxy.factory.javassist;
19  
20  import javassist.CannotCompileException;
21  import javassist.CtClass;
22  import javassist.CtConstructor;
23  import javassist.CtMethod;
24  import org.apache.commons.proxy.Invocation;
25  import org.apache.commons.proxy.ProxyUtils;
26  
27  import java.lang.ref.WeakReference;
28  import java.lang.reflect.Method;
29  import java.util.HashMap;
30  import java.util.Map;
31  import java.util.WeakHashMap;
32  
33  /**
34   * A <a href="http://www.jboss.org/products/javassist">Javassist</a>-based {@link Invocation} implementation.  This
35   * class actually serves as the superclass for all <a href="http://www.jboss.org/products/javassist">Javassist</a>-based
36   * method invocations.  Subclasses are dynamically created to deal with specific interface methods (they're hard-wired).
37   * 
38   * @author James Carman
39   * @since 1.0
40   */
41  public abstract class JavassistInvocation implements Invocation
42  {
43  //----------------------------------------------------------------------------------------------------------------------
44  // Fields
45  //----------------------------------------------------------------------------------------------------------------------
46      private static WeakHashMap loaderToClassCache = new WeakHashMap();
47      protected final Method method;
48      protected final Object target;
49      protected final Object[] arguments;
50  
51  //----------------------------------------------------------------------------------------------------------------------
52  // Static Methods
53  //----------------------------------------------------------------------------------------------------------------------
54  
55      private static String createCastExpression( Class type, String objectToCast )
56      {
57          if( !type.isPrimitive() )
58          {
59              return "( " + ProxyUtils.getJavaClassName( type ) + " )" + objectToCast;
60          }
61          else
62          {
63              return "( ( " + ProxyUtils.getWrapperClass( type ).getName() + " )" + objectToCast + " )." +
64                     type.getName() + "Value()";
65          }
66      }
67  
68      private static Class createInvocationClass( ClassLoader classLoader, Method interfaceMethod )
69              throws CannotCompileException
70      {
71          Class invocationClass;
72          final CtClass ctClass = JavassistUtils.createClass(
73                  getSimpleName( interfaceMethod.getDeclaringClass() ) + "_" + interfaceMethod.getName() +
74                  "_invocation",
75                  JavassistInvocation.class );
76          final CtConstructor constructor = new CtConstructor(
77                  JavassistUtils.resolve( new Class[]{ Method.class, Object.class, Object[].class } ),
78                  ctClass );
79          constructor.setBody( "{\n\tsuper($$);\n}" );
80          ctClass.addConstructor( constructor );
81          final CtMethod proceedMethod = new CtMethod( JavassistUtils.resolve( Object.class ), "proceed",
82                                                       JavassistUtils.resolve( new Class[0] ), ctClass );
83          final Class[] argumentTypes = interfaceMethod.getParameterTypes();
84          final StringBuffer proceedBody = new StringBuffer( "{\n" );
85          if( !Void.TYPE.equals( interfaceMethod.getReturnType() ) )
86          {
87              proceedBody.append( "\treturn " );
88              if( interfaceMethod.getReturnType().isPrimitive() )
89              {
90                  proceedBody.append( "new " );
91                  proceedBody.append( ProxyUtils.getWrapperClass( interfaceMethod.getReturnType() ).getName() );
92                  proceedBody.append( "( " );
93              }
94          }
95          else
96          {
97              proceedBody.append( "\t" );
98          }
99          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