1320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson/*
2320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * Licensed to the Apache Software Foundation (ASF) under one or more
3320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * contributor license agreements.  See the NOTICE file distributed with
4320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * this work for additional information regarding copyright ownership.
5320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * The ASF licenses this file to You under the Apache License, Version 2.0
6320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * (the "License"); you may not use this file except in compliance with
7320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * the License.  You may obtain a copy of the License at
8320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson *
9320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson *     http://www.apache.org/licenses/LICENSE-2.0
10320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson *
11320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * Unless required by applicable law or agreed to in writing, software
12320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS,
13320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * See the License for the specific language governing permissions and
15320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * limitations under the License.
16320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson */
17320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson// $Id: XPathFactoryFinder.java 670432 2008-06-23 02:02:08Z mrglavas $
18320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson
19320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonpackage javax.xml.xpath;
20320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson
21320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport java.io.BufferedReader;
22320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport java.io.File;
23670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilsonimport java.io.FileInputStream;
24320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport java.io.IOException;
25320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport java.io.InputStream;
26320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport java.io.InputStreamReader;
27320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport java.net.URL;
28670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilsonimport java.util.Collections;
29320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport java.util.Enumeration;
30320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport java.util.Iterator;
31320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport java.util.Properties;
32320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport javax.xml.validation.SchemaFactory;
33a7a70410e26802f3ab480b08a1ab499338cb6f7eJesse Wilsonimport libcore.io.IoUtils;
34320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson
35320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson/**
36320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * Implementation of {@link XPathFactory#newInstance(String)}.
37f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes *
38320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * @author <a href="Kohsuke.Kawaguchi@Sun.com">Kohsuke Kawaguchi</a>
39320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * @version $Revision: 670432 $, $Date: 2008-06-22 19:02:08 -0700 (Sun, 22 Jun 2008) $
40320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * @since 1.5
41320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson */
42320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonfinal class XPathFactoryFinder {
43f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
44320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /** debug support code. */
45320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    private static boolean debug = false;
46f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
47320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /**
48320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * Default columns per line.
49320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     */
50320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    private static final int DEFAULT_LINE_LENGTH = 80;
51f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
52320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    static {
53670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        String val = System.getProperty("jaxp.debug");
54670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        // Allow simply setting the prop to turn on debug
55670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        debug = val != null && (! "false".equals(val));
56320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    }
57320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson
58320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /**
59320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * <p>Cache properties for performance.</p>
60320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     */
61d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes    private static Properties cacheProps = new Properties();
62f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
63d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes    /**
64d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes     * <p>First time requires initialization overhead.</p>
65d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes     */
66d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes    private static boolean firstTime = true;
67f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
68320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /**
69320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * <p>Conditional debug printing.</p>
70f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
71320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * @param msg to print
72320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     */
73320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    private static void debugPrintln(String msg) {
74320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        if (debug) {
75320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            System.err.println("JAXP: " + msg);
76320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
77320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    }
78f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
79320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /**
80320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * <p><code>ClassLoader</code> to use to find <code>SchemaFactory</code>.</p>
81320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     */
82320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    private final ClassLoader classLoader;
83f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
84320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /**
85320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * <p>Constructor that specifies <code>ClassLoader</code> to use
86320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * to find <code>SchemaFactory</code>.</p>
87f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
88320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * @param loader
89320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     *      to be used to load resource, {@link SchemaFactory}, and
90670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson     *      {@code SchemaFactoryLoader} implementations during
91320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     *      the resolution process.
92320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     *      If this parameter is null, the default system class loader
93320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     *      will be used.
94320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     */
95320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    public XPathFactoryFinder(ClassLoader loader) {
96320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        this.classLoader = loader;
97670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        if (debug) {
98320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            debugDisplayClassLoader();
99320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
100320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    }
101f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
102320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    private void debugDisplayClassLoader() {
103670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        if (classLoader == Thread.currentThread().getContextClassLoader()) {
104670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson            debugPrintln("using thread context class loader (" + classLoader + ") for search");
105670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson            return;
106320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
107f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
108670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        if (classLoader==ClassLoader.getSystemClassLoader()) {
109670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson            debugPrintln("using system class loader (" + classLoader + ") for search");
110320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            return;
111320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
112320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson
113670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        debugPrintln("using class loader (" + classLoader + ") for search");
114320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    }
115f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
116320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /**
117320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * <p>Creates a new {@link XPathFactory} object for the specified
118320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * schema language.</p>
119f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
120320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * @param uri
121320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     *       Identifies the underlying object model.
122f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
123320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * @return <code>null</code> if the callee fails to create one.
124f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
125320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * @throws NullPointerException
126320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     *      If the parameter is null.
127320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     */
128320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    public XPathFactory newFactory(String uri) {
12986acc043d3334651ee26c65467d78d6cefedd397Kenny Root        if (uri == null) {
13086acc043d3334651ee26c65467d78d6cefedd397Kenny Root            throw new NullPointerException("uri == null");
13186acc043d3334651ee26c65467d78d6cefedd397Kenny Root        }
132320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        XPathFactory f = _newFactory(uri);
133320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        if (debug) {
134320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if (f != null) {
135320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                debugPrintln("factory '" + f.getClass().getName() + "' was found for " + uri);
136320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            } else {
137320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                debugPrintln("unable to find a factory for " + uri);
138320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            }
139320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
140320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        return f;
141320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    }
142f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
143320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /**
144320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * <p>Lookup a {@link XPathFactory} for the given object model.</p>
145f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
146320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * @param uri identifies the object model.
147320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     */
148320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    private XPathFactory _newFactory(String uri) {
149320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        XPathFactory xpf;
150320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        String propertyName = SERVICE_CLASS.getName() + ":" + uri;
151f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
152320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // system property look up
153320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        try {
154320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if (debug) debugPrintln("Looking up system property '"+propertyName+"'" );
155670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson            String r = System.getProperty(propertyName);
156320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if (r != null && r.length() > 0) {
157320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                if (debug) debugPrintln("The value is '"+r+"'");
158320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                xpf = createInstance(r);
159320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                if(xpf!=null)    return xpf;
160670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson            } else if (debug) {
161320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                debugPrintln("The property is undefined.");
162320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            }
163670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        } catch (Exception e) {
164670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson            e.printStackTrace();
165320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
166f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
167670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        String javah = System.getProperty("java.home");
168320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        String configFile = javah + File.separator +
169320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        "lib" + File.separator + "jaxp.properties";
170320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson
171320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        String factoryClassName = null ;
172320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson
173320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // try to read from $java.home/lib/jaxp.properties
174320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        try {
175320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if(firstTime){
176320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                synchronized(cacheProps){
177320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    if(firstTime){
178320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                        File f=new File( configFile );
179320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                        firstTime = false;
180670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson                        if (f.exists()) {
181f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                            if (debug) debugPrintln("Read properties file " + f);
182670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson                            cacheProps.load(new FileInputStream(f));
183320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                        }
184320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    }
185320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                }
186320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            }
187f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            factoryClassName = cacheProps.getProperty(propertyName);
188f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            if (debug) debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties");
189320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson
190320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if (factoryClassName != null) {
191320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                xpf = createInstance(factoryClassName);
192320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                if(xpf != null){
193320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    return xpf;
194320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                }
195320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            }
196320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        } catch (Exception ex) {
197320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if (debug) {
198320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                ex.printStackTrace();
199f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            }
200320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
201f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
202320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // try META-INF/services files
203670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        for (URL resource : createServiceFileIterator()) {
204320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if (debug) debugPrintln("looking into " + resource);
205320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            try {
206670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson                xpf = loadFromServicesFile(uri, resource.toExternalForm(), resource.openStream());
207320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                if(xpf!=null)    return xpf;
208320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            } catch(IOException e) {
209320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                if( debug ) {
210320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    debugPrintln("failed to read "+resource);
211320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    e.printStackTrace();
212320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                }
213320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            }
214320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
215f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
216320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // platform default
217320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        if(uri.equals(XPathFactory.DEFAULT_OBJECT_MODEL_URI)) {
218320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if (debug) debugPrintln("attempting to use the platform default W3C DOM XPath lib");
219320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            return createInstance("org.apache.xpath.jaxp.XPathFactoryImpl");
220320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
221f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
222320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        if (debug) debugPrintln("all things were tried, but none was found. bailing out.");
223320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        return null;
224320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    }
225f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
226320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /**
227320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * <p>Creates an instance of the specified and returns it.</p>
228f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
229320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * @param className
230670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson     *      fully qualified class name to be instantiated.
231f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
232320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * @return null
233f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *      if it fails. Error messages will be printed by this method.
234320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     */
235320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    XPathFactory createInstance( String className ) {
236320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        try {
237670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson            if (debug) debugPrintln("instantiating "+className);
238320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            Class clazz;
239320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if( classLoader!=null )
240320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                clazz = classLoader.loadClass(className);
241320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            else
242320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                clazz = Class.forName(className);
243320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if(debug)       debugPrintln("loaded it from "+which(clazz));
244320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            Object o = clazz.newInstance();
245f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
246320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if( o instanceof XPathFactory )
247320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                return (XPathFactory)o;
248f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
249320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if (debug) debugPrintln(className+" is not assignable to "+SERVICE_CLASS.getName());
250320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
251320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // The VM ran out of memory or there was some other serious problem. Re-throw.
252320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        catch (VirtualMachineError vme) {
253320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            throw vme;
254320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
255320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // ThreadDeath should always be re-thrown
256320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        catch (ThreadDeath td) {
257320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            throw td;
258320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
259320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        catch (Throwable t) {
260320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if (debug) {
261670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson                debugPrintln("failed to instantiate "+className);
262320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                t.printStackTrace();
263320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            }
264320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
265320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        return null;
266320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    }
267f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
268320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /** Searches for a XPathFactory for a given uri in a META-INF/services file. */
269320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    private XPathFactory loadFromServicesFile(String uri, String resourceName, InputStream in) {
270320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson
271320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        if (debug) debugPrintln("Reading " + resourceName );
272f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
273320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        BufferedReader rd;
274320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        try {
275320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            rd = new BufferedReader(new InputStreamReader(in, "UTF-8"), DEFAULT_LINE_LENGTH);
276320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        } catch (java.io.UnsupportedEncodingException e) {
277320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            rd = new BufferedReader(new InputStreamReader(in), DEFAULT_LINE_LENGTH);
278320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
279f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
280670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        String factoryClassName;
281320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        XPathFactory resultFactory = null;
282320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // See spec for provider-configuration files: http://java.sun.com/j2se/1.5.0/docs/guide/jar/jar.html#Provider%20Configuration%20File
283320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        while (true) {
284320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            try {
285f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                factoryClassName = rd.readLine();
286320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            } catch (IOException x) {
287320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                // No provider found
288320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                break;
289320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            }
290320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if (factoryClassName != null) {
291320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                // Ignore comments in the provider-configuration file
292320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                int hashIndex = factoryClassName.indexOf('#');
293320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                if (hashIndex != -1) {
294320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    factoryClassName = factoryClassName.substring(0, hashIndex);
295320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                }
296f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
297320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                // Ignore leading and trailing whitespace
298320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                factoryClassName = factoryClassName.trim();
299f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
300320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                // If there's no text left or if this was a blank line, go to the next one.
301320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                if (factoryClassName.length() == 0) {
302320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    continue;
303320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                }
304f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
305320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                try {
306320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    // Found the right XPathFactory if its isObjectModelSupported(String uri) method returns true.
307670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson                    XPathFactory foundFactory = createInstance(factoryClassName);
308320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    if (foundFactory.isObjectModelSupported(uri)) {
309320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                        resultFactory = foundFactory;
310320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                        break;
311320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    }
312670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson                } catch (Exception ignored) {
313320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                }
314320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            }
315320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            else {
316320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                break;
317320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            }
318320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
319f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
320a7a70410e26802f3ab480b08a1ab499338cb6f7eJesse Wilson        IoUtils.closeQuietly(rd);
321f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
322320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        return resultFactory;
323320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    }
324f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
325320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /**
326f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     * Returns an {@link Iterator} that enumerates all
327320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * the META-INF/services files that we care.
328320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     */
329670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson    private Iterable<URL> createServiceFileIterator() {
330320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        if (classLoader == null) {
331670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson            URL resource = XPathFactoryFinder.class.getClassLoader().getResource(SERVICE_ID);
332670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson            return Collections.singleton(resource);
333320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        } else {
334320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            try {
335670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson                Enumeration<URL> e = classLoader.getResources(SERVICE_ID);
336320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                if (debug && !e.hasMoreElements()) {
337320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    debugPrintln("no "+SERVICE_ID+" file was found");
338320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                }
339f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
340670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson                return Collections.list(e);
341320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            } catch (IOException e) {
342320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                if (debug) {
343320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    debugPrintln("failed to enumerate resources "+SERVICE_ID);
344320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    e.printStackTrace();
345320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                }
346670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson                return Collections.emptySet();
347320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            }
348320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
349320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    }
350f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
351320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    private static final Class SERVICE_CLASS = XPathFactory.class;
352320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    private static final String SERVICE_ID = "META-INF/services/" + SERVICE_CLASS.getName();
353f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
354320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    private static String which( Class clazz ) {
355320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        return which( clazz.getName(), clazz.getClassLoader() );
356320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    }
357320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson
358320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /**
359320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * <p>Search the specified classloader for the given classname.</p>
360320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     *
361320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * @param classname the fully qualified name of the class to search for
362320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * @param loader the classloader to search
363f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
364320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * @return the source location of the resource, or null if it wasn't found
365320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     */
366320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    private static String which(String classname, ClassLoader loader) {
367320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        String classnameAsResource = classname.replace('.', '/') + ".class";
368670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        if (loader==null) loader = ClassLoader.getSystemClassLoader();
369f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
370670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        URL it = loader.getResource(classnameAsResource);
371670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        return it != null ? it.toString() : null;
372320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    }
373320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson}
374