001: package org.apache.turbine.services.factory;
002:
003: /*
004: * Copyright 2001-2005 The Apache Software Foundation.
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License")
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: import java.io.ByteArrayInputStream;
020: import java.io.ByteArrayOutputStream;
021: import java.io.ObjectOutputStream;
022: import java.util.ArrayList;
023: import java.util.HashMap;
024: import java.util.Iterator;
025: import java.util.List;
026:
027: import org.apache.commons.configuration.Configuration;
028:
029: import org.apache.turbine.services.InitializationException;
030: import org.apache.turbine.services.TurbineBaseService;
031: import org.apache.turbine.util.TurbineException;
032: import org.apache.turbine.util.pool.ObjectInputStreamForContext;
033:
034: /**
035: * The Factory Service instantiates objects using specified
036: * class loaders. If none is specified, the default one
037: * will be used.
038: *
039: * @author <a href="mailto:ilkka.priha@simsoft.fi">Ilkka Priha</a>
040: * @version $Id: TurbineFactoryService.java 264148 2005-08-29 14:21:04Z henning $
041: */
042: public class TurbineFactoryService extends TurbineBaseService implements
043: FactoryService {
044: /**
045: * The property specifying a set of additional class loaders.
046: */
047: public static final String CLASS_LOADERS = "class.loaders";
048:
049: /**
050: * The property prefix specifying additional object factories.
051: */
052: public static final String OBJECT_FACTORY = "factory.";
053:
054: /**
055: * Primitive classes for reflection of constructors.
056: */
057: private static HashMap primitiveClasses;
058:
059: {
060: primitiveClasses = new HashMap(8);
061: primitiveClasses.put(Boolean.TYPE.toString(), Boolean.TYPE);
062: primitiveClasses.put(Character.TYPE.toString(), Character.TYPE);
063: primitiveClasses.put(Byte.TYPE.toString(), Byte.TYPE);
064: primitiveClasses.put(Short.TYPE.toString(), Short.TYPE);
065: primitiveClasses.put(Integer.TYPE.toString(), Integer.TYPE);
066: primitiveClasses.put(Long.TYPE.toString(), Long.TYPE);
067: primitiveClasses.put(Float.TYPE.toString(), Float.TYPE);
068: primitiveClasses.put(Double.TYPE.toString(), Double.TYPE);
069: }
070:
071: /**
072: * Additional class loaders.
073: */
074: private ArrayList classLoaders = new ArrayList();
075:
076: /**
077: * Customized object factories.
078: */
079: private HashMap objectFactories = new HashMap();
080:
081: /**
082: * Gets the class of a primitive type.
083: *
084: * @param type a primitive type.
085: * @return the corresponding class, or null.
086: */
087: protected static Class getPrimitiveClass(String type) {
088: return (Class) primitiveClasses.get(type);
089: }
090:
091: /**
092: * Constructs a Factory Service.
093: */
094: public TurbineFactoryService() {
095: }
096:
097: /**
098: * Initializes the service by loading default class loaders
099: * and customized object factories.
100: *
101: * @throws InitializationException if initialization fails.
102: */
103: public void init() throws InitializationException {
104: Configuration conf = getConfiguration();
105: if (conf != null) {
106: List loaders = conf.getList(CLASS_LOADERS);
107: if (loaders != null) {
108: for (int i = 0; i < loaders.size(); i++) {
109: try {
110: classLoaders.add(loadClass(
111: (String) loaders.get(i)).newInstance());
112: } catch (Exception x) {
113: throw new InitializationException(
114: "No such class loader '"
115: + (String) loaders.get(i)
116: + "' for TurbineFactoryService",
117: x);
118: }
119: }
120: }
121:
122: String key, factory;
123: for (Iterator i = conf.getKeys(OBJECT_FACTORY); i.hasNext();) {
124: key = (String) i.next();
125: factory = conf.getString(key);
126:
127: /*
128: * Store the factory to the table as a string and
129: * instantiate it by using the service when needed.
130: */
131: objectFactories.put(key.substring(OBJECT_FACTORY
132: .length()), factory);
133: }
134: }
135: setInit(true);
136: }
137:
138: /**
139: * Gets an instance of a named class.
140: *
141: * @param className the name of the class.
142: * @return the instance.
143: * @throws TurbineException if instantiation fails.
144: */
145: public Object getInstance(String className) throws TurbineException {
146: if (className == null) {
147: throw new TurbineException(new NullPointerException(
148: "String className"));
149: }
150:
151: Factory factory = getFactory(className);
152: if (factory == null) {
153: Class clazz;
154: try {
155: clazz = loadClass(className);
156: } catch (ClassNotFoundException x) {
157: throw new TurbineException(
158: "Instantiation failed for class " + className,
159: x);
160: }
161: return getInstance(clazz);
162: } else {
163: return factory.getInstance();
164: }
165: }
166:
167: /**
168: * Gets an instance of a named class using a specified class loader.
169: *
170: * <p>Class loaders are supported only if the isLoaderSupported
171: * method returns true. Otherwise the loader parameter is ignored.
172: *
173: * @param className the name of the class.
174: * @param loader the class loader.
175: * @return the instance.
176: * @throws TurbineException if instantiation fails.
177: */
178: public Object getInstance(String className, ClassLoader loader)
179: throws TurbineException {
180: if (className == null) {
181: throw new TurbineException(new NullPointerException(
182: "String className"));
183: }
184:
185: Factory factory = getFactory(className);
186: if (factory == null) {
187: if (loader != null) {
188: Class clazz;
189: try {
190: clazz = loadClass(className, loader);
191: } catch (ClassNotFoundException x) {
192: throw new TurbineException(
193: "Instantiation failed for class "
194: + className, x);
195: }
196: return getInstance(clazz);
197: } else {
198: return getInstance(className);
199: }
200: } else {
201: return factory.getInstance(loader);
202: }
203: }
204:
205: /**
206: * Gets an instance of a named class.
207: * Parameters for its constructor are given as an array of objects,
208: * primitive types must be wrapped with a corresponding class.
209: *
210: * @param className the name of the class.
211: * @param params an array containing the parameters of the constructor.
212: * @param signature an array containing the signature of the constructor.
213: * @return the instance.
214: * @throws TurbineException if instantiation fails.
215: */
216: public Object getInstance(String className, Object[] params,
217: String[] signature) throws TurbineException {
218: if (className == null) {
219: throw new TurbineException(new NullPointerException(
220: "String className"));
221: }
222:
223: Factory factory = getFactory(className);
224: if (factory == null) {
225: Class clazz;
226: try {
227: clazz = loadClass(className);
228: } catch (ClassNotFoundException x) {
229: throw new TurbineException(
230: "Instantiation failed for class " + className,
231: x);
232: }
233: return getInstance(clazz, params, signature);
234: } else {
235: return factory.getInstance(params, signature);
236: }
237: }
238:
239: /**
240: * Gets an instance of a named class using a specified class loader.
241: * Parameters for its constructor are given as an array of objects,
242: * primitive types must be wrapped with a corresponding class.
243: *
244: * <p>Class loaders are supported only if the isLoaderSupported
245: * method returns true. Otherwise the loader parameter is ignored.
246: *
247: * @param className the name of the class.
248: * @param loader the class loader.
249: * @param params an array containing the parameters of the constructor.
250: * @param signature an array containing the signature of the constructor.
251: * @return the instance.
252: * @throws TurbineException if instantiation fails.
253: */
254: public Object getInstance(String className, ClassLoader loader,
255: Object[] params, String[] signature)
256: throws TurbineException {
257: if (className == null) {
258: throw new TurbineException(new NullPointerException(
259: "String className"));
260: }
261:
262: Factory factory = getFactory(className);
263: if (factory == null) {
264: if (loader != null) {
265: Class clazz;
266: try {
267: clazz = loadClass(className, loader);
268: } catch (ClassNotFoundException x) {
269: throw new TurbineException(
270: "Instantiation failed for class "
271: + className, x);
272: }
273: return getInstance(clazz, params, signature);
274: } else {
275: return getInstance(className, params, signature);
276: }
277: } else {
278: return factory.getInstance(loader, params, signature);
279: }
280: }
281:
282: /**
283: * Tests if specified class loaders are supported for a named class.
284: *
285: * @param className the name of the class.
286: * @return true if class loaders are supported, false otherwise.
287: * @throws TurbineException if test fails.
288: */
289: public boolean isLoaderSupported(String className)
290: throws TurbineException {
291: Factory factory = getFactory(className);
292: return factory != null ? factory.isLoaderSupported() : true;
293: }
294:
295: /**
296: * Gets an instance of a specified class.
297: *
298: * @param clazz the class.
299: * @return the instance.
300: * @throws TurbineException if instantiation fails.
301: */
302: protected Object getInstance(Class clazz) throws TurbineException {
303: try {
304: return clazz.newInstance();
305: } catch (Exception x) {
306: throw new TurbineException("Instantiation failed for "
307: + clazz.getName(), x);
308: }
309: }
310:
311: /**
312: * Gets an instance of a specified class.
313: * Parameters for its constructor are given as an array of objects,
314: * primitive types must be wrapped with a corresponding class.
315: *
316: * @param clazz the class.
317: * @param params an array containing the parameters of the constructor.
318: * @param signature an array containing the signature of the constructor.
319: * @return the instance.
320: * @throws TurbineException if instantiation fails.
321: */
322: protected Object getInstance(Class clazz, Object params[],
323: String signature[]) throws TurbineException {
324: /* Try to construct. */
325: try {
326: Class[] sign = getSignature(clazz, params, signature);
327: return clazz.getConstructor(sign).newInstance(params);
328: } catch (Exception x) {
329: throw new TurbineException("Instantiation failed for "
330: + clazz.getName(), x);
331: }
332: }
333:
334: /**
335: * Gets the signature classes for parameters of a method of a class.
336: *
337: * @param clazz the class.
338: * @param params an array containing the parameters of the method.
339: * @param signature an array containing the signature of the method.
340: * @return an array of signature classes. Note that in some cases
341: * objects in the parameter array can be switched to the context
342: * of a different class loader.
343: * @throws ClassNotFoundException if any of the classes is not found.
344: */
345: public Class[] getSignature(Class clazz, Object params[],
346: String signature[]) throws ClassNotFoundException {
347: if (signature != null) {
348: /* We have parameters. */
349: ClassLoader tempLoader;
350: ClassLoader loader = clazz.getClassLoader();
351: Class[] sign = new Class[signature.length];
352: for (int i = 0; i < signature.length; i++) {
353: /* Check primitive types. */
354: sign[i] = getPrimitiveClass(signature[i]);
355: if (sign[i] == null) {
356: /* Not a primitive one, continue building. */
357: if (loader != null) {
358: /* Use the class loader of the target object. */
359: sign[i] = loader.loadClass(signature[i]);
360: tempLoader = sign[i].getClassLoader();
361: if ((params[i] != null)
362: && (tempLoader != null)
363: && !tempLoader.equals(params[i]
364: .getClass().getClassLoader())) {
365: /*
366: * The class uses a different class loader,
367: * switch the parameter.
368: */
369: params[i] = switchObjectContext(params[i],
370: loader);
371: }
372: } else {
373: /* Use the default class loader. */
374: sign[i] = loadClass(signature[i]);
375: }
376: }
377: }
378: return sign;
379: } else {
380: return null;
381: }
382: }
383:
384: /**
385: * Switches an object into the context of a different class loader.
386: *
387: * @param object an object to switch.
388: * @param loader the loader of the new context.
389: */
390: protected Object switchObjectContext(Object object,
391: ClassLoader loader) {
392: ByteArrayOutputStream bout = new ByteArrayOutputStream();
393: try {
394: ObjectOutputStream out = new ObjectOutputStream(bout);
395: out.writeObject(object);
396: out.flush();
397: } catch (Exception x) {
398: return object;
399: }
400:
401: try {
402: ByteArrayInputStream bin = new ByteArrayInputStream(bout
403: .toByteArray());
404: ObjectInputStreamForContext in = new ObjectInputStreamForContext(
405: bin, loader);
406:
407: return in.readObject();
408: } catch (Exception x) {
409: return object;
410: }
411: }
412:
413: /**
414: * Loads the named class using the default class loader.
415: *
416: * @param className the name of the class to load.
417: * @return the loaded class.
418: * @throws ClassNotFoundException if the class was not found.
419: */
420: protected Class loadClass(String className)
421: throws ClassNotFoundException {
422: ClassLoader loader = this .getClass().getClassLoader();
423: try {
424: return loader != null ? loader.loadClass(className) : Class
425: .forName(className);
426: } catch (ClassNotFoundException x) {
427: /* Go through additional loaders. */
428: for (Iterator i = classLoaders.iterator(); i.hasNext();) {
429: try {
430: return ((ClassLoader) i.next())
431: .loadClass(className);
432: } catch (ClassNotFoundException xx) {
433: }
434: }
435:
436: /* Give up. */
437: throw x;
438: }
439: }
440:
441: /**
442: * Loads the named class using a specified class loader.
443: *
444: * @param className the name of the class to load.
445: * @param loader the loader to use.
446: * @return the loaded class.
447: * @throws ClassNotFoundException if the class was not found.
448: */
449: protected Class loadClass(String className, ClassLoader loader)
450: throws ClassNotFoundException {
451: return loader != null ? loader.loadClass(className)
452: : loadClass(className);
453: }
454:
455: /**
456: * Gets a customized factory for a named class.
457: *
458: * @param className the name of the class to load.
459: * @return the factory or null if not specified.
460: * @throws TurbineException if instantiation of the factory fails.
461: */
462: protected Factory getFactory(String className)
463: throws TurbineException {
464: HashMap factories = objectFactories;
465: Object factory = factories.get(className);
466: if (factory != null) {
467: if (factory instanceof String) {
468: /* Not yet instantiated... */
469: try {
470: factory = (Factory) getInstance((String) factory);
471: ((Factory) factory).init(className);
472: } catch (TurbineException x) {
473: throw x;
474: } catch (ClassCastException x) {
475: throw new TurbineException("Incorrect factory "
476: + (String) factory + " for class "
477: + className, x);
478: }
479: factories = (HashMap) factories.clone();
480: factories.put(className, factory);
481: objectFactories = factories;
482: }
483: return (Factory) factory;
484: } else {
485: return null;
486: }
487: }
488: }
|