1. // $Id: SchemaFactoryFinder.java,v 1.11.6.1.2.1.4.1 2004/05/07 01:22:37 jsuttor Exp $
  2. /*
  3. * @(#)SchemaFactoryFinder.java 1.8 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.validation;
  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 SchemaFactory#newInstance(String)}.
  21. *
  22. * @author <a href="Kohsuke.Kawaguchi@Sun.com">Kohsuke Kawaguchi</a>
  23. * @version $Revision: 1.11.6.1.2.1.4.1 $, $Date: 2004/05/07 01:22:37 $
  24. * @since 1.5
  25. */
  26. class SchemaFactoryFinder {
  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 SchemaFactoryFinder(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 SchemaFactory} object for the specified
  85. * schema language.</p>
  86. *
  87. * @param schemaLanguage
  88. * See <a href="SchemaFactory.html#schemaLanguage">
  89. * the list of available schema languages</a>
  90. *
  91. * @return <code>null</code> if the callee fails to create one.
  92. *
  93. * @throws NullPointerException
  94. * If the <tt>schemaLanguage</tt> parameter is null.
  95. */
  96. public SchemaFactory newFactory(String schemaLanguage) {
  97. if(schemaLanguage==null) throw new NullPointerException();
  98. SchemaFactory f = _newFactory(schemaLanguage);
  99. if (f != null) {
  100. debugPrintln("factory '" + f.getClass().getName() + "' was found for " + schemaLanguage);
  101. } else {
  102. debugPrintln("unable to find a factory for " + schemaLanguage);
  103. }
  104. return f;
  105. }
  106. /**
  107. * <p>Lookup a <code>SchemaFactory</code> for the given <code>schemaLanguage</code>.</p>
  108. *
  109. * @param schemaLanguage Schema language to lookup <code>SchemaFactory</code> for.
  110. *
  111. * @return <code>SchemaFactory</code> for the given <code>schemaLanguage</code>.
  112. */
  113. private SchemaFactory _newFactory(String schemaLanguage) {
  114. SchemaFactory sf;
  115. String propertyName = SERVICE_CLASS.getName() + ":" + schemaLanguage;
  116. // system property look up
  117. try {
  118. debugPrintln("Looking up system property '"+propertyName+"'" );
  119. String r = System.getProperty(propertyName);
  120. if(r!=null) {
  121. debugPrintln("The value is '"+r+"'");
  122. sf = createInstance(r);
  123. if(sf!=null) return sf;
  124. } else
  125. debugPrintln("The property is undefined.");
  126. } catch( Throwable t ) {
  127. if( debug ) {
  128. debugPrintln("failed to look up system property '"+propertyName+"'" );
  129. t.printStackTrace();
  130. }
  131. }
  132. // try to read from $java.home/lib/jaxp.properties
  133. try {
  134. String javah=System.getProperty( "java.home" );
  135. String configFile = javah + File.separator +
  136. "lib" + File.separator + "jaxp.properties";
  137. File f=new File( configFile );
  138. if( f.exists()) {
  139. sf = loadFromProperty(
  140. propertyName,f.getAbsolutePath(), new FileInputStream(f));
  141. if(sf!=null) return sf;
  142. } else {
  143. debugPrintln("Tried to read "+ f.getAbsolutePath()+", but it doesn't exist.");
  144. }
  145. } catch(Throwable e) {
  146. if( debug ) {
  147. debugPrintln("failed to read $java.home/lib/jaxp.properties");
  148. e.printStackTrace();
  149. }
  150. }
  151. // try META-INF/services files
  152. Iterator sitr = createServiceFileIterator();
  153. while(sitr.hasNext()) {
  154. URL resource = (URL)sitr.next();
  155. debugPrintln("looking into " + resource);
  156. try {
  157. sf = loadFromProperty(schemaLanguage,resource.toExternalForm(),resource.openStream());
  158. if(sf!=null) return sf;
  159. } catch(IOException e) {
  160. if( debug ) {
  161. debugPrintln("failed to read "+resource);
  162. e.printStackTrace();
  163. }
  164. }
  165. }
  166. // platform default
  167. if(schemaLanguage.equals("http://www.w3.org/2001/XMLSchema")) {
  168. debugPrintln("attempting to use the platform default XML Schema validator");
  169. return createInstance("com.sun.org.apache.xerces.internal.jaxp.validation.xs.SchemaFactoryImpl");
  170. }
  171. debugPrintln("all things were tried, but none was found. bailing out.");
  172. return null;
  173. }
  174. /**
  175. * <p>Creates an instance of the specified and returns it.</p>
  176. *
  177. * @param className
  178. * fully qualified class name to be instanciated.
  179. *
  180. * @return null
  181. * if it fails. Error messages will be printed by this method.
  182. */
  183. private SchemaFactory createInstance( String className ) {
  184. try {
  185. debugPrintln("instanciating "+className);
  186. Class clazz;
  187. if( classLoader!=null )
  188. clazz = classLoader.loadClass(className);
  189. else
  190. clazz = Class.forName(className);
  191. if(debug) debugPrintln("loaded it from "+which(clazz));
  192. Object o = clazz.newInstance();
  193. if( o instanceof SchemaFactory )
  194. return (SchemaFactory)o;
  195. debugPrintln(className+" is not assignable to "+SERVICE_CLASS.getName());
  196. } catch( Throwable t ) {
  197. debugPrintln("failed to instanciate "+className);
  198. if(debug) t.printStackTrace();
  199. }
  200. return null;
  201. }
  202. /** Iterator that lazily computes one value and returns it. */
  203. private static abstract class SingleIterator implements Iterator {
  204. private boolean seen = false;
  205. public final void remove() { throw new UnsupportedOperationException(); }
  206. public final boolean hasNext() { return !seen; }
  207. public final Object next() {
  208. if(seen) throw new NoSuchElementException();
  209. seen = true;
  210. return value();
  211. }
  212. protected abstract Object value();
  213. }
  214. /**
  215. * Looks up a value in a property file
  216. * while producing all sorts of debug messages.
  217. *
  218. * @return null
  219. * if there was an error.
  220. */
  221. private SchemaFactory loadFromProperty( String keyName, String resourceName, InputStream in )
  222. throws IOException {
  223. debugPrintln("Reading "+resourceName );
  224. Properties props=new Properties();
  225. props.load(in);
  226. in.close();
  227. String factoryClassName = props.getProperty(keyName);
  228. if(factoryClassName != null){
  229. debugPrintln("found "+keyName+" = " + factoryClassName);
  230. return createInstance(factoryClassName);
  231. } else {
  232. debugPrintln(keyName+" is not in the property file");
  233. return null;
  234. }
  235. }
  236. /**
  237. * Returns an {@link Iterator} that enumerates all
  238. * the META-INF/services files that we care.
  239. */
  240. private Iterator createServiceFileIterator() {
  241. if (classLoader == null) {
  242. return new SingleIterator() {
  243. protected Object value() {
  244. return (ClassLoader.getSystemResource( SERVICE_ID ));
  245. }
  246. };
  247. } else {
  248. try {
  249. final Enumeration e = classLoader.getResources(SERVICE_ID);
  250. if(!e.hasMoreElements()) {
  251. debugPrintln("no "+SERVICE_ID+" file was found");
  252. }
  253. // wrap it into an Iterator.
  254. return new Iterator() {
  255. public void remove() {
  256. throw new UnsupportedOperationException();
  257. }
  258. public boolean hasNext() {
  259. return e.hasMoreElements();
  260. }
  261. public Object next() {
  262. return e.nextElement();
  263. }
  264. };
  265. } catch (IOException e) {
  266. debugPrintln("failed to enumerate resources "+SERVICE_ID);
  267. if(debug) e.printStackTrace();
  268. return new ArrayList().iterator(); // empty iterator
  269. }
  270. }
  271. }
  272. private static final Class SERVICE_CLASS = SchemaFactory.class;
  273. private static final String SERVICE_ID = "META-INF/services/" + SERVICE_CLASS.getName();
  274. private static String which( Class clazz ) {
  275. return which( clazz.getName(), clazz.getClassLoader() );
  276. }
  277. /**
  278. * <p>Search the specified classloader for the given classname.</p>
  279. *
  280. * @param classname the fully qualified name of the class to search for
  281. * @param loader the classloader to search
  282. *
  283. * @return the source location of the resource, or null if it wasn't found
  284. */
  285. private static String which(String classname, ClassLoader loader) {
  286. String classnameAsResource = classname.replace('.', '/') + ".class";
  287. if( loader==null ) loader = ClassLoader.getSystemClassLoader();
  288. URL it = loader.getResource(classnameAsResource);
  289. if (it != null) {
  290. return it.toString();
  291. } else {
  292. return null;
  293. }
  294. }
  295. }