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