1 /*
2 * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26 package javax.xml.parsers;
27
28 import java.io.File;
29 import java.io.FileInputStream;
30
31 import java.util.Properties;
32 import java.io.BufferedReader;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.io.InputStreamReader;
36 import java.net.URL;
37
38 /**
39 * <p>Implements pluggable Datatypes.</p>
40 *
41 * <p>This class is duplicated for each JAXP subpackage so keep it in
42 * sync. It is package private for secure class loading.</p>
43 *
44 * @author Santiago.PericasGeertsen@sun.com
45 */
46 class FactoryFinder {
47
48 /**
49 * Internal debug flag.
50 */
51 private static boolean debug = false;
52
53 /**
54 * Cache for properties in java.home/lib/jaxp.properties
55 */
56 static Properties cacheProps = new Properties();
57
58 /**
59 * Flag indicating if properties from java.home/lib/jaxp.properties
60 * have been cached.
61 */
62 static boolean firstTime = true;
63
64 /**
65 * Security support class use to check access control before
66 * getting certain system resources.
67 */
68 static SecuritySupport ss = new SecuritySupport();
69
70 // Define system property "jaxp.debug" to get output
71 static {
72 // Use try/catch block to support applets, which throws
73 // SecurityException out of this code.
74 try {
75 String val = ss.getSystemProperty("jaxp.debug");
76 // Allow simply setting the prop to turn on debug
77 debug = val != null && !"false".equals(val);
78 }
79 catch (SecurityException se) {
80 debug = false;
81 }
82 }
83
84 private static void dPrint(String msg) {
85 if (debug) {
86 System.err.println("JAXP: " + msg);
87 }
88 }
89
90 /**
91 * Attempt to load a class using the class loader supplied. If that fails
92 * and fall back is enabled, the current (i.e. bootstrap) class loader is
93 * tried.
94 *
95 * If the class loader supplied is <code>null</code>, first try using the
96 * context class loader followed by the current (i.e. bootstrap) class
97 * loader.
98 */
99 static private Class getProviderClass(String className, ClassLoader cl,
100 boolean doFallback) throws ClassNotFoundException
101 {
102 try {
103 if (cl == null) {
104 cl = ss.getContextClassLoader();
105 if (cl == null) {
106 throw new ClassNotFoundException();
107 }
108 else {
109 return cl.loadClass(className);
110 }
111 }
112 else {
113 return cl.loadClass(className);
114 }
115 }
116 catch (ClassNotFoundException e1) {
117 if (doFallback) {
118 // Use current class loader - should always be bootstrap CL
119 return Class.forName(className, true, FactoryFinder.class.getClassLoader());
120 }
121 else {
122 throw e1;
123 }
124 }
125 }
126
127 /**
128 * Create an instance of a class. Delegates to method
129 * <code>getProviderClass()</code> in order to load the class.
130 *
131 * @param className Name of the concrete class corresponding to the
132 * service provider
133 *
134 * @param cl ClassLoader to use to load the class, null means to use
135 * the bootstrap ClassLoader
136 *
137 * @param doFallback True if the current ClassLoader should be tried as
138 * a fallback if the class is not found using cl
139 */
140 static Object newInstance(String className, ClassLoader cl, boolean doFallback)
141 throws ConfigurationError
142 {
143 try {
144 Class providerClass = getProviderClass(className, cl, doFallback);
145 Object instance = providerClass.newInstance();
146 if (debug) { // Extra check to avoid computing cl strings
147 dPrint("created new instance of " + providerClass +
148 " using ClassLoader: " + cl);
149 }
150 return instance;
151 }
152 catch (ClassNotFoundException x) {
153 throw new ConfigurationError(
154 "Provider " + className + " not found", x);
155 }
156 catch (Exception x) {
157 throw new ConfigurationError(
158 "Provider " + className + " could not be instantiated: " + x,
159 x);
160 }
161 }
162
163 /**
164 * Finds the implementation Class object in the specified order. Main
165 * entry point.
166 * @return Class object of factory, never null
167 *
168 * @param factoryId Name of the factory to find, same as
169 * a property name
170 * @param fallbackClassName Implementation class name, if nothing else
171 * is found. Use null to mean no fallback.
172 *
173 * Package private so this code can be shared.
174 */
175 static Object find(String factoryId, String fallbackClassName)
176 throws ConfigurationError
177 {
178 dPrint("find factoryId =" + factoryId);
179
180 // Use the system property first
181 try {
182 String systemProp = ss.getSystemProperty(factoryId);
183 if (systemProp != null) {
184 dPrint("found system property, value=" + systemProp);
185 return newInstance(systemProp, null, true);
186 }
187 }
188 catch (SecurityException se) {
189 if (debug) se.printStackTrace();
190 }
191
192 // try to read from $java.home/lib/jaxp.properties
193 try {
194 String factoryClassName = null;
195 if (firstTime) {
196 synchronized (cacheProps) {
197 if (firstTime) {
198 String configFile = ss.getSystemProperty("java.home") + File.separator +
199 "lib" + File.separator + "jaxp.properties";
200 File f = new File(configFile);
201 firstTime = false;
202 if (ss.doesFileExist(f)) {
203 dPrint("Read properties file "+f);
204 cacheProps.load(ss.getFileInputStream(f));
205 }
206 }
207 }
208 }
209 factoryClassName = cacheProps.getProperty(factoryId);
210
211 if (factoryClassName != null) {
212 dPrint("found in $java.home/jaxp.properties, value=" + factoryClassName);
213 return newInstance(factoryClassName, null, true);
214 }
215 }
216 catch (Exception ex) {
217 if (debug) ex.printStackTrace();
218 }
219
220 // Try Jar Service Provider Mechanism
221 Object provider = findJarServiceProvider(factoryId);
222 if (provider != null) {
223 return provider;
224 }
225 if (fallbackClassName == null) {
226 throw new ConfigurationError(
227 "Provider for " + factoryId + " cannot be found", null);
228 }
229
230 dPrint("loaded from fallback value: " + fallbackClassName);
231 return newInstance(fallbackClassName, null, true);
232 }
233
234 /*
235 * Try to find provider using Jar Service Provider Mechanism
236 *
237 * @return instance of provider class if found or null
238 */
239 private static Object findJarServiceProvider(String factoryId)
240 throws ConfigurationError
241 {
242 String serviceId = "META-INF/services/" + factoryId;
243 InputStream is = null;
244
245 // First try the Context ClassLoader
246 ClassLoader cl = ss.getContextClassLoader();
247 if (cl != null) {
248 is = ss.getResourceAsStream(cl, serviceId);
249
250 // If no provider found then try the current ClassLoader
251 if (is == null) {
252 cl = FactoryFinder.class.getClassLoader();
253 is = ss.getResourceAsStream(cl, serviceId);
254 }
255 } else {
256 // No Context ClassLoader, try the current ClassLoader
257 cl = FactoryFinder.class.getClassLoader();
258 is = ss.getResourceAsStream(cl, serviceId);
259 }
260
261 if (is == null) {
262 // No provider found
263 return null;
264 }
265
266 if (debug) { // Extra check to avoid computing cl strings
267 dPrint("found jar resource=" + serviceId + " using ClassLoader: " + cl);
268 }
269
270 BufferedReader rd;
271 try {
272 rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
273 }
274 catch (java.io.UnsupportedEncodingException e) {
275 rd = new BufferedReader(new InputStreamReader(is));
276 }
277
278 String factoryClassName = null;
279 try {
280 // XXX Does not handle all possible input as specified by the
281 // Jar Service Provider specification
282 factoryClassName = rd.readLine();
283 rd.close();
284 } catch (IOException x) {
285 // No provider found
286 return null;
287 }
288
289 if (factoryClassName != null && !"".equals(factoryClassName)) {
290 dPrint("found in resource, value=" + factoryClassName);
291
292 // Note: here we do not want to fall back to the current
293 // ClassLoader because we want to avoid the case where the
294 // resource file was found using one ClassLoader and the
295 // provider class was instantiated using a different one.
296 return newInstance(factoryClassName, cl, false);
297 }
298
299 // No provider found
300 return null;
301 }
302
303 static class ConfigurationError extends Error {
304 private Exception exception;
305
306 /**
307 * Construct a new instance with the specified detail string and
308 * exception.
309 */
310 ConfigurationError(String msg, Exception x) {
311 super(msg);
312 this.exception = x;
313 }
314
315 Exception getException() {
316 return exception;
317 }
318 }
319
320 }