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