1 /**********************************************************************
2 * BaseRemotingHandler.java
3 * created on 23.07.2006 by netseeker
4 * $Id$
5 * $Log$
6 * Revision 1.11 2007/11/17 10:57:00 netseeker
7 * *** empty log message ***
8 *
9 * Revision 1.10 2007/03/22 21:01:35 netseeker
10 * *** empty log message ***
11 *
12 * Revision 1.9 2006/12/02 19:10:36 netseeker
13 * adapted move of RemotingRequest to the main package
14 *
15 * Revision 1.8 2006/11/27 22:58:28 netseeker
16 * *** empty log message ***
17 *
18 * Revision 1.7 2006/11/12 20:34:44 netseeker
19 * *** empty log message ***
20 *
21 * Revision 1.6 2006/11/11 19:13:16 netseeker
22 * added security mechanism for remote reflection as well as for permitted adapters
23 *
24 * Revision 1.5 2006/11/06 08:50:30 netseeker
25 * fixed java 1.4 support
26 *
27 * Revision 1.4 2006/11/05 16:59:31 netseeker
28 * added support for remote reflection via usage of RemotingHandlers
29 *
30 * Revision 1.3 2006/07/27 20:35:15 netseeker
31 * *** empty log message ***
32 *
33 *
34 * ====================================================================
35 *
36 * Copyright 2006 netseeker aka Michael Manske
37 *
38 * Licensed under the Apache License, Version 2.0 (the "License");
39 * you may not use this file except in compliance with the License.
40 * You may obtain a copy of the License at
41 *
42 * http://www.apache.org/licenses/LICENSE-2.0
43 *
44 * Unless required by applicable law or agreed to in writing, software
45 * distributed under the License is distributed on an "AS IS" BASIS,
46 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
47 * See the License for the specific language governing permissions and
48 * limitations under the License.
49 * ====================================================================
50 *
51 * This file is part of the EJOE framework.
52 * For more information on the author, please see
53 * <http://www.manskes.de/>.
54 *
55 *********************************************************************/
56
57 package de.netseeker.ejoe.handler;
58
59 import java.io.InputStream;
60 import java.security.AccessControlException;
61 import java.util.Collections;
62 import java.util.HashSet;
63 import java.util.Hashtable;
64 import java.util.Iterator;
65 import java.util.Map;
66 import java.util.Properties;
67 import java.util.Set;
68 import java.util.logging.Level;
69 import java.util.logging.Logger;
70
71 import de.netseeker.ejoe.cache.ClassRegistry;
72 import de.netseeker.ejoe.cache.LRUMap;
73 import de.netseeker.ejoe.io.IOUtil;
74 import de.netseeker.ejoe.request.RemotingRequest;
75
76 /***
77 * Base class for the family of reflection based ServerHandlers. Basically these ServerHandlers allow the usage of
78 * remote method invocations which the EJOE Sever handles using:
79 * <ul>
80 * <li>either standard java reflection with caching enhancements or</li>
81 * <li>assisted reflection with dynmically generated and cached proxy classes, which don't have to use reflection after
82 * the first invocation</li>
83 * </ul>
84 *
85 * @author netseeker
86 * @since 0.3.9.1
87 */
88 public abstract class BaseRemotingHandler implements ServerHandler, ClassRegistry
89 {
90 private static final Logger logger = Logger.getLogger( BaseRemotingHandler.class.getName() );
91
92 /***
93 * cache for all primitive-wrapper combinations
94 */
95 private static Hashtable primitiveToWrapper = new Hashtable();
96
97 /***
98 * cache for all wrapper-primitive combinations
99 */
100 private static Hashtable wrapperToPrimitive = new Hashtable();
101
102 /***
103 * permission store containing package/class -names which are allowed or not allowed to be used in remote reflection
104 * calls
105 */
106 private static String[] reflectionPermissions;
107
108 /***
109 * all permitted class names will be stored in this cache for faster lookup
110 */
111 private static Set permissionCache = Collections.synchronizedSet( new HashSet() );
112
113 /***
114 * pre-cache all known Wrapper-Primitive-Pairs as well as all known Primitive-Wrapper-Pairs
115 */
116
117 protected Map classCache = new LRUMap();
118
119 static
120 {
121 registerTypeMappings();
122 loadReflectionPermissions();
123 }
124
125 private static void registerTypeMappings()
126 {
127 primitiveToWrapper.put( Boolean.TYPE, Boolean.class );
128 primitiveToWrapper.put( Byte.TYPE, Byte.class );
129 primitiveToWrapper.put( Short.TYPE, Short.class );
130 primitiveToWrapper.put( Character.TYPE, Character.class );
131 primitiveToWrapper.put( Integer.TYPE, Integer.class );
132 primitiveToWrapper.put( Long.TYPE, Long.class );
133 primitiveToWrapper.put( Float.TYPE, Float.class );
134 primitiveToWrapper.put( Double.TYPE, Double.class );
135 wrapperToPrimitive.put( Boolean.class, Boolean.TYPE );
136 wrapperToPrimitive.put( Byte.class, Byte.TYPE );
137 wrapperToPrimitive.put( Short.class, Short.TYPE );
138 wrapperToPrimitive.put( Character.class, Character.TYPE );
139 wrapperToPrimitive.put( Integer.class, Integer.TYPE );
140 wrapperToPrimitive.put( Long.class, Long.TYPE );
141 wrapperToPrimitive.put( Float.class, Float.TYPE );
142 wrapperToPrimitive.put( Double.class, Double.TYPE );
143 }
144
145 private static void loadReflectionPermissions()
146 {
147 Properties props = new Properties();
148 InputStream in = null;
149 try
150 {
151 in = BaseRemotingHandler.class.getResourceAsStream( "/ejoe-reflection-conf.properties" );
152 props.load( in );
153 }
154 catch ( Exception e )
155 {
156 logger
157 .log(
158 Level.INFO,
159 "No customized ejoe-reflection-conf.properties found on classpath! If you don't want to use"
160 + " the default reflection permissions (META-INF/ejoe-reflection-conf.properties) "
161 + " make sure you have placed a valid ejoe-reflection-conf.properties file in your classpath." );
162 try
163 {
164 in = BaseRemotingHandler.class.getResourceAsStream( "/META-INF/ejoe-reflection-conf.properties" );
165 props.load( in );
166 }
167 catch ( Exception ioe )
168 {
169 logger.log( Level.SEVERE,
170 "Reflection permissions could no be read from /META-INF/ejoe-reflection-conf.properties!!! "
171 + " No remote reflection will be available in EJServer!" );
172 }
173 }
174 finally
175 {
176 IOUtil.closeQuiet( in );
177 }
178
179 if ( props != null )
180 {
181 String name = null;
182 reflectionPermissions = new String[props.size()];
183 int count = 0;
184
185 for ( Iterator it = props.keySet().iterator(); it.hasNext(); )
186 {
187 name = (String) it.next();
188 reflectionPermissions[count] = name;
189 count++;
190 }
191 }
192 }
193
194
195
196
197
198
199 public Object handle( Object params ) throws Exception
200 {
201 RemotingRequest request = (RemotingRequest) params;
202
203 if ( request.getClazz() == null )
204 {
205 throw new IllegalArgumentException( "Parameter \"targetClass\" not found in request!" );
206 }
207
208 if ( request.getMethod() == null )
209 {
210 throw new IllegalArgumentException( "Parameter \"targetMethod\" not found in request!" );
211 }
212
213 Class clazz = getClassByName( request.getClazz() );
214
215 if ( clazz.isInterface() )
216 {
217 throw new IllegalArgumentException( "Parameter \"targetClass\" must not be an interface!" );
218 }
219 else if ( clazz.isPrimitive() )
220 {
221 throw new IllegalArgumentException( "Parameter \"targetClass\" must not be an primitive!" );
222 }
223
224 return handle( clazz, request.getMethod(), request.getArgs() );
225 }
226
227 /***
228 * Returns the appropiate wrapper type for the given primitive type, eg. Integer for int
229 *
230 * @param primitive primitive java type
231 * @return appropiate java wrapper type
232 */
233 public static Class getWrapperForPrimitive( Class primitive )
234 {
235 return (Class) primitiveToWrapper.get( primitive );
236 }
237
238 /***
239 * Returns the appropiate primitive type for the given wrapper type, eg. int for Integer
240 *
241 * @param wrapper java wrapper type
242 * @return appropiate java primitive type
243 */
244 public static Class getPrimitiveForWrapper( Class wrapper )
245 {
246 return (Class) wrapperToPrimitive.get( wrapper );
247 }
248
249 /***
250 * Either loads a class via reflection or (if already cached) from a Class-cache
251 *
252 * @param cName complete name of the class with package names
253 * @return a Class
254 * @throws ClassNotFoundException
255 */
256 protected Class getClassByName( String cName ) throws ClassNotFoundException, AccessControlException
257 {
258
259 Class clazz = (Class) classCache.get( cName );
260
261
262 if ( clazz == null )
263 {
264 if ( isReflectionPermitted( cName ) )
265 {
266 clazz = Class.forName( cName );
267 classCache.put( cName, clazz );
268 }
269 else
270 {
271 throw new AccessControlException( "EJOE does not permit usage of \"" + cName
272 + "\" for remote reflection calls!" );
273 }
274 }
275
276 return clazz;
277 }
278
279 private final boolean isReflectionPermitted( String className )
280 {
281 if ( permissionCache.contains( className ) ) return true;
282
283 String entry = null, subName = className;
284 int index = -1;
285
286 for ( int i = 0; i < reflectionPermissions.length; i++ )
287 {
288 entry = reflectionPermissions[i];
289 if ( entry.equals( className ) )
290 {
291 permissionCache.add( className );
292 return true;
293 }
294 if ( entry.endsWith( "*" ) )
295 {
296 index = subName.lastIndexOf( '.' );
297 while ( index != -1 )
298 {
299 subName = subName.substring( 0, index );
300 if ( entry.startsWith( subName ) )
301 {
302 permissionCache.add( className );
303 return true;
304 }
305 index = subName.lastIndexOf( '.' );
306 }
307 }
308 }
309
310 return false;
311 }
312
313
314
315
316
317
318 public void registerClassMapping( String clazz, String alias )
319 {
320 if ( !classCache.containsKey( alias ) )
321 {
322 try
323 {
324 Class theClass = Class.forName( clazz );
325 classCache.put( alias, theClass );
326 classCache.put( clazz, theClass );
327 }
328 catch ( ClassNotFoundException e )
329 {
330 logger.log( Level.SEVERE, e.getMessage(), e );
331 }
332 }
333 }
334
335
336
337
338
339
340 public boolean deRegisterClassMapping( String alias )
341 {
342 Class theClass = (Class) classCache.get( alias );
343 boolean removed = false;
344
345 if ( theClass != null )
346 {
347 classCache.remove( alias );
348 classCache.remove( theClass.getName() );
349 removed = true;
350 }
351
352 return removed;
353 }
354
355 /***
356 * @param target
357 * @param method
358 * @param args
359 * @return
360 */
361 abstract Object handle( Class target, String method, Object[] args ) throws Exception;
362 }