1 /* 2 * Copyright (c) 1997, 2013, 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.util.ArrayList; 67 import java.util.Enumeration; 68 import java.util.Iterator; 69 import java.util.List; 70 import java.util.StringTokenizer; 71 import java.util.logging.Level; 72 import java.util.logging.Logger; 73 74 /** 75 * @author WS Development Team 76 */ 77 public class XmlUtil { 78 79 // not in older JDK, so must be duplicated here, otherwise javax.xml.XMLConstants should be used 80 private static final String ACCESS_EXTERNAL_SCHEMA = "http://javax.xml.XMLConstants/property/accessExternalSchema"; 81 82 private final static String LEXICAL_HANDLER_PROPERTY = 83 "http://xml.org/sax/properties/lexical-handler"; 84 85 private static final Logger LOGGER = Logger.getLogger(XmlUtil.class.getName()); 86 87 private static boolean globalSecureXmlProcessingEnabled; 88 89 static { 90 String disableSecureXmlProcessing = System.getProperty("disableSecureXmlProcessing"); 91 globalSecureXmlProcessingEnabled = disableSecureXmlProcessing == null || !Boolean.valueOf(disableSecureXmlProcessing); 92 } 93 94 public static String getPrefix(String s) { 95 int i = s.indexOf(':'); 96 if (i == -1) 97 return null; 98 return s.substring(0, i); 99 } 100 101 public static String getLocalPart(String s) { 102 int i = s.indexOf(':'); 103 if (i == -1) 104 return s; 105 return s.substring(i + 1); 106 } 107 108 109 110 public static String getAttributeOrNull(Element e, String name) { 111 Attr a = e.getAttributeNode(name); 112 if (a == null) 113 return null; 114 return a.getValue(); 115 } 116 117 public static String getAttributeNSOrNull( 118 Element e, 119 String name, 120 String nsURI) { 121 Attr a = e.getAttributeNodeNS(nsURI, name); 122 if (a == null) 123 return null; 124 return a.getValue(); 125 } 126 127 public static String getAttributeNSOrNull( 128 Element e, 129 QName name) { 130 Attr a = e.getAttributeNodeNS(name.getNamespaceURI(), name.getLocalPart()); 131 if (a == null) 132 return null; 133 return a.getValue(); 134 } 135 136 /* public static boolean matchesTagNS(Element e, String tag, String nsURI) { 137 try { 138 return e.getLocalName().equals(tag) 139 && e.getNamespaceURI().equals(nsURI); 140 } catch (NullPointerException npe) { 141 142 // localname not null since parsing would fail before here 143 throw new WSDLParseException( 144 "null.namespace.found", 145 e.getLocalName()); 146 } 147 } 148 149 public static boolean matchesTagNS( 150 Element e, 151 javax.xml.namespace.QName name) { 152 try { 153 return e.getLocalName().equals(name.getLocalPart()) 154 && e.getNamespaceURI().equals(name.getNamespaceURI()); 155 } catch (NullPointerException npe) { 156 157 // localname not null since parsing would fail before here 158 throw new WSDLParseException( 159 "null.namespace.found", 160 e.getLocalName()); 161 } 162 }*/ 163 164 public static Iterator getAllChildren(Element element) { 165 return new NodeListIterator(element.getChildNodes()); 166 } 167 168 public static Iterator getAllAttributes(Element element) { 169 return new NamedNodeMapIterator(element.getAttributes()); 170 } 171 172 public static List<String> parseTokenList(String tokenList) { 173 List<String> result = new ArrayList<String>(); 174 StringTokenizer tokenizer = new StringTokenizer(tokenList, " "); 175 while (tokenizer.hasMoreTokens()) { 176 result.add(tokenizer.nextToken()); 177 } 178 return result; 179 } 180 181 public static String getTextForNode(Node node) { 182 StringBuilder sb = new StringBuilder(); 183 184 NodeList children = node.getChildNodes(); 185 if (children.getLength() == 0) 186 return null; 187 188 for (int i = 0; i < children.getLength(); ++i) { 189 Node n = children.item(i); 190 191 if (n instanceof Text) 192 sb.append(n.getNodeValue()); 193 else if (n instanceof EntityReference) { 194 String s = getTextForNode(n); 195 if (s == null) 196 return null; 197 else 198 sb.append(s); 199 } else 200 return null; 201 } 202 203 return sb.toString(); 204 } 205 206 public static InputStream getUTF8Stream(String s) { 207 try { 208 ByteArrayBuffer bab = new ByteArrayBuffer(); 209 Writer w = new OutputStreamWriter(bab, "utf-8"); 210 w.write(s); 211 w.close(); 212 return bab.newInputStream(); 213 } catch (IOException e) { 214 throw new RuntimeException("should not happen"); 215 } 216 } 217 218 static final TransformerFactory transformerFactory = newTransformerFactory(); 219 220 static final SAXParserFactory saxParserFactory = newSAXParserFactory(true); 221 222 static { 223 saxParserFactory.setNamespaceAware(true); 224 } 225 226 /** 227 * Creates a new identity transformer. 228 */ 229 public static Transformer newTransformer() { 230 try { 231 return transformerFactory.newTransformer(); 232 } catch (TransformerConfigurationException tex) { 233 throw new IllegalStateException("Unable to create a JAXP transformer"); 234 } 235 } 236 237 /** 238 * Performs identity transformation. 239 */ 240 public static <T extends Result> 241 T identityTransform(Source src, T result) throws TransformerException, SAXException, ParserConfigurationException, IOException { 242 if (src instanceof StreamSource) { 243 // work around a bug in JAXP in JDK6u4 and earlier where the namespace processing 244 // is not turned on by default 245 StreamSource ssrc = (StreamSource) src; 246 TransformerHandler th = ((SAXTransformerFactory) transformerFactory).newTransformerHandler(); 247 th.setResult(result); 248 XMLReader reader = saxParserFactory.newSAXParser().getXMLReader(); 249 reader.setContentHandler(th); 250 reader.setProperty(LEXICAL_HANDLER_PROPERTY, th); 251 reader.parse(toInputSource(ssrc)); 252 } else { 253 newTransformer().transform(src, result); 254 } 255 return result; 256 } 257 258 private static InputSource toInputSource(StreamSource src) { 259 InputSource is = new InputSource(); 260 is.setByteStream(src.getInputStream()); 261 is.setCharacterStream(src.getReader()); 262 is.setPublicId(src.getPublicId()); 263 is.setSystemId(src.getSystemId()); 264 return is; 265 } 266 267 /* 268 * Gets an EntityResolver using XML catalog 269 */ 270 public static EntityResolver createEntityResolver(@Nullable URL catalogUrl) { 271 // set up a manager 272 CatalogManager manager = new CatalogManager(); 273 manager.setIgnoreMissingProperties(true); 274 // Using static catalog may result in to sharing of the catalog by multiple apps running in a container 275 manager.setUseStaticCatalog(false); 276 Catalog catalog = manager.getCatalog(); 277 try { 278 if (catalogUrl != null) { 279 catalog.parseCatalog(catalogUrl); 280 } 281 } catch (IOException e) { 282 throw new ServerRtException("server.rt.err",e); 283 } 284 return workaroundCatalogResolver(catalog); 285 } 286 287 /** 288 * Gets a default EntityResolver for catalog at META-INF/jaxws-catalog.xml 289 */ 290 public static EntityResolver createDefaultCatalogResolver() { 291 292 // set up a manager 293 CatalogManager manager = new CatalogManager(); 294 manager.setIgnoreMissingProperties(true); 295 // Using static catalog may result in to sharing of the catalog by multiple apps running in a container 296 manager.setUseStaticCatalog(false); 297 // parse the catalog 298 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 299 Enumeration<URL> catalogEnum; 300 Catalog catalog = manager.getCatalog(); 301 try { 302 if (cl == null) { 303 catalogEnum = ClassLoader.getSystemResources("META-INF/jax-ws-catalog.xml"); 304 } else { 305 catalogEnum = cl.getResources("META-INF/jax-ws-catalog.xml"); 306 } 307 308 while(catalogEnum.hasMoreElements()) { 309 URL url = catalogEnum.nextElement(); 310 catalog.parseCatalog(url); 311 } 312 } catch (IOException e) { 313 throw new WebServiceException(e); 314 } 315 316 return workaroundCatalogResolver(catalog); 317 } 318 319 /** 320 * Default CatalogResolver implementation is broken as it depends on CatalogManager.getCatalog() which will always create a new one when 321 * useStaticCatalog is false. 322 * This returns a CatalogResolver that uses the catalog passed as parameter. 323 * @param catalog 324 * @return CatalogResolver 325 */ 326 private static CatalogResolver workaroundCatalogResolver(final Catalog catalog) { 327 // set up a manager 328 CatalogManager manager = new CatalogManager() { 329 @Override 330 public Catalog getCatalog() { 331 return catalog; 332 } 333 }; 334 manager.setIgnoreMissingProperties(true); 335 // Using static catalog may result in to sharing of the catalog by multiple apps running in a container 336 manager.setUseStaticCatalog(false); 337 338 return new CatalogResolver(manager); 339 } 340 341 /** 342 * {@link ErrorHandler} that always treat the error as fatal. 343 */ 344 public static final ErrorHandler DRACONIAN_ERROR_HANDLER = new ErrorHandler() { 345 @Override 346 public void warning(SAXParseException exception) { 347 } 348 349 @Override 350 public void error(SAXParseException exception) throws SAXException { 351 throw exception; 352 } 353 354 @Override 355 public void fatalError(SAXParseException exception) throws SAXException { 356 throw exception; 357 } 358 }; 359 360 public static DocumentBuilderFactory newDocumentBuilderFactory() { 361 return newDocumentBuilderFactory(true); 362 } 363 364 public static DocumentBuilderFactory newDocumentBuilderFactory(boolean secureXmlProcessing) { 365 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 366 try { 367 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, checkGlobalOverride(secureXmlProcessing)); 368 } catch (ParserConfigurationException e) { 369 LOGGER.log(Level.WARNING, "Factory [{}] doesn't support secure xml processing!", new Object[] { factory.getClass().getName() } ); 370 } 371 return factory; 372 } 373 374 public static TransformerFactory newTransformerFactory(boolean secureXmlProcessingEnabled) { 375 TransformerFactory factory = TransformerFactory.newInstance(); 376 try { 377 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, checkGlobalOverride(secureXmlProcessingEnabled)); 378 } catch (TransformerConfigurationException e) { 379 LOGGER.log(Level.WARNING, "Factory [{}] doesn't support secure xml processing!", new Object[]{factory.getClass().getName()}); 380 } 381 return factory; 382 } 383 384 public static TransformerFactory newTransformerFactory() { 385 return newTransformerFactory(true); 386 } 387 388 public static SAXParserFactory newSAXParserFactory(boolean secureXmlProcessingEnabled) { 389 SAXParserFactory factory = SAXParserFactory.newInstance(); 390 try { 391 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, checkGlobalOverride(secureXmlProcessingEnabled)); 392 } catch (Exception e) { 393 LOGGER.log(Level.WARNING, "Factory [{}] doesn't support secure xml processing!", new Object[]{factory.getClass().getName()}); 394 } 395 return factory; 396 } 397 398 public static XPathFactory newXPathFactory(boolean secureXmlProcessingEnabled) { 399 XPathFactory factory = XPathFactory.newInstance(); 400 try { 401 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, checkGlobalOverride(secureXmlProcessingEnabled)); 402 } catch (XPathFactoryConfigurationException e) { 403 LOGGER.log(Level.WARNING, "Factory [{}] doesn't support secure xml processing!", new Object[] { factory.getClass().getName() } ); 404 } 405 return factory; 406 } 407 408 public static XMLInputFactory newXMLInputFactory(boolean secureXmlProcessingEnabled) { 409 XMLInputFactory factory = XMLInputFactory.newInstance(); 410 if (checkGlobalOverride(secureXmlProcessingEnabled)) { 411 // TODO-Miran: are those apppropriate defaults? 412 factory.setProperty(XMLInputFactory.SUPPORT_DTD, false); 413 factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); 414 } 415 return factory; 416 } 417 418 private static boolean checkGlobalOverride(boolean localSecureXmlProcessingEnabled) { 419 return globalSecureXmlProcessingEnabled && localSecureXmlProcessingEnabled; 420 } 421 422 public static SchemaFactory allowFileAccess(SchemaFactory sf, boolean disableSecureProcessing) { 423 424 // if feature secure processing enabled, nothing to do, file is allowed, 425 // or user is able to control access by standard JAXP mechanisms 426 if (checkGlobalOverride(disableSecureProcessing)) { 427 return sf; 428 } 429 430 try { 431 sf.setProperty(ACCESS_EXTERNAL_SCHEMA, "file"); 432 LOGGER.log(Level.FINE, "Property \"{}\" is supported and has been successfully set by used JAXP implementation.", new Object[]{ACCESS_EXTERNAL_SCHEMA}); 433 } catch (SAXException ignored) { 434 // depending on JDK/SAX implementation used 435 LOGGER.log(Level.CONFIG, "Property \"{}\" is not supported by used JAXP implementation.", new Object[]{ACCESS_EXTERNAL_SCHEMA}); 436 } 437 return sf; 438 } 439 }