001/* 002 * Copyright (c) 2004 World Wide Web Consortium, 003 * 004 * (Massachusetts Institute of Technology, European Research Consortium for 005 * Informatics and Mathematics, Keio University). All Rights Reserved. This 006 * work is distributed under the W3C(r) Software License [1] in the hope that 007 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied 008 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 009 * 010 * [1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 011 */ 012 013 014package org.w3c.dom.bootstrap; 015 016import java.util.StringTokenizer; 017import java.util.Vector; 018import org.w3c.dom.DOMImplementationSource; 019import org.w3c.dom.DOMImplementationList; 020import org.w3c.dom.DOMImplementation; 021import java.io.InputStream; 022import java.io.BufferedReader; 023import java.io.InputStreamReader; 024import java.security.AccessController; 025import java.security.PrivilegedAction; 026 027/** 028 * A factory that enables applications to obtain instances of 029 * <code>DOMImplementation</code>. 030 * 031 * <p> 032 * Example: 033 * </p> 034 * 035 * <pre class='example'> 036 * // get an instance of the DOMImplementation registry 037 * DOMImplementationRegistry registry = 038 * DOMImplementationRegistry.newInstance(); 039 * // get a DOM implementation the Level 3 XML module 040 * DOMImplementation domImpl = 041 * registry.getDOMImplementation("XML 3.0"); 042 * </pre> 043 * 044 * <p> 045 * This provides an application with an implementation-independent starting 046 * point. DOM implementations may modify this class to meet new security 047 * standards or to provide *additional* fallbacks for the list of 048 * DOMImplementationSources. 049 * </p> 050 * 051 * @see DOMImplementation 052 * @see DOMImplementationSource 053 * @since DOM Level 3 054 */ 055public final class DOMImplementationRegistry { 056 /** 057 * The system property to specify the 058 * DOMImplementationSource class names. 059 */ 060 public static final String PROPERTY = 061 "org.w3c.dom.DOMImplementationSourceList"; 062 063 /** 064 * Default columns per line. 065 */ 066 private static final int DEFAULT_LINE_LENGTH = 80; 067 068 /** 069 * The list of DOMImplementationSources. 070 */ 071 private Vector sources; 072 073 /** 074 * Private constructor. 075 * @param srcs Vector List of DOMImplementationSources 076 */ 077 private DOMImplementationRegistry(final Vector srcs) { 078 sources = srcs; 079 } 080 081 /** 082 * Obtain a new instance of a <code>DOMImplementationRegistry</code>. 083 * 084 085 * The <code>DOMImplementationRegistry</code> is initialized by the 086 * application or the implementation, depending on the context, by 087 * first checking the value of the Java system property 088 * <code>org.w3c.dom.DOMImplementationSourceList</code> and 089 * the the service provider whose contents are at 090 * "<code>META_INF/services/org.w3c.dom.DOMImplementationSourceList</code>" 091 * The value of this property is a white-space separated list of 092 * names of availables classes implementing the 093 * <code>DOMImplementationSource</code> interface. Each class listed 094 * in the class name list is instantiated and any exceptions 095 * encountered are thrown to the application. 096 * 097 * @return an initialized instance of DOMImplementationRegistry 098 * @throws ClassNotFoundException 099 * 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}