blob: eaeaa977b519cd1c680570a8a785e604d28a8246 [file] [log] [blame]
ohair6c320662012-03-04 11:55:34 -08001/*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5/*
6 * Copyright 2001-2005 The Apache Software Foundation.
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21package com.sun.org.apache.xerces.internal.dom;
22
23import java.io.InputStream;
24import java.io.IOException;
25import java.io.File;
26import java.io.FileInputStream;
27
28import java.util.Properties;
29import java.io.BufferedReader;
30import java.io.InputStreamReader;
31
32/**
33 * This class is duplicated for each JAXP subpackage so keep it in sync.
34 * It is package private and therefore is not exposed as part of the JAXP
35 * API.
36 * <p>
37 * This code is designed to implement the JAXP 1.1 spec pluggability
38 * feature and is designed to run on JDK version 1.1 and
39 * later, and to compile on JDK 1.2 and onward.
40 * The code also runs both as part of an unbundled jar file and
41 * when bundled as part of the JDK.
42 * <p>
43 *
44 * @xerces.internal
45 *
46 */
47final class ObjectFactory {
48
49 //
50 // Constants
51 //
52
53 // name of default properties file to look for in JDK's jre/lib directory
54 private static final String DEFAULT_PROPERTIES_FILENAME = "xerces.properties";
55
56 /** Set to true for debugging */
57 private static final boolean DEBUG = false;
58
59 /**
60 * Default columns per line.
61 */
62 private static final int DEFAULT_LINE_LENGTH = 80;
63
64 /** cache the contents of the xerces.properties file.
65 * Until an attempt has been made to read this file, this will
66 * be null; if the file does not exist or we encounter some other error
67 * during the read, this will be empty.
68 */
69 private static Properties fXercesProperties = null;
70
71 /***
72 * Cache the time stamp of the xerces.properties file so
73 * that we know if it's been modified and can invalidate
74 * the cache when necessary.
75 */
76 private static long fLastModified = -1;
77
78 //
79 // static methods
80 //
81
82 /**
83 * Finds the implementation Class object in the specified order. The
84 * specified order is the following:
85 * <ol>
86 * <li>query the system property using <code>System.getProperty</code>
87 * <li>read <code>META-INF/services/<i>factoryId</i></code> file
88 * <li>use fallback classname
89 * </ol>
90 *
91 * @return Class object of factory, never null
92 *
93 * @param factoryId Name of the factory to find, same as
94 * a property name
95 * @param fallbackClassName Implementation class name, if nothing else
96 * is found. Use null to mean no fallback.
97 *
98 * @exception ObjectFactory.ConfigurationError
99 */
100 static Object createObject(String factoryId, String fallbackClassName)
101 throws ConfigurationError {
102 return createObject(factoryId, null, fallbackClassName);
103 } // createObject(String,String):Object
104
105 /**
106 * Finds the implementation Class object in the specified order. The
107 * specified order is the following:
108 * <ol>
109 * <li>query the system property using <code>System.getProperty</code>
110 * <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
111 * <li>read <code>META-INF/services/<i>factoryId</i></code> file
112 * <li>use fallback classname
113 * </ol>
114 *
115 * @return Class object of factory, never null
116 *
117 * @param factoryId Name of the factory to find, same as
118 * a property name
119 * @param propertiesFilename The filename in the $java.home/lib directory
120 * of the properties file. If none specified,
121 * ${java.home}/lib/xerces.properties will be used.
122 * @param fallbackClassName Implementation class name, if nothing else
123 * is found. Use null to mean no fallback.
124 *
125 * @exception ObjectFactory.ConfigurationError
126 */
127 static Object createObject(String factoryId,
128 String propertiesFilename,
129 String fallbackClassName)
130 throws ConfigurationError
131 {
132 if (DEBUG) debugPrintln("debug is on");
133
134 SecuritySupport ss = SecuritySupport.getInstance();
135 ClassLoader cl = findClassLoader();
136
137 // Use the system property first
138 try {
139 String systemProp = ss.getSystemProperty(factoryId);
140 if (systemProp != null) {
141 if (DEBUG) debugPrintln("found system property, value=" + systemProp);
142 return newInstance(systemProp, cl, true);
143 }
144 } catch (SecurityException se) {
145 // Ignore and continue w/ next location
146 }
147
148 // JAXP specific change
149 // always use fallback class to avoid the expense of constantly
150 // "stat"ing a non-existent "xerces.properties" and jar SPI entry
151 // see CR 6400863: Expensive creating of SAX parser in Mustang
152 if (true) {
153 if (fallbackClassName == null) {
154 throw new ConfigurationError(
155 "Provider for " + factoryId + " cannot be found", null);
156 }
157
158 if (DEBUG) debugPrintln("using fallback, value=" + fallbackClassName);
159 return newInstance(fallbackClassName, cl, true);
160 }
161
162 // Try to read from propertiesFilename, or $java.home/lib/xerces.properties
163 String factoryClassName = null;
164 // no properties file name specified; use $JAVA_HOME/lib/xerces.properties:
165 if (propertiesFilename == null) {
166 File propertiesFile = null;
167 boolean propertiesFileExists = false;
168 try {
169 String javah = ss.getSystemProperty("java.home");
170 propertiesFilename = javah + File.separator +
171 "lib" + File.separator + DEFAULT_PROPERTIES_FILENAME;
172 propertiesFile = new File(propertiesFilename);
173 propertiesFileExists = ss.getFileExists(propertiesFile);
174 } catch (SecurityException e) {
175 // try again...
176 fLastModified = -1;
177 fXercesProperties = null;
178 }
179
180 synchronized (ObjectFactory.class) {
181 boolean loadProperties = false;
182 FileInputStream fis = null;
183 try {
184 // file existed last time
185 if(fLastModified >= 0) {
186 if(propertiesFileExists &&
187 (fLastModified < (fLastModified = ss.getLastModified(propertiesFile)))) {
188 loadProperties = true;
189 } else {
190 // file has stopped existing...
191 if(!propertiesFileExists) {
192 fLastModified = -1;
193 fXercesProperties = null;
194 } // else, file wasn't modified!
195 }
196 } else {
197 // file has started to exist:
198 if(propertiesFileExists) {
199 loadProperties = true;
200 fLastModified = ss.getLastModified(propertiesFile);
201 } // else, nothing's changed
202 }
203 if(loadProperties) {
204 // must never have attempted to read xerces.properties before (or it's outdeated)
205 fXercesProperties = new Properties();
206 fis = ss.getFileInputStream(propertiesFile);
207 fXercesProperties.load(fis);
208 }
209 } catch (Exception x) {
210 fXercesProperties = null;
211 fLastModified = -1;
212 // assert(x instanceof FileNotFoundException
213 // || x instanceof SecurityException)
214 // In both cases, ignore and continue w/ next location
215 }
216 finally {
217 // try to close the input stream if one was opened.
218 if (fis != null) {
219 try {
220 fis.close();
221 }
222 // Ignore the exception.
223 catch (IOException exc) {}
224 }
225 }
226 }
227 if(fXercesProperties != null) {
228 factoryClassName = fXercesProperties.getProperty(factoryId);
229 }
230 } else {
231 FileInputStream fis = null;
232 try {
233 fis = ss.getFileInputStream(new File(propertiesFilename));
234 Properties props = new Properties();
235 props.load(fis);
236 factoryClassName = props.getProperty(factoryId);
237 } catch (Exception x) {
238 // assert(x instanceof FileNotFoundException
239 // || x instanceof SecurityException)
240 // In both cases, ignore and continue w/ next location
241 }
242 finally {
243 // try to close the input stream if one was opened.
244 if (fis != null) {
245 try {
246 fis.close();
247 }
248 // Ignore the exception.
249 catch (IOException exc) {}
250 }
251 }
252 }
253 if (factoryClassName != null) {
254 if (DEBUG) debugPrintln("found in " + propertiesFilename + ", value=" + factoryClassName);
255 return newInstance(factoryClassName, cl, true);
256 }
257
258 // Try Jar Service Provider Mechanism
259 Object provider = findJarServiceProvider(factoryId);
260 if (provider != null) {
261 return provider;
262 }
263
264 if (fallbackClassName == null) {
265 throw new ConfigurationError(
266 "Provider for " + factoryId + " cannot be found", null);
267 }
268
269 if (DEBUG) debugPrintln("using fallback, value=" + fallbackClassName);
270 return newInstance(fallbackClassName, cl, true);
271 } // createObject(String,String,String):Object
272
273 //
274 // Private static methods
275 //
276
277 /** Prints a message to standard error if debugging is enabled. */
278 private static void debugPrintln(String msg) {
279 if (DEBUG) {
280 System.err.println("JAXP: " + msg);
281 }
282 } // debugPrintln(String)
283
284 /**
285 * Figure out which ClassLoader to use. For JDK 1.2 and later use
286 * the context ClassLoader.
287 */
288 static ClassLoader findClassLoader()
289 throws ConfigurationError
290 {
291 SecuritySupport ss = SecuritySupport.getInstance();
292
293 // Figure out which ClassLoader to use for loading the provider
294 // class. If there is a Context ClassLoader then use it.
295 ClassLoader context = ss.getContextClassLoader();
296 ClassLoader system = ss.getSystemClassLoader();
297
298 ClassLoader chain = system;
299 while (true) {
300 if (context == chain) {
301 // Assert: we are on JDK 1.1 or we have no Context ClassLoader
302 // or any Context ClassLoader in chain of system classloader
303 // (including extension ClassLoader) so extend to widest
304 // ClassLoader (always look in system ClassLoader if Xerces
305 // is in boot/extension/system classpath and in current
306 // ClassLoader otherwise); normal classloaders delegate
307 // back to system ClassLoader first so this widening doesn't
308 // change the fact that context ClassLoader will be consulted
309 ClassLoader current = ObjectFactory.class.getClassLoader();
310
311 chain = system;
312 while (true) {
313 if (current == chain) {
314 // Assert: Current ClassLoader in chain of
315 // boot/extension/system ClassLoaders
316 return system;
317 }
318 if (chain == null) {
319 break;
320 }
321 chain = ss.getParentClassLoader(chain);
322 }
323
324 // Assert: Current ClassLoader not in chain of
325 // boot/extension/system ClassLoaders
326 return current;
327 }
328
329 if (chain == null) {
330 // boot ClassLoader reached
331 break;
332 }
333
334 // Check for any extension ClassLoaders in chain up to
335 // boot ClassLoader
336 chain = ss.getParentClassLoader(chain);
337 };
338
339 // Assert: Context ClassLoader not in chain of
340 // boot/extension/system ClassLoaders
341 return context;
342 } // findClassLoader():ClassLoader
343
344 /**
345 * Create an instance of a class using the specified ClassLoader
346 */
347 static Object newInstance(String className, ClassLoader cl,
348 boolean doFallback)
349 throws ConfigurationError
350 {
351 // assert(className != null);
352 try{
353 Class providerClass = findProviderClass(className, cl, doFallback);
354 Object instance = providerClass.newInstance();
355 if (DEBUG) debugPrintln("created new instance of " + providerClass +
356 " using ClassLoader: " + cl);
357 return instance;
358 } catch (ClassNotFoundException x) {
359 throw new ConfigurationError(
360 "Provider " + className + " not found", x);
361 } catch (Exception x) {
362 throw new ConfigurationError(
363 "Provider " + className + " could not be instantiated: " + x,
364 x);
365 }
366 }
367
368 /**
369 * Find a Class using the specified ClassLoader
370 */
371 static Class findProviderClass(String className, ClassLoader cl,
372 boolean doFallback)
373 throws ClassNotFoundException, ConfigurationError
374 {
375 //throw security exception if the calling thread is not allowed to access the package
376 //restrict the access to package as speicified in java.security policy
377 SecurityManager security = System.getSecurityManager();
378 if (security != null) {
379 final int lastDot = className.lastIndexOf(".");
380 String packageName = className;
381 if (lastDot != -1) packageName = className.substring(0, lastDot);
382 security.checkPackageAccess(packageName);
383 }
384 Class providerClass;
385 if (cl == null) {
386 // XXX Use the bootstrap ClassLoader. There is no way to
387 // load a class using the bootstrap ClassLoader that works
388 // in both JDK 1.1 and Java 2. However, this should still
389 // work b/c the following should be true:
390 //
391 // (cl == null) iff current ClassLoader == null
392 //
393 // Thus Class.forName(String) will use the current
394 // ClassLoader which will be the bootstrap ClassLoader.
395 providerClass = Class.forName(className);
396 } else {
397 try {
398 providerClass = cl.loadClass(className);
399 } catch (ClassNotFoundException x) {
400 if (doFallback) {
401 // Fall back to current classloader
402 ClassLoader current = ObjectFactory.class.getClassLoader();
403 if (current == null) {
404 providerClass = Class.forName(className);
405 } else if (cl != current) {
406 cl = current;
407 providerClass = cl.loadClass(className);
408 } else {
409 throw x;
410 }
411 } else {
412 throw x;
413 }
414 }
415 }
416
417 return providerClass;
418 }
419
420 /*
421 * Try to find provider using Jar Service Provider Mechanism
422 *
423 * @return instance of provider class if found or null
424 */
425 private static Object findJarServiceProvider(String factoryId)
426 throws ConfigurationError
427 {
428 SecuritySupport ss = SecuritySupport.getInstance();
429 String serviceId = "META-INF/services/" + factoryId;
430 InputStream is = null;
431
432 // First try the Context ClassLoader
433 ClassLoader cl = findClassLoader();
434
435 is = ss.getResourceAsStream(cl, serviceId);
436
437 // If no provider found then try the current ClassLoader
438 if (is == null) {
439 ClassLoader current = ObjectFactory.class.getClassLoader();
440 if (cl != current) {
441 cl = current;
442 is = ss.getResourceAsStream(cl, serviceId);
443 }
444 }
445
446 if (is == null) {
447 // No provider found
448 return null;
449 }
450
451 if (DEBUG) debugPrintln("found jar resource=" + serviceId +
452 " using ClassLoader: " + cl);
453
454 // Read the service provider name in UTF-8 as specified in
455 // the jar spec. Unfortunately this fails in Microsoft
456 // VJ++, which does not implement the UTF-8
457 // encoding. Theoretically, we should simply let it fail in
458 // that case, since the JVM is obviously broken if it
459 // doesn't support such a basic standard. But since there
460 // are still some users attempting to use VJ++ for
461 // development, we have dropped in a fallback which makes a
462 // second attempt using the platform's default encoding. In
463 // VJ++ this is apparently ASCII, which is a subset of
464 // UTF-8... and since the strings we'll be reading here are
465 // also primarily limited to the 7-bit ASCII range (at
466 // least, in English versions), this should work well
467 // enough to keep us on the air until we're ready to
468 // officially decommit from VJ++. [Edited comment from
469 // jkesselm]
470 BufferedReader rd;
471 try {
472 rd = new BufferedReader(new InputStreamReader(is, "UTF-8"), DEFAULT_LINE_LENGTH);
473 } catch (java.io.UnsupportedEncodingException e) {
474 rd = new BufferedReader(new InputStreamReader(is), DEFAULT_LINE_LENGTH);
475 }
476
477 String factoryClassName = null;
478 try {
479 // XXX Does not handle all possible input as specified by the
480 // Jar Service Provider specification
481 factoryClassName = rd.readLine();
482 } catch (IOException x) {
483 // No provider found
484 return null;
485 }
486 finally {
487 try {
488 // try to close the reader.
489 rd.close();
490 }
491 // Ignore the exception.
492 catch (IOException exc) {}
493 }
494
495 if (factoryClassName != null &&
496 ! "".equals(factoryClassName)) {
497 if (DEBUG) debugPrintln("found in resource, value="
498 + factoryClassName);
499
500 // Note: here we do not want to fall back to the current
501 // ClassLoader because we want to avoid the case where the
502 // resource file was found using one ClassLoader and the
503 // provider class was instantiated using a different one.
504 return newInstance(factoryClassName, cl, false);
505 }
506
507 // No provider found
508 return null;
509 }
510
511 //
512 // Classes
513 //
514
515 /**
516 * A configuration error.
517 */
518 static final class ConfigurationError
519 extends Error {
520
521 /** Serialization version. */
522 static final long serialVersionUID = 1914065341994951202L;
523
524 //
525 // Data
526 //
527
528 /** Exception. */
529 private Exception exception;
530
531 //
532 // Constructors
533 //
534
535 /**
536 * Construct a new instance with the specified detail string and
537 * exception.
538 */
539 ConfigurationError(String msg, Exception x) {
540 super(msg);
541 this.exception = x;
542 } // <init>(String,Exception)
543
544 //
545 // methods
546 //
547
548 /** Returns the exception associated to this error. */
549 Exception getException() {
550 return exception;
551 } // getException():Exception
552
553 } // class ConfigurationError
554
555} // class ObjectFactory