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