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
83
84
85
86
87
88
89
90
91
92
93
94
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
126
127 argTypes = new Class[params.length];
128
129
130
131 argTypesPrimitive = new Class[params.length];
132
133 for ( int i = 0; i < params.length; i++ )
134 {
135
136 argTypes[i] = params[i].getClass();
137
138
139
140 if ( !argTypes[i].isPrimitive() )
141 {
142
143 String clName = argTypes[i].getName();
144
145
146
147 if ( !paramCache.containsKey( clName ) )
148 {
149 try
150 {
151
152
153 Field field = argTypes[i].getField( "TYPE" );
154 argTypesPrimitive[i] = (Class) field.get( params[i] );
155
156 paramCache.put( clName, argTypesPrimitive[i] );
157 }
158 catch ( NoSuchFieldException e )
159 {
160
161
162 argTypesPrimitive[i] = argTypes[i];
163 }
164 catch ( Exception e )
165 {
166
167
168 argTypesPrimitive[i] = argTypes[i];
169 }
170 }
171
172 else
173 {
174 argTypesPrimitive[i] = (Class) paramCache.get( clName );
175 }
176 }
177
178 else
179 {
180 argTypesPrimitive[i] = argTypes[i];
181 }
182 }
183 }
184
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
211
212
213 Constructor constr = (Constructor) constructorCache.get( className + ContentStringBuilder.toString( argTypes ) );
214
215 if ( constr == null )
216 {
217
218
219
220 constr = (Constructor) constructorCache
221 .get( className + ContentStringBuilder.toString( argTypesPrimitive ) );
222 }
223
224 if ( constr == null )
225 {
226
227 try
228 {
229
230
231
232 constr = clazz.getConstructor( argTypes );
233 constructorCache.put( className + ContentStringBuilder.toString( argTypes ), constr );
234 }
235 catch ( NoSuchMethodException e )
236 {
237
238
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
263
264 Method mtd = (Method) methodCache.get( key + argTypesKey );
265
266 if ( mtd == null && argTypesPrimitive != null )
267 {
268
269
270 mtd = (Method) methodCache.get( key + ContentStringBuilder.toString( argTypesPrimitive ) );
271 }
272
273 if ( mtd == null )
274 {
275
276 try
277 {
278
279
280 mtd = clazz.getMethod( mName, argTypes );
281 methodCache.put( key + ContentStringBuilder.toString( argTypes ), mtd );
282 }
283 catch ( NoSuchMethodException e )
284 {
285
286
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 }