1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 /* 19 * $Id: ObjectFactory.java 468646 2006-10-28 06:57:58Z minchau $ 20 */ 21 22 package org.apache.xalan.xslt; 23 24 import java.io.InputStream; 25 import java.io.IOException; 26 import java.io.File; 27 import java.io.FileInputStream; 28 29 import java.util.Properties; 30 import java.io.BufferedReader; 31 import java.io.InputStreamReader; 32 33 /** 34 * This class is duplicated for each JAXP subpackage so keep it in sync. 35 * It is package private and therefore is not exposed as part of the JAXP 36 * API. 37 * <p> 38 * This code is designed to implement the JAXP 1.1 spec pluggability 39 * feature and is designed to run on JDK version 1.1 and 40 * later, and to compile on JDK 1.2 and onward. 41 * The code also runs both as part of an unbundled jar file and 42 * when bundled as part of the JDK. 43 * <p> 44 * This class was moved from the <code>javax.xml.parsers.ObjectFactory</code> 45 * class and modified to be used as a general utility for creating objects 46 * dynamically. 47 * 48 * @version $Id: ObjectFactory.java 468646 2006-10-28 06:57:58Z minchau $ 49 */ 50 class ObjectFactory { 51 52 // 53 // Constants 54 // 55 56 // name of default properties file to look for in JDK's jre/lib directory 57 private static final String DEFAULT_PROPERTIES_FILENAME = 58 "xalan.properties"; 59 60 private static final String SERVICES_PATH = "META-INF/services/"; 61 62 /** Set to true for debugging */ 63 private static final boolean DEBUG = false; 64 65 /** cache the contents of the xalan.properties file. 66 * Until an attempt has been made to read this file, this will 67 * be null; if the file does not exist or we encounter some other error 68 * during the read, this will be empty. 69 */ 70 private static Properties fXalanProperties = null; 71 72 /*** 73 * Cache the time stamp of the xalan.properties file so 74 * that we know if it's been modified and can invalidate 75 * the cache when necessary. 76 */ 77 private static long fLastModified = -1; 78 79 // 80 // Public static methods 81 // 82 83 /** 84 * Finds the implementation Class object in the specified order. The 85 * specified order is the following: 86 * <ol> 87 * <li>query the system property using <code>System.getProperty</code> 88 * <li>read <code>META-INF/services/<i>factoryId</i></code> file 89 * <li>use fallback classname 90 * </ol> 91 * 92 * @return instance of factory, never null 93 * 94 * @param factoryId Name of the factory to find, same as 95 * a property name 96 * @param fallbackClassName Implementation class name, if nothing else 97 * is found. Use null to mean no fallback. 98 * 99 * @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 SecuritySupport ss = SecuritySupport.getInstance(); 262 263 // Use the system property first 264 try { 265 String systemProp = ss.getSystemProperty(factoryId); 266 if (systemProp != null) { 267 debugPrintln("found system property, value=" + systemProp); 268 return systemProp; 269 } 270 } catch (SecurityException se) { 271 // Ignore and continue w/ next location 272 } 273 274 // Try to read from propertiesFilename, or 275 // $java.home/lib/xalan.properties 276 String factoryClassName = null; 277 // no properties file name specified; use 278 // $JAVA_HOME/lib/xalan.properties: 279 if (propertiesFilename == null) { 280 File propertiesFile = null; 281 boolean propertiesFileExists = false; 282 try { 283 String javah = ss.getSystemProperty("java.home"); 284 propertiesFilename = javah + File.separator + 285 "lib" + File.separator + DEFAULT_PROPERTIES_FILENAME; 286 propertiesFile = new File(propertiesFilename); 287 propertiesFileExists = ss.getFileExists(propertiesFile); 288 } catch (SecurityException e) { 289 // try again... 290 fLastModified = -1; 291 fXalanProperties = null; 292 } 293 294 synchronized (ObjectFactory.class) { 295 boolean loadProperties = false; 296 FileInputStream fis = null; 297 try { 298 // file existed last time 299 if(fLastModified >= 0) { 300 if(propertiesFileExists && 301 (fLastModified < (fLastModified = ss.getLastModified(propertiesFile)))) { 302 loadProperties = true; 303 } else { 304 // file has stopped existing... 305 if(!propertiesFileExists) { 306 fLastModified = -1; 307 fXalanProperties = null; 308 } // else, file wasn't modified! 309 } 310 } else { 311 // file has started to exist: 312 if(propertiesFileExists) { 313 loadProperties = true; 314 fLastModified = ss.getLastModified(propertiesFile); 315 } // else, nothing's changed 316 } 317 if(loadProperties) { 318 // must never have attempted to read xalan.properties 319 // before (or it's outdeated) 320 fXalanProperties = new Properties(); 321 fis = ss.getFileInputStream(propertiesFile); 322 fXalanProperties.load(fis); 323 } 324 } catch (Exception x) { 325 fXalanProperties = null; 326 fLastModified = -1; 327 // assert(x instanceof FileNotFoundException 328 // || x instanceof SecurityException) 329 // In both cases, ignore and continue w/ next location 330 } 331 finally { 332 // try to close the input stream if one was opened. 333 if (fis != null) { 334 try { 335 fis.close(); 336 } 337 // Ignore the exception. 338 catch (IOException exc) {} 339 } 340 } 341 } 342 if(fXalanProperties != null) { 343 factoryClassName = fXalanProperties.getProperty(factoryId); 344 } 345 } else { 346 FileInputStream fis = null; 347 try { 348 fis = ss.getFileInputStream(new File(propertiesFilename)); 349 Properties props = new Properties(); 350 props.load(fis); 351 factoryClassName = props.getProperty(factoryId); 352 } catch (Exception x) { 353 // assert(x instanceof FileNotFoundException 354 // || x instanceof SecurityException) 355 // In both cases, ignore and continue w/ next location 356 } 357 finally { 358 // try to close the input stream if one was opened. 359 if (fis != null) { 360 try { 361 fis.close(); 362 } 363 // Ignore the exception. 364 catch (IOException exc) {} 365 } 366 } 367 } 368 if (factoryClassName != null) { 369 debugPrintln("found in " + propertiesFilename + ", value=" 370 + factoryClassName); 371 return factoryClassName; 372 } 373 374 // Try Jar Service Provider Mechanism 375 return findJarServiceProviderName(factoryId); 376 } // lookUpFactoryClass(String,String):String 377 378 // 379 // Private static methods 380 // 381 382 /** Prints a message to standard error if debugging is enabled. */ 383 private static void debugPrintln(String msg) { 384 if (DEBUG) { 385 System.err.println("JAXP: " + msg); 386 } 387 } // debugPrintln(String) 388 389 /** 390 * Figure out which ClassLoader to use. For JDK 1.2 and later use 391 * the context ClassLoader. 392 */ 393 static ClassLoader findClassLoader() 394 throws ConfigurationError 395 { 396 SecuritySupport ss = SecuritySupport.getInstance(); 397 398 // Figure out which ClassLoader to use for loading the provider 399 // class. If there is a Context ClassLoader then use it. 400 ClassLoader context = ss.getContextClassLoader(); 401 ClassLoader system = ss.getSystemClassLoader(); 402 403 ClassLoader chain = system; 404 while (true) { 405 if (context == chain) { 406 // Assert: we are on JDK 1.1 or we have no Context ClassLoader 407 // or any Context ClassLoader in chain of system classloader 408 // (including extension ClassLoader) so extend to widest 409 // ClassLoader (always look in system ClassLoader if Xalan 410 // is in boot/extension/system classpath and in current 411 // ClassLoader otherwise); normal classloaders delegate 412 // back to system ClassLoader first so this widening doesn't 413 // change the fact that context ClassLoader will be consulted 414 ClassLoader current = ObjectFactory.class.getClassLoader(); 415 416 chain = system; 417 while (true) { 418 if (current == chain) { 419 // Assert: Current ClassLoader in chain of 420 // boot/extension/system ClassLoaders 421 return system; 422 } 423 if (chain == null) { 424 break; 425 } 426 chain = ss.getParentClassLoader(chain); 427 } 428 429 // Assert: Current ClassLoader not in chain of 430 // boot/extension/system ClassLoaders 431 return current; 432 } 433 434 if (chain == null) { 435 // boot ClassLoader reached 436 break; 437 } 438 439 // Check for any extension ClassLoaders in chain up to 440 // boot ClassLoader 441 chain = ss.getParentClassLoader(chain); 442 }; 443 444 // Assert: Context ClassLoader not in chain of 445 // boot/extension/system ClassLoaders 446 return context; 447 } // findClassLoader():ClassLoader 448 449 /** 450 * Create an instance of a class using the specified ClassLoader 451 */ 452 static Object newInstance(String className, ClassLoader cl, 453 boolean doFallback) 454 throws ConfigurationError 455 { 456 // assert(className != null); 457 try{ 458 Class providerClass = findProviderClass(className, cl, doFallback); 459 Object instance = providerClass.newInstance(); 460 debugPrintln("created new instance of " + providerClass + 461 " using ClassLoader: " + cl); 462 return instance; 463 } catch (ClassNotFoundException x) { 464 throw new ConfigurationError( 465 "Provider " + className + " not found", x); 466 } catch (Exception x) { 467 throw new ConfigurationError( 468 "Provider " + className + " could not be instantiated: " + x, 469 x); 470 } 471 } 472 473 /** 474 * Find a Class using the specified ClassLoader 475 */ 476 static Class findProviderClass(String className, ClassLoader cl, 477 boolean doFallback) 478 throws ClassNotFoundException, ConfigurationError 479 { 480 //throw security exception if the calling thread is not allowed to access the 481 //class. Restrict the access to the package classes as specified in java.security policy. 482 SecurityManager security = System.getSecurityManager(); 483 try{ 484 if (security != null){ 485 final int lastDot = className.lastIndexOf("."); 486 String packageName = className; 487 if (lastDot != -1) packageName = className.substring(0, lastDot); 488 security.checkPackageAccess(packageName); 489 } 490 }catch(SecurityException e){ 491 throw e; 492 } 493 494 Class providerClass; 495 if (cl == null) { 496 // XXX Use the bootstrap ClassLoader. There is no way to 497 // load a class using the bootstrap ClassLoader that works 498 // in both JDK 1.1 and Java 2. However, this should still 499 // work b/c the following should be true: 500 // 501 // (cl == null) iff current ClassLoader == null 502 // 503 // Thus Class.forName(String) will use the current 504 // ClassLoader which will be the bootstrap ClassLoader. 505 providerClass = Class.forName(className); 506 } else { 507 try { 508 providerClass = cl.loadClass(className); 509 } catch (ClassNotFoundException x) { 510 if (doFallback) { 511 // Fall back to current classloader 512 ClassLoader current = ObjectFactory.class.getClassLoader(); 513 if (current == null) { 514 providerClass = Class.forName(className); 515 } else if (cl != current) { 516 cl = current; 517 providerClass = cl.loadClass(className); 518 } else { 519 throw x; 520 } 521 } else { 522 throw x; 523 } 524 } 525 } 526 527 return providerClass; 528 } 529 530 /** 531 * Find the name of service provider using Jar Service Provider Mechanism 532 * 533 * @return instance of provider class if found or null 534 */ 535 private static String findJarServiceProviderName(String factoryId) 536 { 537 SecuritySupport ss = SecuritySupport.getInstance(); 538 String serviceId = SERVICES_PATH + factoryId; 539 InputStream is = null; 540 541 // First try the Context ClassLoader 542 ClassLoader cl = findClassLoader(); 543 544 is = ss.getResourceAsStream(cl, serviceId); 545 546 // If no provider found then try the current ClassLoader 547 if (is == null) { 548 ClassLoader current = ObjectFactory.class.getClassLoader(); 549 if (cl != current) { 550 cl = current; 551 is = ss.getResourceAsStream(cl, serviceId); 552 } 553 } 554 555 if (is == null) { 556 // No provider found 557 return null; 558 } 559 560 debugPrintln("found jar resource=" + serviceId + 561 " using ClassLoader: " + cl); 562 563 // Read the service provider name in UTF-8 as specified in 564 // the jar spec. Unfortunately this fails in Microsoft 565 // VJ++, which does not implement the UTF-8 566 // encoding. Theoretically, we should simply let it fail in 567 // that case, since the JVM is obviously broken if it 568 // doesn't support such a basic standard. But since there 569 // are still some users attempting to use VJ++ for 570 // development, we have dropped in a fallback which makes a 571 // second attempt using the platform's default encoding. In 572 // VJ++ this is apparently ASCII, which is a subset of 573 // UTF-8... and since the strings we'll be reading here are 574 // also primarily limited to the 7-bit ASCII range (at 575 // least, in English versions), this should work well 576 // enough to keep us on the air until we're ready to 577 // officially decommit from VJ++. [Edited comment from 578 // jkesselm] 579 BufferedReader rd; 580 try { 581 rd = new BufferedReader(new InputStreamReader(is, "UTF-8")); 582 } catch (java.io.UnsupportedEncodingException e) { 583 rd = new BufferedReader(new InputStreamReader(is)); 584 } 585 586 String factoryClassName = null; 587 try { 588 // XXX Does not handle all possible input as specified by the 589 // Jar Service Provider specification 590 factoryClassName = rd.readLine(); 591 } catch (IOException x) { 592 // No provider found 593 return null; 594 } 595 finally { 596 try { 597 // try to close the reader. 598 rd.close(); 599 } 600 // Ignore the exception. 601 catch (IOException exc) {} 602 } 603 604 if (factoryClassName != null && 605 ! "".equals(factoryClassName)) { 606 debugPrintln("found in resource, value=" 607 + factoryClassName); 608 609 // Note: here we do not want to fall back to the current 610 // ClassLoader because we want to avoid the case where the 611 // resource file was found using one ClassLoader and the 612 // provider class was instantiated using a different one. 613 return factoryClassName; 614 } 615 616 // No provider found 617 return null; 618 } 619 620 // 621 // Classes 622 // 623 624 /** 625 * A configuration error. 626 */ 627 static class ConfigurationError 628 extends Error { 629 static final long serialVersionUID = 2276082712114762609L; 630 // 631 // Data 632 // 633 634 /** Exception. */ 635 private Exception exception; 636 637 // 638 // Constructors 639 // 640 641 /** 642 * Construct a new instance with the specified detail string and 643 * exception. 644 */ 645 ConfigurationError(String msg, Exception x) { 646 super(msg); 647 this.exception = x; 648 } // <init>(String,Exception) 649 650 // 651 // Public methods 652 // 653 654 /** Returns the exception associated to this error. */ 655 Exception getException() { 656 return exception; 657 } // getException():Exception 658 659 } // class ConfigurationError 660 661 } // class ObjectFactory 662