1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 2001-2003 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.xml.internal.dtm;
  56. import java.io.BufferedReader;
  57. import java.io.File;
  58. import java.io.FileInputStream;
  59. import java.io.IOException;
  60. import java.io.InputStream;
  61. import java.io.InputStreamReader;
  62. import java.util.Properties;
  63. /**
  64. * This class is based on the FactoryFinder classes in the JAXP subpackages
  65. * in the xml-commons project (xml-apis.jar)
  66. *
  67. * This copy of FactoryFinder is for the DTMManager. It caches the class
  68. * name after it is found the first time, if the System.property is not set.
  69. * If the System.property is set, then it is always used.
  70. *
  71. * It does not use context class loaders, but we will probably need to add
  72. * this support in the future. Question: If we use context class loaders, can
  73. * we still cache the class (do we need to also cache the class loader for
  74. * comparison purposes)?
  75. *
  76. * @author Edwin Goei, Ilene Seelemann
  77. */
  78. class FactoryFinder {
  79. /** Controls debugging output to stderr */
  80. private static boolean debug;
  81. /**
  82. * Avoid reading all the files when the findFactory
  83. * method is called the second time (cache the result of
  84. * finding the default impl).
  85. */
  86. private static String foundFactory = null;
  87. // Define system property "jaxp.debug" to get output
  88. static {
  89. try {
  90. String val =
  91. SecuritySupport.getInstance().getSystemProperty("jaxp.debug");
  92. // Allow simply setting the prop to turn on debug
  93. debug = val != null && (! "false".equals(val));
  94. } catch (SecurityException se) {
  95. debug = false;
  96. }
  97. }
  98. /**
  99. * Main entry point. Finds and creates a new instance of a concrete
  100. * factory implementation in the specified order as stated in the JAXP
  101. * spec. This code attempts to find a factory implementation in
  102. * serveral locations. If one fails, the next one is tried. To be
  103. * more robust, this occurs even if a SecurityException is thrown, but
  104. * perhaps it may be better to propogate the SecurityException instead,
  105. * so SecurityException-s are not masked.
  106. *
  107. * @return A new instance of the concrete factory class, never null
  108. *
  109. * @param factoryId
  110. * Name of the factory to find, same as a property name
  111. *
  112. * @param fallbackClassName
  113. * Implementation class name, if nothing else is found. Use
  114. * null to mean not to use a fallback.
  115. *
  116. * @throws FactoryFinder.ConfigurationError
  117. * If a factory instance cannot be returned
  118. *
  119. * Package private so this code can be shared.
  120. */
  121. static Object find(String factoryId, String fallbackClassName)
  122. throws ConfigurationError
  123. {
  124. SecuritySupport ss = SecuritySupport.getInstance();
  125. ClassLoader cl = FactoryFinder.class.getClassLoader();
  126. dPrint("find factoryId=" + factoryId);
  127. // Use the system property first
  128. try {
  129. String systemProp = ss.getSystemProperty(factoryId);
  130. if (systemProp != null) {
  131. dPrint("found system property, value=" + systemProp);
  132. return newInstance(systemProp, cl, true);
  133. }
  134. } catch (SecurityException se) {
  135. // Ignore and continue w/ next location
  136. }
  137. synchronized (FactoryFinder.class) {
  138. // This block will only run once, and then foundFactory will
  139. // be set and immutable. Currently there is no support in this
  140. // class for context class loaders. If the contents of the
  141. // xalan.properties file changes, or the class loader changes,
  142. // this will *not* affect the cached class.
  143. if (foundFactory == null) {
  144. // Try to read from $java.home/lib/xalan.properties
  145. Properties xalanProperties = null;
  146. try {
  147. String javah = ss.getSystemProperty("java.home");
  148. String configFile = javah + File.separator +
  149. "lib" + File.separator + "xalan.properties";
  150. File f = new File(configFile);
  151. FileInputStream fis = ss.getFileInputStream(f);
  152. xalanProperties = new Properties();
  153. xalanProperties.load(fis);
  154. fis.close();
  155. } catch (Exception x) {
  156. // assert(x instanceof FileNotFoundException
  157. // || x instanceof SecurityException)
  158. // In both cases, ignore and continue w/ next location
  159. }
  160. if (xalanProperties != null) {
  161. foundFactory = xalanProperties.getProperty(factoryId);
  162. if (foundFactory != null) {
  163. dPrint("found in xalan.properties, value=" + foundFactory);
  164. }
  165. } else {
  166. // Try Jar Service Provider Mechanism
  167. // (foundFactory gets set in findJarServiceProvider method)
  168. findJarServiceProvider(factoryId);
  169. if (foundFactory == null) {
  170. if (fallbackClassName == null) {
  171. throw new ConfigurationError(
  172. "Provider for " + factoryId + " cannot be found", null);
  173. }
  174. dPrint("using fallback, value=" + fallbackClassName);
  175. foundFactory = fallbackClassName;
  176. }
  177. }
  178. }
  179. }
  180. return newInstance(foundFactory, cl, true);
  181. }
  182. private static void dPrint(String msg) {
  183. if (debug) {
  184. System.err.println("JAXP: " + msg);
  185. }
  186. }
  187. /**
  188. * Create an instance of a class using the specified ClassLoader and
  189. * optionally fall back to the current ClassLoader if not found.
  190. *
  191. * @param className Name of the concrete class corresponding to the
  192. * service provider
  193. *
  194. * @param cl ClassLoader to use to load the class, null means to use
  195. * the bootstrap ClassLoader
  196. *
  197. * @param doFallback true if the current ClassLoader should be tried as
  198. * a fallback if the class is not found using cl
  199. */
  200. private static Object newInstance(String className, ClassLoader cl,
  201. boolean doFallback)
  202. throws ConfigurationError
  203. {
  204. // assert(className != null);
  205. try {
  206. Class providerClass;
  207. if (cl == null) {
  208. // XXX Use the bootstrap ClassLoader. There is no way to
  209. // load a class using the bootstrap ClassLoader that works
  210. // in both JDK 1.1 and Java 2. However, this should still
  211. // work b/c the following should be true:
  212. //
  213. // (cl == null) iff current ClassLoader == null
  214. //
  215. // Thus Class.forName(String) will use the current
  216. // ClassLoader which will be the bootstrap ClassLoader.
  217. providerClass = Class.forName(className);
  218. } else {
  219. try {
  220. providerClass = cl.loadClass(className);
  221. } catch (ClassNotFoundException x) {
  222. if (doFallback) {
  223. // Fall back to current classloader
  224. cl = FactoryFinder.class.getClassLoader();
  225. providerClass = cl.loadClass(className);
  226. } else {
  227. throw x;
  228. }
  229. }
  230. }
  231. Object instance = providerClass.newInstance();
  232. dPrint("created new instance of " + providerClass +
  233. " using ClassLoader: " + cl);
  234. return instance;
  235. } catch (ClassNotFoundException x) {
  236. throw new ConfigurationError(
  237. "Provider " + className + " not found", x);
  238. } catch (Exception x) {
  239. throw new ConfigurationError(
  240. "Provider " + className + " could not be instantiated: " + x,
  241. x);
  242. }
  243. }
  244. /*
  245. * Try to find provider using Jar Service Provider Mechanism
  246. *
  247. * @return instance of provider class if found or null
  248. */
  249. private static String findJarServiceProvider(String factoryId)
  250. throws ConfigurationError
  251. {
  252. SecuritySupport ss = SecuritySupport.getInstance();
  253. String serviceId = "META-INF/services/" + factoryId;
  254. InputStream is = null;
  255. // No support yet for context class loader
  256. ClassLoader cl = FactoryFinder.class.getClassLoader();
  257. is = ss.getResourceAsStream(cl, serviceId);
  258. if (is == null) {
  259. // No provider found
  260. return null;
  261. }
  262. dPrint("found jar resource=" + serviceId +
  263. " using ClassLoader: " + cl);
  264. // Read the service provider name in UTF-8 as specified in
  265. // the jar spec. Unfortunately this fails in Microsoft
  266. // VJ++, which does not implement the UTF-8
  267. // encoding. Theoretically, we should simply let it fail in
  268. // that case, since the JVM is obviously broken if it
  269. // doesn't support such a basic standard. But since there
  270. // are still some users attempting to use VJ++ for
  271. // development, we have dropped in a fallback which makes a
  272. // second attempt using the platform's default encoding. In
  273. // VJ++ this is apparently ASCII, which is a subset of
  274. // UTF-8... and since the strings we'll be reading here are
  275. // also primarily limited to the 7-bit ASCII range (at
  276. // least, in English versions), this should work well
  277. // enough to keep us on the air until we're ready to
  278. // officially decommit from VJ++. [Edited comment from
  279. // jkesselm]
  280. BufferedReader rd;
  281. try {
  282. rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
  283. } catch (java.io.UnsupportedEncodingException e) {
  284. rd = new BufferedReader(new InputStreamReader(is));
  285. }
  286. String factoryClassName = null;
  287. try {
  288. // XXX Does not handle all possible input as specified by the
  289. // Jar Service Provider specification
  290. factoryClassName = rd.readLine();
  291. rd.close();
  292. } catch (IOException x) {
  293. // No provider found
  294. return null;
  295. }
  296. if (factoryClassName != null &&
  297. ! "".equals(factoryClassName)) {
  298. dPrint("found in resource, value="
  299. + factoryClassName);
  300. // Note: here we do not want to fall back to the current
  301. // ClassLoader because we want to avoid the case where the
  302. // resource file was found using one ClassLoader and the
  303. // provider class was instantiated using a different one.
  304. return factoryClassName;
  305. }
  306. // No provider found
  307. return null;
  308. }
  309. static class ConfigurationError extends Error {
  310. private Exception exception;
  311. /**
  312. * Construct a new instance with the specified detail string and
  313. * exception.
  314. */
  315. ConfigurationError(String msg, Exception x) {
  316. super(msg);
  317. this.exception = x;
  318. }
  319. Exception getException() {
  320. return exception;
  321. }
  322. }
  323. }