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