2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 // $Id: FactoryFinder.java 670432 2008-06-23 02:02:08Z mrglavas $
20 package javax.xml.datatype;
22 import java.io.BufferedReader;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.InputStreamReader;
28 import java.util.Properties;
31 * <p>Implement pluggabile Datatypes.</p>
33 * <p>This class is duplicated for each JAXP subpackage so keep it in
34 * sync. It is package private for secure class loading.</p>
36 * @author <a href="mailto:Jeff.Suttor@Sun.com">Jeff Suttor</a>
37 * @version $Revision: 670432 $, $Date: 2008-06-22 19:02:08 -0700 (Sun, 22 Jun 2008) $
40 final class FactoryFinder {
43 * <p>Name of class to display in output messages.</p>
45 private static final String CLASS_NAME = "javax.xml.datatype.FactoryFinder";
48 * <p>Debug flag to trace loading process.</p>
50 private static boolean debug = false;
53 * <p>Cache properties for performance.</p>
55 private static Properties cacheProps = new Properties();
58 * <p>First time requires initialization overhead.</p>
60 private static boolean firstTime = true;
63 * Default columns per line.
65 private static final int DEFAULT_LINE_LENGTH = 80;
68 * <p>Check to see if debugging enabled by property.</p>
70 * <p>Use try/catch block to support applets, which throws
71 * SecurityException out of this code.</p>
76 String val = SecuritySupport.getSystemProperty("jaxp.debug");
77 // Allow simply setting the prop to turn on debug
78 debug = val != null && (! "false".equals(val));
79 } catch (Exception x) {
84 private FactoryFinder() {}
87 * <p>Output debugging messages.</p>
89 * @param msg <code>String</code> to print to <code>stderr</code>.
91 private static void debugPrintln(String msg) {
101 * <p>Find the appropriate <code>ClassLoader</code> to use.</p>
103 * <p>The context ClassLoader is prefered.</p>
105 * @return <code>ClassLoader</code> to use.
107 * @throws ConfigurationError If a valid <code>ClassLoader</code> cannot be identified.
109 private static ClassLoader findClassLoader()
110 throws ConfigurationError {
111 ClassLoader classLoader;
113 // Figure out which ClassLoader to use for loading the provider
114 // class. If there is a Context ClassLoader then use it.
116 classLoader = SecuritySupport.getContextClassLoader();
118 if (debug) debugPrintln(
119 "Using context class loader: "
122 if (classLoader == null) {
123 // if we have no Context ClassLoader
124 // so use the current ClassLoader
125 classLoader = FactoryFinder.class.getClassLoader();
126 if (debug) debugPrintln(
127 "Using the class loader of FactoryFinder: "
135 * <p>Create an instance of a class using the specified ClassLoader.</p>
137 * @param className Name of class to create.
138 * @param classLoader ClassLoader to use to create named class.
140 * @return New instance of specified class created using the specified ClassLoader.
142 * @throws ConfigurationError If class could not be created.
144 static Object newInstance(
146 ClassLoader classLoader)
147 throws ConfigurationError {
151 if (classLoader == null) {
152 spiClass = Class.forName(className);
154 spiClass = classLoader.loadClass(className);
158 debugPrintln("Loaded " + className + " from " + which(spiClass));
161 return spiClass.newInstance();
162 } catch (ClassNotFoundException x) {
163 throw new ConfigurationError(
164 "Provider " + className + " not found", x);
165 } catch (Exception x) {
166 throw new ConfigurationError(
167 "Provider " + className + " could not be instantiated: " + x,
173 * Finds the implementation Class object in the specified order. Main
175 * Package private so this code can be shared.
177 * @param factoryId Name of the factory to find, same as a property name
178 * @param fallbackClassName Implementation class name, if nothing else is found. Use null to mean no fallback.
180 * @return Class Object of factory, never null
182 * @throws ConfigurationError If Class cannot be found.
184 static Object find(String factoryId, String fallbackClassName)
185 throws ConfigurationError {
187 ClassLoader classLoader = findClassLoader();
189 // Use the system property first
191 String systemProp = SecuritySupport.getSystemProperty(factoryId);
192 if (systemProp != null && systemProp.length() > 0) {
193 if (debug) debugPrintln("found " + systemProp + " in the system property " + factoryId);
194 return newInstance(systemProp, classLoader);
196 } catch (SecurityException se) {
197 ; // NOP, explicitly ignore SecurityException
200 // try to read from $java.home/lib/jaxp.properties
202 String javah = SecuritySupport.getSystemProperty("java.home");
203 String configFile = javah + File.separator + "lib" + File.separator + "jaxp.properties";
204 String factoryClassName = null;
206 synchronized (cacheProps) {
208 File f = new File(configFile);
210 if (SecuritySupport.doesFileExist(f)) {
211 if (debug) debugPrintln("Read properties file " + f);
212 cacheProps.load(SecuritySupport.getFileInputStream(f));
217 factoryClassName = cacheProps.getProperty(factoryId);
218 if (debug) debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties");
220 if (factoryClassName != null) {
221 return newInstance(factoryClassName, classLoader);
223 } catch (Exception ex) {
225 ex.printStackTrace();
229 // Try Jar Service Provider Mechanism
230 Object provider = findJarServiceProvider(factoryId);
231 if (provider != null) {
235 if (fallbackClassName == null) {
236 throw new ConfigurationError(
237 "Provider for " + factoryId + " cannot be found", null);
240 if (debug) debugPrintln("loaded from fallback value: " + fallbackClassName);
241 return newInstance(fallbackClassName, classLoader);
245 * Try to find provider using Jar Service Provider Mechanism
247 * @return instance of provider class if found or null
249 private static Object findJarServiceProvider(String factoryId)
250 throws ConfigurationError
253 String serviceId = "META-INF/services/" + factoryId;
254 InputStream is = null;
256 // First try the Context ClassLoader
257 ClassLoader cl = SecuritySupport.getContextClassLoader();
259 is = SecuritySupport.getResourceAsStream(cl, serviceId);
261 // If no provider found then try the current ClassLoader
263 cl = FactoryFinder.class.getClassLoader();
264 is = SecuritySupport.getResourceAsStream(cl, serviceId);
267 // No Context ClassLoader, try the current
269 cl = FactoryFinder.class.getClassLoader();
270 is = SecuritySupport.getResourceAsStream(cl, serviceId);
278 if (debug) debugPrintln("found jar resource=" + serviceId +
279 " using ClassLoader: " + cl);
283 rd = new BufferedReader(new InputStreamReader(is, "UTF-8"), DEFAULT_LINE_LENGTH);
284 } catch (java.io.UnsupportedEncodingException e) {
285 rd = new BufferedReader(new InputStreamReader(is), DEFAULT_LINE_LENGTH);
288 String factoryClassName = null;
290 // XXX Does not handle all possible input as specified by the
291 // Jar Service Provider specification
292 factoryClassName = rd.readLine();
294 catch (IOException x) {
300 // try to close the reader.
303 // Ignore the exception.
304 catch (IOException exc) {}
307 if (factoryClassName != null &&
308 ! "".equals(factoryClassName)) {
309 if (debug) debugPrintln("found in resource, value="
312 return newInstance(factoryClassName, cl);
320 * <p>Configuration Error.</p>
322 static class ConfigurationError extends Error {
324 private static final long serialVersionUID = -3644413026244211347L;
327 * <p>Exception that caused the error.</p>
329 private Exception exception;
332 * <p>Construct a new instance with the specified detail string and
335 * @param msg Detail message for this error.
336 * @param x Exception that caused the error.
338 ConfigurationError(String msg, Exception x) {
344 * <p>Get the Exception that caused the error.</p>
346 * @return Exception that caused the error.
348 Exception getException() {
356 * Returns the location where the given Class is loaded from.
358 * @param clazz Class to find load location.
360 * @return Location where class would be loaded from.
362 private static String which(Class clazz) {
364 String classnameAsResource = clazz.getName().replace('.', '/') + ".class";
366 ClassLoader loader = clazz.getClassLoader();
370 if (loader != null) {
371 it = loader.getResource(classnameAsResource);
373 it = ClassLoader.getSystemResource(classnameAsResource);
377 return it.toString();
380 // The VM ran out of memory or there was some other serious problem. Re-throw.
381 catch (VirtualMachineError vme) {
384 // ThreadDeath should always be re-thrown
385 catch (ThreadDeath td) {
388 catch (Throwable t) {
394 return "unknown location";