001: /*
002: * @(#)FactoryFinder.java 1.4 01/12/03
003: *
004: * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
005: * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
006: */
007:
008: package javax.xml.parsers;
009:
010: import java.io.InputStream;
011: import java.io.IOException;
012: import java.io.File;
013: import java.io.FileInputStream;
014:
015: import java.util.Properties;
016: import java.io.BufferedReader;
017: import java.io.InputStreamReader;
018:
019: /**
020: * This class is duplicated for each JAXP subpackage so keep it in
021: * sync. It is package private.
022: *
023: * This code is designed to implement the JAXP 1.1 spec pluggability
024: * feature and is designed to run on JDK version 1.1 and later including
025: * JVMs that perform early linking like the Microsoft JVM in IE 5. Note
026: * however that it must be compiled on a JDK version 1.2 or later system
027: * since it calls Thread#getContextClassLoader(). The code also runs both
028: * as part of an unbundled jar file and when bundled as part of the JDK.
029: */
030: class FactoryFinder {
031: /** Temp debug code - this will be removed after we test everything
032: */
033: private static boolean debug = false;
034: static {
035: // Use try/catch block to support applets
036: try {
037: debug = System.getProperty("jaxp.debug") != null;
038: } catch (Exception x) {
039: }
040: }
041:
042: private static void debugPrintln(String msg) {
043: if (debug) {
044: System.err.println("JAXP: " + msg);
045: }
046: }
047:
048: /**
049: * Figure out which ClassLoader to use. For JDK 1.2 and later use the
050: * context ClassLoader if possible. Note: we defer linking the class
051: * that calls an API only in JDK 1.2 until runtime so that we can catch
052: * LinkageError so that this code will run in older non-Sun JVMs such
053: * as the Microsoft JVM in IE.
054: */
055: private static ClassLoader findClassLoader()
056: throws ConfigurationError {
057: ClassLoader classLoader;
058: try {
059: // Construct the name of the concrete class to instantiate
060: Class clazz = Class.forName(FactoryFinder.class.getName()
061: + "$ClassLoaderFinderConcrete");
062: ClassLoaderFinder clf = (ClassLoaderFinder) clazz
063: .newInstance();
064: classLoader = clf.getContextClassLoader();
065: } catch (LinkageError le) {
066: // Assume that we are running JDK 1.1, use the current ClassLoader
067: classLoader = FactoryFinder.class.getClassLoader();
068: } catch (ClassNotFoundException x) {
069: // This case should not normally happen. MS IE can throw this
070: // instead of a LinkageError the second time Class.forName() is
071: // called so assume that we are running JDK 1.1 and use the
072: // current ClassLoader
073: classLoader = FactoryFinder.class.getClassLoader();
074: } catch (Exception x) {
075: // Something abnormal happened so throw an error
076: throw new ConfigurationError(x.toString(), x);
077: }
078: return classLoader;
079: }
080:
081: /**
082: * Create an instance of a class using the specified ClassLoader
083: */
084: private static Object newInstance(String className,
085: ClassLoader classLoader) throws ConfigurationError {
086: try {
087: Class spiClass;
088: if (classLoader == null) {
089: spiClass = Class.forName(className);
090: } else {
091: spiClass = classLoader.loadClass(className);
092: }
093: return spiClass.newInstance();
094: } catch (ClassNotFoundException x) {
095: throw new ConfigurationError("Provider " + className
096: + " not found", x);
097: } catch (Exception x) {
098: throw new ConfigurationError("Provider " + className
099: + " could not be instantiated: " + x, x);
100: }
101: }
102:
103: /**
104: * Finds the implementation Class object in the specified order. Main
105: * entry point.
106: * @return Class object of factory, never null
107: *
108: * @param factoryId Name of the factory to find, same as
109: * a property name
110: * @param fallbackClassName Implementation class name, if nothing else
111: * is found. Use null to mean no fallback.
112: *
113: * Package private so this code can be shared.
114: */
115: static Object find(String factoryId, String fallbackClassName)
116: throws ConfigurationError {
117: ClassLoader classLoader = findClassLoader();
118:
119: // Use the system property first
120: try {
121: String systemProp = System.getProperty(factoryId);
122: if (systemProp != null) {
123: debugPrintln("found system property" + systemProp);
124: return newInstance(systemProp, classLoader);
125: }
126: } catch (SecurityException se) {
127: }
128:
129: // try to read from $java.home/lib/xml.properties
130: try {
131: String javah = System.getProperty("java.home");
132: String configFile = javah + File.separator + "lib"
133: + File.separator + "jaxp.properties";
134: File f = new File(configFile);
135: if (f.exists()) {
136: Properties props = new Properties();
137: props.load(new FileInputStream(f));
138: String factoryClassName = props.getProperty(factoryId);
139: debugPrintln("found java.home property "
140: + factoryClassName);
141: return newInstance(factoryClassName, classLoader);
142: }
143: } catch (Exception ex) {
144: if (debug)
145: ex.printStackTrace();
146: }
147:
148: String serviceId = "META-INF/services/" + factoryId;
149: // try to find services in CLASSPATH
150: try {
151: InputStream is = null;
152: if (classLoader == null) {
153: is = ClassLoader.getSystemResourceAsStream(serviceId);
154: } else {
155: is = classLoader.getResourceAsStream(serviceId);
156: }
157:
158: if (is != null) {
159: debugPrintln("found " + serviceId);
160: BufferedReader rd = new BufferedReader(
161: new InputStreamReader(is, "UTF-8"));
162:
163: String factoryClassName = rd.readLine();
164: rd.close();
165:
166: if (factoryClassName != null
167: && !"".equals(factoryClassName)) {
168: debugPrintln("loaded from services: "
169: + factoryClassName);
170: return newInstance(factoryClassName, classLoader);
171: }
172: }
173: } catch (Exception ex) {
174: if (debug)
175: ex.printStackTrace();
176: }
177:
178: if (fallbackClassName == null) {
179: throw new ConfigurationError("Provider for " + factoryId
180: + " cannot be found", null);
181: }
182:
183: debugPrintln("loaded from fallback value: " + fallbackClassName);
184: return newInstance(fallbackClassName, classLoader);
185: }
186:
187: static class ConfigurationError extends Error {
188: private Exception exception;
189:
190: /**
191: * Construct a new instance with the specified detail string and
192: * exception.
193: */
194: ConfigurationError(String msg, Exception x) {
195: super (msg);
196: this .exception = x;
197: }
198:
199: Exception getException() {
200: return exception;
201: }
202: }
203:
204: /*
205: * The following nested classes allow getContextClassLoader() to be
206: * called only on JDK 1.2 and yet run in older JDK 1.1 JVMs
207: */
208:
209: private static abstract class ClassLoaderFinder {
210: abstract ClassLoader getContextClassLoader();
211: }
212:
213: static class ClassLoaderFinderConcrete extends ClassLoaderFinder {
214: ClassLoader getContextClassLoader() {
215: return Thread.currentThread().getContextClassLoader();
216: }
217: }
218: }
|