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