| /* |
| * The Apache Software License, Version 1.1 |
| * |
| * |
| * Copyright (c) 2001-2003 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, |
| * if any, must include the following acknowledgment: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowledgment may appear in the software itself, |
| * if and wherever such third-party acknowledgments normally appear. |
| * |
| * 4. The name "Apache Software Foundation" must not be used to endorse or |
| * promote products derived from this software without prior written |
| * permission. For written permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache", |
| * nor may "Apache" appear in their name, without prior written |
| * permission of the Apache Software Foundation. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation and was |
| * originally based on software copyright (c) 1999-2001, Sun Microsystems, |
| * Inc., http://www.sun.com. For more information on the Apache Software |
| * Foundation, please see <http://www.apache.org/>. |
| */ |
| |
| package org.apache.xml.dtm; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.InputStream; |
| import java.io.IOException; |
| |
| import java.io.BufferedReader; |
| import java.io.InputStreamReader; |
| import java.util.Properties; |
| |
| /** |
| * This class is based on the FactoryFinder classes in the JAXP subpackages |
| * in the xml-commons project (xml-apis.jar) |
| * |
| * This copy of FactoryFinder is for the DTMManager. It caches the class |
| * name after it is found the first time, if the System.property is not set. |
| * If the System.property is set, then it is always used. |
| * |
| * It does not use context class loaders, but we will probably need to add |
| * this support in the future. Question: If we use context class loaders, can |
| * we still cache the class (do we need to also cache the class loader for |
| * comparison purposes)? |
| * |
| * @author Edwin Goei, Ilene Seelemann |
| */ |
| class FactoryFinder { |
| /** Controls debugging output to stderr */ |
| private static boolean debug; |
| |
| /** |
| * Avoid reading all the files when the findFactory |
| * method is called the second time (cache the result of |
| * finding the default impl). |
| */ |
| private static String foundFactory = null; |
| |
| |
| // Define system property "jaxp.debug" to get output |
| static { |
| try { |
| String val = |
| SecuritySupport.getInstance().getSystemProperty("jaxp.debug"); |
| // Allow simply setting the prop to turn on debug |
| debug = val != null && (! "false".equals(val)); |
| } catch (SecurityException se) { |
| debug = false; |
| } |
| } |
| |
| /** |
| * Main entry point. Finds and creates a new instance of a concrete |
| * factory implementation in the specified order as stated in the JAXP |
| * spec. This code attempts to find a factory implementation in |
| * serveral locations. If one fails, the next one is tried. To be |
| * more robust, this occurs even if a SecurityException is thrown, but |
| * perhaps it may be better to propogate the SecurityException instead, |
| * so SecurityException-s are not masked. |
| * |
| * @return A new instance of the concrete factory class, never null |
| * |
| * @param factoryId |
| * Name of the factory to find, same as a property name |
| * |
| * @param fallbackClassName |
| * Implementation class name, if nothing else is found. Use |
| * null to mean not to use a fallback. |
| * |
| * @throws FactoryFinder.ConfigurationError |
| * If a factory instance cannot be returned |
| * |
| * Package private so this code can be shared. |
| */ |
| static Object find(String factoryId, String fallbackClassName) |
| throws ConfigurationError |
| { |
| SecuritySupport ss = SecuritySupport.getInstance(); |
| ClassLoader cl = FactoryFinder.class.getClassLoader(); |
| dPrint("find factoryId=" + factoryId); |
| |
| // Use the system property first |
| try { |
| String systemProp = ss.getSystemProperty(factoryId); |
| if (systemProp != null) { |
| dPrint("found system property, value=" + systemProp); |
| |
| return newInstance(systemProp, cl, true); |
| } |
| |
| } catch (SecurityException se) { |
| // Ignore and continue w/ next location |
| } |
| |
| |
| synchronized (FactoryFinder.class) { |
| // This block will only run once, and then foundFactory will |
| // be set and immutable. Currently there is no support in this |
| // class for context class loaders. If the contents of the |
| // xalan.properties file changes, or the class loader changes, |
| // this will *not* affect the cached class. |
| |
| if (foundFactory == null) { |
| |
| // Try to read from $java.home/lib/xalan.properties |
| Properties xalanProperties = null; |
| try { |
| String javah = ss.getSystemProperty("java.home"); |
| String configFile = javah + File.separator + |
| "lib" + File.separator + "xalan.properties"; |
| |
| File f = new File(configFile); |
| FileInputStream fis = ss.getFileInputStream(f); |
| xalanProperties = new Properties(); |
| xalanProperties.load(fis); |
| fis.close(); |
| |
| } catch (Exception x) { |
| // assert(x instanceof FileNotFoundException |
| // || x instanceof SecurityException) |
| // In both cases, ignore and continue w/ next location |
| } |
| |
| if (xalanProperties != null) { |
| foundFactory = xalanProperties.getProperty(factoryId); |
| if (foundFactory != null) { |
| dPrint("found in xalan.properties, value=" + foundFactory); |
| } |
| } else { |
| // Try Jar Service Provider Mechanism |
| // (foundFactory gets set in findJarServiceProvider method) |
| findJarServiceProvider(factoryId); |
| |
| if (foundFactory == null) { |
| if (fallbackClassName == null) { |
| throw new ConfigurationError( |
| "Provider for " + factoryId + " cannot be found", null); |
| } |
| |
| dPrint("using fallback, value=" + fallbackClassName); |
| foundFactory = fallbackClassName; |
| } |
| } |
| } |
| } |
| |
| return newInstance(foundFactory, cl, true); |
| } |
| |
| private static void dPrint(String msg) { |
| if (debug) { |
| System.err.println("JAXP: " + msg); |
| } |
| } |
| |
| /** |
| * Create an instance of a class using the specified ClassLoader and |
| * optionally fall back to the current ClassLoader if not found. |
| * |
| * @param className Name of the concrete class corresponding to the |
| * service provider |
| * |
| * @param cl ClassLoader to use to load the class, null means to use |
| * the bootstrap ClassLoader |
| * |
| * @param doFallback true if the current ClassLoader should be tried as |
| * a fallback if the class is not found using cl |
| */ |
| private static Object newInstance(String className, ClassLoader cl, |
| boolean doFallback) |
| throws ConfigurationError |
| { |
| // assert(className != null); |
| |
| try { |
| Class providerClass; |
| if (cl == null) { |
| // XXX Use the bootstrap ClassLoader. There is no way to |
| // load a class using the bootstrap ClassLoader that works |
| // in both JDK 1.1 and Java 2. However, this should still |
| // work b/c the following should be true: |
| // |
| // (cl == null) iff current ClassLoader == null |
| // |
| // Thus Class.forName(String) will use the current |
| // ClassLoader which will be the bootstrap ClassLoader. |
| providerClass = Class.forName(className); |
| } else { |
| try { |
| providerClass = cl.loadClass(className); |
| } catch (ClassNotFoundException x) { |
| if (doFallback) { |
| // Fall back to current classloader |
| cl = FactoryFinder.class.getClassLoader(); |
| providerClass = cl.loadClass(className); |
| } else { |
| throw x; |
| } |
| } |
| } |
| Object instance = providerClass.newInstance(); |
| dPrint("created new instance of " + providerClass + |
| " using ClassLoader: " + cl); |
| return instance; |
| } catch (ClassNotFoundException x) { |
| throw new ConfigurationError( |
| "Provider " + className + " not found", x); |
| } catch (Exception x) { |
| throw new ConfigurationError( |
| "Provider " + className + " could not be instantiated: " + x, |
| x); |
| } |
| } |
| |
| /* |
| * Try to find provider using Jar Service Provider Mechanism |
| * |
| * @return instance of provider class if found or null |
| */ |
| private static String findJarServiceProvider(String factoryId) |
| throws ConfigurationError |
| { |
| SecuritySupport ss = SecuritySupport.getInstance(); |
| String serviceId = "META-INF/services/" + factoryId; |
| InputStream is = null; |
| // No support yet for context class loader |
| ClassLoader cl = FactoryFinder.class.getClassLoader(); |
| is = ss.getResourceAsStream(cl, serviceId); |
| |
| if (is == null) { |
| // No provider found |
| return null; |
| } |
| |
| dPrint("found jar resource=" + serviceId + |
| " using ClassLoader: " + cl); |
| |
| // Read the service provider name in UTF-8 as specified in |
| // the jar spec. Unfortunately this fails in Microsoft |
| // VJ++, which does not implement the UTF-8 |
| // encoding. Theoretically, we should simply let it fail in |
| // that case, since the JVM is obviously broken if it |
| // doesn't support such a basic standard. But since there |
| // are still some users attempting to use VJ++ for |
| // development, we have dropped in a fallback which makes a |
| // second attempt using the platform's default encoding. In |
| // VJ++ this is apparently ASCII, which is a subset of |
| // UTF-8... and since the strings we'll be reading here are |
| // also primarily limited to the 7-bit ASCII range (at |
| // least, in English versions), this should work well |
| // enough to keep us on the air until we're ready to |
| // officially decommit from VJ++. [Edited comment from |
| // jkesselm] |
| BufferedReader rd; |
| try { |
| rd = new BufferedReader(new InputStreamReader(is, "UTF-8")); |
| } catch (java.io.UnsupportedEncodingException e) { |
| rd = new BufferedReader(new InputStreamReader(is)); |
| } |
| |
| String factoryClassName = null; |
| try { |
| // XXX Does not handle all possible input as specified by the |
| // Jar Service Provider specification |
| factoryClassName = rd.readLine(); |
| rd.close(); |
| } catch (IOException x) { |
| // No provider found |
| return null; |
| } |
| |
| if (factoryClassName != null && |
| ! "".equals(factoryClassName)) { |
| dPrint("found in resource, value=" |
| + factoryClassName); |
| |
| // Note: here we do not want to fall back to the current |
| // ClassLoader because we want to avoid the case where the |
| // resource file was found using one ClassLoader and the |
| // provider class was instantiated using a different one. |
| return factoryClassName; |
| } |
| |
| // No provider found |
| return null; |
| } |
| |
| static class ConfigurationError extends Error { |
| private Exception exception; |
| |
| /** |
| * Construct a new instance with the specified detail string and |
| * exception. |
| */ |
| ConfigurationError(String msg, Exception x) { |
| super(msg); |
| this.exception = x; |
| } |
| |
| Exception getException() { |
| return exception; |
| } |
| } |
| } |