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 = sources.get(i); 217 DOMImplementation impl = source.getDOMImplementation(features); 218 if (impl != null) { 219 return impl; 220 } 221 } 222 return null; 223 } 224 225 /** 226 * Return a list of implementations that support the 227 * desired features. 228 * 229 * @param features 230 * A string that specifies which features are required. This is 231 * a space separated list in which each feature is specified by 232 * its name optionally followed by a space and a version number. 233 * This is something like: "XML 1.0 Traversal +Events 2.0" 234 * @return A list of DOMImplementations that support the desired features. 235 */ 236 public DOMImplementationList getDOMImplementationList(final String features) { 237 final List<DOMImplementation> implementations = new ArrayList<>(); 238 int size = sources.size(); 239 for (int i = 0; i < size; i++) { 240 DOMImplementationSource source = sources.get(i); 241 DOMImplementationList impls = 242 source.getDOMImplementationList(features); 243 for (int j = 0; j < impls.getLength(); j++) { 244 DOMImplementation impl = impls.item(j); 245 implementations.add(impl); 246 } 247 } 248 return new DOMImplementationList() { 249 public DOMImplementation item(final int index) { 250 if (index >= 0 && index < implementations.size()) { 251 try { 252 return implementations.get(index); 253 } catch (IndexOutOfBoundsException e) { 254 return null; 255 } 256 } 257 return null; 258 } 259 260 public int getLength() { 261 return implementations.size(); 262 } 263 }; 264 } 265 266 /** 267 * Register an implementation. 268 * 269 * @param s The source to be registered, may not be <code>null</code> 270 */ 271 public void addSource(final DOMImplementationSource s) { 272 if (s == null) { 273 throw new NullPointerException(); 274 } 275 if (!sources.contains(s)) { 276 sources.add(s); 277 } 278 } 279 280 /** 281 * 282 * Gets a class loader. 283 * 284 * @return A class loader, possibly <code>null</code> 285 */ 286 private static ClassLoader getClassLoader() { 287 try { 288 ClassLoader contextClassLoader = getContextClassLoader(); 289 290 if (contextClassLoader != null) { 291 return contextClassLoader; 292 } 293 } catch (Exception e) { 294 // Assume that the DOM application is in a JRE 1.1, use the 295 // current ClassLoader 296 return DOMImplementationRegistry.class.getClassLoader(); 297 } 298 return DOMImplementationRegistry.class.getClassLoader(); 299 } 300 301 /** 302 * This method attempts to return the first line of the resource 303 * META_INF/services/org.w3c.dom.DOMImplementationSourceList 304 * from the provided ClassLoader. 305 * 306 * @param classLoader classLoader, may not be <code>null</code>. 307 * @return first line of resource, or <code>null</code> 308 */ 309 private static String getServiceValue(final ClassLoader classLoader) { 310 String serviceId = "META-INF/services/" + PROPERTY; 311 // try to find services in CLASSPATH 312 try { 313 InputStream is = getResourceAsStream(classLoader, serviceId); 314 315 if (is != null) { 316 BufferedReader rd; 317 try { 318 rd = 319 new BufferedReader(new InputStreamReader(is, "UTF-8"), 320 DEFAULT_LINE_LENGTH); 321 } catch (java.io.UnsupportedEncodingException e) { 322 rd = 323 new BufferedReader(new InputStreamReader(is), 324 DEFAULT_LINE_LENGTH); 325 } 326 String serviceValue = rd.readLine(); 327 rd.close(); 328 if (serviceValue != null && serviceValue.length() > 0) { 329 return serviceValue; 330 } 331 } 332 } catch (Exception ex) { 333 return null; 334 } 335 return null; 336 } 337 338 /** 339 * This method returns the ContextClassLoader. 340 * 341 * @return The Context Classloader 342 */ 343 private static ClassLoader getContextClassLoader() { 344 return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { 345 @Override 346 public ClassLoader run() { 347 ClassLoader classLoader = null; 348 try { 349 classLoader = 350 Thread.currentThread().getContextClassLoader(); 351 } catch (SecurityException ex) { 352 } 353 return classLoader; 354 } 355 }); 356 } 357 358 /** 359 * This method returns the system property indicated by the specified name 360 * after checking access control privileges. 361 * 362 * @param name the name of the system property 363 * @return the system property 364 */ 365 private static String getSystemProperty(final String name) { 366 return AccessController.doPrivileged(new PrivilegedAction<String>() { 367 @Override 368 public String run() { 369 return System.getProperty(name); 370 } 371 }); 372 } 373 374 /** 375 * This method returns an Inputstream for the reading resource 376 * META_INF/services/org.w3c.dom.DOMImplementationSourceList after checking 377 * access control privileges. 378 * 379 * @param classLoader classLoader 380 * @param name the resource 381 * @return an Inputstream for the resource specified 382 */ 383 private static InputStream getResourceAsStream(final ClassLoader classLoader, 384 final String name) { 385 return AccessController.doPrivileged(new PrivilegedAction<InputStream>() { 386 @Override 387 public InputStream run() { 388 InputStream ris; 389 if (classLoader == null) { 390 ris = 391 ClassLoader.getSystemResourceAsStream(name); 392 } else { 393 ris = classLoader.getResourceAsStream(name); 394 } 395 return ris; 396 } 397 }); 398 } 399 }