001: /*
002: * The Apache Software License, Version 1.1
003: *
004: *
005: * Copyright (c) 2001, 2002 The Apache Software Foundation. All rights
006: * reserved.
007: *
008: * Redistribution and use in source and binary forms, with or without
009: * modification, are permitted provided that the following conditions
010: * are met:
011: *
012: * 1. Redistributions of source code must retain the above copyright
013: * notice, this list of conditions and the following disclaimer.
014: *
015: * 2. Redistributions in binary form must reproduce the above copyright
016: * notice, this list of conditions and the following disclaimer in
017: * the documentation and/or other materials provided with the
018: * distribution.
019: *
020: * 3. The end-user documentation included with the redistribution,
021: * if any, must include the following acknowledgment:
022: * "This product includes software developed by the
023: * Apache Software Foundation (http://www.apache.org/)."
024: * Alternately, this acknowledgment may appear in the software itself,
025: * if and wherever such third-party acknowledgments normally appear.
026: *
027: * 4. The name "Apache Software Foundation" must not be used to endorse or
028: * promote products derived from this software without prior written
029: * permission. For written permission, please contact apache@apache.org.
030: *
031: * 5. Products derived from this software may not be called "Apache",
032: * nor may "Apache" appear in their name, without prior written
033: * permission of the Apache Software Foundation.
034: *
035: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: *
049: * This software consists of voluntary contributions made by many
050: * individuals on behalf of the Apache Software Foundation and was
051: * originally based on software copyright (c) 1999-2001, Sun Microsystems,
052: * Inc., http://www.sun.com. For more information on the Apache Software
053: * Foundation, please see <http://www.apache.org/>.
054: */
055:
056: package com.sun.xml.stream.xerces.util;
057:
058: import java.io.*;
059: import java.util.Properties;
060:
061: /**
062: * This class is duplicated for each JAXP subpackage so keep it in sync.
063: * It is package private and therefore is not exposed as part of the JAXP
064: * API.
065: * <p>
066: * This code is designed to implement the JAXP 1.1 spec pluggability
067: * feature and is designed to run on JDK version 1.1 and
068: * later, and to compile on JDK 1.2 and onward.
069: * The code also runs both as part of an unbundled jar file and
070: * when bundled as part of the JDK.
071: * <p>
072: * This class was moved from the <code>javax.xml.parsers.ObjectFactory</code>
073: * class and modified to be used as a general utility for creating objects
074: * dynamically.
075: *
076: * @version $Id: ObjectFactory.java,v 1.2 2006/04/01 06:01:39 jeffsuttor Exp $
077: */
078: public class ObjectFactory {
079:
080: //
081: // Constants
082: //
083:
084: // name of default properties file to look for in JDK's jre/lib directory
085: private static final String DEFAULT_PROPERTIES_FILENAME = "xerces.properties";
086:
087: /** Set to true for debugging */
088: private static final boolean DEBUG = false;
089:
090: //
091: // Public static methods
092: //
093:
094: /**
095: * Finds the implementation Class object in the specified order. The
096: * specified order is the following:
097: * <ol>
098: * <li>query the system property using <code>System.getProperty</code>
099: * <li>read <code>META-INF/services/<i>factoryId</i></code> file
100: * <li>use fallback classname
101: * </ol>
102: *
103: * @return Class object of factory, never null
104: *
105: * @param factoryId Name of the factory to find, same as
106: * a property name
107: * @param fallbackClassName Implementation class name, if nothing else
108: * is found. Use null to mean no fallback.
109: *
110: * @exception ObjectFactory.ConfigurationError
111: */
112: public static Object createObject(String factoryId,
113: String fallbackClassName) throws ConfigurationError {
114: return createObject(factoryId, null, fallbackClassName);
115: } // createObject(String,String):Object
116:
117: /**
118: * Finds the implementation Class object in the specified order. The
119: * specified order is the following:
120: * <ol>
121: * <li>query the system property using <code>System.getProperty</code>
122: * <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
123: * <li>read <code>META-INF/services/<i>factoryId</i></code> file
124: * <li>use fallback classname
125: * </ol>
126: *
127: * @return Class object of factory, never null
128: *
129: * @param factoryId Name of the factory to find, same as
130: * a property name
131: * @param propertiesFilename The filename in the $java.home/lib directory
132: * of the properties file. If none specified,
133: * ${java.home}/lib/xerces.properties will be used.
134: * @param fallbackClassName Implementation class name, if nothing else
135: * is found. Use null to mean no fallback.
136: *
137: * @exception ObjectFactory.ConfigurationError
138: */
139: public static Object createObject(String factoryId,
140: String propertiesFilename, String fallbackClassName)
141: throws ConfigurationError {
142: debugPrintln("debug is on");
143:
144: SecuritySupport ss = SecuritySupport.getInstance();
145: ClassLoader cl = findClassLoader();
146:
147: // Use the system property first
148: try {
149: String systemProp = ss.getSystemProperty(factoryId);
150: if (systemProp != null) {
151: debugPrintln("found system property, value="
152: + systemProp);
153: return newInstance(systemProp, cl, true);
154: }
155: } catch (SecurityException se) {
156: // Ignore and continue w/ next location
157: }
158:
159: // Try to read from propertiesFilename, or $java.home/lib/xerces.properties
160: try {
161: if (propertiesFilename == null) {
162: String javah = ss.getSystemProperty("java.home");
163: propertiesFilename = javah + File.separator + "lib"
164: + File.separator + DEFAULT_PROPERTIES_FILENAME;
165: }
166: FileInputStream fis = ss.getFileInputStream(new File(
167: propertiesFilename));
168: Properties props = new Properties();
169: props.load(fis);
170: String factoryClassName = props.getProperty(factoryId);
171: if (factoryClassName != null) {
172: debugPrintln("found in " + propertiesFilename
173: + ", value=" + factoryClassName);
174: return newInstance(factoryClassName, cl, true);
175: }
176: } catch (Exception x) {
177: // assert(x instanceof FileNotFoundException
178: // || x instanceof SecurityException)
179: // In both cases, ignore and continue w/ next location
180: }
181:
182: // Try Jar Service Provider Mechanism
183: Object provider = findJarServiceProvider(factoryId);
184: if (provider != null) {
185: return provider;
186: }
187:
188: if (fallbackClassName == null) {
189: throw new ConfigurationError("Provider for " + factoryId
190: + " cannot be found", null);
191: }
192:
193: debugPrintln("using fallback, value=" + fallbackClassName);
194: return newInstance(fallbackClassName, cl, true);
195: } // createObject(String,String,String):Object
196:
197: //
198: // Private static methods
199: //
200:
201: /** Prints a message to standard error if debugging is enabled. */
202: private static void debugPrintln(String msg) {
203: if (DEBUG) {
204: System.err.println("JAXP: " + msg);
205: }
206: } // debugPrintln(String)
207:
208: /**
209: * Figure out which ClassLoader to use. For JDK 1.2 and later use
210: * the context ClassLoader.
211: */
212: public static ClassLoader findClassLoader()
213: throws ConfigurationError {
214: SecuritySupport ss = SecuritySupport.getInstance();
215:
216: // Figure out which ClassLoader to use for loading the provider
217: // class. If there is a Context ClassLoader then use it.
218: ClassLoader cl = ss.getContextClassLoader();
219: if (cl == null) {
220: // Assert: we are on JDK 1.1 or we have no Context ClassLoader
221: // so use the current ClassLoader
222: cl = ObjectFactory.class.getClassLoader();
223: }
224: return cl;
225:
226: } // findClassLoader():ClassLoader
227:
228: /**
229: * Create an instance of a class using the specified ClassLoader
230: */
231: public static Object newInstance(String className, ClassLoader cl,
232: boolean doFallback) throws ConfigurationError {
233: // assert(className != null);
234: try {
235: Class providerClass = findProviderClass(className, cl,
236: doFallback);
237: Object instance = providerClass.newInstance();
238: debugPrintln("created new instance of " + providerClass
239: + " using ClassLoader: " + cl);
240: return instance;
241: } catch (ClassNotFoundException x) {
242: throw new ConfigurationError("Provider " + className
243: + " not found", x);
244: } catch (Exception x) {
245: throw new ConfigurationError("Provider " + className
246: + " could not be instantiated: " + x, x);
247: }
248: }
249:
250: /**
251: * Find a Class using the specified ClassLoader
252: */
253: public static Class findProviderClass(String className,
254: ClassLoader cl, boolean doFallback)
255: throws ClassNotFoundException, ConfigurationError {
256: Class providerClass;
257: if (cl == null) {
258: // XXX Use the bootstrap ClassLoader. There is no way to
259: // load a class using the bootstrap ClassLoader that works
260: // in both JDK 1.1 and Java 2. However, this should still
261: // work b/c the following should be true:
262: //
263: // (cl == null) iff current ClassLoader == null
264: //
265: // Thus Class.forName(String) will use the current
266: // ClassLoader which will be the bootstrap ClassLoader.
267: providerClass = Class.forName(className);
268: } else {
269: try {
270: providerClass = cl.loadClass(className);
271: } catch (ClassNotFoundException x) {
272: if (doFallback) {
273: // Fall back to current classloader
274: cl = ObjectFactory.class.getClassLoader();
275: providerClass = cl.loadClass(className);
276: } else {
277: throw x;
278: }
279: }
280: }
281: return providerClass;
282: }
283:
284: /*
285: * Try to find provider using Jar Service Provider Mechanism
286: *
287: * @return instance of provider class if found or null
288: */
289: private static Object findJarServiceProvider(String factoryId)
290: throws ConfigurationError {
291: SecuritySupport ss = SecuritySupport.getInstance();
292: String serviceId = "META-INF/services/" + factoryId;
293: InputStream is = null;
294:
295: // First try the Context ClassLoader
296: ClassLoader cl = ss.getContextClassLoader();
297: if (cl != null) {
298: is = ss.getResourceAsStream(cl, serviceId);
299:
300: // If no provider found then try the current ClassLoader
301: if (is == null) {
302: cl = ObjectFactory.class.getClassLoader();
303: is = ss.getResourceAsStream(cl, serviceId);
304: }
305: } else {
306: // No Context ClassLoader or JDK 1.1 so try the current
307: // ClassLoader
308: cl = ObjectFactory.class.getClassLoader();
309: is = ss.getResourceAsStream(cl, serviceId);
310: }
311:
312: if (is == null) {
313: // No provider found
314: return null;
315: }
316:
317: debugPrintln("found jar resource=" + serviceId
318: + " using ClassLoader: " + cl);
319:
320: // Read the service provider name in UTF-8 as specified in
321: // the jar spec. Unfortunately this fails in Microsoft
322: // VJ++, which does not implement the UTF-8
323: // encoding. Theoretically, we should simply let it fail in
324: // that case, since the JVM is obviously broken if it
325: // doesn't support such a basic standard. But since there
326: // are still some users attempting to use VJ++ for
327: // development, we have dropped in a fallback which makes a
328: // second attempt using the platform's default encoding. In
329: // VJ++ this is apparently ASCII, which is a subset of
330: // UTF-8... and since the strings we'll be reading here are
331: // also primarily limited to the 7-bit ASCII range (at
332: // least, in English versions), this should work well
333: // enough to keep us on the air until we're ready to
334: // officially decommit from VJ++. [Edited comment from
335: // jkesselm]
336: BufferedReader rd;
337: try {
338: rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
339: } catch (java.io.UnsupportedEncodingException e) {
340: rd = new BufferedReader(new InputStreamReader(is));
341: }
342:
343: String factoryClassName = null;
344: try {
345: // XXX Does not handle all possible input as specified by the
346: // Jar Service Provider specification
347: factoryClassName = rd.readLine();
348: rd.close();
349: } catch (IOException x) {
350: // No provider found
351: return null;
352: }
353:
354: if (factoryClassName != null && !"".equals(factoryClassName)) {
355: debugPrintln("found in resource, value=" + factoryClassName);
356:
357: // Note: here we do not want to fall back to the current
358: // ClassLoader because we want to avoid the case where the
359: // resource file was found using one ClassLoader and the
360: // provider class was instantiated using a different one.
361: return newInstance(factoryClassName, cl, false);
362: }
363:
364: // No provider found
365: return null;
366: }
367:
368: //
369: // Classes
370: //
371:
372: /**
373: * A configuration error.
374: */
375: public static class ConfigurationError extends Error {
376:
377: //
378: // Data
379: //
380:
381: /** Exception. */
382: private Exception exception;
383:
384: //
385: // Constructors
386: //
387:
388: /**
389: * Construct a new instance with the specified detail string and
390: * exception.
391: */
392: public ConfigurationError(String msg, Exception x) {
393: super (msg);
394: this .exception = x;
395: } // <init>(String,Exception)
396:
397: //
398: // Public methods
399: //
400:
401: /** Returns the exception associated to this error. */
402: public Exception getException() {
403: return exception;
404: } // getException():Exception
405:
406: } // class ConfigurationError
407:
408: } // class ObjectFactory
|