1 package org.apache.turbine.services.factory;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.io.ByteArrayInputStream;
20 import java.io.ByteArrayOutputStream;
21 import java.io.ObjectOutputStream;
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.List;
26
27 import org.apache.commons.configuration.Configuration;
28
29 import org.apache.turbine.services.InitializationException;
30 import org.apache.turbine.services.TurbineBaseService;
31 import org.apache.turbine.util.TurbineException;
32 import org.apache.turbine.util.pool.ObjectInputStreamForContext;
33
34 /***
35 * The Factory Service instantiates objects using specified
36 * class loaders. If none is specified, the default one
37 * will be used.
38 *
39 * @author <a href="mailto:ilkka.priha@simsoft.fi">Ilkka Priha</a>
40 * @version $Id: TurbineFactoryService.java 264148 2005-08-29 14:21:04Z henning $
41 */
42 public class TurbineFactoryService
43 extends TurbineBaseService
44 implements FactoryService
45 {
46 /***
47 * The property specifying a set of additional class loaders.
48 */
49 public static final String CLASS_LOADERS = "class.loaders";
50
51 /***
52 * The property prefix specifying additional object factories.
53 */
54 public static final String OBJECT_FACTORY = "factory.";
55
56 /***
57 * Primitive classes for reflection of constructors.
58 */
59 private static HashMap primitiveClasses;
60
61 {
62 primitiveClasses = new HashMap(8);
63 primitiveClasses.put(Boolean.TYPE.toString(), Boolean.TYPE);
64 primitiveClasses.put(Character.TYPE.toString(), Character.TYPE);
65 primitiveClasses.put(Byte.TYPE.toString(), Byte.TYPE);
66 primitiveClasses.put(Short.TYPE.toString(), Short.TYPE);
67 primitiveClasses.put(Integer.TYPE.toString(), Integer.TYPE);
68 primitiveClasses.put(Long.TYPE.toString(), Long.TYPE);
69 primitiveClasses.put(Float.TYPE.toString(), Float.TYPE);
70 primitiveClasses.put(Double.TYPE.toString(), Double.TYPE);
71 }
72
73 /***
74 * Additional class loaders.
75 */
76 private ArrayList classLoaders = new ArrayList();
77
78 /***
79 * Customized object factories.
80 */
81 private HashMap objectFactories = new HashMap();
82
83 /***
84 * Gets the class of a primitive type.
85 *
86 * @param type a primitive type.
87 * @return the corresponding class, or null.
88 */
89 protected static Class getPrimitiveClass(String type)
90 {
91 return (Class) primitiveClasses.get(type);
92 }
93
94 /***
95 * Constructs a Factory Service.
96 */
97 public TurbineFactoryService()
98 {
99 }
100
101 /***
102 * Initializes the service by loading default class loaders
103 * and customized object factories.
104 *
105 * @throws InitializationException if initialization fails.
106 */
107 public void init() throws InitializationException
108 {
109 Configuration conf = getConfiguration();
110 if (conf != null)
111 {
112 List loaders = conf.getList(CLASS_LOADERS);
113 if (loaders != null)
114 {
115 for (int i = 0; i < loaders.size(); i++)
116 {
117 try
118 {
119 classLoaders.add(
120 loadClass((String) loaders.get(i)).newInstance());
121 }
122 catch (Exception x)
123 {
124 throw new InitializationException(
125 "No such class loader '" +
126 (String) loaders.get(i) +
127 "' for TurbineFactoryService", x);
128 }
129 }
130 }
131
132 String key,factory;
133 for (Iterator i = conf.getKeys(OBJECT_FACTORY); i.hasNext();)
134 {
135 key = (String) i.next();
136 factory = conf.getString(key);
137
138
139
140
141
142 objectFactories.put(
143 key.substring(OBJECT_FACTORY.length()), factory);
144 }
145 }
146 setInit(true);
147 }
148
149 /***
150 * Gets an instance of a named class.
151 *
152 * @param className the name of the class.
153 * @return the instance.
154 * @throws TurbineException if instantiation fails.
155 */
156 public Object getInstance(String className)
157 throws TurbineException
158 {
159 if (className == null)
160 {
161 throw new TurbineException(
162 new NullPointerException("String className"));
163 }
164
165 Factory factory = getFactory(className);
166 if (factory == null)
167 {
168 Class clazz;
169 try
170 {
171 clazz = loadClass(className);
172 }
173 catch (ClassNotFoundException x)
174 {
175 throw new TurbineException(
176 "Instantiation failed for class " + className, x);
177 }
178 return getInstance(clazz);
179 }
180 else
181 {
182 return factory.getInstance();
183 }
184 }
185
186 /***
187 * Gets an instance of a named class using a specified class loader.
188 *
189 * <p>Class loaders are supported only if the isLoaderSupported
190 * method returns true. Otherwise the loader parameter is ignored.
191 *
192 * @param className the name of the class.
193 * @param loader the class loader.
194 * @return the instance.
195 * @throws TurbineException if instantiation fails.
196 */
197 public Object getInstance(String className,
198 ClassLoader loader)
199 throws TurbineException
200 {
201 if (className == null)
202 {
203 throw new TurbineException(
204 new NullPointerException("String className"));
205 }
206
207 Factory factory = getFactory(className);
208 if (factory == null)
209 {
210 if (loader != null)
211 {
212 Class clazz;
213 try
214 {
215 clazz = loadClass(className, loader);
216 }
217 catch (ClassNotFoundException x)
218 {
219 throw new TurbineException(
220 "Instantiation failed for class " + className, x);
221 }
222 return getInstance(clazz);
223 }
224 else
225 {
226 return getInstance(className);
227 }
228 }
229 else
230 {
231 return factory.getInstance(loader);
232 }
233 }
234
235 /***
236 * Gets an instance of a named class.
237 * Parameters for its constructor are given as an array of objects,
238 * primitive types must be wrapped with a corresponding class.
239 *
240 * @param className the name of the class.
241 * @param params an array containing the parameters of the constructor.
242 * @param signature an array containing the signature of the constructor.
243 * @return the instance.
244 * @throws TurbineException if instantiation fails.
245 */
246 public Object getInstance(String className,
247 Object[] params,
248 String[] signature)
249 throws TurbineException
250 {
251 if (className == null)
252 {
253 throw new TurbineException(
254 new NullPointerException("String className"));
255 }
256
257 Factory factory = getFactory(className);
258 if (factory == null)
259 {
260 Class clazz;
261 try
262 {
263 clazz = loadClass(className);
264 }
265 catch (ClassNotFoundException x)
266 {
267 throw new TurbineException(
268 "Instantiation failed for class " + className, x);
269 }
270 return getInstance(clazz, params, signature);
271 }
272 else
273 {
274 return factory.getInstance(params, signature);
275 }
276 }
277
278 /***
279 * Gets an instance of a named class using a specified class loader.
280 * Parameters for its constructor are given as an array of objects,
281 * primitive types must be wrapped with a corresponding class.
282 *
283 * <p>Class loaders are supported only if the isLoaderSupported
284 * method returns true. Otherwise the loader parameter is ignored.
285 *
286 * @param className the name of the class.
287 * @param loader the class loader.
288 * @param params an array containing the parameters of the constructor.
289 * @param signature an array containing the signature of the constructor.
290 * @return the instance.
291 * @throws TurbineException if instantiation fails.
292 */
293 public Object getInstance(String className,
294 ClassLoader loader,
295 Object[] params,
296 String[] signature)
297 throws TurbineException
298 {
299 if (className == null)
300 {
301 throw new TurbineException(
302 new NullPointerException("String className"));
303 }
304
305 Factory factory = getFactory(className);
306 if (factory == null)
307 {
308 if (loader != null)
309 {
310 Class clazz;
311 try
312 {
313 clazz = loadClass(className, loader);
314 }
315 catch (ClassNotFoundException x)
316 {
317 throw new TurbineException(
318 "Instantiation failed for class " + className, x);
319 }
320 return getInstance(clazz, params, signature);
321 }
322 else
323 {
324 return getInstance(className, params, signature);
325 }
326 }
327 else
328 {
329 return factory.getInstance(loader, params, signature);
330 }
331 }
332
333 /***
334 * Tests if specified class loaders are supported for a named class.
335 *
336 * @param className the name of the class.
337 * @return true if class loaders are supported, false otherwise.
338 * @throws TurbineException if test fails.
339 */
340 public boolean isLoaderSupported(String className)
341 throws TurbineException
342 {
343 Factory factory = getFactory(className);
344 return factory != null ?
345 factory.isLoaderSupported() : true;
346 }
347
348 /***
349 * Gets an instance of a specified class.
350 *
351 * @param clazz the class.
352 * @return the instance.
353 * @throws TurbineException if instantiation fails.
354 */
355 protected Object getInstance(Class clazz)
356 throws TurbineException
357 {
358 try
359 {
360 return clazz.newInstance();
361 }
362 catch (Exception x)
363 {
364 throw new TurbineException(
365 "Instantiation failed for " + clazz.getName(), x);
366 }
367 }
368
369 /***
370 * Gets an instance of a specified class.
371 * Parameters for its constructor are given as an array of objects,
372 * primitive types must be wrapped with a corresponding class.
373 *
374 * @param clazz the class.
375 * @param params an array containing the parameters of the constructor.
376 * @param signature an array containing the signature of the constructor.
377 * @return the instance.
378 * @throws TurbineException if instantiation fails.
379 */
380 protected Object getInstance(Class clazz,
381 Object params[],
382 String signature[])
383 throws TurbineException
384 {
385
386 try
387 {
388 Class[] sign = getSignature(clazz, params, signature);
389 return clazz.getConstructor(sign).newInstance(params);
390 }
391 catch (Exception x)
392 {
393 throw new TurbineException(
394 "Instantiation failed for " + clazz.getName(), x);
395 }
396 }
397
398 /***
399 * Gets the signature classes for parameters of a method of a class.
400 *
401 * @param clazz the class.
402 * @param params an array containing the parameters of the method.
403 * @param signature an array containing the signature of the method.
404 * @return an array of signature classes. Note that in some cases
405 * objects in the parameter array can be switched to the context
406 * of a different class loader.
407 * @throws ClassNotFoundException if any of the classes is not found.
408 */
409 public Class[] getSignature(Class clazz,
410 Object params[],
411 String signature[])
412 throws ClassNotFoundException
413 {
414 if (signature != null)
415 {
416
417 ClassLoader tempLoader;
418 ClassLoader loader = clazz.getClassLoader();
419 Class[] sign = new Class[signature.length];
420 for (int i = 0; i < signature.length; i++)
421 {
422
423 sign[i] = getPrimitiveClass(signature[i]);
424 if (sign[i] == null)
425 {
426
427 if (loader != null)
428 {
429
430 sign[i] = loader.loadClass(signature[i]);
431 tempLoader = sign[i].getClassLoader();
432 if ((params[i] != null) &&
433 (tempLoader != null) &&
434 !tempLoader.equals(params[i].getClass().getClassLoader()))
435 {
436
437
438
439
440 params[i] = switchObjectContext(params[i], loader);
441 }
442 }
443 else
444 {
445
446 sign[i] = loadClass(signature[i]);
447 }
448 }
449 }
450 return sign;
451 }
452 else
453 {
454 return null;
455 }
456 }
457
458 /***
459 * Switches an object into the context of a different class loader.
460 *
461 * @param object an object to switch.
462 * @param loader the loader of the new context.
463 */
464 protected Object switchObjectContext(Object object,
465 ClassLoader loader)
466 {
467 ByteArrayOutputStream bout =
468 new ByteArrayOutputStream();
469 try
470 {
471 ObjectOutputStream out =
472 new ObjectOutputStream(bout);
473 out.writeObject(object);
474 out.flush();
475 }
476 catch (Exception x)
477 {
478 return object;
479 }
480
481 try
482 {
483 ByteArrayInputStream bin =
484 new ByteArrayInputStream(bout.toByteArray());
485 ObjectInputStreamForContext in =
486 new ObjectInputStreamForContext(bin, loader);
487
488 return in.readObject();
489 }
490 catch (Exception x)
491 {
492 return object;
493 }
494 }
495
496 /***
497 * Loads the named class using the default class loader.
498 *
499 * @param className the name of the class to load.
500 * @return the loaded class.
501 * @throws ClassNotFoundException if the class was not found.
502 */
503 protected Class loadClass(String className)
504 throws ClassNotFoundException
505 {
506 ClassLoader loader = this.getClass().getClassLoader();
507 try
508 {
509 return loader != null ?
510 loader.loadClass(className) : Class.forName(className);
511 }
512 catch (ClassNotFoundException x)
513 {
514
515 for (Iterator i = classLoaders.iterator(); i.hasNext();)
516 {
517 try
518 {
519 return ((ClassLoader) i.next()).loadClass(className);
520 }
521 catch (ClassNotFoundException xx)
522 {
523 }
524 }
525
526
527 throw x;
528 }
529 }
530
531 /***
532 * Loads the named class using a specified class loader.
533 *
534 * @param className the name of the class to load.
535 * @param loader the loader to use.
536 * @return the loaded class.
537 * @throws ClassNotFoundException if the class was not found.
538 */
539 protected Class loadClass(String className,
540 ClassLoader loader)
541 throws ClassNotFoundException
542 {
543 return loader != null ?
544 loader.loadClass(className) : loadClass(className);
545 }
546
547 /***
548 * Gets a customized factory for a named class.
549 *
550 * @param className the name of the class to load.
551 * @return the factory or null if not specified.
552 * @throws TurbineException if instantiation of the factory fails.
553 */
554 protected Factory getFactory(String className)
555 throws TurbineException
556 {
557 HashMap factories = objectFactories;
558 Object factory = factories.get(className);
559 if (factory != null)
560 {
561 if (factory instanceof String)
562 {
563
564 try
565 {
566 factory = (Factory) getInstance((String) factory);
567 ((Factory) factory).init(className);
568 }
569 catch (TurbineException x)
570 {
571 throw x;
572 }
573 catch (ClassCastException x)
574 {
575 throw new TurbineException(
576 "Incorrect factory " + (String) factory +
577 " for class " + className, x);
578 }
579 factories = (HashMap) factories.clone();
580 factories.put(className, factory);
581 objectFactories = factories;
582 }
583 return (Factory) factory;
584 }
585 else
586 {
587 return null;
588 }
589 }
590 }