1. // $Id: FactoryFinder.java,v 1.7 2004/03/17 10:31:30 nb131165 Exp $
  2. /*
  3. * @(#)FactoryFinder.java 1.7 04/07/26
  4. *
  5. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  6. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  7. */
  8. package javax.xml.parsers;
  9. import java.io.File;
  10. import java.io.FileInputStream;
  11. import java.util.Properties;
  12. import java.io.BufferedReader;
  13. import java.io.IOException;
  14. import java.io.InputStream;
  15. import java.io.InputStreamReader;
  16. import java.net.URL;
  17. /**
  18. * This class is duplicated for each JAXP subpackage so keep it in
  19. * sync. It is package private.
  20. *
  21. * This code is designed to implement the JAXP 1.1 spec pluggability
  22. * feature and is designed to run on JDK version 1.1 and later including
  23. * JVMs that perform early linking like the Microsoft JVM in IE 5. Note
  24. * however that it must be compiled on a JDK version 1.2 or later system
  25. * since it calls Thread#getContextClassLoader(). The code also runs both
  26. * as part of an unbundled jar file and when bundled as part of the JDK.
  27. */
  28. class FactoryFinder {
  29. /** Temp debug code - this will be removed after we test everything
  30. */
  31. private static boolean debug = false;
  32. static Properties cacheProps= new Properties();
  33. static SecuritySupport ss = new SecuritySupport() ;
  34. static boolean firstTime = true;
  35. // Define system property "jaxp.debug" to get output
  36. static {
  37. // Use try/catch block to support applets, which throws
  38. // SecurityException out of this code.
  39. try {
  40. String val = ss.getSystemProperty("jaxp.debug");
  41. // Allow simply setting the prop to turn on debug
  42. debug = val != null && (! "false".equals(val));
  43. } catch (SecurityException se) {
  44. debug = false;
  45. }
  46. }
  47. private static void dPrint(String msg) {
  48. if (debug) {
  49. System.err.println("JAXP: " + msg);
  50. }
  51. }
  52. /**
  53. * Create an instance of a class using the specified ClassLoader and
  54. * optionally fall back to the current ClassLoader if not found.
  55. *
  56. * @param className Name of the concrete class corresponding to the
  57. * service provider
  58. *
  59. * @param cl ClassLoader to use to load the class, null means to use
  60. * the bootstrap ClassLoader
  61. *
  62. * @param doFallback true if the current ClassLoader should be tried as
  63. * a fallback if the class is not found using cl
  64. */
  65. private static Object newInstance(String className, ClassLoader cl,
  66. boolean doFallback)
  67. throws ConfigurationError
  68. {
  69. // assert(className != null);
  70. try {
  71. Class providerClass;
  72. if (cl == null) {
  73. // If classloader is null Use the bootstrap ClassLoader.
  74. // Thus Class.forName(String) will use the current
  75. // ClassLoader which will be the bootstrap ClassLoader.
  76. providerClass = Class.forName(className);
  77. } else {
  78. try {
  79. providerClass = cl.loadClass(className);
  80. } catch (ClassNotFoundException x) {
  81. if (doFallback) {
  82. // Fall back to current classloader
  83. cl = FactoryFinder.class.getClassLoader();
  84. providerClass = cl.loadClass(className);
  85. } else {
  86. throw x;
  87. }
  88. }
  89. }
  90. Object instance = providerClass.newInstance();
  91. dPrint("created new instance of " + providerClass +
  92. " using ClassLoader: " + cl);
  93. return instance;
  94. } catch (ClassNotFoundException x) {
  95. throw new ConfigurationError(
  96. "Provider " + className + " not found", x);
  97. } catch (Exception x) {
  98. throw new ConfigurationError(
  99. "Provider " + className + " could not be instantiated: " + x,
  100. x);
  101. }
  102. }
  103. /**
  104. * Finds the implementation Class object in the specified order. Main
  105. * entry point.
  106. * @return Class object of factory, never null
  107. *
  108. * @param factoryId Name of the factory to find, same as
  109. * a property name
  110. * @param fallbackClassName Implementation class name, if nothing else
  111. * is found. Use null to mean no fallback.
  112. *
  113. * Package private so this code can be shared.
  114. */
  115. static Object find(String factoryId, String fallbackClassName)
  116. throws ConfigurationError
  117. {
  118. // Figure out which ClassLoader to use for loading the provider
  119. // class. If there is a Context ClassLoader then use it.
  120. ClassLoader classLoader = ss.getContextClassLoader();
  121. if (classLoader == null) {
  122. // if we have no Context ClassLoader
  123. // so use the current ClassLoader
  124. classLoader = FactoryFinder.class.getClassLoader();
  125. }
  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, classLoader, true );
  133. }
  134. } catch (SecurityException se) {
  135. //if first option fails due to any reason we should try next option in the
  136. //look up algorithm.
  137. }
  138. // try to read from $java.home/lib/jaxp.properties
  139. try {
  140. String javah = ss.getSystemProperty("java.home");
  141. String configFile = javah + File.separator +
  142. "lib" + File.separator + "jaxp.properties";
  143. String factoryClassName = null;
  144. if(firstTime){
  145. synchronized(cacheProps){
  146. if(firstTime){
  147. File f=new File( configFile );
  148. firstTime = false;
  149. if(ss.doesFileExist(f)){
  150. dPrint("Read properties file "+f);
  151. //cacheProps.load( new FileInputStream(f));
  152. cacheProps.load(ss.getFileInputStream(f));
  153. }
  154. }
  155. }
  156. }
  157. factoryClassName = cacheProps.getProperty(factoryId);
  158. if(factoryClassName != null){
  159. dPrint("found in $java.home/jaxp.properties, value=" + factoryClassName);
  160. return newInstance(factoryClassName, classLoader, true);
  161. }
  162. } catch(Exception ex ) {
  163. if( debug ) ex.printStackTrace();
  164. }
  165. // Try Jar Service Provider Mechanism
  166. Object provider = findJarServiceProvider(factoryId);
  167. if (provider != null) {
  168. return provider;
  169. }
  170. if (fallbackClassName == null) {
  171. throw new ConfigurationError(
  172. "Provider for " + factoryId + " cannot be found", null);
  173. }
  174. dPrint("loaded from fallback value: " + fallbackClassName);
  175. return newInstance(fallbackClassName, classLoader, true);
  176. }
  177. /*
  178. * Try to find provider using Jar Service Provider Mechanism
  179. *
  180. * @return instance of provider class if found or null
  181. */
  182. private static Object findJarServiceProvider(String factoryId)
  183. throws ConfigurationError
  184. {
  185. String serviceId = "META-INF/services/" + factoryId;
  186. InputStream is = null;
  187. // First try the Context ClassLoader
  188. ClassLoader cl = ss.getContextClassLoader();
  189. if (cl != null) {
  190. is = ss.getResourceAsStream(cl, serviceId);
  191. // If no provider found then try the current ClassLoader
  192. if (is == null) {
  193. cl = FactoryFinder.class.getClassLoader();
  194. is = ss.getResourceAsStream(cl, serviceId);
  195. }
  196. } else {
  197. // No Context ClassLoader, try the current
  198. // ClassLoader
  199. cl = FactoryFinder.class.getClassLoader();
  200. is = ss.getResourceAsStream(cl, serviceId);
  201. }
  202. if (is == null) {
  203. // No provider found
  204. return null;
  205. }
  206. dPrint("found jar resource=" + serviceId +
  207. " using ClassLoader: " + cl);
  208. // Read the service provider name in UTF-8 as specified in
  209. // the jar spec. Unfortunately this fails in Microsoft
  210. // VJ++, which does not implement the UTF-8
  211. // encoding. Theoretically, we should simply let it fail in
  212. // that case, since the JVM is obviously broken if it
  213. // doesn't support such a basic standard. But since there
  214. // are still some users attempting to use VJ++ for
  215. // development, we have dropped in a fallback which makes a
  216. // second attempt using the platform's default encoding. In
  217. // VJ++ this is apparently ASCII, which is a subset of
  218. // UTF-8... and since the strings we'll be reading here are
  219. // also primarily limited to the 7-bit ASCII range (at
  220. // least, in English versions), this should work well
  221. // enough to keep us on the air until we're ready to
  222. // officially decommit from VJ++. [Edited comment from
  223. // jkesselm]
  224. BufferedReader rd;
  225. try {
  226. rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
  227. } catch (java.io.UnsupportedEncodingException e) {
  228. rd = new BufferedReader(new InputStreamReader(is));
  229. }
  230. String factoryClassName = null;
  231. try {
  232. // XXX Does not handle all possible input as specified by the
  233. // Jar Service Provider specification
  234. factoryClassName = rd.readLine();
  235. rd.close();
  236. } catch (IOException x) {
  237. // No provider found
  238. return null;
  239. }
  240. if (factoryClassName != null &&
  241. ! "".equals(factoryClassName)) {
  242. dPrint("found in resource, value="
  243. + factoryClassName);
  244. // Note: here we do not want to fall back to the current
  245. // ClassLoader because we want to avoid the case where the
  246. // resource file was found using one ClassLoader and the
  247. // provider class was instantiated using a different one.
  248. return newInstance(factoryClassName, cl, false);
  249. }
  250. // No provider found
  251. return null;
  252. }
  253. static class ConfigurationError extends Error {
  254. private Exception exception;
  255. /**
  256. * Construct a new instance with the specified detail string and
  257. * exception.
  258. */
  259. ConfigurationError(String msg, Exception x) {
  260. super(msg);
  261. this.exception = x;
  262. }
  263. Exception getException() {
  264. return exception;
  265. }
  266. }
  267. }