1. // $Id: XPathFactoryFinder.java,v 1.2.8.1 2004/05/07 00:34:05 jsuttor Exp $
  2. /*
  3. * @(#)XPathFactoryFinder.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.xpath;
  9. import java.io.File;
  10. import java.io.FileInputStream;
  11. import java.io.IOException;
  12. import java.io.InputStream;
  13. import java.net.URL;
  14. import java.util.ArrayList;
  15. import java.util.Enumeration;
  16. import java.util.Iterator;
  17. import java.util.NoSuchElementException;
  18. import java.util.Properties;
  19. /**
  20. * Implementation of {@link XPathFactory#newInstance(String)}.
  21. *
  22. * @author <a href="Kohsuke.Kawaguchi@Sun.com">Kohsuke Kawaguchi</a>
  23. * @version $Revision: 1.2.8.1 $, $Date: 2004/05/07 00:34:05 $
  24. * @since 1.5
  25. */
  26. class XPathFactoryFinder {
  27. /** debug support code. */
  28. private static boolean debug = false;
  29. static {
  30. // Use try/catch block to support applets
  31. try {
  32. debug = System.getProperty("jaxp.debug") != null;
  33. } catch (Exception _) {
  34. debug = false;
  35. }
  36. }
  37. /**
  38. * <p>Conditional debug printing.</p>
  39. *
  40. * @param msg to print
  41. */
  42. private static void debugPrintln(String msg) {
  43. if (debug) {
  44. System.err.println("JAXP: " + msg);
  45. }
  46. }
  47. /**
  48. * <p><code>ClassLoader</code> to use to find <code>SchemaFactory</code>.</p>
  49. */
  50. private final ClassLoader classLoader;
  51. /**
  52. * <p>Constructor that specifies <code>ClassLoader</code> to use
  53. * to find <code>SchemaFactory</code>.</p>
  54. *
  55. * @param loader
  56. * to be used to load resource, {@link SchemaFactory}, and
  57. * {@link SchemaFactoryLoader} implementations during
  58. * the resolution process.
  59. * If this parameter is null, the default system class loader
  60. * will be used.
  61. */
  62. public XPathFactoryFinder(ClassLoader loader) {
  63. this.classLoader = loader;
  64. if( debug ) {
  65. debugDisplayClassLoader();
  66. }
  67. }
  68. private void debugDisplayClassLoader() {
  69. try {
  70. if( classLoader==Thread.currentThread().getContextClassLoader() ) {
  71. debugPrintln("using thread context class loader ("+classLoader+") for search");
  72. return;
  73. }
  74. } catch( Throwable _ ) {
  75. ; // getContextClassLoader() undefined in JDK1.1
  76. }
  77. if( classLoader==ClassLoader.getSystemClassLoader() ) {
  78. debugPrintln("using system class loader ("+classLoader+") for search");
  79. return;
  80. }
  81. debugPrintln("using class loader ("+classLoader+") for search");
  82. }
  83. /**
  84. * <p>Creates a new {@link XPathFactory} object for the specified
  85. * schema language.</p>
  86. *
  87. * @param uri
  88. * Identifies the underlying object model.
  89. *
  90. * @return <code>null</code> if the callee fails to create one.
  91. *
  92. * @throws NullPointerException
  93. * If the parameter is null.
  94. */
  95. public XPathFactory newFactory(String uri) {
  96. if(uri==null) throw new NullPointerException();
  97. XPathFactory f = _newFactory(uri);
  98. if (f != null) {
  99. debugPrintln("factory '" + f.getClass().getName() + "' was found for " + uri);
  100. } else {
  101. debugPrintln("unable to find a factory for " + uri);
  102. }
  103. return f;
  104. }
  105. /**
  106. * <p>Lookup a {@link XPathFactory} for the given object model.</p>
  107. *
  108. * @param uri identifies the object model.
  109. *
  110. * @return {@link XPathFactory} for the given object model.
  111. */
  112. private XPathFactory _newFactory(String uri) {
  113. XPathFactory sf;
  114. String propertyName = SERVICE_CLASS.getName() + ":" + uri;
  115. // system property look up
  116. try {
  117. debugPrintln("Looking up system property '"+propertyName+"'" );
  118. String r = System.getProperty(propertyName);
  119. if(r!=null) {
  120. debugPrintln("The value is '"+r+"'");
  121. sf = createInstance(r);
  122. if(sf!=null) return sf;
  123. } else
  124. debugPrintln("The property is undefined.");
  125. } catch( Throwable t ) {
  126. if( debug ) {
  127. debugPrintln("failed to look up system property '"+propertyName+"'" );
  128. t.printStackTrace();
  129. }
  130. }
  131. // try to read from $java.home/lib/jaxp.properties
  132. try {
  133. String javah=System.getProperty( "java.home" );
  134. String configFile = javah + File.separator +
  135. "lib" + File.separator + "jaxp.properties";
  136. File f=new File( configFile );
  137. if( f.exists()) {
  138. sf = loadFromProperty(
  139. propertyName,f.getAbsolutePath(), new FileInputStream(f));
  140. if(sf!=null) return sf;
  141. } else {
  142. debugPrintln("Tried to read "+ f.getAbsolutePath()+", but it doesn't exist.");
  143. }
  144. } catch(Throwable e) {
  145. if( debug ) {
  146. debugPrintln("failed to read $java.home/lib/jaxp.properties");
  147. e.printStackTrace();
  148. }
  149. }
  150. // try META-INF/services files
  151. Iterator sitr = createServiceFileIterator();
  152. while(sitr.hasNext()) {
  153. URL resource = (URL)sitr.next();
  154. debugPrintln("looking into " + resource);
  155. try {
  156. sf = loadFromProperty(uri,resource.toExternalForm(),resource.openStream());
  157. if(sf!=null) return sf;
  158. } catch(IOException e) {
  159. if( debug ) {
  160. debugPrintln("failed to read "+resource);
  161. e.printStackTrace();
  162. }
  163. }
  164. }
  165. // platform default
  166. if(uri.equals(XPathFactory.DEFAULT_OBJECT_MODEL_URI)) {
  167. debugPrintln("attempting to use the platform default W3C DOM XPath lib");
  168. return createInstance("com.sun.org.apache.xpath.internal.jaxp.XPathFactoryImpl");
  169. }
  170. debugPrintln("all things were tried, but none was found. bailing out.");
  171. return null;
  172. }
  173. /**
  174. * <p>Creates an instance of the specified and returns it.</p>
  175. *
  176. * @param className
  177. * fully qualified class name to be instanciated.
  178. *
  179. * @return null
  180. * if it fails. Error messages will be printed by this method.
  181. */
  182. private XPathFactory createInstance( String className ) {
  183. try {
  184. debugPrintln("instanciating "+className);
  185. Class clazz;
  186. if( classLoader!=null )
  187. clazz = classLoader.loadClass(className);
  188. else
  189. clazz = Class.forName(className);
  190. if(debug) debugPrintln("loaded it from "+which(clazz));
  191. Object o = clazz.newInstance();
  192. if( o instanceof XPathFactory )
  193. return (XPathFactory)o;
  194. debugPrintln(className+" is not assignable to "+SERVICE_CLASS.getName());
  195. } catch( Throwable t ) {
  196. debugPrintln("failed to instanciate "+className);
  197. if(debug) t.printStackTrace();
  198. }
  199. return null;
  200. }
  201. /** Iterator that lazily computes one value and returns it. */
  202. private static abstract class SingleIterator implements Iterator {
  203. private boolean seen = false;
  204. public final void remove() { throw new UnsupportedOperationException(); }
  205. public final boolean hasNext() { return !seen; }
  206. public final Object next() {
  207. if(seen) throw new NoSuchElementException();
  208. seen = true;
  209. return value();
  210. }
  211. protected abstract Object value();
  212. }
  213. /**
  214. * Looks up a value in a property file
  215. * while producing all sorts of debug messages.
  216. *
  217. * @return null
  218. * if there was an error.
  219. */
  220. private XPathFactory loadFromProperty( String keyName, String resourceName, InputStream in )
  221. throws IOException {
  222. debugPrintln("Reading "+resourceName );
  223. Properties props=new Properties();
  224. props.load(in);
  225. in.close();
  226. String factoryClassName = props.getProperty(keyName);
  227. if(factoryClassName != null){
  228. debugPrintln("found "+keyName+" = " + factoryClassName);
  229. return createInstance(factoryClassName);
  230. } else {
  231. debugPrintln(keyName+" is not in the property file");
  232. return null;
  233. }
  234. }
  235. /**
  236. * Returns an {@link Iterator} that enumerates all
  237. * the META-INF/services files that we care.
  238. */
  239. private Iterator createServiceFileIterator() {
  240. if (classLoader == null) {
  241. return new SingleIterator() {
  242. protected Object value() {
  243. return (ClassLoader.getSystemResource( SERVICE_ID ));
  244. }
  245. };
  246. } else {
  247. try {
  248. final Enumeration e = classLoader.getResources(SERVICE_ID);
  249. if(!e.hasMoreElements()) {
  250. debugPrintln("no "+SERVICE_ID+" file was found");
  251. }
  252. // wrap it into an Iterator.
  253. return new Iterator() {
  254. public void remove() {
  255. throw new UnsupportedOperationException();
  256. }
  257. public boolean hasNext() {
  258. return e.hasMoreElements();
  259. }
  260. public Object next() {
  261. return e.nextElement();
  262. }
  263. };
  264. } catch (IOException e) {
  265. debugPrintln("failed to enumerate resources "+SERVICE_ID);
  266. if(debug) e.printStackTrace();
  267. return new ArrayList().iterator(); // empty iterator
  268. }
  269. }
  270. }
  271. private static final Class SERVICE_CLASS = XPathFactory.class;
  272. private static final String SERVICE_ID = "META-INF/services/" + SERVICE_CLASS.getName();
  273. private static String which( Class clazz ) {
  274. return which( clazz.getName(), clazz.getClassLoader() );
  275. }
  276. /**
  277. * <p>Search the specified classloader for the given classname.</p>
  278. *
  279. * @param classname the fully qualified name of the class to search for
  280. * @param loader the classloader to search
  281. *
  282. * @return the source location of the resource, or null if it wasn't found
  283. */
  284. private static String which(String classname, ClassLoader loader) {
  285. String classnameAsResource = classname.replace('.', '/') + ".class";
  286. if( loader==null ) loader = ClassLoader.getSystemClassLoader();
  287. URL it = loader.getResource(classnameAsResource);
  288. if (it != null) {
  289. return it.toString();
  290. } else {
  291. return null;
  292. }
  293. }
  294. }