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: SchemaFactoryFinder.java 727367 2008-12-17 13:05:26Z mrglavas $
18320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson
19320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonpackage javax.xml.validation;
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.XMLConstants;
33a7a70410e26802f3ab480b08a1ab499338cb6f7eJesse Wilsonimport libcore.io.IoUtils;
34320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson
35320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson/**
36320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * Implementation of {@link SchemaFactory#newInstance(String)}.
37f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes *
38320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * @author <a href="Kohsuke.Kawaguchi@Sun.com">Kohsuke Kawaguchi</a>
39320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * @version $Revision: 727367 $, $Date: 2008-12-17 05:05:26 -0800 (Wed, 17 Dec 2008) $
40320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * @since 1.5
41320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson */
42320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonfinal class SchemaFactoryFinder  {
43f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
44320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /** XML Schema language identifiers. */
45320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    private static final String W3C_XML_SCHEMA10_NS_URI = "http://www.w3.org/XML/XMLSchema/v1.0";
46f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes    private static final String W3C_XML_SCHEMA11_NS_URI = "http://www.w3.org/XML/XMLSchema/v1.1";
47320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson
48320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /** debug support code. */
49320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    private static boolean debug = false;
50320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson
51320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /**
52320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * <p>Cache properties for performance.</p>
53320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     */
54d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes    private static Properties cacheProps = new Properties();
55f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
56d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes    /**
57d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes     * <p>First time requires initialization overhead.</p>
58d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes     */
59d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes    private static boolean firstTime = true;
60f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
61320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /**
62320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * Default columns per line.
63320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     */
64320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    private static final int DEFAULT_LINE_LENGTH = 80;
65f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
66320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    static {
67670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        String val = System.getProperty("jaxp.debug");
68670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        // Allow simply setting the prop to turn on debug
69670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        debug = val != null && (! "false".equals(val));
70320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    }
71320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson
72320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /**
73320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * <p>Conditional debug printing.</p>
74f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
75320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * @param msg to print
76320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     */
77320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    private static void debugPrintln(String msg) {
78320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        if (debug) {
79320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            System.err.println("JAXP: " + msg);
80320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
81320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    }
82f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
83320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /**
84320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * <p><code>ClassLoader</code> to use to find <code>SchemaFactory</code>.</p>
85320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     */
86320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    private final ClassLoader classLoader;
87f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
88320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /**
89320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * <p>Constructor that specifies <code>ClassLoader</code> to use
90320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * to find <code>SchemaFactory</code>.</p>
91f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
92320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * @param loader
93320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     *      to be used to load resource, {@link SchemaFactory}, and
94320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     *      {@link SchemaFactoryLoader} implementations during
95320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     *      the resolution process.
96320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     *      If this parameter is null, the default system class loader
97320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     *      will be used.
98320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     */
99320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    public SchemaFactoryFinder(ClassLoader loader) {
100320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        this.classLoader = loader;
101320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        if( debug ) {
102320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            debugDisplayClassLoader();
103320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
104320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    }
105f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
106320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    private void debugDisplayClassLoader() {
107670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        if (classLoader == Thread.currentThread().getContextClassLoader()) {
108670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson            debugPrintln("using thread context class loader ("+classLoader+") for search");
109670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson            return;
110320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
111f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
112670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        if (classLoader == ClassLoader.getSystemClassLoader()) {
113320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            debugPrintln("using system class loader ("+classLoader+") for search");
114320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            return;
115320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
116320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson
117670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        debugPrintln("using class loader (" + classLoader + ") for search");
118320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    }
119f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
120320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /**
121320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * <p>Creates a new {@link SchemaFactory} object for the specified
122320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * schema language.</p>
123f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
124320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * @param schemaLanguage
125320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     *      See {@link SchemaFactory Schema Language} table in <code>SchemaFactory</code>
126320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     *      for the list of available schema languages.
127f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
128320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * @return <code>null</code> if the callee fails to create one.
129f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
130320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * @throws NullPointerException
131320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     *      If the <tt>schemaLanguage</tt> parameter is null.
132320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     */
133320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    public SchemaFactory newFactory(String schemaLanguage) {
13486acc043d3334651ee26c65467d78d6cefedd397Kenny Root        if (schemaLanguage == null) {
13586acc043d3334651ee26c65467d78d6cefedd397Kenny Root            throw new NullPointerException("schemaLanguage == null");
13686acc043d3334651ee26c65467d78d6cefedd397Kenny Root        }
137320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        SchemaFactory f = _newFactory(schemaLanguage);
138320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        if (debug) {
139320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if (f != null) {
140320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                debugPrintln("factory '" + f.getClass().getName() + "' was found for " + schemaLanguage);
141320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            } else {
142320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                debugPrintln("unable to find a factory for " + schemaLanguage);
143320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            }
144320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
145320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        return f;
146320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    }
147f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
148320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /**
149320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * <p>Lookup a <code>SchemaFactory</code> for the given <code>schemaLanguage</code>.</p>
150f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
151320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * @param schemaLanguage Schema language to lookup <code>SchemaFactory</code> for.
152f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
153320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * @return <code>SchemaFactory</code> for the given <code>schemaLanguage</code>.
154320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     */
155320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    private SchemaFactory _newFactory(String schemaLanguage) {
156320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        SchemaFactory sf;
157320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        String propertyName = SERVICE_CLASS.getName() + ":" + schemaLanguage;
158f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
159320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // system property look up
160320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        try {
161320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if (debug) debugPrintln("Looking up system property '"+propertyName+"'" );
162670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson            String r = System.getProperty(propertyName);
163320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if (r != null && r.length() > 0) {
164320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                if (debug) debugPrintln("The value is '"+r+"'");
165320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                sf = createInstance(r);
166320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                if(sf!=null)    return sf;
167f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            }
168320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            else if (debug) {
169320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                debugPrintln("The property is undefined.");
170320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            }
171320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
172320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // The VM ran out of memory or there was some other serious problem. Re-throw.
173320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        catch (VirtualMachineError vme) {
174320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            throw vme;
175320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
176320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // ThreadDeath should always be re-thrown
177320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        catch (ThreadDeath td) {
178320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            throw td;
179320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
180320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        catch (Throwable t) {
181320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if( debug ) {
182320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                debugPrintln("failed to look up system property '"+propertyName+"'" );
183320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                t.printStackTrace();
184320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            }
185320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
186320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson
187670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        String javah = System.getProperty("java.home");
188320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        String configFile = javah + File.separator +
189320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        "lib" + File.separator + "jaxp.properties";
190320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson
191320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        String factoryClassName = null ;
192320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson
193320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // try to read from $java.home/lib/jaxp.properties
194320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        try {
195320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if(firstTime){
196320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                synchronized(cacheProps){
197320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    if(firstTime){
198320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                        File f=new File( configFile );
199320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                        firstTime = false;
200670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson                        if(f.exists()){
201f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                            if (debug) debugPrintln("Read properties file " + f);
202670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson                            cacheProps.load(new FileInputStream(f));
203320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                        }
204320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    }
205320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                }
206320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            }
207f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            factoryClassName = cacheProps.getProperty(propertyName);
208f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            if (debug) debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties");
209320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson
210320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if (factoryClassName != null) {
211320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                sf = createInstance(factoryClassName);
212320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                if(sf != null){
213320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    return sf;
214320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                }
215320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            }
216320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        } catch (Exception ex) {
217320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if (debug) {
218320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                ex.printStackTrace();
219f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            }
220320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
221320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson
222320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // try META-INF/services files
223670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        for (URL resource : createServiceFileIterator()) {
224320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if (debug) debugPrintln("looking into " + resource);
225320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            try {
226670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson                sf = loadFromServicesFile(schemaLanguage,resource.toExternalForm(),
227670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson                        resource.openStream());
228320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                if(sf!=null)    return sf;
229320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            } catch(IOException e) {
230320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                if( debug ) {
231320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    debugPrintln("failed to read "+resource);
232320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    e.printStackTrace();
233320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                }
234320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            }
235320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
236f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
237320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // platform defaults
238320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        if (schemaLanguage.equals(XMLConstants.W3C_XML_SCHEMA_NS_URI) || schemaLanguage.equals(W3C_XML_SCHEMA10_NS_URI)) {
239320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if (debug) debugPrintln("attempting to use the platform default XML Schema 1.0 validator");
240320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            return createInstance("org.apache.xerces.jaxp.validation.XMLSchemaFactory");
241320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
242320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        else if (schemaLanguage.equals(W3C_XML_SCHEMA11_NS_URI)) {
243320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if (debug) debugPrintln("attempting to use the platform default XML Schema 1.1 validator");
244320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            return createInstance("org.apache.xerces.jaxp.validation.XMLSchema11Factory");
245320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
246f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
247320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        if (debug) debugPrintln("all things were tried, but none was found. bailing out.");
248320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        return null;
249320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    }
250f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
251320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /**
252320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * <p>Creates an instance of the specified and returns it.</p>
253f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
254320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * @param className
255320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     *      fully qualified class name to be instantiated.
256f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
257320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * @return null
258f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *      if it fails. Error messages will be printed by this method.
259320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     */
260320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    SchemaFactory createInstance( String className ) {
261320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        try {
262670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson            if (debug) debugPrintln("instantiating "+className);
263320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            Class clazz;
264320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if( classLoader!=null )
265320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                clazz = classLoader.loadClass(className);
266320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            else
267320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                clazz = Class.forName(className);
268320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if(debug)       debugPrintln("loaded it from "+which(clazz));
269320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            Object o = clazz.newInstance();
270f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
271320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if( o instanceof SchemaFactory )
272320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                return (SchemaFactory)o;
273f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
274320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if (debug) debugPrintln(className+" is not assignable to "+SERVICE_CLASS.getName());
275320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
276320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // The VM ran out of memory or there was some other serious problem. Re-throw.
277320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        catch (VirtualMachineError vme) {
278320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            throw vme;
279320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
280320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // ThreadDeath should always be re-thrown
281320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        catch (ThreadDeath td) {
282320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            throw td;
283320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
284320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        catch (Throwable t) {
285670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson            debugPrintln("failed to instantiate "+className);
286320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if(debug)   t.printStackTrace();
287320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
288320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        return null;
289320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    }
290f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
291320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /**
292f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     * Returns an {@link Iterator} that enumerates all
293320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * the META-INF/services files that we care.
294320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     */
295670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson    private Iterable<URL> createServiceFileIterator() {
296320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        if (classLoader == null) {
297670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson            ClassLoader classLoader = SchemaFactoryFinder.class.getClassLoader();
298670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson            return Collections.singleton(classLoader.getResource(SERVICE_ID));
299320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        } else {
300320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            try {
301670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson                Enumeration<URL> e = classLoader.getResources(SERVICE_ID);
302670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson                if (debug && !e.hasMoreElements()) {
303320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    debugPrintln("no "+SERVICE_ID+" file was found");
304320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                }
305f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
306320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                // wrap it into an Iterator.
307670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson                return Collections.list(e);
308320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            } catch (IOException e) {
309320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                if (debug) {
310320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    debugPrintln("failed to enumerate resources "+SERVICE_ID);
311320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    e.printStackTrace();
312320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                }
313670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson                return Collections.emptySet();
314320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            }
315320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
316320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    }
317f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
318320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /** Searches for a SchemaFactory for a given schema language in a META-INF/services file. */
319320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    private SchemaFactory loadFromServicesFile(String schemaLanguage, String resourceName, InputStream in) {
320320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson
321320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        if (debug) debugPrintln("Reading "+resourceName );
322f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
323320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // Read the service provider name in UTF-8 as specified in
324320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // the jar spec.  Unfortunately this fails in Microsoft
325320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // VJ++, which does not implement the UTF-8
326320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // encoding. Theoretically, we should simply let it fail in
327320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // that case, since the JVM is obviously broken if it
328320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // doesn't support such a basic standard.  But since there
329320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // are still some users attempting to use VJ++ for
330320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // development, we have dropped in a fallback which makes a
331320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // second attempt using the platform's default encoding. In
332320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // VJ++ this is apparently ASCII, which is a subset of
333320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // UTF-8... and since the strings we'll be reading here are
334320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // also primarily limited to the 7-bit ASCII range (at
335320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // least, in English versions), this should work well
336320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // enough to keep us on the air until we're ready to
337320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // officially decommit from VJ++. [Edited comment from
338320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // jkesselm]
339320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        BufferedReader rd;
340320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        try {
341320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            rd = new BufferedReader(new InputStreamReader(in, "UTF-8"), DEFAULT_LINE_LENGTH);
342320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        } catch (java.io.UnsupportedEncodingException e) {
343320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            rd = new BufferedReader(new InputStreamReader(in), DEFAULT_LINE_LENGTH);
344320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
345f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
346320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        String factoryClassName = null;
347320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        SchemaFactory resultFactory = null;
348320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        // See spec for provider-configuration files: http://java.sun.com/j2se/1.5.0/docs/guide/jar/jar.html#Provider%20Configuration%20File
349320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        while (true) {
350320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            try {
351f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                factoryClassName = rd.readLine();
352320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            } catch (IOException x) {
353320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                // No provider found
354320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                break;
355320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            }
356320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            if (factoryClassName != null) {
357320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                // Ignore comments in the provider-configuration file
358320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                int hashIndex = factoryClassName.indexOf('#');
359320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                if (hashIndex != -1) {
360320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    factoryClassName = factoryClassName.substring(0, hashIndex);
361320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                }
362f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
363320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                // Ignore leading and trailing whitespace
364320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                factoryClassName = factoryClassName.trim();
365f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
366320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                // If there's no text left or if this was a blank line, go to the next one.
367320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                if (factoryClassName.length() == 0) {
368320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    continue;
369320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                }
370f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
371320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                try {
372320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    // Found the right SchemaFactory if its isSchemaLanguageSupported(schemaLanguage) method returns true.
373320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    SchemaFactory foundFactory = (SchemaFactory) createInstance(factoryClassName);
374320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    if (foundFactory.isSchemaLanguageSupported(schemaLanguage)) {
375320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                        resultFactory = foundFactory;
376320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                        break;
377320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                    }
378320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                }
379670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson                catch (Exception ignored) {}
380320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            }
381320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            else {
382320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson                break;
383320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson            }
384320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        }
385f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
386a7a70410e26802f3ab480b08a1ab499338cb6f7eJesse Wilson        IoUtils.closeQuietly(rd);
387f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
388320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        return resultFactory;
389320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    }
390f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
391320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    private static final Class SERVICE_CLASS = SchemaFactory.class;
392320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    private static final String SERVICE_ID = "META-INF/services/" + SERVICE_CLASS.getName();
393f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
394320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    private static String which( Class clazz ) {
395320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        return which( clazz.getName(), clazz.getClassLoader() );
396320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    }
397320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson
398320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    /**
399320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * <p>Search the specified classloader for the given classname.</p>
400320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     *
401320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * @param classname the fully qualified name of the class to search for
402320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * @param loader the classloader to search
403f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
404320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     * @return the source location of the resource, or null if it wasn't found
405320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson     */
406320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    private static String which(String classname, ClassLoader loader) {
407320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson        String classnameAsResource = classname.replace('.', '/') + ".class";
408f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
409670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        if (loader == null)  loader = ClassLoader.getSystemClassLoader();
410f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
411670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        URL it = loader.getResource(classnameAsResource);
412670c354edf01dcda0d9c4363a0996923bb30169bJesse Wilson        return it != null ? it.toString() : null;
413320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson    }
414320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson}
415