001: /*
002: * Copyright 2001-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: /*
017: * $Id: ObjectFactory.java,v 1.7 2004/10/14 21:45:05 minchau Exp $
018: */
019:
020: package org.apache.xml.serializer;
021:
022: import java.io.BufferedReader;
023: import java.io.File;
024: import java.io.FileInputStream;
025: import java.io.IOException;
026: import java.io.InputStream;
027: import java.io.InputStreamReader;
028: import java.util.Properties;
029:
030: /**
031: * This class is duplicated for each JAXP subpackage so keep it in sync.
032: * It is package private and therefore is not exposed as part of the JAXP
033: * API.
034: * <p>
035: * This code is designed to implement the JAXP 1.1 spec pluggability
036: * feature and is designed to run on JDK version 1.1 and
037: * later, and to compile on JDK 1.2 and onward.
038: * The code also runs both as part of an unbundled jar file and
039: * when bundled as part of the JDK.
040: * <p>
041: * This class was moved from the <code>javax.xml.parsers.ObjectFactory</code>
042: * class and modified to be used as a general utility for creating objects
043: * dynamically.
044: *
045: * @xsl.usage internal
046: */
047: class ObjectFactory {
048:
049: //
050: // Constants
051: //
052:
053: // name of default properties file to look for in JDK's jre/lib directory
054: private static final String DEFAULT_PROPERTIES_FILENAME = "xalan.properties";
055:
056: private static final String SERVICES_PATH = "META-INF/services/";
057:
058: /** Set to true for debugging */
059: private static final boolean DEBUG = false;
060:
061: /** cache the contents of the xalan.properties file.
062: * Until an attempt has been made to read this file, this will
063: * be null; if the file does not exist or we encounter some other error
064: * during the read, this will be empty.
065: */
066: private static Properties fXalanProperties = null;
067:
068: /***
069: * Cache the time stamp of the xalan.properties file so
070: * that we know if it's been modified and can invalidate
071: * the cache when necessary.
072: */
073: private static long fLastModified = -1;
074:
075: //
076: // Public static methods
077: //
078:
079: /**
080: * Finds the implementation Class object in the specified order. The
081: * specified order is the following:
082: * <ol>
083: * <li>query the system property using <code>System.getProperty</code>
084: * <li>read <code>META-INF/services/<i>factoryId</i></code> file
085: * <li>use fallback classname
086: * </ol>
087: *
088: * @return instance of factory, never null
089: *
090: * @param factoryId Name of the factory to find, same as
091: * a property name
092: * @param fallbackClassName Implementation class name, if nothing else
093: * is found. Use null to mean no fallback.
094: *
095: * @exception ObjectFactory.ConfigurationError
096: */
097: static Object createObject(String factoryId,
098: String fallbackClassName) throws ConfigurationError {
099: return createObject(factoryId, null, fallbackClassName);
100: } // createObject(String,String):Object
101:
102: /**
103: * Finds the implementation Class object in the specified order. The
104: * specified order is the following:
105: * <ol>
106: * <li>query the system property using <code>System.getProperty</code>
107: * <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
108: * <li>read <code>META-INF/services/<i>factoryId</i></code> file
109: * <li>use fallback classname
110: * </ol>
111: *
112: * @return instance of factory, never null
113: *
114: * @param factoryId Name of the factory to find, same as
115: * a property name
116: * @param propertiesFilename The filename in the $java.home/lib directory
117: * of the properties file. If none specified,
118: * ${java.home}/lib/xalan.properties will be used.
119: * @param fallbackClassName Implementation class name, if nothing else
120: * is found. Use null to mean no fallback.
121: *
122: * @exception ObjectFactory.ConfigurationError
123: */
124: static Object createObject(String factoryId,
125: String propertiesFilename, String fallbackClassName)
126: throws ConfigurationError {
127: Class factoryClass = lookUpFactoryClass(factoryId,
128: propertiesFilename, fallbackClassName);
129:
130: if (factoryClass == null) {
131: throw new ConfigurationError("Provider for " + factoryId
132: + " cannot be found", null);
133: }
134:
135: try {
136: Object instance = factoryClass.newInstance();
137: debugPrintln("created new instance of factory " + factoryId);
138: return instance;
139: } catch (Exception x) {
140: throw new ConfigurationError("Provider for factory "
141: + factoryId + " could not be instantiated: " + x, x);
142: }
143: } // createObject(String,String,String):Object
144:
145: /**
146: * Finds the implementation Class object in the specified order. The
147: * specified order is the following:
148: * <ol>
149: * <li>query the system property using <code>System.getProperty</code>
150: * <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
151: * <li>read <code>META-INF/services/<i>factoryId</i></code> file
152: * <li>use fallback classname
153: * </ol>
154: *
155: * @return Class object of factory, never null
156: *
157: * @param factoryId Name of the factory to find, same as
158: * a property name
159: * @param propertiesFilename The filename in the $java.home/lib directory
160: * of the properties file. If none specified,
161: * ${java.home}/lib/xalan.properties will be used.
162: * @param fallbackClassName Implementation class name, if nothing else
163: * is found. Use null to mean no fallback.
164: *
165: * @exception ObjectFactory.ConfigurationError
166: */
167: static Class lookUpFactoryClass(String factoryId)
168: throws ConfigurationError {
169: return lookUpFactoryClass(factoryId, null, null);
170: } // lookUpFactoryClass(String):Class
171:
172: /**
173: * Finds the implementation Class object in the specified order. The
174: * specified order is the following:
175: * <ol>
176: * <li>query the system property using <code>System.getProperty</code>
177: * <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
178: * <li>read <code>META-INF/services/<i>factoryId</i></code> file
179: * <li>use fallback classname
180: * </ol>
181: *
182: * @return Class object that provides factory service, never null
183: *
184: * @param factoryId Name of the factory to find, same as
185: * a property name
186: * @param propertiesFilename The filename in the $java.home/lib directory
187: * of the properties file. If none specified,
188: * ${java.home}/lib/xalan.properties will be used.
189: * @param fallbackClassName Implementation class name, if nothing else
190: * is found. Use null to mean no fallback.
191: *
192: * @exception ObjectFactory.ConfigurationError
193: */
194: static Class lookUpFactoryClass(String factoryId,
195: String propertiesFilename, String fallbackClassName)
196: throws ConfigurationError {
197: String factoryClassName = lookUpFactoryClassName(factoryId,
198: propertiesFilename, fallbackClassName);
199: ClassLoader cl = findClassLoader();
200:
201: if (factoryClassName == null) {
202: factoryClassName = fallbackClassName;
203: }
204:
205: // assert(className != null);
206: try {
207: Class providerClass = findProviderClass(factoryClassName,
208: cl, true);
209: debugPrintln("created new instance of " + providerClass
210: + " using ClassLoader: " + cl);
211: return providerClass;
212: } catch (ClassNotFoundException x) {
213: throw new ConfigurationError("Provider " + factoryClassName
214: + " not found", x);
215: } catch (Exception x) {
216: throw new ConfigurationError("Provider " + factoryClassName
217: + " could not be instantiated: " + x, x);
218: }
219: } // lookUpFactoryClass(String,String,String):Class
220:
221: /**
222: * Finds the name of the required implementation class in the specified
223: * order. The specified order is the following:
224: * <ol>
225: * <li>query the system property using <code>System.getProperty</code>
226: * <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
227: * <li>read <code>META-INF/services/<i>factoryId</i></code> file
228: * <li>use fallback classname
229: * </ol>
230: *
231: * @return name of class that provides factory service, never null
232: *
233: * @param factoryId Name of the factory to find, same as
234: * a property name
235: * @param propertiesFilename The filename in the $java.home/lib directory
236: * of the properties file. If none specified,
237: * ${java.home}/lib/xalan.properties will be used.
238: * @param fallbackClassName Implementation class name, if nothing else
239: * is found. Use null to mean no fallback.
240: *
241: * @exception ObjectFactory.ConfigurationError
242: */
243: static String lookUpFactoryClassName(String factoryId,
244: String propertiesFilename, String fallbackClassName) {
245: SecuritySupport ss = SecuritySupport.getInstance();
246:
247: // Use the system property first
248: try {
249: String systemProp = ss.getSystemProperty(factoryId);
250: if (systemProp != null) {
251: debugPrintln("found system property, value="
252: + systemProp);
253: return systemProp;
254: }
255: } catch (SecurityException se) {
256: // Ignore and continue w/ next location
257: }
258:
259: // Try to read from propertiesFilename, or
260: // $java.home/lib/xalan.properties
261: String factoryClassName = null;
262: // no properties file name specified; use
263: // $JAVA_HOME/lib/xalan.properties:
264: if (propertiesFilename == null) {
265: File propertiesFile = null;
266: boolean propertiesFileExists = false;
267: try {
268: String javah = ss.getSystemProperty("java.home");
269: propertiesFilename = javah + File.separator + "lib"
270: + File.separator + DEFAULT_PROPERTIES_FILENAME;
271: propertiesFile = new File(propertiesFilename);
272: propertiesFileExists = ss.getFileExists(propertiesFile);
273: } catch (SecurityException e) {
274: // try again...
275: fLastModified = -1;
276: fXalanProperties = null;
277: }
278:
279: synchronized (ObjectFactory.class) {
280: boolean loadProperties = false;
281: FileInputStream fis = null;
282: try {
283: // file existed last time
284: if (fLastModified >= 0) {
285: if (propertiesFileExists
286: && (fLastModified < (fLastModified = ss
287: .getLastModified(propertiesFile)))) {
288: loadProperties = true;
289: } else {
290: // file has stopped existing...
291: if (!propertiesFileExists) {
292: fLastModified = -1;
293: fXalanProperties = null;
294: } // else, file wasn't modified!
295: }
296: } else {
297: // file has started to exist:
298: if (propertiesFileExists) {
299: loadProperties = true;
300: fLastModified = ss
301: .getLastModified(propertiesFile);
302: } // else, nothing's changed
303: }
304: if (loadProperties) {
305: // must never have attempted to read xalan.properties
306: // before (or it's outdeated)
307: fXalanProperties = new Properties();
308: fis = ss.getFileInputStream(propertiesFile);
309: fXalanProperties.load(fis);
310: }
311: } catch (Exception x) {
312: fXalanProperties = null;
313: fLastModified = -1;
314: // assert(x instanceof FileNotFoundException
315: // || x instanceof SecurityException)
316: // In both cases, ignore and continue w/ next location
317: } finally {
318: // try to close the input stream if one was opened.
319: if (fis != null) {
320: try {
321: fis.close();
322: }
323: // Ignore the exception.
324: catch (IOException exc) {
325: }
326: }
327: }
328: }
329: if (fXalanProperties != null) {
330: factoryClassName = fXalanProperties
331: .getProperty(factoryId);
332: }
333: } else {
334: FileInputStream fis = null;
335: try {
336: fis = ss
337: .getFileInputStream(new File(propertiesFilename));
338: Properties props = new Properties();
339: props.load(fis);
340: factoryClassName = props.getProperty(factoryId);
341: } catch (Exception x) {
342: // assert(x instanceof FileNotFoundException
343: // || x instanceof SecurityException)
344: // In both cases, ignore and continue w/ next location
345: } finally {
346: // try to close the input stream if one was opened.
347: if (fis != null) {
348: try {
349: fis.close();
350: }
351: // Ignore the exception.
352: catch (IOException exc) {
353: }
354: }
355: }
356: }
357: if (factoryClassName != null) {
358: debugPrintln("found in " + propertiesFilename + ", value="
359: + factoryClassName);
360: return factoryClassName;
361: }
362:
363: // Try Jar Service Provider Mechanism
364: return findJarServiceProviderName(factoryId);
365: } // lookUpFactoryClass(String,String):String
366:
367: //
368: // Private static methods
369: //
370:
371: /** Prints a message to standard error if debugging is enabled. */
372: private static void debugPrintln(String msg) {
373: if (DEBUG) {
374: System.err.println("JAXP: " + msg);
375: }
376: } // debugPrintln(String)
377:
378: /**
379: * Figure out which ClassLoader to use. For JDK 1.2 and later use
380: * the context ClassLoader.
381: */
382: static ClassLoader findClassLoader() throws ConfigurationError {
383: SecuritySupport ss = SecuritySupport.getInstance();
384:
385: // Figure out which ClassLoader to use for loading the provider
386: // class. If there is a Context ClassLoader then use it.
387: ClassLoader context = ss.getContextClassLoader();
388: ClassLoader system = ss.getSystemClassLoader();
389:
390: ClassLoader chain = system;
391: while (true) {
392: if (context == chain) {
393: // Assert: we are on JDK 1.1 or we have no Context ClassLoader
394: // or any Context ClassLoader in chain of system classloader
395: // (including extension ClassLoader) so extend to widest
396: // ClassLoader (always look in system ClassLoader if Xalan
397: // is in boot/extension/system classpath and in current
398: // ClassLoader otherwise); normal classloaders delegate
399: // back to system ClassLoader first so this widening doesn't
400: // change the fact that context ClassLoader will be consulted
401: ClassLoader current = ObjectFactory.class
402: .getClassLoader();
403:
404: chain = system;
405: while (true) {
406: if (current == chain) {
407: // Assert: Current ClassLoader in chain of
408: // boot/extension/system ClassLoaders
409: return system;
410: }
411: if (chain == null) {
412: break;
413: }
414: chain = ss.getParentClassLoader(chain);
415: }
416:
417: // Assert: Current ClassLoader not in chain of
418: // boot/extension/system ClassLoaders
419: return current;
420: }
421:
422: if (chain == null) {
423: // boot ClassLoader reached
424: break;
425: }
426:
427: // Check for any extension ClassLoaders in chain up to
428: // boot ClassLoader
429: chain = ss.getParentClassLoader(chain);
430: }
431: ;
432:
433: // Assert: Context ClassLoader not in chain of
434: // boot/extension/system ClassLoaders
435: return context;
436: } // findClassLoader():ClassLoader
437:
438: /**
439: * Create an instance of a class using the specified ClassLoader
440: */
441: static Object newInstance(String className, ClassLoader cl,
442: boolean doFallback) throws ConfigurationError {
443: // assert(className != null);
444: try {
445: Class providerClass = findProviderClass(className, cl,
446: doFallback);
447: Object instance = providerClass.newInstance();
448: debugPrintln("created new instance of " + providerClass
449: + " using ClassLoader: " + cl);
450: return instance;
451: } catch (ClassNotFoundException x) {
452: throw new ConfigurationError("Provider " + className
453: + " not found", x);
454: } catch (Exception x) {
455: throw new ConfigurationError("Provider " + className
456: + " could not be instantiated: " + x, x);
457: }
458: }
459:
460: /**
461: * Find a Class using the specified ClassLoader
462: */
463: static Class findProviderClass(String className, ClassLoader cl,
464: boolean doFallback) throws ClassNotFoundException,
465: ConfigurationError {
466: //throw security exception if the calling thread is not allowed to access the
467: //class. Restrict the access to the package classes as specified in java.security policy.
468: SecurityManager security = System.getSecurityManager();
469: try {
470: if (security != null) {
471: final int lastDot = className.lastIndexOf(".");
472: String packageName = className;
473: if (lastDot != -1)
474: packageName = className.substring(0, lastDot);
475: security.checkPackageAccess(packageName);
476: }
477: } catch (SecurityException e) {
478: throw e;
479: }
480:
481: Class providerClass;
482: if (cl == null) {
483: // XXX Use the bootstrap ClassLoader. There is no way to
484: // load a class using the bootstrap ClassLoader that works
485: // in both JDK 1.1 and Java 2. However, this should still
486: // work b/c the following should be true:
487: //
488: // (cl == null) iff current ClassLoader == null
489: //
490: // Thus Class.forName(String) will use the current
491: // ClassLoader which will be the bootstrap ClassLoader.
492: providerClass = Class.forName(className);
493: } else {
494: try {
495: providerClass = cl.loadClass(className);
496: } catch (ClassNotFoundException x) {
497: if (doFallback) {
498: // Fall back to current classloader
499: ClassLoader current = ObjectFactory.class
500: .getClassLoader();
501: if (current == null) {
502: providerClass = Class.forName(className);
503: } else if (cl != current) {
504: cl = current;
505: providerClass = cl.loadClass(className);
506: } else {
507: throw x;
508: }
509: } else {
510: throw x;
511: }
512: }
513: }
514:
515: return providerClass;
516: }
517:
518: /**
519: * Find the name of service provider using Jar Service Provider Mechanism
520: *
521: * @return instance of provider class if found or null
522: */
523: private static String findJarServiceProviderName(String factoryId) {
524: SecuritySupport ss = SecuritySupport.getInstance();
525: String serviceId = SERVICES_PATH + factoryId;
526: InputStream is = null;
527:
528: // First try the Context ClassLoader
529: ClassLoader cl = findClassLoader();
530:
531: is = ss.getResourceAsStream(cl, serviceId);
532:
533: // If no provider found then try the current ClassLoader
534: if (is == null) {
535: ClassLoader current = ObjectFactory.class.getClassLoader();
536: if (cl != current) {
537: cl = current;
538: is = ss.getResourceAsStream(cl, serviceId);
539: }
540: }
541:
542: if (is == null) {
543: // No provider found
544: return null;
545: }
546:
547: debugPrintln("found jar resource=" + serviceId
548: + " using ClassLoader: " + cl);
549:
550: // Read the service provider name in UTF-8 as specified in
551: // the jar spec. Unfortunately this fails in Microsoft
552: // VJ++, which does not implement the UTF-8
553: // encoding. Theoretically, we should simply let it fail in
554: // that case, since the JVM is obviously broken if it
555: // doesn't support such a basic standard. But since there
556: // are still some users attempting to use VJ++ for
557: // development, we have dropped in a fallback which makes a
558: // second attempt using the platform's default encoding. In
559: // VJ++ this is apparently ASCII, which is a subset of
560: // UTF-8... and since the strings we'll be reading here are
561: // also primarily limited to the 7-bit ASCII range (at
562: // least, in English versions), this should work well
563: // enough to keep us on the air until we're ready to
564: // officially decommit from VJ++. [Edited comment from
565: // jkesselm]
566: BufferedReader rd;
567: try {
568: rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
569: } catch (java.io.UnsupportedEncodingException e) {
570: rd = new BufferedReader(new InputStreamReader(is));
571: }
572:
573: String factoryClassName = null;
574: try {
575: // XXX Does not handle all possible input as specified by the
576: // Jar Service Provider specification
577: factoryClassName = rd.readLine();
578: } catch (IOException x) {
579: // No provider found
580: return null;
581: } finally {
582: try {
583: // try to close the reader.
584: rd.close();
585: }
586: // Ignore the exception.
587: catch (IOException exc) {
588: }
589: }
590:
591: if (factoryClassName != null && !"".equals(factoryClassName)) {
592: debugPrintln("found in resource, value=" + factoryClassName);
593:
594: // Note: here we do not want to fall back to the current
595: // ClassLoader because we want to avoid the case where the
596: // resource file was found using one ClassLoader and the
597: // provider class was instantiated using a different one.
598: return factoryClassName;
599: }
600:
601: // No provider found
602: return null;
603: }
604:
605: //
606: // Classes
607: //
608:
609: /**
610: * A configuration error.
611: */
612: static class ConfigurationError extends Error {
613: static final long serialVersionUID = 8859254254255146542L;
614: //
615: // Data
616: //
617:
618: /** Exception. */
619: private Exception exception;
620:
621: //
622: // Constructors
623: //
624:
625: /**
626: * Construct a new instance with the specified detail string and
627: * exception.
628: */
629: ConfigurationError(String msg, Exception x) {
630: super (msg);
631: this .exception = x;
632: } // <init>(String,Exception)
633:
634: //
635: // Public methods
636: //
637:
638: /** Returns the exception associated to this error. */
639: Exception getException() {
640: return exception;
641: } // getException():Exception
642:
643: } // class ConfigurationError
644:
645: } // class ObjectFactory
|