1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 2001-2004 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The name "Apache Software Foundation" must not be used to endorse or
  28. * promote products derived from this software without prior written
  29. * permission. For written permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache",
  32. * nor may "Apache" appear in their name, without prior written
  33. * permission of the Apache Software Foundation.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation and was
  51. * originally based on software copyright (c) 1999-2001, Sun Microsystems,
  52. * Inc., http://www.sun.com. For more information on the Apache Software
  53. * Foundation, please see <http://www.apache.org/>.
  54. */
  55. package com.sun.org.apache.xerces.internal.parsers;
  56. import java.io.InputStream;
  57. import java.io.IOException;
  58. import java.io.File;
  59. import java.io.FileInputStream;
  60. import java.util.Properties;
  61. import java.io.BufferedReader;
  62. import java.io.InputStreamReader;
  63. /**
  64. * This class is duplicated for each JAXP subpackage so keep it in sync.
  65. * It is package private and therefore is not exposed as part of the JAXP
  66. * API.
  67. * <p>
  68. * This code is designed to implement the JAXP 1.1 spec pluggability
  69. * feature and is designed to run on JDK version 1.1 and
  70. * later, and to compile on JDK 1.2 and onward.
  71. * The code also runs both as part of an unbundled jar file and
  72. * when bundled as part of the JDK.
  73. * <p>
  74. *
  75. * @version $Id: ObjectFactory.java,v 1.3 2004/05/08 11:07:44 vk112360 Exp $
  76. */
  77. class ObjectFactory {
  78. //
  79. // Constants
  80. //
  81. // name of default properties file to look for in JDK's jre/lib directory
  82. private static final String DEFAULT_PROPERTIES_FILENAME = "xerces.properties";
  83. /** Set to true for debugging */
  84. private static final boolean DEBUG = false;
  85. /**
  86. * Default columns per line.
  87. */
  88. private static final int DEFAULT_LINE_LENGTH = 80;
  89. /** cache the contents of the xerces.properties file.
  90. * Until an attempt has been made to read this file, this will
  91. * be null; if the file does not exist or we encounter some other error
  92. * during the read, this will be empty.
  93. */
  94. private static Properties fXercesProperties = null;
  95. /***
  96. * Cache the time stamp of the xerces.properties file so
  97. * that we know if it's been modified and can invalidate
  98. * the cache when necessary.
  99. */
  100. private static long fLastModified = -1;
  101. //
  102. // static methods
  103. //
  104. /**
  105. * Finds the implementation Class object in the specified order. The
  106. * specified order is the following:
  107. * <ol>
  108. * <li>query the system property using <code>System.getProperty</code>
  109. * <li>read <code>META-INF/services/<i>factoryId</i></code> file
  110. * <li>use fallback classname
  111. * </ol>
  112. *
  113. * @return Class object of factory, never null
  114. *
  115. * @param factoryId Name of the factory to find, same as
  116. * a property name
  117. * @param fallbackClassName Implementation class name, if nothing else
  118. * is found. Use null to mean no fallback.
  119. *
  120. * @exception ObjectFactory.ConfigurationError
  121. */
  122. static Object createObject(String factoryId, String fallbackClassName)
  123. throws ConfigurationError {
  124. return createObject(factoryId, null, fallbackClassName);
  125. } // createObject(String,String):Object
  126. /**
  127. * Finds the implementation Class object in the specified order. The
  128. * specified order is the following:
  129. * <ol>
  130. * <li>query the system property using <code>System.getProperty</code>
  131. * <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
  132. * <li>read <code>META-INF/services/<i>factoryId</i></code> file
  133. * <li>use fallback classname
  134. * </ol>
  135. *
  136. * @return Class object of factory, never null
  137. *
  138. * @param factoryId Name of the factory to find, same as
  139. * a property name
  140. * @param propertiesFilename The filename in the $java.home/lib directory
  141. * of the properties file. If none specified,
  142. * ${java.home}/lib/xerces.properties will be used.
  143. * @param fallbackClassName Implementation class name, if nothing else
  144. * is found. Use null to mean no fallback.
  145. *
  146. * @exception ObjectFactory.ConfigurationError
  147. */
  148. static Object createObject(String factoryId,
  149. String propertiesFilename,
  150. String fallbackClassName)
  151. throws ConfigurationError
  152. {
  153. if (DEBUG) debugPrintln("debug is on");
  154. SecuritySupport ss = SecuritySupport.getInstance();
  155. ClassLoader cl = findClassLoader();
  156. // Use the system property first
  157. try {
  158. String systemProp = ss.getSystemProperty(factoryId);
  159. if (systemProp != null) {
  160. if (DEBUG) debugPrintln("found system property, value=" + systemProp);
  161. return newInstance(systemProp, cl, true);
  162. }
  163. } catch (SecurityException se) {
  164. // Ignore and continue w/ next location
  165. }
  166. // Try to read from propertiesFilename, or $java.home/lib/xerces.properties
  167. String factoryClassName = null;
  168. // no properties file name specified; use $JAVA_HOME/lib/xerces.properties:
  169. if (propertiesFilename == null) {
  170. File propertiesFile = null;
  171. boolean propertiesFileExists = false;
  172. try {
  173. String javah = ss.getSystemProperty("java.home");
  174. propertiesFilename = javah + File.separator +
  175. "lib" + File.separator + DEFAULT_PROPERTIES_FILENAME;
  176. propertiesFile = new File(propertiesFilename);
  177. propertiesFileExists = ss.getFileExists(propertiesFile);
  178. } catch (SecurityException e) {
  179. // try again...
  180. fLastModified = -1;
  181. fXercesProperties = null;
  182. }
  183. synchronized (ObjectFactory.class) {
  184. boolean loadProperties = false;
  185. try {
  186. // file existed last time
  187. if(fLastModified >= 0) {
  188. if(propertiesFileExists &&
  189. (fLastModified < (fLastModified = ss.getLastModified(propertiesFile)))) {
  190. loadProperties = true;
  191. } else {
  192. // file has stopped existing...
  193. if(!propertiesFileExists) {
  194. fLastModified = -1;
  195. fXercesProperties = null;
  196. } // else, file wasn't modified!
  197. }
  198. } else {
  199. // file has started to exist:
  200. if(propertiesFileExists) {
  201. loadProperties = true;
  202. fLastModified = ss.getLastModified(propertiesFile);
  203. } // else, nothing's changed
  204. }
  205. if(loadProperties) {
  206. // must never have attempted to read xerces.properties before (or it's outdeated)
  207. fXercesProperties = new Properties();
  208. FileInputStream fis = ss.getFileInputStream(propertiesFile);
  209. fXercesProperties.load(fis);
  210. fis.close();
  211. }
  212. } catch (Exception x) {
  213. fXercesProperties = null;
  214. fLastModified = -1;
  215. // assert(x instanceof FileNotFoundException
  216. // || x instanceof SecurityException)
  217. // In both cases, ignore and continue w/ next location
  218. }
  219. }
  220. if(fXercesProperties != null) {
  221. factoryClassName = fXercesProperties.getProperty(factoryId);
  222. }
  223. } else {
  224. try {
  225. FileInputStream fis = ss.getFileInputStream(new File(propertiesFilename));
  226. Properties props = new Properties();
  227. props.load(fis);
  228. fis.close();
  229. factoryClassName = props.getProperty(factoryId);
  230. } catch (Exception x) {
  231. // assert(x instanceof FileNotFoundException
  232. // || x instanceof SecurityException)
  233. // In both cases, ignore and continue w/ next location
  234. }
  235. }
  236. if (factoryClassName != null) {
  237. if (DEBUG) debugPrintln("found in " + propertiesFilename + ", value=" + factoryClassName);
  238. return newInstance(factoryClassName, cl, true);
  239. }
  240. // Try Jar Service Provider Mechanism
  241. Object provider = findJarServiceProvider(factoryId);
  242. if (provider != null) {
  243. return provider;
  244. }
  245. if (fallbackClassName == null) {
  246. throw new ConfigurationError(
  247. "Provider for " + factoryId + " cannot be found", null);
  248. }
  249. if (DEBUG) debugPrintln("using fallback, value=" + fallbackClassName);
  250. return newInstance(fallbackClassName, cl, true);
  251. } // createObject(String,String,String):Object
  252. //
  253. // Private static methods
  254. //
  255. /** Prints a message to standard error if debugging is enabled. */
  256. private static void debugPrintln(String msg) {
  257. if (DEBUG) {
  258. System.err.println("JAXP: " + msg);
  259. }
  260. } // debugPrintln(String)
  261. /**
  262. * Figure out which ClassLoader to use. For JDK 1.2 and later use
  263. * the context ClassLoader.
  264. */
  265. static ClassLoader findClassLoader()
  266. throws ConfigurationError
  267. {
  268. SecuritySupport ss = SecuritySupport.getInstance();
  269. // Figure out which ClassLoader to use for loading the provider
  270. // class. If there is a Context ClassLoader then use it.
  271. ClassLoader context = ss.getContextClassLoader();
  272. ClassLoader system = ss.getSystemClassLoader();
  273. ClassLoader chain = system;
  274. while (true) {
  275. if (context == chain) {
  276. // Assert: we are on JDK 1.1 or we have no Context ClassLoader
  277. // or any Context ClassLoader in chain of system classloader
  278. // (including extension ClassLoader) so extend to widest
  279. // ClassLoader (always look in system ClassLoader if Xerces
  280. // is in boot/extension/system classpath and in current
  281. // ClassLoader otherwise); normal classloaders delegate
  282. // back to system ClassLoader first so this widening doesn't
  283. // change the fact that context ClassLoader will be consulted
  284. ClassLoader current = ObjectFactory.class.getClassLoader();
  285. chain = system;
  286. while (true) {
  287. if (current == chain) {
  288. // Assert: Current ClassLoader in chain of
  289. // boot/extension/system ClassLoaders
  290. return system;
  291. }
  292. if (chain == null) {
  293. break;
  294. }
  295. chain = ss.getParentClassLoader(chain);
  296. }
  297. // Assert: Current ClassLoader not in chain of
  298. // boot/extension/system ClassLoaders
  299. return current;
  300. }
  301. if (chain == null) {
  302. // boot ClassLoader reached
  303. break;
  304. }
  305. // Check for any extension ClassLoaders in chain up to
  306. // boot ClassLoader
  307. chain = ss.getParentClassLoader(chain);
  308. };
  309. // Assert: Context ClassLoader not in chain of
  310. // boot/extension/system ClassLoaders
  311. return context;
  312. } // findClassLoader():ClassLoader
  313. /**
  314. * Create an instance of a class using the specified ClassLoader
  315. */
  316. static Object newInstance(String className, ClassLoader cl,
  317. boolean doFallback)
  318. throws ConfigurationError
  319. {
  320. // assert(className != null);
  321. try{
  322. Class providerClass = findProviderClass(className, cl, doFallback);
  323. Object instance = providerClass.newInstance();
  324. if (DEBUG) debugPrintln("created new instance of " + providerClass +
  325. " using ClassLoader: " + cl);
  326. return instance;
  327. } catch (ClassNotFoundException x) {
  328. throw new ConfigurationError(
  329. "Provider " + className + " not found", x);
  330. } catch (Exception x) {
  331. throw new ConfigurationError(
  332. "Provider " + className + " could not be instantiated: " + x,
  333. x);
  334. }
  335. }
  336. /**
  337. * Find a Class using the specified ClassLoader
  338. */
  339. static Class findProviderClass(String className, ClassLoader cl,
  340. boolean doFallback)
  341. throws ClassNotFoundException, ConfigurationError
  342. {
  343. //throw security exception if the calling thread is not allowed to access the package
  344. //restrict the access to package as speicified in java.security policy
  345. SecurityManager security = System.getSecurityManager();
  346. try{
  347. if (security != null) {
  348. final int lastDot = className.lastIndexOf(".");
  349. String packageName = className;
  350. if (lastDot != -1) packageName = className.substring(0, lastDot);
  351. security.checkPackageAccess(packageName);
  352. }
  353. }catch(SecurityException e){
  354. throw e ;
  355. }
  356. Class providerClass;
  357. if (cl == null) {
  358. // XXX Use the bootstrap ClassLoader. There is no way to
  359. // load a class using the bootstrap ClassLoader that works
  360. // in both JDK 1.1 and Java 2. However, this should still
  361. // work b/c the following should be true:
  362. //
  363. // (cl == null) iff current ClassLoader == null
  364. //
  365. // Thus Class.forName(String) will use the current
  366. // ClassLoader which will be the bootstrap ClassLoader.
  367. providerClass = Class.forName(className);
  368. } else {
  369. try {
  370. providerClass = cl.loadClass(className);
  371. } catch (ClassNotFoundException x) {
  372. if (doFallback) {
  373. // Fall back to current classloader
  374. ClassLoader current = ObjectFactory.class.getClassLoader();
  375. if (current == null) {
  376. providerClass = Class.forName(className);
  377. } else if (cl != current) {
  378. cl = current;
  379. providerClass = cl.loadClass(className);
  380. } else {
  381. throw x;
  382. }
  383. } else {
  384. throw x;
  385. }
  386. }
  387. }
  388. return providerClass;
  389. }
  390. /*
  391. * Try to find provider using Jar Service Provider Mechanism
  392. *
  393. * @return instance of provider class if found or null
  394. */
  395. private static Object findJarServiceProvider(String factoryId)
  396. throws ConfigurationError
  397. {
  398. SecuritySupport ss = SecuritySupport.getInstance();
  399. String serviceId = "META-INF/services/" + factoryId;
  400. InputStream is = null;
  401. // First try the Context ClassLoader
  402. ClassLoader cl = findClassLoader();
  403. is = ss.getResourceAsStream(cl, serviceId);
  404. // If no provider found then try the current ClassLoader
  405. if (is == null) {
  406. ClassLoader current = ObjectFactory.class.getClassLoader();
  407. if (cl != current) {
  408. cl = current;
  409. is = ss.getResourceAsStream(cl, serviceId);
  410. }
  411. }
  412. if (is == null) {
  413. // No provider found
  414. return null;
  415. }
  416. if (DEBUG) debugPrintln("found jar resource=" + serviceId +
  417. " using ClassLoader: " + cl);
  418. // Read the service provider name in UTF-8 as specified in
  419. // the jar spec. Unfortunately this fails in Microsoft
  420. // VJ++, which does not implement the UTF-8
  421. // encoding. Theoretically, we should simply let it fail in
  422. // that case, since the JVM is obviously broken if it
  423. // doesn't support such a basic standard. But since there
  424. // are still some users attempting to use VJ++ for
  425. // development, we have dropped in a fallback which makes a
  426. // second attempt using the platform's default encoding. In
  427. // VJ++ this is apparently ASCII, which is a subset of
  428. // UTF-8... and since the strings we'll be reading here are
  429. // also primarily limited to the 7-bit ASCII range (at
  430. // least, in English versions), this should work well
  431. // enough to keep us on the air until we're ready to
  432. // officially decommit from VJ++. [Edited comment from
  433. // jkesselm]
  434. BufferedReader rd;
  435. try {
  436. rd = new BufferedReader(new InputStreamReader(is, "UTF-8"), DEFAULT_LINE_LENGTH);
  437. } catch (java.io.UnsupportedEncodingException e) {
  438. rd = new BufferedReader(new InputStreamReader(is), DEFAULT_LINE_LENGTH);
  439. }
  440. String factoryClassName = null;
  441. try {
  442. // XXX Does not handle all possible input as specified by the
  443. // Jar Service Provider specification
  444. factoryClassName = rd.readLine();
  445. rd.close();
  446. } catch (IOException x) {
  447. // No provider found
  448. return null;
  449. }
  450. if (factoryClassName != null &&
  451. ! "".equals(factoryClassName)) {
  452. if (DEBUG) debugPrintln("found in resource, value="
  453. + factoryClassName);
  454. // Note: here we do not want to fall back to the current
  455. // ClassLoader because we want to avoid the case where the
  456. // resource file was found using one ClassLoader and the
  457. // provider class was instantiated using a different one.
  458. return newInstance(factoryClassName, cl, false);
  459. }
  460. // No provider found
  461. return null;
  462. }
  463. //
  464. // Classes
  465. //
  466. /**
  467. * A configuration error.
  468. */
  469. static class ConfigurationError
  470. extends Error {
  471. //
  472. // Data
  473. //
  474. /** Exception. */
  475. private Exception exception;
  476. //
  477. // Constructors
  478. //
  479. /**
  480. * Construct a new instance with the specified detail string and
  481. * exception.
  482. */
  483. ConfigurationError(String msg, Exception x) {
  484. super(msg);
  485. this.exception = x;
  486. } // <init>(String,Exception)
  487. //
  488. // methods
  489. //
  490. /** Returns the exception associated to this error. */
  491. Exception getException() {
  492. return exception;
  493. } // getException():Exception
  494. } // class ConfigurationError
  495. } // class ObjectFactory