View Javadoc

1   /**********************************************************************
2    * DefaultRemotingHandler.java
3    * created on 09.07.2006 by netseeker
4    * $$Source$$
5    * $$Date$$
6    * $$Revision$$
7    *
8    * ====================================================================
9    *
10   *  Copyright 2006 netseeker aka Michael Manske
11   *
12   *  Licensed under the Apache License, Version 2.0 (the "License");
13   *  you may not use this file except in compliance with the License.
14   *  You may obtain a copy of the License at
15   *
16   *      http://www.apache.org/licenses/LICENSE-2.0
17   *
18   *  Unless required by applicable law or agreed to in writing, software
19   *  distributed under the License is distributed on an "AS IS" BASIS,
20   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21   *  See the License for the specific language governing permissions and
22   *  limitations under the License.
23   * ====================================================================
24   *
25   * This file is part of the EJOE framework.
26   * For more information on the author, please see
27   * <http://www.manskes.de/>.
28   *
29   *********************************************************************/
30  
31  package de.netseeker.ejoe.handler;
32  
33  import java.lang.reflect.Constructor;
34  import java.lang.reflect.Field;
35  import java.lang.reflect.Method;
36  import java.util.Map;
37  import java.util.logging.Level;
38  import java.util.logging.Logger;
39  
40  import de.netseeker.ejoe.cache.LRUMap;
41  import de.netseeker.ejoe.util.ContentStringBuilder;
42  
43  /***
44   * Reflection based ServerHandler. This is the default implementation for EJOE's remote reflection feature. This class
45   * handles automatic mapping of object types to primitives for the method parameter to find the right method signature.
46   * Classes, parameter classes (including object-primitive-mapping) as well as methods will be cached to outperform usual
47   * reflection.
48   * 
49   * @author netseeker
50   * @since 0.3.9.1
51   */
52  public class DefaultRemotingHandler extends BaseRemotingHandler
53  {
54  
55      /***
56       * 
57       */
58      private static final long serialVersionUID = 1L;
59  
60      private static final Logger log              = Logger.getLogger( DefaultRemotingHandler.class.getName() );
61  
62      private Map                 constructorCache = new LRUMap();
63  
64      private Map                 methodCache      = new LRUMap();
65  
66      private Map                 paramCache       = new LRUMap();
67  
68      Object handle( Class target, String method, Object[] args ) throws Exception
69      {
70          Object ret = null;
71  
72          Object[] types = getArgTypes( args );
73          Class[] argTypes = (Class[]) types[0];
74          Class[] argTypesPrimitive = (Class[]) types[1];
75  
76          if ( log.isLoggable( Level.FINE ) )
77          {
78              log.log( Level.FINE, target.getName() + '#' + method + '(' + ContentStringBuilder.toString( argTypes )
79                      + ')' );
80          }
81  
82  /* Java 5 break compatibility with Java <= 1.4, because SUN changed the behavior of Class.getName()
83   * so the following code would break Java 1.4 backward compatibility
84          if ( !target.getSimpleName().equals( method ) )
85          {
86              Method mtd = getMethodForArgumentTypes( target, method, argTypes, argTypesPrimitive );
87  
88              Object o = target.newInstance();
89              ret = mtd.invoke( o, args );
90          }
91          else
92          {
93              Constructor constructor = getConstructorForArgumentTypes( target, argTypes, argTypesPrimitive );
94              ret = constructor.newInstance( args );
95          }
96  */
97          try
98          {
99              Method mtd = getMethodForArgumentTypes( target, method, argTypes, argTypesPrimitive );
100 
101             Object o = target.newInstance();
102             ret = mtd.invoke( o, args );
103         }
104         catch(NoSuchMethodException e)
105         {
106             Constructor constructor = getConstructorForArgumentTypes( target, argTypes, argTypesPrimitive );
107             ret = constructor.newInstance( args );
108         }
109         
110         return ret;
111     }
112 
113     /***
114      * @param params array of objects for which the argument types has to be determined
115      * @return an array of two class-arrays (Class[]) containing object types in the first class-array as well as their
116      *         primitive representation in the second class-array
117      */
118     protected Object[] getArgTypes( Object[] params )
119     {
120         Class[] argTypes = null;
121         Class[] argTypesPrimitive = null;
122 
123         if ( params != null && params.length > 0 )
124         {
125             // now we have to create an array of parameter types to find the
126             // right method in the required class via reflection
127             argTypes = new Class[params.length];
128             // because we might get primitive parameters as objects, we have to
129             // create a second array of parameter types
130             // containing the corresponding primitve types
131             argTypesPrimitive = new Class[params.length];
132 
133             for ( int i = 0; i < params.length; i++ )
134             {
135                 // add the object type to the regular array of parameter types
136                 argTypes[i] = params[i].getClass();
137 
138                 // now check if the parameter is a object type instead of a
139                 // primitive
140                 if ( !argTypes[i].isPrimitive() )
141                 {
142                     // get the class name of the object type
143                     String clName = argTypes[i].getName();
144 
145                     // do we have the corresponding primitive type already in
146                     // cache?
147                     if ( !paramCache.containsKey( clName ) )
148                     {
149                         try
150                         {
151                             // no, hence we have determine the corresponding
152                             // primitive class via reflection
153                             Field field = argTypes[i].getField( "TYPE" );
154                             argTypesPrimitive[i] = (Class) field.get( params[i] );
155                             // add the primitive type to the cache
156                             paramCache.put( clName, argTypesPrimitive[i] );
157                         }
158                         catch ( NoSuchFieldException e )
159                         {
160                             // ok, can happen if the object type just has no
161                             // corresponding primitive type
162                             argTypesPrimitive[i] = argTypes[i];
163                         }
164                         catch ( Exception e )
165                         {
166                             // shouldn't happen
167 
168                             argTypesPrimitive[i] = argTypes[i];
169                         }
170                     }
171                     // the corresponding primitive type was found in the cache
172                     else
173                     {
174                         argTypesPrimitive[i] = (Class) paramCache.get( clName );
175                     }
176                 }
177                 // the parameter was given as primitive
178                 else
179                 {
180                     argTypesPrimitive[i] = argTypes[i];
181                 }
182             }
183         }
184         // ok, no parameters given
185         else
186         {
187             argTypes = new Class[] {};
188             argTypesPrimitive = new Class[] {};
189         }
190 
191         return new Object[] { argTypes, argTypesPrimitive };
192     }
193 
194     /***
195      * Similiar to {@link #getMethodForArgumentTypes(Class, String, Class[], Class[])} but tries to lookup constructors
196      * instead
197      * 
198      * @param clazz
199      * @param argTypes
200      * @param argTypesPrimitive
201      * @return
202      * @throws SecurityException
203      * @throws NoSuchMethodException
204      */
205     protected Constructor getConstructorForArgumentTypes( Class clazz, Class[] argTypes, Class[] argTypesPrimitive )
206             throws SecurityException, NoSuchMethodException
207     {
208         String className = clazz.getName();
209 
210         // try to find the method in the constructor-cache while using the array
211         // of
212         // object types for the given parameters
213         Constructor constr = (Constructor) constructorCache.get( className + ContentStringBuilder.toString( argTypes ) );
214 
215         if ( constr == null )
216         {
217             // ok, then try to find the constructor in the constructor-cache
218             // while using
219             // the array of primitive types for the given parameters
220             constr = (Constructor) constructorCache
221                     .get( className + ContentStringBuilder.toString( argTypesPrimitive ) );
222         }
223 
224         if ( constr == null )
225         {
226             // seems as this is the first search for the constructor
227             try
228             {
229                 // try to get the constructor via reflection while using the
230                 // array of
231                 // object types for the given parameters
232                 constr = clazz.getConstructor( argTypes );
233                 constructorCache.put( className + ContentStringBuilder.toString( argTypes ), constr );
234             }
235             catch ( NoSuchMethodException e )
236             {
237                 // ok, maybe the parameter types don't match, lets try the array
238                 // of primitive types for the given parameters
239                 constr = clazz.getConstructor( argTypesPrimitive );
240                 constructorCache.put( className + ContentStringBuilder.toString( argTypesPrimitive ), constr );
241             }
242         }
243 
244         return constr;
245     }
246 
247     /***
248      * @param clazz
249      * @param mName
250      * @param argTypes
251      * @param argTypesPrimitive
252      * @return
253      * @throws SecurityException
254      * @throws NoSuchMethodException
255      */
256     protected Method getMethodForArgumentTypes( Class clazz, String mName, Class[] argTypes, Class[] argTypesPrimitive )
257             throws SecurityException, NoSuchMethodException
258     {
259         String key = clazz.getName() + '#' + mName;
260         String argTypesKey = ContentStringBuilder.toString( argTypes );
261 
262         // try to find the method in the method-cache while using the array of
263         // object types for the given parameters
264         Method mtd = (Method) methodCache.get( key + argTypesKey );
265 
266         if ( mtd == null && argTypesPrimitive != null )
267         {
268             // ok, then try to find the method in the method-cache while using
269             // the array of primitive types for the given parameters
270             mtd = (Method) methodCache.get( key + ContentStringBuilder.toString( argTypesPrimitive ) );
271         }
272 
273         if ( mtd == null )
274         {
275             // seems as this is the first invocation of the method
276             try
277             {
278                 // try to get the method via reflection while using the array of
279                 // object types for the given parameters
280                 mtd = clazz.getMethod( mName, argTypes );
281                 methodCache.put( key + ContentStringBuilder.toString( argTypes ), mtd );
282             }
283             catch ( NoSuchMethodException e )
284             {
285                 // ok, maybe the parameter types don't match, lets try the array
286                 // of primitive types for the given parameters
287                 if ( argTypesPrimitive != null )
288                 {
289                     mtd = clazz.getMethod( mName, argTypesPrimitive );
290                     methodCache.put( key + ContentStringBuilder.toString( argTypesPrimitive ), mtd );
291                 }
292                 else
293                 {
294                     throw new NoSuchMethodException( key + argTypesKey );
295                 }
296             }
297         }
298 
299         return mtd;
300     }
301 }