001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: ObjectFactory.java 1225583 2011-12-29 16:08:00Z mrglavas $
020 */
021
022 package org.apache.xml.serializer;
023
024 import java.io.BufferedReader;
025 import java.io.File;
026 import java.io.FileInputStream;
027 import java.io.IOException;
028 import java.io.InputStream;
029 import java.io.InputStreamReader;
030 import java.util.Properties;
031
032 /**
033 * This class is duplicated for each JAXP subpackage so keep it in sync.
034 * It is package private and therefore is not exposed as part of the JAXP
035 * API.
036 * <p>
037 * This code is designed to implement the JAXP 1.1 spec pluggability
038 * feature and is designed to run on JDK version 1.1 and
039 * later, and to compile on JDK 1.2 and onward.
040 * The code also runs both as part of an unbundled jar file and
041 * when bundled as part of the JDK.
042 * <p>
043 * This class was moved from the <code>javax.xml.parsers.ObjectFactory</code>
044 * class and modified to be used as a general utility for creating objects
045 * dynamically.
046 *
047 * @xsl.usage internal
048 */
049 final class ObjectFactory {
050
051 //
052 // Constants
053 //
054
055 // name of default properties file to look for in JDK's jre/lib directory
056 private static final String DEFAULT_PROPERTIES_FILENAME =
057 "xalan.properties";
058
059 private static final String SERVICES_PATH = "META-INF/services/";
060
061 /** Set to true for debugging */
062 private static final boolean DEBUG = false;
063
064 /** cache the contents of the xalan.properties file.
065 * Until an attempt has been made to read this file, this will
066 * be null; if the file does not exist or we encounter some other error
067 * during the read, this will be empty.
068 */
069 private static Properties fXalanProperties = null;
070
071 /***
072 * Cache the time stamp of the xalan.properties file so
073 * that we know if it's been modified and can invalidate
074 * the cache when necessary.
075 */
076 private static long fLastModified = -1;
077
078 //
079 // Public static methods
080 //
081
082 /**
083 * Finds the implementation Class object in the specified order. The
084 * specified order is the following:
085 * <ol>
086 * <li>query the system property using <code>System.getProperty</code>
087 * <li>read <code>META-INF/services/<i>factoryId</i></code> file
088 * <li>use fallback classname
089 * </ol>
090 *
091 * @return instance of factory, never null
092 *
093 * @param factoryId Name of the factory to find, same as
094 * a property name
095 * @param fallbackClassName Implementation class name, if nothing else
096 * is found. Use null to mean no fallback.
097 *
098 * @exception ObjectFactory.ConfigurationError
099 */
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 instance 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/xalan.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 Class factoryClass = lookUpFactoryClass(factoryId,
133 propertiesFilename,
134 fallbackClassName);
135
136 if (factoryClass == null) {
137 throw new ConfigurationError(
138 "Provider for " + factoryId + " cannot be found", null);
139 }
140
141 try{
142 Object instance = factoryClass.newInstance();
143 debugPrintln("created new instance of factory " + factoryId);
144 return instance;
145 } catch (Exception x) {
146 throw new ConfigurationError(
147 "Provider for factory " + factoryId
148 + " could not be instantiated: " + x, x);
149 }
150 } // createObject(String,String,String):Object
151
152 /**
153 * Finds the implementation Class object in the specified order. The
154 * specified order is the following:
155 * <ol>
156 * <li>query the system property using <code>System.getProperty</code>
157 * <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
158 * <li>read <code>META-INF/services/<i>factoryId</i></code> file
159 * <li>use fallback classname
160 * </ol>
161 *
162 * @return Class object of factory, never null
163 *
164 * @param factoryId Name of the factory to find, same as
165 * a property name
166 * @param propertiesFilename The filename in the $java.home/lib directory
167 * of the properties file. If none specified,
168 * ${java.home}/lib/xalan.properties will be used.
169 * @param fallbackClassName Implementation class name, if nothing else
170 * is found. Use null to mean no fallback.
171 *
172 * @exception ObjectFactory.ConfigurationError
173 */
174 static Class lookUpFactoryClass(String factoryId)
175 throws ConfigurationError
176 {
177 return lookUpFactoryClass(factoryId, null, null);
178 } // lookUpFactoryClass(String):Class
179
180 /**
181 * Finds the implementation Class object in the specified order. The
182 * specified order is the following:
183 * <ol>
184 * <li>query the system property using <code>System.getProperty</code>
185 * <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
186 * <li>read <code>META-INF/services/<i>factoryId</i></code> file
187 * <li>use fallback classname
188 * </ol>
189 *
190 * @return Class object that provides factory service, never null
191 *
192 * @param factoryId Name of the factory to find, same as
193 * a property name
194 * @param propertiesFilename The filename in the $java.home/lib directory
195 * of the properties file. If none specified,
196 * ${java.home}/lib/xalan.properties will be used.
197 * @param fallbackClassName Implementation class name, if nothing else
198 * is found. Use null to mean no fallback.
199 *
200 * @exception ObjectFactory.ConfigurationError
201 */
202 static Class lookUpFactoryClass(String factoryId,
203 String propertiesFilename,
204 String fallbackClassName)
205 throws ConfigurationError
206 {
207 String factoryClassName = lookUpFactoryClassName(factoryId,
208 propertiesFilename,
209 fallbackClassName);
210 ClassLoader cl = findClassLoader();
211
212 if (factoryClassName == null) {
213 factoryClassName = fallbackClassName;
214 }
215
216 // assert(className != null);
217 try{
218 Class providerClass = findProviderClass(factoryClassName,
219 cl,
220 true);
221 debugPrintln("created new instance of " + providerClass +
222 " using ClassLoader: " + cl);
223 return providerClass;
224 } catch (ClassNotFoundException x) {
225 throw new ConfigurationError(
226 "Provider " + factoryClassName + " not found", x);
227 } catch (Exception x) {
228 throw new ConfigurationError(
229 "Provider "+factoryClassName+" could not be instantiated: "+x,
230 x);
231 }
232 } // lookUpFactoryClass(String,String,String):Class
233
234 /**
235 * Finds the name of the required implementation class in the specified
236 * order. The specified order is the following:
237 * <ol>
238 * <li>query the system property using <code>System.getProperty</code>
239 * <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
240 * <li>read <code>META-INF/services/<i>factoryId</i></code> file
241 * <li>use fallback classname
242 * </ol>
243 *
244 * @return name of class that provides factory service, never null
245 *
246 * @param factoryId Name of the factory to find, same as
247 * a property name
248 * @param propertiesFilename The filename in the $java.home/lib directory
249 * of the properties file. If none specified,
250 * ${java.home}/lib/xalan.properties will be used.
251 * @param fallbackClassName Implementation class name, if nothing else
252 * is found. Use null to mean no fallback.
253 *
254 * @exception ObjectFactory.ConfigurationError
255 */
256 static String lookUpFactoryClassName(String factoryId,
257 String propertiesFilename,
258 String fallbackClassName)
259 {
260 // Use the system property first
261 try {
262 String systemProp = SecuritySupport.getSystemProperty(factoryId);
263 if (systemProp != null) {
264 debugPrintln("found system property, value=" + systemProp);
265 return systemProp;
266 }
267 } catch (SecurityException se) {
268 // Ignore and continue w/ next location
269 }
270
271 // Try to read from propertiesFilename, or
272 // $java.home/lib/xalan.properties
273 String factoryClassName = null;
274 // no properties file name specified; use
275 // $JAVA_HOME/lib/xalan.properties:
276 if (propertiesFilename == null) {
277 File propertiesFile = null;
278 boolean propertiesFileExists = false;
279 try {
280 String javah = SecuritySupport.getSystemProperty("java.home");
281 propertiesFilename = javah + File.separator +
282 "lib" + File.separator + DEFAULT_PROPERTIES_FILENAME;
283 propertiesFile = new File(propertiesFilename);
284 propertiesFileExists = SecuritySupport.getFileExists(propertiesFile);
285 } catch (SecurityException e) {
286 // try again...
287 fLastModified = -1;
288 fXalanProperties = null;
289 }
290
291 synchronized (ObjectFactory.class) {
292 boolean loadProperties = false;
293 FileInputStream fis = null;
294 try {
295 // file existed last time
296 if(fLastModified >= 0) {
297 if(propertiesFileExists &&
298 (fLastModified < (fLastModified = SecuritySupport.getLastModified(propertiesFile)))) {
299 loadProperties = true;
300 } else {
301 // file has stopped existing...
302 if(!propertiesFileExists) {
303 fLastModified = -1;
304 fXalanProperties = null;
305 } // else, file wasn't modified!
306 }
307 } else {
308 // file has started to exist:
309 if(propertiesFileExists) {
310 loadProperties = true;
311 fLastModified = SecuritySupport.getLastModified(propertiesFile);
312 } // else, nothing's changed
313 }
314 if(loadProperties) {
315 // must never have attempted to read xalan.properties
316 // before (or it's outdeated)
317 fXalanProperties = new Properties();
318 fis = SecuritySupport.getFileInputStream(propertiesFile);
319 fXalanProperties.load(fis);
320 }
321 } catch (Exception x) {
322 fXalanProperties = null;
323 fLastModified = -1;
324 // assert(x instanceof FileNotFoundException
325 // || x instanceof SecurityException)
326 // In both cases, ignore and continue w/ next location
327 }
328 finally {
329 // try to close the input stream if one was opened.
330 if (fis != null) {
331 try {
332 fis.close();
333 }
334 // Ignore the exception.
335 catch (IOException exc) {}
336 }
337 }
338 }
339 if(fXalanProperties != null) {
340 factoryClassName = fXalanProperties.getProperty(factoryId);
341 }
342 } else {
343 FileInputStream fis = null;
344 try {
345 fis = SecuritySupport.getFileInputStream(new File(propertiesFilename));
346 Properties props = new Properties();
347 props.load(fis);
348 factoryClassName = props.getProperty(factoryId);
349 } catch (Exception x) {
350 // assert(x instanceof FileNotFoundException
351 // || x instanceof SecurityException)
352 // In both cases, ignore and continue w/ next location
353 }
354 finally {
355 // try to close the input stream if one was opened.
356 if (fis != null) {
357 try {
358 fis.close();
359 }
360 // Ignore the exception.
361 catch (IOException exc) {}
362 }
363 }
364 }
365 if (factoryClassName != null) {
366 debugPrintln("found in " + propertiesFilename + ", value="
367 + factoryClassName);
368 return factoryClassName;
369 }
370
371 // Try Jar Service Provider Mechanism
372 return findJarServiceProviderName(factoryId);
373 } // lookUpFactoryClass(String,String):String
374
375 //
376 // Private static methods
377 //
378
379 /** Prints a message to standard error if debugging is enabled. */
380 private static void debugPrintln(String msg) {
381 if (DEBUG) {
382 System.err.println("JAXP: " + msg);
383 }
384 } // debugPrintln(String)
385
386 /**
387 * Figure out which ClassLoader to use. For JDK 1.2 and later use
388 * the context ClassLoader.
389 */
390 static ClassLoader findClassLoader()
391 throws ConfigurationError
392 {
393 // Figure out which ClassLoader to use for loading the provider
394 // class. If there is a Context ClassLoader then use it.
395 ClassLoader context = SecuritySupport.getContextClassLoader();
396 ClassLoader system = SecuritySupport.getSystemClassLoader();
397
398 ClassLoader chain = system;
399 while (true) {
400 if (context == chain) {
401 // Assert: we are on JDK 1.1 or we have no Context ClassLoader
402 // or any Context ClassLoader in chain of system classloader
403 // (including extension ClassLoader) so extend to widest
404 // ClassLoader (always look in system ClassLoader if Xalan
405 // is in boot/extension/system classpath and in current
406 // ClassLoader otherwise); normal classloaders delegate
407 // back to system ClassLoader first so this widening doesn't
408 // change the fact that context ClassLoader will be consulted
409 ClassLoader current = ObjectFactory.class.getClassLoader();
410
411 chain = system;
412 while (true) {
413 if (current == chain) {
414 // Assert: Current ClassLoader in chain of
415 // boot/extension/system ClassLoaders
416 return system;
417 }
418 if (chain == null) {
419 break;
420 }
421 chain = SecuritySupport.getParentClassLoader(chain);
422 }
423
424 // Assert: Current ClassLoader not in chain of
425 // boot/extension/system ClassLoaders
426 return current;
427 }
428
429 if (chain == null) {
430 // boot ClassLoader reached
431 break;
432 }
433
434 // Check for any extension ClassLoaders in chain up to
435 // boot ClassLoader
436 chain = SecuritySupport.getParentClassLoader(chain);
437 };
438
439 // Assert: Context ClassLoader not in chain of
440 // boot/extension/system ClassLoaders
441 return context;
442 } // findClassLoader():ClassLoader
443
444 /**
445 * Create an instance of a class using the specified ClassLoader
446 */
447 static Object newInstance(String className, ClassLoader cl,
448 boolean doFallback)
449 throws ConfigurationError
450 {
451 // assert(className != null);
452 try{
453 Class providerClass = findProviderClass(className, cl, doFallback);
454 Object instance = providerClass.newInstance();
455 debugPrintln("created new instance of " + providerClass +
456 " using ClassLoader: " + cl);
457 return instance;
458 } catch (ClassNotFoundException x) {
459 throw new ConfigurationError(
460 "Provider " + className + " not found", x);
461 } catch (Exception x) {
462 throw new ConfigurationError(
463 "Provider " + className + " could not be instantiated: " + x,
464 x);
465 }
466 }
467
468 /**
469 * Find a Class using the specified ClassLoader
470 */
471 static Class findProviderClass(String className, ClassLoader cl,
472 boolean doFallback)
473 throws ClassNotFoundException, ConfigurationError
474 {
475 //throw security exception if the calling thread is not allowed to access the
476 //class. Restrict the access to the package classes as specified in java.security policy.
477 SecurityManager security = System.getSecurityManager();
478 try{
479 if (security != null){
480 final int lastDot = className.lastIndexOf('.');
481 String packageName = className;
482 if (lastDot != -1) packageName = className.substring(0, lastDot);
483 security.checkPackageAccess(packageName);
484 }
485 }catch(SecurityException e){
486 throw e;
487 }
488
489 Class providerClass;
490 if (cl == null) {
491 // XXX Use the bootstrap ClassLoader. There is no way to
492 // load a class using the bootstrap ClassLoader that works
493 // in both JDK 1.1 and Java 2. However, this should still
494 // work b/c the following should be true:
495 //
496 // (cl == null) iff current ClassLoader == null
497 //
498 // Thus Class.forName(String) will use the current
499 // ClassLoader which will be the bootstrap ClassLoader.
500 providerClass = Class.forName(className);
501 } else {
502 try {
503 providerClass = cl.loadClass(className);
504 } catch (ClassNotFoundException x) {
505 if (doFallback) {
506 // Fall back to current classloader
507 ClassLoader current = ObjectFactory.class.getClassLoader();
508 if (current == null) {
509 providerClass = Class.forName(className);
510 } else if (cl != current) {
511 cl = current;
512 providerClass = cl.loadClass(className);
513 } else {
514 throw x;
515 }
516 } else {
517 throw x;
518 }
519 }
520 }
521
522 return providerClass;
523 }
524
525 /**
526 * Find the name of service provider using Jar Service Provider Mechanism
527 *
528 * @return instance of provider class if found or null
529 */
530 private static String findJarServiceProviderName(String factoryId)
531 {
532 String serviceId = SERVICES_PATH + factoryId;
533 InputStream is = null;
534
535 // First try the Context ClassLoader
536 ClassLoader cl = findClassLoader();
537
538 is = SecuritySupport.getResourceAsStream(cl, serviceId);
539
540 // If no provider found then try the current ClassLoader
541 if (is == null) {
542 ClassLoader current = ObjectFactory.class.getClassLoader();
543 if (cl != current) {
544 cl = current;
545 is = SecuritySupport.getResourceAsStream(cl, serviceId);
546 }
547 }
548
549 if (is == null) {
550 // No provider found
551 return null;
552 }
553
554 debugPrintln("found jar resource=" + serviceId +
555 " using ClassLoader: " + cl);
556
557 // Read the service provider name in UTF-8 as specified in
558 // the jar spec. Unfortunately this fails in Microsoft
559 // VJ++, which does not implement the UTF-8
560 // encoding. Theoretically, we should simply let it fail in
561 // that case, since the JVM is obviously broken if it
562 // doesn't support such a basic standard. But since there
563 // are still some users attempting to use VJ++ for
564 // development, we have dropped in a fallback which makes a
565 // second attempt using the platform's default encoding. In
566 // VJ++ this is apparently ASCII, which is a subset of
567 // UTF-8... and since the strings we'll be reading here are
568 // also primarily limited to the 7-bit ASCII range (at
569 // least, in English versions), this should work well
570 // enough to keep us on the air until we're ready to
571 // officially decommit from VJ++. [Edited comment from
572 // jkesselm]
573 BufferedReader rd;
574 try {
575 rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
576 } catch (java.io.UnsupportedEncodingException e) {
577 rd = new BufferedReader(new InputStreamReader(is));
578 }
579
580 String factoryClassName = null;
581 try {
582 // XXX Does not handle all possible input as specified by the
583 // Jar Service Provider specification
584 factoryClassName = rd.readLine();
585 } catch (IOException x) {
586 // No provider found
587 return null;
588 }
589 finally {
590 try {
591 // try to close the reader.
592 rd.close();
593 }
594 // Ignore the exception.
595 catch (IOException exc) {}
596 }
597
598 if (factoryClassName != null &&
599 ! "".equals(factoryClassName)) {
600 debugPrintln("found in resource, value="
601 + factoryClassName);
602
603 // Note: here we do not want to fall back to the current
604 // ClassLoader because we want to avoid the case where the
605 // resource file was found using one ClassLoader and the
606 // provider class was instantiated using a different one.
607 return factoryClassName;
608 }
609
610 // No provider found
611 return null;
612 }
613
614 //
615 // Classes
616 //
617
618 /**
619 * A configuration error.
620 */
621 static class ConfigurationError
622 extends Error {
623 static final long serialVersionUID = 8859254254255146542L;
624 //
625 // Data
626 //
627
628 /** Exception. */
629 private Exception exception;
630
631 //
632 // Constructors
633 //
634
635 /**
636 * Construct a new instance with the specified detail string and
637 * exception.
638 */
639 ConfigurationError(String msg, Exception x) {
640 super(msg);
641 this.exception = x;
642 } // <init>(String,Exception)
643
644 //
645 // Public methods
646 //
647
648 /** Returns the exception associated to this error. */
649 Exception getException() {
650 return exception;
651 } // getException():Exception
652
653 } // class ConfigurationError
654
655 } // class ObjectFactory