1 /* 2 * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.xml.internal.ws.util.xml; 27 28 import com.sun.istack.internal.Nullable; 29 import com.sun.org.apache.xml.internal.resolver.Catalog; 30 import com.sun.org.apache.xml.internal.resolver.CatalogManager; 31 import com.sun.org.apache.xml.internal.resolver.tools.CatalogResolver; 32 import com.sun.xml.internal.ws.server.ServerRtException; 33 import com.sun.xml.internal.ws.util.ByteArrayBuffer; 34 import org.w3c.dom.Attr; 35 import org.w3c.dom.Element; 36 import org.w3c.dom.EntityReference; 37 import org.w3c.dom.Node; 38 import org.w3c.dom.NodeList; 39 import org.w3c.dom.Text; 40 import org.xml.sax.*; 41 42 import javax.xml.XMLConstants; 43 import javax.xml.namespace.QName; 44 import javax.xml.parsers.DocumentBuilderFactory; 45 import javax.xml.parsers.ParserConfigurationException; 46 import javax.xml.parsers.SAXParserFactory; 47 import javax.xml.stream.XMLInputFactory; 48 import javax.xml.transform.Result; 49 import javax.xml.transform.Source; 50 import javax.xml.transform.Transformer; 51 import javax.xml.transform.TransformerConfigurationException; 52 import javax.xml.transform.TransformerException; 53 import javax.xml.transform.TransformerFactory; 54 import javax.xml.transform.sax.SAXTransformerFactory; 55 import javax.xml.transform.sax.TransformerHandler; 56 import javax.xml.transform.stream.StreamSource; 57 import javax.xml.validation.SchemaFactory; 58 import javax.xml.ws.WebServiceException; 59 import javax.xml.xpath.XPathFactory; 60 import javax.xml.xpath.XPathFactoryConfigurationException; 61 import java.io.IOException; 62 import java.io.InputStream; 63 import java.io.OutputStreamWriter; 64 import java.io.Writer; 65 import java.net.URL; 66 import java.security.AccessController; 67 import java.security.PrivilegedAction; 68 import java.util.ArrayList; 69 import java.util.Enumeration; 70 import java.util.Iterator; 71 import java.util.List; 72 import java.util.StringTokenizer; 73 import java.util.logging.Level; 74 import java.util.logging.Logger; 75 76 /** 77 * @author WS Development Team 78 */ 79 public class XmlUtil { 80 81 // not in older JDK, so must be duplicated here, otherwise javax.xml.XMLConstants should be used 82 private static final String ACCESS_EXTERNAL_SCHEMA = "http://javax.xml.XMLConstants/property/accessExternalSchema"; 83 84 private final static String LEXICAL_HANDLER_PROPERTY = 85 "http://xml.org/sax/properties/lexical-handler"; 86 87 private static final Logger LOGGER = Logger.getLogger(XmlUtil.class.getName()); 88 89 private static final String DISABLE_XML_SECURITY = "com.sun.xml.internal.ws.disableXmlSecurity"; 90 91 private static boolean XML_SECURITY_DISABLED = AccessController.doPrivileged( 92 new PrivilegedAction<Boolean>() { 93 @Override 94 public Boolean run() { 95 return Boolean.getBoolean(DISABLE_XML_SECURITY); 96 } 97 } 98 ); 99 100 public static String getPrefix(String s) { 101 int i = s.indexOf(':'); 102 if (i == -1) 103 return null; 104 return s.substring(0, i); 105 } 106 107 public static String getLocalPart(String s) { 108 int i = s.indexOf(':'); 109 if (i == -1) 110 return s; 111 return s.substring(i + 1); 112 } 113 114 115 116 public static String getAttributeOrNull(Element e, String name) { 117 Attr a = e.getAttributeNode(name); 118 if (a == null) 119 return null; 120 return a.getValue(); 121 } 122 123 public static String getAttributeNSOrNull( 124 Element e, 125 String name, 126 String nsURI) { 127 Attr a = e.getAttributeNodeNS(nsURI, name); 128 if (a == null) 129 return null; 130 return a.getValue(); 131 } 132 133 public static String getAttributeNSOrNull( 134 Element e, 135 QName name) { 136 Attr a = e.getAttributeNodeNS(name.getNamespaceURI(), name.getLocalPart()); 137 if (a == null) 138 return null; 139 return a.getValue(); 140 } 141 142 /* public static boolean matchesTagNS(Element e, String tag, String nsURI) { 143 try { 144 return e.getLocalName().equals(tag) 145 && e.getNamespaceURI().equals(nsURI); 146 } catch (NullPointerException npe) { 147 148 // localname not null since parsing would fail before here 149 throw new WSDLParseException( 150 "null.namespace.found", 151 e.getLocalName()); 152 } 153 } 154 155 public static boolean matchesTagNS( 156 Element e, 157 javax.xml.namespace.QName name) { 158 try { 159 return e.getLocalName().equals(name.getLocalPart()) 160 && e.getNamespaceURI().equals(name.getNamespaceURI()); 161 } catch (NullPointerException npe) { 162 163 // localname not null since parsing would fail before here 164 throw new WSDLParseException( 165 "null.namespace.found", 166 e.getLocalName()); 167 } 168 }*/ 169 170 public static Iterator getAllChildren(Element element) { 171 return new NodeListIterator(element.getChildNodes()); 172 } 173 174 public static Iterator getAllAttributes(Element element) { 175 return new NamedNodeMapIterator(element.getAttributes()); 176 } 177 178 public static List<String> parseTokenList(String tokenList) { 179 List<String> result = new ArrayList<String>(); 180 StringTokenizer tokenizer = new StringTokenizer(tokenList, " "); 181 while (tokenizer.hasMoreTokens()) { 182 result.add(tokenizer.nextToken()); 183 } 184 return result; 185 } 186 187 public static String getTextForNode(Node node) { 188 StringBuilder sb = new StringBuilder(); 189 190 NodeList children = node.getChildNodes(); 191 if (children.getLength() == 0) 192 return null; 193 194 for (int i = 0; i < children.getLength(); ++i) { 195 Node n = children.item(i); 196 197 if (n instanceof Text) 198 sb.append(n.getNodeValue()); 199 else if (n instanceof EntityReference) { 200 String s = getTextForNode(n); 201 if (s == null) 202 return null; 203 else 204 sb.append(s); 205 } else 206 return null; 207 } 208 209 return sb.toString(); 210 } 211 212 public static InputStream getUTF8Stream(String s) { 213 try { 214 ByteArrayBuffer bab = new ByteArrayBuffer(); 215 Writer w = new OutputStreamWriter(bab, "utf-8"); 216 w.write(s); 217 w.close(); 218 return bab.newInputStream(); 219 } catch (IOException e) { 220 throw new RuntimeException("should not happen"); 221 } 222 } 223 224 static final ContextClassloaderLocal<TransformerFactory> transformerFactory = new ContextClassloaderLocal<TransformerFactory>() { 225 @Override 226 protected TransformerFactory initialValue() throws Exception { 227 return TransformerFactory.newInstance(); 228 } 229 }; 230 231 static final ContextClassloaderLocal<SAXParserFactory> saxParserFactory = new ContextClassloaderLocal<SAXParserFactory>() { 232 @Override 233 protected SAXParserFactory initialValue() throws Exception { 234 SAXParserFactory factory = newSAXParserFactory(true); 235 factory.setNamespaceAware(true); 236 return factory; 237 } 238 }; 239 240 /** 241 * Creates a new identity transformer. 242 */ 243 public static Transformer newTransformer() { 244 try { 245 return transformerFactory.get().newTransformer(); 246 } catch (TransformerConfigurationException tex) { 247 throw new IllegalStateException("Unable to create a JAXP transformer"); 248 } 249 } 250 251 /** 252 * Performs identity transformation. 253 */ 254 public static <T extends Result> 255 T identityTransform(Source src, T result) throws TransformerException, SAXException, ParserConfigurationException, IOException { 256 if (src instanceof StreamSource) { 257 // work around a bug in JAXP in JDK6u4 and earlier where the namespace processing 258 // is not turned on by default 259 StreamSource ssrc = (StreamSource) src; 260 TransformerHandler th = ((SAXTransformerFactory) transformerFactory.get()).newTransformerHandler(); 261 th.setResult(result); 262 XMLReader reader = saxParserFactory.get().newSAXParser().getXMLReader(); 263 reader.setContentHandler(th); 264 reader.setProperty(LEXICAL_HANDLER_PROPERTY, th); 265 reader.parse(toInputSource(ssrc)); 266 } else { 267 newTransformer().transform(src, result); 268 } 269 return result; 270 } 271 272 private static InputSource toInputSource(StreamSource src) { 273 InputSource is = new InputSource(); 274 is.setByteStream(src.getInputStream()); 275 is.setCharacterStream(src.getReader()); 276 is.setPublicId(src.getPublicId()); 277 is.setSystemId(src.getSystemId()); 278 return is; 279 } 280 281 /* 282 * Gets an EntityResolver using XML catalog 283 */ 284 public static EntityResolver createEntityResolver(@Nullable URL catalogUrl) { 285 // set up a manager 286 CatalogManager manager = new CatalogManager(); 287 manager.setIgnoreMissingProperties(true); 288 // Using static catalog may result in to sharing of the catalog by multiple apps running in a container 289 manager.setUseStaticCatalog(false); 290 Catalog catalog = manager.getCatalog(); 291 try { 292 if (catalogUrl != null) { 293 catalog.parseCatalog(catalogUrl); 294 } 295 } catch (IOException e) { 296 throw new ServerRtException("server.rt.err",e); 297 } 298 return workaroundCatalogResolver(catalog); 299 } 300 301 /** 302 * Gets a default EntityResolver for catalog at META-INF/jaxws-catalog.xml 303 */ 304 public static EntityResolver createDefaultCatalogResolver() { 305 306 // set up a manager 307 CatalogManager manager = new CatalogManager(); 308 manager.setIgnoreMissingProperties(true); 309 // Using static catalog may result in to sharing of the catalog by multiple apps running in a container 310 manager.setUseStaticCatalog(false); 311 // parse the catalog 312 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 313 Enumeration<URL> catalogEnum; 314 Catalog catalog = manager.getCatalog(); 315 try { 316 if (cl == null) { 317 catalogEnum = ClassLoader.getSystemResources("META-INF/jax-ws-catalog.xml"); 318 } else { 319 catalogEnum = cl.getResources("META-INF/jax-ws-catalog.xml"); 320 } 321 322 while(catalogEnum.hasMoreElements()) { 323 URL url = catalogEnum.nextElement(); 324 catalog.parseCatalog(url); 325 } 326 } catch (IOException e) { 327 throw new WebServiceException(e); 328 } 329 330 return workaroundCatalogResolver(catalog); 331 } 332 333 /** 334 * Default CatalogResolver implementation is broken as it depends on CatalogManager.getCatalog() which will always create a new one when 335 * useStaticCatalog is false. 336 * This returns a CatalogResolver that uses the catalog passed as parameter. 337 * @param catalog 338 * @return CatalogResolver 339 */ 340 private static CatalogResolver workaroundCatalogResolver(final Catalog catalog) { 341 // set up a manager 342 CatalogManager manager = new CatalogManager() { 343 @Override 344 public Catalog getCatalog() { 345 return catalog; 346 } 347 }; 348 manager.setIgnoreMissingProperties(true); 349 // Using static catalog may result in to sharing of the catalog by multiple apps running in a container 350 manager.setUseStaticCatalog(false); 351 352 return new CatalogResolver(manager); 353 } 354 355 /** 356 * {@link ErrorHandler} that always treat the error as fatal. 357 */ 358 public static final ErrorHandler DRACONIAN_ERROR_HANDLER = new ErrorHandler() { 359 @Override 360 public void warning(SAXParseException exception) { 361 } 362 363 @Override 364 public void error(SAXParseException exception) throws SAXException { 365 throw exception; 366 } 367 368 @Override 369 public void fatalError(SAXParseException exception) throws SAXException { 370 throw exception; 371 } 372 }; 373 374 public static DocumentBuilderFactory newDocumentBuilderFactory(boolean disableSecurity) { 375 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 376 try { 377 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, !xmlSecurityDisabled(disableSecurity)); 378 } catch (ParserConfigurationException e) { 379 LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support secure xml processing!", new Object[] { factory.getClass().getName() } ); 380 } 381 return factory; 382 } 383 384 public static TransformerFactory newTransformerFactory(boolean disableSecurity) { 385 TransformerFactory factory = TransformerFactory.newInstance(); 386 try { 387 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, !xmlSecurityDisabled(disableSecurity)); 388 } catch (TransformerConfigurationException e) { 389 LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support secure xml processing!", new Object[]{factory.getClass().getName()}); 390 } 391 return factory; 392 } 393 394 public static SAXParserFactory newSAXParserFactory(boolean disableSecurity) { 395 SAXParserFactory factory = SAXParserFactory.newInstance(); 396 try { 397 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, !xmlSecurityDisabled(disableSecurity)); 398 } catch (Exception e) { 399 LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support secure xml processing!", new Object[]{factory.getClass().getName()}); 400 } 401 return factory; 402 } 403 404 public static XPathFactory newXPathFactory(boolean disableSecurity) { 405 XPathFactory factory = XPathFactory.newInstance(); 406 try { 407 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, !xmlSecurityDisabled(disableSecurity)); 408 } catch (XPathFactoryConfigurationException e) { 409 LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support secure xml processing!", new Object[] { factory.getClass().getName() } ); 410 } 411 return factory; 412 } 413 414 public static XMLInputFactory newXMLInputFactory(boolean disableSecurity) { 415 XMLInputFactory factory = XMLInputFactory.newInstance(); 416 if (xmlSecurityDisabled(disableSecurity)) { 417 // TODO-Miran: are those apppropriate defaults? 418 factory.setProperty(XMLInputFactory.SUPPORT_DTD, false); 419 factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); 420 } 421 return factory; 422 } 423 424 private static boolean xmlSecurityDisabled(boolean runtimeDisabled) { 425 return XML_SECURITY_DISABLED || runtimeDisabled; 426 } 427 428 public static SchemaFactory allowExternalAccess(SchemaFactory sf, String value, boolean disableSecurity) { 429 430 // if xml security (feature secure processing) disabled, nothing to do, no restrictions applied 431 if (xmlSecurityDisabled(disableSecurity)) { 432 if (LOGGER.isLoggable(Level.FINE)) { 433 LOGGER.log(Level.FINE, "Xml Security disabled, no JAXP xsd external access configuration necessary."); 434 } 435 return sf; 436 } 437 438 if (System.getProperty("javax.xml.accessExternalSchema") != null) { 439 if (LOGGER.isLoggable(Level.FINE)) { 440 LOGGER.log(Level.FINE, "Detected explicitly JAXP configuration, no JAXP xsd external access configuration necessary."); 441 } 442 return sf; 443 } 444 445 try { 446 sf.setProperty(ACCESS_EXTERNAL_SCHEMA, value); 447 if (LOGGER.isLoggable(Level.FINE)) { 448 LOGGER.log(Level.FINE, "Property \"{0}\" is supported and has been successfully set by used JAXP implementation.", new Object[]{ACCESS_EXTERNAL_SCHEMA}); 449 } 450 } catch (SAXException ignored) { 451 // nothing to do; support depends on version JDK or SAX implementation 452 if (LOGGER.isLoggable(Level.CONFIG)) { 453 LOGGER.log(Level.CONFIG, "Property \"{0}\" is not supported by used JAXP implementation.", new Object[]{ACCESS_EXTERNAL_SCHEMA}); 454 } 455 } 456 return sf; 457 } 458 459 }