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