1. /*
  2. * @(#)FactoryFinder.java 1.6 03/02/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.xml.transform;
  8. import java.io.InputStream;
  9. import java.io.IOException;
  10. import java.io.File;
  11. import java.io.FileInputStream;
  12. import java.util.Properties;
  13. import java.io.BufferedReader;
  14. import java.io.InputStreamReader;
  15. /**
  16. * This class is duplicated for each JAXP subpackage so keep it in
  17. * sync. It is package private.
  18. *
  19. * This code is designed to implement the JAXP 1.1 spec pluggability
  20. * feature and is designed to run on JDK version 1.1 and later including
  21. * JVMs that perform early linking like the Microsoft JVM in IE 5. Note
  22. * however that it must be compiled on a JDK version 1.2 or later system
  23. * since it calls Thread#getContextClassLoader(). The code also runs both
  24. * as part of an unbundled jar file and when bundled as part of the JDK.
  25. */
  26. class FactoryFinder {
  27. /** Temp debug code - this will be removed after we test everything
  28. */
  29. private static boolean debug = false;
  30. static {
  31. // Use try/catch block to support applets
  32. try {
  33. debug = System.getProperty("jaxp.debug") != null;
  34. } catch (Exception x) {
  35. }
  36. }
  37. private static void debugPrintln(String msg) {
  38. if (debug) {
  39. System.err.println("JAXP: " + msg);
  40. }
  41. }
  42. /**
  43. * Figure out which ClassLoader to use. For JDK 1.2 and later use the
  44. * context ClassLoader if possible. Note: we defer linking the class
  45. * that calls an API only in JDK 1.2 until runtime so that we can catch
  46. * LinkageError so that this code will run in older non-Sun JVMs such
  47. * as the Microsoft JVM in IE.
  48. */
  49. private static ClassLoader findClassLoader()
  50. throws ConfigurationError
  51. {
  52. ClassLoader classLoader;
  53. try {
  54. // Construct the name of the concrete class to instantiate
  55. Class clazz = Class.forName(FactoryFinder.class.getName()
  56. + "$ClassLoaderFinderConcrete");
  57. ClassLoaderFinder clf = (ClassLoaderFinder) clazz.newInstance();
  58. classLoader = clf.getContextClassLoader();
  59. } catch (LinkageError le) {
  60. // Assume that we are running JDK 1.1, use the current ClassLoader
  61. classLoader = FactoryFinder.class.getClassLoader();
  62. } catch (ClassNotFoundException x) {
  63. // This case should not normally happen. MS IE can throw this
  64. // instead of a LinkageError the second time Class.forName() is
  65. // called so assume that we are running JDK 1.1 and use the
  66. // current ClassLoader
  67. classLoader = FactoryFinder.class.getClassLoader();
  68. } catch (Exception x) {
  69. // Something abnormal happened so throw an error
  70. throw new ConfigurationError(x.toString(), x);
  71. }
  72. return classLoader;
  73. }
  74. /**
  75. * Create an instance of a class using the specified ClassLoader
  76. */
  77. private static Object newInstance(String className,
  78. ClassLoader classLoader)
  79. throws ConfigurationError
  80. {
  81. try {
  82. Class spiClass;
  83. if (classLoader == null) {
  84. spiClass = Class.forName(className);
  85. } else {
  86. spiClass = classLoader.loadClass(className);
  87. }
  88. return spiClass.newInstance();
  89. } catch (ClassNotFoundException x) {
  90. throw new ConfigurationError(
  91. "Provider " + className + " not found", x);
  92. } catch (Exception x) {
  93. throw new ConfigurationError(
  94. "Provider " + className + " could not be instantiated: " + x,
  95. x);
  96. }
  97. }
  98. /**
  99. * Finds the implementation Class object in the specified order. Main
  100. * entry point.
  101. * @return Class object of factory, never null
  102. *
  103. * @param factoryId Name of the factory to find, same as
  104. * a property name
  105. * @param fallbackClassName Implementation class name, if nothing else
  106. * is found. Use null to mean no fallback.
  107. *
  108. * Package private so this code can be shared.
  109. */
  110. static Object find(String factoryId, String fallbackClassName)
  111. throws ConfigurationError
  112. {
  113. ClassLoader classLoader = findClassLoader();
  114. // Use the system property first
  115. try {
  116. String systemProp =
  117. System.getProperty( factoryId );
  118. if( systemProp!=null) {
  119. debugPrintln("found system property" + systemProp);
  120. return newInstance(systemProp, classLoader);
  121. }
  122. } catch (SecurityException se) {
  123. }
  124. // try to read from $java.home/lib/jaxp.properties
  125. try {
  126. String javah=System.getProperty( "java.home" );
  127. String configFile = javah + File.separator +
  128. "lib" + File.separator + "jaxp.properties";
  129. File f=new File( configFile );
  130. if( f.exists()) {
  131. Properties props=new Properties();
  132. props.load( new FileInputStream(f));
  133. String factoryClassName = props.getProperty(factoryId);
  134. debugPrintln("found java.home property " + factoryClassName);
  135. if(factoryClassName != null){
  136. return newInstance(factoryClassName, classLoader);
  137. }
  138. }
  139. } catch(Exception ex ) {
  140. if( debug ) ex.printStackTrace();
  141. }
  142. String serviceId = "META-INF/services/" + factoryId;
  143. // try to find services in CLASSPATH
  144. try {
  145. InputStream is=null;
  146. if (classLoader == null) {
  147. is=ClassLoader.getSystemResourceAsStream( serviceId );
  148. } else {
  149. is=classLoader.getResourceAsStream( serviceId );
  150. }
  151. if( is!=null ) {
  152. debugPrintln("found " + serviceId);
  153. BufferedReader rd =
  154. new BufferedReader(new InputStreamReader(is, "UTF-8"));
  155. String factoryClassName = rd.readLine();
  156. rd.close();
  157. if (factoryClassName != null &&
  158. ! "".equals(factoryClassName)) {
  159. debugPrintln("loaded from services: " + factoryClassName);
  160. return newInstance(factoryClassName, classLoader);
  161. }
  162. }
  163. } catch( Exception ex ) {
  164. if( debug ) ex.printStackTrace();
  165. }
  166. if (fallbackClassName == null) {
  167. throw new ConfigurationError(
  168. "Provider for " + factoryId + " cannot be found", null);
  169. }
  170. debugPrintln("loaded from fallback value: " + fallbackClassName);
  171. return newInstance(fallbackClassName, classLoader);
  172. }
  173. static class ConfigurationError extends Error {
  174. private Exception exception;
  175. /**
  176. * Construct a new instance with the specified detail string and
  177. * exception.
  178. */
  179. ConfigurationError(String msg, Exception x) {
  180. super(msg);
  181. this.exception = x;
  182. }
  183. Exception getException() {
  184. return exception;
  185. }
  186. }
  187. /*
  188. * The following nested classes allow getContextClassLoader() to be
  189. * called only on JDK 1.2 and yet run in older JDK 1.1 JVMs
  190. */
  191. private static abstract class ClassLoaderFinder {
  192. abstract ClassLoader getContextClassLoader();
  193. }
  194. static class ClassLoaderFinderConcrete extends ClassLoaderFinder {
  195. ClassLoader getContextClassLoader() {
  196. return Thread.currentThread().getContextClassLoader();
  197. }
  198. }
  199. }