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