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