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