1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /*
  17. * $Id: ObjectFactory.java,v 1.2 2004/03/11 00:56:38 bhakti Exp $
  18. */
  19. package com.sun.org.apache.xml.internal.dtm;
  20. import java.io.InputStream;
  21. import java.io.IOException;
  22. import java.io.File;
  23. import java.io.FileInputStream;
  24. import java.util.Properties;
  25. import java.io.BufferedReader;
  26. import java.io.InputStreamReader;
  27. /**
  28. * This class is duplicated for each JAXP subpackage so keep it in sync.
  29. * It is package private and therefore is not exposed as part of the JAXP
  30. * API.
  31. * <p>
  32. * This code is designed to implement the JAXP 1.1 spec pluggability
  33. * feature and is designed to run on JDK version 1.1 and
  34. * later, and to compile on JDK 1.2 and onward.
  35. * The code also runs both as part of an unbundled jar file and
  36. * when bundled as part of the JDK.
  37. * <p>
  38. * This class was moved from the <code>javax.xml.parsers.ObjectFactory</code>
  39. * class and modified to be used as a general utility for creating objects
  40. * dynamically.
  41. *
  42. * @version $Id: ObjectFactory.java,v 1.2 2004/03/11 00:56:38 bhakti Exp $
  43. */
  44. class ObjectFactory {
  45. //
  46. // Constants
  47. //
  48. // name of default properties file to look for in JDK's jre/lib directory
  49. private static final String DEFAULT_PROPERTIES_FILENAME =
  50. "xalan.properties";
  51. private static final String SERVICES_PATH = "META-INF/services/";
  52. /** Set to true for debugging */
  53. private static final boolean DEBUG = false;
  54. /** cache the contents of the xalan.properties file.
  55. * Until an attempt has been made to read this file, this will
  56. * be null; if the file does not exist or we encounter some other error
  57. * during the read, this will be empty.
  58. */
  59. private static Properties fXalanProperties = null;
  60. /***
  61. * Cache the time stamp of the xalan.properties file so
  62. * that we know if it's been modified and can invalidate
  63. * the cache when necessary.
  64. */
  65. private static long fLastModified = -1;
  66. //
  67. // Public static methods
  68. //
  69. /**
  70. * Finds the implementation Class object in the specified order. The
  71. * specified order is the following:
  72. * <ol>
  73. * <li>query the system property using <code>System.getProperty</code>
  74. * <li>read <code>META-INF/services/<i>factoryId</i></code> file
  75. * <li>use fallback classname
  76. * </ol>
  77. *
  78. * @return instance of factory, never null
  79. *
  80. * @param factoryId Name of the factory to find, same as
  81. * a property name
  82. * @param fallbackClassName Implementation class name, if nothing else
  83. * is found. Use null to mean no fallback.
  84. *
  85. * @exception ObjectFactory.ConfigurationError
  86. */
  87. static Object createObject(String factoryId, String fallbackClassName)
  88. throws ConfigurationError {
  89. return createObject(factoryId, null, fallbackClassName);
  90. } // createObject(String,String):Object
  91. /**
  92. * Finds the implementation Class object in the specified order. The
  93. * specified order is the following:
  94. * <ol>
  95. * <li>query the system property using <code>System.getProperty</code>
  96. * <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
  97. * <li>read <code>META-INF/services/<i>factoryId</i></code> file
  98. * <li>use fallback classname
  99. * </ol>
  100. *
  101. * @return instance of factory, never null
  102. *
  103. * @param factoryId Name of the factory to find, same as
  104. * a property name
  105. * @param propertiesFilename The filename in the $java.home/lib directory
  106. * of the properties file. If none specified,
  107. * ${java.home}/lib/xalan.properties will be used.
  108. * @param fallbackClassName Implementation class name, if nothing else
  109. * is found. Use null to mean no fallback.
  110. *
  111. * @exception ObjectFactory.ConfigurationError
  112. */
  113. static Object createObject(String factoryId,
  114. String propertiesFilename,
  115. String fallbackClassName)
  116. throws ConfigurationError
  117. {
  118. Class factoryClass = lookUpFactoryClass(factoryId,
  119. propertiesFilename,
  120. fallbackClassName);
  121. if (factoryClass == null) {
  122. throw new ConfigurationError(
  123. "Provider for " + factoryId + " cannot be found", null);
  124. }
  125. try{
  126. Object instance = factoryClass.newInstance();
  127. debugPrintln("created new instance of factory " + factoryId);
  128. return instance;
  129. } catch (Exception x) {
  130. throw new ConfigurationError(
  131. "Provider for factory " + factoryId
  132. + " could not be instantiated: " + x, x);
  133. }
  134. } // createObject(String,String,String):Object
  135. /**
  136. * Finds the implementation Class object in the specified order. The
  137. * specified order is the following:
  138. * <ol>
  139. * <li>query the system property using <code>System.getProperty</code>
  140. * <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
  141. * <li>read <code>META-INF/services/<i>factoryId</i></code> file
  142. * <li>use fallback classname
  143. * </ol>
  144. *
  145. * @return Class object of factory, never null
  146. *
  147. * @param factoryId Name of the factory to find, same as
  148. * a property name
  149. * @param propertiesFilename The filename in the $java.home/lib directory
  150. * of the properties file. If none specified,
  151. * ${java.home}/lib/xalan.properties will be used.
  152. * @param fallbackClassName Implementation class name, if nothing else
  153. * is found. Use null to mean no fallback.
  154. *
  155. * @exception ObjectFactory.ConfigurationError
  156. */
  157. static Class lookUpFactoryClass(String factoryId)
  158. throws ConfigurationError
  159. {
  160. return lookUpFactoryClass(factoryId, null, null);
  161. } // lookUpFactoryClass(String):Class
  162. /**
  163. * Finds the implementation Class object in the specified order. The
  164. * specified order is the following:
  165. * <ol>
  166. * <li>query the system property using <code>System.getProperty</code>
  167. * <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
  168. * <li>read <code>META-INF/services/<i>factoryId</i></code> file
  169. * <li>use fallback classname
  170. * </ol>
  171. *
  172. * @return Class object that provides factory service, never null
  173. *
  174. * @param factoryId Name of the factory to find, same as
  175. * a property name
  176. * @param propertiesFilename The filename in the $java.home/lib directory
  177. * of the properties file. If none specified,
  178. * ${java.home}/lib/xalan.properties will be used.
  179. * @param fallbackClassName Implementation class name, if nothing else
  180. * is found. Use null to mean no fallback.
  181. *
  182. * @exception ObjectFactory.ConfigurationError
  183. */
  184. static Class lookUpFactoryClass(String factoryId,
  185. String propertiesFilename,
  186. String fallbackClassName)
  187. throws ConfigurationError
  188. {
  189. String factoryClassName = lookUpFactoryClassName(factoryId,
  190. propertiesFilename,
  191. fallbackClassName);
  192. ClassLoader cl = findClassLoader();
  193. if (factoryClassName == null) {
  194. factoryClassName = fallbackClassName;
  195. }
  196. // assert(className != null);
  197. try{
  198. Class providerClass = findProviderClass(factoryClassName,
  199. cl,
  200. true);
  201. debugPrintln("created new instance of " + providerClass +
  202. " using ClassLoader: " + cl);
  203. return providerClass;
  204. } catch (ClassNotFoundException x) {
  205. throw new ConfigurationError(
  206. "Provider " + factoryClassName + " not found", x);
  207. } catch (Exception x) {
  208. throw new ConfigurationError(
  209. "Provider "+factoryClassName+" could not be instantiated: "+x,
  210. x);
  211. }
  212. } // lookUpFactoryClass(String,String,String):Class
  213. /**
  214. * Finds the name of the required implementation class in the specified
  215. * order. The specified order is the following:
  216. * <ol>
  217. * <li>query the system property using <code>System.getProperty</code>
  218. * <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
  219. * <li>read <code>META-INF/services/<i>factoryId</i></code> file
  220. * <li>use fallback classname
  221. * </ol>
  222. *
  223. * @return name of class that provides factory service, never null
  224. *
  225. * @param factoryId Name of the factory to find, same as
  226. * a property name
  227. * @param propertiesFilename The filename in the $java.home/lib directory
  228. * of the properties file. If none specified,
  229. * ${java.home}/lib/xalan.properties will be used.
  230. * @param fallbackClassName Implementation class name, if nothing else
  231. * is found. Use null to mean no fallback.
  232. *
  233. * @exception ObjectFactory.ConfigurationError
  234. */
  235. static String lookUpFactoryClassName(String factoryId,
  236. String propertiesFilename,
  237. String fallbackClassName)
  238. {
  239. SecuritySupport ss = SecuritySupport.getInstance();
  240. // Use the system property first
  241. try {
  242. String systemProp = ss.getSystemProperty(factoryId);
  243. if (systemProp != null) {
  244. debugPrintln("found system property, value=" + systemProp);
  245. return systemProp;
  246. }
  247. } catch (SecurityException se) {
  248. // Ignore and continue w/ next location
  249. }
  250. // Try to read from propertiesFilename, or
  251. // $java.home/lib/xalan.properties
  252. String factoryClassName = null;
  253. // no properties file name specified; use
  254. // $JAVA_HOME/lib/xalan.properties:
  255. if (propertiesFilename == null) {
  256. File propertiesFile = null;
  257. boolean propertiesFileExists = false;
  258. try {
  259. String javah = ss.getSystemProperty("java.home");
  260. propertiesFilename = javah + File.separator +
  261. "lib" + File.separator + DEFAULT_PROPERTIES_FILENAME;
  262. propertiesFile = new File(propertiesFilename);
  263. propertiesFileExists = ss.getFileExists(propertiesFile);
  264. } catch (SecurityException e) {
  265. // try again...
  266. fLastModified = -1;
  267. fXalanProperties = null;
  268. }
  269. synchronized (ObjectFactory.class) {
  270. boolean loadProperties = false;
  271. try {
  272. // file existed last time
  273. if(fLastModified >= 0) {
  274. if(propertiesFileExists &&
  275. (fLastModified < (fLastModified = ss.getLastModified(propertiesFile)))) {
  276. loadProperties = true;
  277. } else {
  278. // file has stopped existing...
  279. if(!propertiesFileExists) {
  280. fLastModified = -1;
  281. fXalanProperties = null;
  282. } // else, file wasn't modified!
  283. }
  284. } else {
  285. // file has started to exist:
  286. if(propertiesFileExists) {
  287. loadProperties = true;
  288. fLastModified = ss.getLastModified(propertiesFile);
  289. } // else, nothing's changed
  290. }
  291. if(loadProperties) {
  292. // must never have attempted to read xalan.properties
  293. // before (or it's outdeated)
  294. fXalanProperties = new Properties();
  295. FileInputStream fis =
  296. ss.getFileInputStream(propertiesFile);
  297. fXalanProperties.load(fis);
  298. fis.close();
  299. }
  300. } catch (Exception x) {
  301. fXalanProperties = null;
  302. fLastModified = -1;
  303. // assert(x instanceof FileNotFoundException
  304. // || x instanceof SecurityException)
  305. // In both cases, ignore and continue w/ next location
  306. }
  307. }
  308. if(fXalanProperties != null) {
  309. factoryClassName = fXalanProperties.getProperty(factoryId);
  310. }
  311. } else {
  312. try {
  313. FileInputStream fis =
  314. ss.getFileInputStream(new File(propertiesFilename));
  315. Properties props = new Properties();
  316. props.load(fis);
  317. fis.close();
  318. factoryClassName = props.getProperty(factoryId);
  319. } catch (Exception x) {
  320. // assert(x instanceof FileNotFoundException
  321. // || x instanceof SecurityException)
  322. // In both cases, ignore and continue w/ next location
  323. }
  324. }
  325. if (factoryClassName != null) {
  326. debugPrintln("found in " + propertiesFilename + ", value="
  327. + factoryClassName);
  328. return factoryClassName;
  329. }
  330. // Try Jar Service Provider Mechanism
  331. return findJarServiceProviderName(factoryId);
  332. } // lookUpFactoryClass(String,String):String
  333. //
  334. // Private static methods
  335. //
  336. /** Prints a message to standard error if debugging is enabled. */
  337. private static void debugPrintln(String msg) {
  338. if (DEBUG) {
  339. System.err.println("JAXP: " + msg);
  340. }
  341. } // debugPrintln(String)
  342. /**
  343. * Figure out which ClassLoader to use. For JDK 1.2 and later use
  344. * the context ClassLoader.
  345. */
  346. static ClassLoader findClassLoader()
  347. throws ConfigurationError
  348. {
  349. SecuritySupport ss = SecuritySupport.getInstance();
  350. // Figure out which ClassLoader to use for loading the provider
  351. // class. If there is a Context ClassLoader then use it.
  352. ClassLoader context = ss.getContextClassLoader();
  353. ClassLoader system = ss.getSystemClassLoader();
  354. ClassLoader chain = system;
  355. while (true) {
  356. if (context == chain) {
  357. // Assert: we are on JDK 1.1 or we have no Context ClassLoader
  358. // or any Context ClassLoader in chain of system classloader
  359. // (including extension ClassLoader) so extend to widest
  360. // ClassLoader (always look in system ClassLoader if Xalan
  361. // is in boot/extension/system classpath and in current
  362. // ClassLoader otherwise); normal classloaders delegate
  363. // back to system ClassLoader first so this widening doesn't
  364. // change the fact that context ClassLoader will be consulted
  365. ClassLoader current = ObjectFactory.class.getClassLoader();
  366. chain = system;
  367. while (true) {
  368. if (current == chain) {
  369. // Assert: Current ClassLoader in chain of
  370. // boot/extension/system ClassLoaders
  371. return system;
  372. }
  373. if (chain == null) {
  374. break;
  375. }
  376. chain = ss.getParentClassLoader(chain);
  377. }
  378. // Assert: Current ClassLoader not in chain of
  379. // boot/extension/system ClassLoaders
  380. return current;
  381. }
  382. if (chain == null) {
  383. // boot ClassLoader reached
  384. break;
  385. }
  386. // Check for any extension ClassLoaders in chain up to
  387. // boot ClassLoader
  388. chain = ss.getParentClassLoader(chain);
  389. };
  390. // Assert: Context ClassLoader not in chain of
  391. // boot/extension/system ClassLoaders
  392. return context;
  393. } // findClassLoader():ClassLoader
  394. /**
  395. * Create an instance of a class using the specified ClassLoader
  396. */
  397. static Object newInstance(String className, ClassLoader cl,
  398. boolean doFallback)
  399. throws ConfigurationError
  400. {
  401. // assert(className != null);
  402. try{
  403. Class providerClass = findProviderClass(className, cl, doFallback);
  404. Object instance = providerClass.newInstance();
  405. debugPrintln("created new instance of " + providerClass +
  406. " using ClassLoader: " + cl);
  407. return instance;
  408. } catch (ClassNotFoundException x) {
  409. throw new ConfigurationError(
  410. "Provider " + className + " not found", x);
  411. } catch (Exception x) {
  412. throw new ConfigurationError(
  413. "Provider " + className + " could not be instantiated: " + x,
  414. x);
  415. }
  416. }
  417. /**
  418. * Find a Class using the specified ClassLoader
  419. */
  420. static Class findProviderClass(String className, ClassLoader cl,
  421. boolean doFallback)
  422. throws ClassNotFoundException, ConfigurationError
  423. {
  424. //throw security exception if the calling thread is not allowed to access the
  425. //class. Restrict the access to the package classes as specified in java.security policy.
  426. SecurityManager security = System.getSecurityManager();
  427. try{
  428. if (security != null){
  429. final int lastDot = className.lastIndexOf(".");
  430. String packageName = className;
  431. if (lastDot != -1)
  432. packageName = className.substring(0, lastDot);
  433. security.checkPackageAccess(packageName);
  434. }
  435. }catch(SecurityException e){
  436. throw e;
  437. }
  438. Class providerClass;
  439. if (cl == null) {
  440. // XXX Use the bootstrap ClassLoader. There is no way to
  441. // load a class using the bootstrap ClassLoader that works
  442. // in both JDK 1.1 and Java 2. However, this should still
  443. // work b/c the following should be true:
  444. //
  445. // (cl == null) iff current ClassLoader == null
  446. //
  447. // Thus Class.forName(String) will use the current
  448. // ClassLoader which will be the bootstrap ClassLoader.
  449. providerClass = Class.forName(className);
  450. } else {
  451. try {
  452. providerClass = cl.loadClass(className);
  453. } catch (ClassNotFoundException x) {
  454. if (doFallback) {
  455. // Fall back to current classloader
  456. ClassLoader current = ObjectFactory.class.getClassLoader();
  457. if (current == null) {
  458. providerClass = Class.forName(className);
  459. } else if (cl != current) {
  460. cl = current;
  461. providerClass = cl.loadClass(className);
  462. } else {
  463. throw x;
  464. }
  465. } else {
  466. throw x;
  467. }
  468. }
  469. }
  470. return providerClass;
  471. }
  472. /**
  473. * Find the name of service provider using Jar Service Provider Mechanism
  474. *
  475. * @return instance of provider class if found or null
  476. */
  477. private static String findJarServiceProviderName(String factoryId)
  478. {
  479. SecuritySupport ss = SecuritySupport.getInstance();
  480. String serviceId = SERVICES_PATH + factoryId;
  481. InputStream is = null;
  482. // First try the Context ClassLoader
  483. ClassLoader cl = findClassLoader();
  484. is = ss.getResourceAsStream(cl, serviceId);
  485. // If no provider found then try the current ClassLoader
  486. if (is == null) {
  487. ClassLoader current = ObjectFactory.class.getClassLoader();
  488. if (cl != current) {
  489. cl = current;
  490. is = ss.getResourceAsStream(cl, serviceId);
  491. }
  492. }
  493. if (is == null) {
  494. // No provider found
  495. return null;
  496. }
  497. debugPrintln("found jar resource=" + serviceId +
  498. " using ClassLoader: " + cl);
  499. // Read the service provider name in UTF-8 as specified in
  500. // the jar spec. Unfortunately this fails in Microsoft
  501. // VJ++, which does not implement the UTF-8
  502. // encoding. Theoretically, we should simply let it fail in
  503. // that case, since the JVM is obviously broken if it
  504. // doesn't support such a basic standard. But since there
  505. // are still some users attempting to use VJ++ for
  506. // development, we have dropped in a fallback which makes a
  507. // second attempt using the platform's default encoding. In
  508. // VJ++ this is apparently ASCII, which is a subset of
  509. // UTF-8... and since the strings we'll be reading here are
  510. // also primarily limited to the 7-bit ASCII range (at
  511. // least, in English versions), this should work well
  512. // enough to keep us on the air until we're ready to
  513. // officially decommit from VJ++. [Edited comment from
  514. // jkesselm]
  515. BufferedReader rd;
  516. try {
  517. rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
  518. } catch (java.io.UnsupportedEncodingException e) {
  519. rd = new BufferedReader(new InputStreamReader(is));
  520. }
  521. String factoryClassName = null;
  522. try {
  523. // XXX Does not handle all possible input as specified by the
  524. // Jar Service Provider specification
  525. factoryClassName = rd.readLine();
  526. rd.close();
  527. } catch (IOException x) {
  528. // No provider found
  529. return null;
  530. }
  531. if (factoryClassName != null &&
  532. ! "".equals(factoryClassName)) {
  533. debugPrintln("found in resource, value="
  534. + factoryClassName);
  535. // Note: here we do not want to fall back to the current
  536. // ClassLoader because we want to avoid the case where the
  537. // resource file was found using one ClassLoader and the
  538. // provider class was instantiated using a different one.
  539. return factoryClassName;
  540. }
  541. // No provider found
  542. return null;
  543. }
  544. //
  545. // Classes
  546. //
  547. /**
  548. * A configuration error.
  549. */
  550. static class ConfigurationError
  551. extends Error {
  552. //
  553. // Data
  554. //
  555. /** Exception. */
  556. private Exception exception;
  557. //
  558. // Constructors
  559. //
  560. /**
  561. * Construct a new instance with the specified detail string and
  562. * exception.
  563. */
  564. ConfigurationError(String msg, Exception x) {
  565. super(msg);
  566. this.exception = x;
  567. } // <init>(String,Exception)
  568. //
  569. // Public methods
  570. //
  571. /** Returns the exception associated to this error. */
  572. Exception getException() {
  573. return exception;
  574. } // getException():Exception
  575. } // class ConfigurationError
  576. } // class ObjectFactory