Frames | No Frames |
1: /* 2: * Copyright (c) 2004 World Wide Web Consortium, 3: * 4: * (Massachusetts Institute of Technology, European Research Consortium for 5: * Informatics and Mathematics, Keio University). All Rights Reserved. This 6: * work is distributed under the W3C(r) Software License [1] in the hope that 7: * it will be useful, but WITHOUT ANY WARRANTY; without even the implied 8: * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 9: * 10: * [1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 11: */ 12: 13: 14: package org.w3c.dom.bootstrap; 15: 16: import java.util.StringTokenizer; 17: import java.util.Vector; 18: import org.w3c.dom.DOMImplementationSource; 19: import org.w3c.dom.DOMImplementationList; 20: import org.w3c.dom.DOMImplementation; 21: import java.io.InputStream; 22: import java.io.BufferedReader; 23: import java.io.InputStreamReader; 24: import java.security.AccessController; 25: import java.security.PrivilegedAction; 26: 27: /** 28: * A factory that enables applications to obtain instances of 29: * <code>DOMImplementation</code>. 30: * 31: * <p> 32: * Example: 33: * </p> 34: * 35: * <pre class='example'> 36: * // get an instance of the DOMImplementation registry 37: * DOMImplementationRegistry registry = 38: * DOMImplementationRegistry.newInstance(); 39: * // get a DOM implementation the Level 3 XML module 40: * DOMImplementation domImpl = 41: * registry.getDOMImplementation("XML 3.0"); 42: * </pre> 43: * 44: * <p> 45: * This provides an application with an implementation-independent starting 46: * point. DOM implementations may modify this class to meet new security 47: * standards or to provide *additional* fallbacks for the list of 48: * DOMImplementationSources. 49: * </p> 50: * 51: * @see DOMImplementation 52: * @see DOMImplementationSource 53: * @since DOM Level 3 54: */ 55: public final class DOMImplementationRegistry { 56: /** 57: * The system property to specify the 58: * DOMImplementationSource class names. 59: */ 60: public static final String PROPERTY = 61: "org.w3c.dom.DOMImplementationSourceList"; 62: 63: /** 64: * Default columns per line. 65: */ 66: private static final int DEFAULT_LINE_LENGTH = 80; 67: 68: /** 69: * The list of DOMImplementationSources. 70: */ 71: private Vector sources; 72: 73: /** 74: * Private constructor. 75: * @param srcs Vector List of DOMImplementationSources 76: */ 77: private DOMImplementationRegistry(final Vector srcs) { 78: sources = srcs; 79: } 80: 81: /** 82: * Obtain a new instance of a <code>DOMImplementationRegistry</code>. 83: * 84: 85: * The <code>DOMImplementationRegistry</code> is initialized by the 86: * application or the implementation, depending on the context, by 87: * first checking the value of the Java system property 88: * <code>org.w3c.dom.DOMImplementationSourceList</code> and 89: * the the service provider whose contents are at 90: * "<code>META_INF/services/org.w3c.dom.DOMImplementationSourceList</code>" 91: * The value of this property is a white-space separated list of 92: * names of availables classes implementing the 93: * <code>DOMImplementationSource</code> interface. Each class listed 94: * in the class name list is instantiated and any exceptions 95: * encountered are thrown to the application. 96: * 97: * @return an initialized instance of DOMImplementationRegistry 98: * @throws ClassNotFoundException 99: * If any specified class can not be found 100: * @throws InstantiationException 101: * If any specified class is an interface or abstract class 102: * @throws IllegalAccessException 103: * If the default constructor of a specified class is not accessible 104: * @throws ClassCastException 105: * If any specified class does not implement 106: * <code>DOMImplementationSource</code> 107: */ 108: public static DOMImplementationRegistry newInstance() 109: throws 110: ClassNotFoundException, 111: InstantiationException, 112: IllegalAccessException, 113: ClassCastException { 114: Vector sources = new Vector(); 115: 116: ClassLoader classLoader = getClassLoader(); 117: // fetch system property: 118: String p = getSystemProperty(PROPERTY); 119: 120: // 121: // if property is not specified then use contents of 122: // META_INF/org.w3c.dom.DOMImplementationSourceList from classpath 123: if (p == null) { 124: p = getServiceValue(classLoader); 125: } 126: if (p == null) { 127: // 128: // DOM Implementations can modify here to add *additional* fallback 129: // mechanisms to access a list of default DOMImplementationSources. 130: p = "gnu.xml.dom.ImplementationSource"; 131: } 132: if (p != null) { 133: StringTokenizer st = new StringTokenizer(p); 134: while (st.hasMoreTokens()) { 135: String sourceName = st.nextToken(); 136: // Use context class loader, falling back to Class.forName 137: // if and only if this fails... 138: Class sourceClass = null; 139: if (classLoader != null) { 140: sourceClass = classLoader.loadClass(sourceName); 141: } else { 142: sourceClass = Class.forName(sourceName); 143: } 144: DOMImplementationSource source = 145: (DOMImplementationSource) sourceClass.newInstance(); 146: sources.addElement(source); 147: } 148: } 149: return new DOMImplementationRegistry(sources); 150: } 151: 152: /** 153: * Return the first implementation that has the desired 154: * features, or <code>null</code> if none is found. 155: * 156: * @param features 157: * A string that specifies which features are required. This is 158: * a space separated list in which each feature is specified by 159: * its name optionally followed by a space and a version number. 160: * This is something like: "XML 1.0 Traversal +Events 2.0" 161: * @return An implementation that has the desired features, 162: * or <code>null</code> if none found. 163: */ 164: public DOMImplementation getDOMImplementation(final String features) { 165: int size = sources.size(); 166: String name = null; 167: for (int i = 0; i < size; i++) { 168: DOMImplementationSource source = 169: (DOMImplementationSource) sources.elementAt(i); 170: DOMImplementation impl = source.getDOMImplementation(features); 171: if (impl != null) { 172: return impl; 173: } 174: } 175: return null; 176: } 177: 178: /** 179: * Return a list of implementations that support the 180: * desired features. 181: * 182: * @param features 183: * A string that specifies which features are required. This is 184: * a space separated list in which each feature is specified by 185: * its name optionally followed by a space and a version number. 186: * This is something like: "XML 1.0 Traversal +Events 2.0" 187: * @return A list of DOMImplementations that support the desired features. 188: */ 189: public DOMImplementationList getDOMImplementationList(final String features) { 190: final Vector implementations = new Vector(); 191: int size = sources.size(); 192: for (int i = 0; i < size; i++) { 193: DOMImplementationSource source = 194: (DOMImplementationSource) sources.elementAt(i); 195: DOMImplementationList impls = 196: source.getDOMImplementationList(features); 197: for (int j = 0; j < impls.getLength(); j++) { 198: DOMImplementation impl = impls.item(j); 199: implementations.addElement(impl); 200: } 201: } 202: return new DOMImplementationList() { 203: public DOMImplementation item(final int index) { 204: if (index >= 0 && index < implementations.size()) { 205: try { 206: return (DOMImplementation) 207: implementations.elementAt(index); 208: } catch (ArrayIndexOutOfBoundsException e) { 209: return null; 210: } 211: } 212: return null; 213: } 214: 215: public int getLength() { 216: return implementations.size(); 217: } 218: }; 219: } 220: 221: /** 222: * Register an implementation. 223: * 224: * @param s The source to be registered, may not be <code>null</code> 225: */ 226: public void addSource(final DOMImplementationSource s) { 227: if (s == null) { 228: throw new NullPointerException(); 229: } 230: if (!sources.contains(s)) { 231: sources.addElement(s); 232: } 233: } 234: 235: /** 236: * 237: * Gets a class loader. 238: * 239: * @return A class loader, possibly <code>null</code> 240: */ 241: private static ClassLoader getClassLoader() { 242: try { 243: ClassLoader contextClassLoader = getContextClassLoader(); 244: 245: if (contextClassLoader != null) { 246: return contextClassLoader; 247: } 248: } catch (Exception e) { 249: // Assume that the DOM application is in a JRE 1.1, use the 250: // current ClassLoader 251: return DOMImplementationRegistry.class.getClassLoader(); 252: } 253: return DOMImplementationRegistry.class.getClassLoader(); 254: } 255: 256: /** 257: * This method attempts to return the first line of the resource 258: * META_INF/services/org.w3c.dom.DOMImplementationSourceList 259: * from the provided ClassLoader. 260: * 261: * @param classLoader classLoader, may not be <code>null</code>. 262: * @return first line of resource, or <code>null</code> 263: */ 264: private static String getServiceValue(final ClassLoader classLoader) { 265: String serviceId = "META-INF/services/" + PROPERTY; 266: // try to find services in CLASSPATH 267: try { 268: InputStream is = getResourceAsStream(classLoader, serviceId); 269: 270: if (is != null) { 271: BufferedReader rd; 272: try { 273: rd = 274: new BufferedReader(new InputStreamReader(is, "UTF-8"), 275: DEFAULT_LINE_LENGTH); 276: } catch (java.io.UnsupportedEncodingException e) { 277: rd = 278: new BufferedReader(new InputStreamReader(is), 279: DEFAULT_LINE_LENGTH); 280: } 281: String serviceValue = rd.readLine(); 282: rd.close(); 283: if (serviceValue != null && serviceValue.length() > 0) { 284: return serviceValue; 285: } 286: } 287: } catch (Exception ex) { 288: return null; 289: } 290: return null; 291: } 292: 293: /** 294: * A simple JRE (Java Runtime Environment) 1.1 test 295: * 296: * @return <code>true</code> if JRE 1.1 297: */ 298: private static boolean isJRE11() { 299: try { 300: Class c = Class.forName("java.security.AccessController"); 301: // java.security.AccessController existed since 1.2 so, if no 302: // exception was thrown, the DOM application is running in a JRE 303: // 1.2 or higher 304: return false; 305: } catch (Exception ex) { 306: // ignore 307: } 308: return true; 309: } 310: 311: /** 312: * This method returns the ContextClassLoader or <code>null</code> if 313: * running in a JRE 1.1 314: * 315: * @return The Context Classloader 316: */ 317: private static ClassLoader getContextClassLoader() { 318: return isJRE11() 319: ? null 320: : (ClassLoader) 321: AccessController.doPrivileged(new PrivilegedAction() { 322: public Object run() { 323: ClassLoader classLoader = null; 324: try { 325: classLoader = 326: Thread.currentThread().getContextClassLoader(); 327: } catch (SecurityException ex) { 328: } 329: return classLoader; 330: } 331: }); 332: } 333: 334: /** 335: * This method returns the system property indicated by the specified name 336: * after checking access control privileges. For a JRE 1.1, this check is 337: * not done. 338: * 339: * @param name the name of the system property 340: * @return the system property 341: */ 342: private static String getSystemProperty(final String name) { 343: return isJRE11() 344: ? (String) System.getProperty(name) 345: : (String) AccessController.doPrivileged(new PrivilegedAction() { 346: public Object run() { 347: return System.getProperty(name); 348: } 349: }); 350: } 351: 352: /** 353: * This method returns an Inputstream for the reading resource 354: * META_INF/services/org.w3c.dom.DOMImplementationSourceList after checking 355: * access control privileges. For a JRE 1.1, this check is not done. 356: * 357: * @param classLoader classLoader 358: * @param name the resource 359: * @return an Inputstream for the resource specified 360: */ 361: private static InputStream getResourceAsStream(final ClassLoader classLoader, 362: final String name) { 363: if (isJRE11()) { 364: InputStream ris; 365: if (classLoader == null) { 366: ris = ClassLoader.getSystemResourceAsStream(name); 367: } else { 368: ris = classLoader.getResourceAsStream(name); 369: } 370: return ris; 371: } else { 372: return (InputStream) 373: AccessController.doPrivileged(new PrivilegedAction() { 374: public Object run() { 375: InputStream ris; 376: if (classLoader == null) { 377: ris = 378: ClassLoader.getSystemResourceAsStream(name); 379: } else { 380: ris = classLoader.getResourceAsStream(name); 381: } 382: return ris; 383: } 384: }); 385: } 386: } 387: }