Sun, 25 Jun 2017 00:13:53 +0100
8182054: Improve wsdl support
Summary: Also reviewed by Roman Grigoriadi <roman.grigoriadi@oracle.com>
Reviewed-by: joehw, lancea
1 /*
2 * Copyright (c) 1997, 2017, 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 */
26 package com.sun.xml.internal.ws.util.xml;
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.*;
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;
76 /**
77 * @author WS Development Team
78 */
79 public class XmlUtil {
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";
84 private final static String LEXICAL_HANDLER_PROPERTY =
85 "http://xml.org/sax/properties/lexical-handler";
87 private static final String DISALLOW_DOCTYPE_DECL = "http://apache.org/xml/features/disallow-doctype-decl";
89 private static final String EXTERNAL_GE = "http://xml.org/sax/features/external-general-entities";
91 private static final String EXTERNAL_PE = "http://xml.org/sax/features/external-parameter-entities";
93 private static final String LOAD_EXTERNAL_DTD = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
95 private static final Logger LOGGER = Logger.getLogger(XmlUtil.class.getName());
97 private static final String DISABLE_XML_SECURITY = "com.sun.xml.internal.ws.disableXmlSecurity";
99 private static boolean XML_SECURITY_DISABLED = AccessController.doPrivileged(
100 new PrivilegedAction<Boolean>() {
101 @Override
102 public Boolean run() {
103 return Boolean.getBoolean(DISABLE_XML_SECURITY);
104 }
105 }
106 );
108 public static String getPrefix(String s) {
109 int i = s.indexOf(':');
110 if (i == -1)
111 return null;
112 return s.substring(0, i);
113 }
115 public static String getLocalPart(String s) {
116 int i = s.indexOf(':');
117 if (i == -1)
118 return s;
119 return s.substring(i + 1);
120 }
124 public static String getAttributeOrNull(Element e, String name) {
125 Attr a = e.getAttributeNode(name);
126 if (a == null)
127 return null;
128 return a.getValue();
129 }
131 public static String getAttributeNSOrNull(
132 Element e,
133 String name,
134 String nsURI) {
135 Attr a = e.getAttributeNodeNS(nsURI, name);
136 if (a == null)
137 return null;
138 return a.getValue();
139 }
141 public static String getAttributeNSOrNull(
142 Element e,
143 QName name) {
144 Attr a = e.getAttributeNodeNS(name.getNamespaceURI(), name.getLocalPart());
145 if (a == null)
146 return null;
147 return a.getValue();
148 }
150 /* public static boolean matchesTagNS(Element e, String tag, String nsURI) {
151 try {
152 return e.getLocalName().equals(tag)
153 && e.getNamespaceURI().equals(nsURI);
154 } catch (NullPointerException npe) {
156 // localname not null since parsing would fail before here
157 throw new WSDLParseException(
158 "null.namespace.found",
159 e.getLocalName());
160 }
161 }
163 public static boolean matchesTagNS(
164 Element e,
165 javax.xml.namespace.QName name) {
166 try {
167 return e.getLocalName().equals(name.getLocalPart())
168 && e.getNamespaceURI().equals(name.getNamespaceURI());
169 } catch (NullPointerException npe) {
171 // localname not null since parsing would fail before here
172 throw new WSDLParseException(
173 "null.namespace.found",
174 e.getLocalName());
175 }
176 }*/
178 public static Iterator getAllChildren(Element element) {
179 return new NodeListIterator(element.getChildNodes());
180 }
182 public static Iterator getAllAttributes(Element element) {
183 return new NamedNodeMapIterator(element.getAttributes());
184 }
186 public static List<String> parseTokenList(String tokenList) {
187 List<String> result = new ArrayList<String>();
188 StringTokenizer tokenizer = new StringTokenizer(tokenList, " ");
189 while (tokenizer.hasMoreTokens()) {
190 result.add(tokenizer.nextToken());
191 }
192 return result;
193 }
195 public static String getTextForNode(Node node) {
196 StringBuilder sb = new StringBuilder();
198 NodeList children = node.getChildNodes();
199 if (children.getLength() == 0)
200 return null;
202 for (int i = 0; i < children.getLength(); ++i) {
203 Node n = children.item(i);
205 if (n instanceof Text)
206 sb.append(n.getNodeValue());
207 else if (n instanceof EntityReference) {
208 String s = getTextForNode(n);
209 if (s == null)
210 return null;
211 else
212 sb.append(s);
213 } else
214 return null;
215 }
217 return sb.toString();
218 }
220 public static InputStream getUTF8Stream(String s) {
221 try {
222 ByteArrayBuffer bab = new ByteArrayBuffer();
223 Writer w = new OutputStreamWriter(bab, "utf-8");
224 w.write(s);
225 w.close();
226 return bab.newInputStream();
227 } catch (IOException e) {
228 throw new RuntimeException("should not happen");
229 }
230 }
232 static final ContextClassloaderLocal<TransformerFactory> transformerFactory = new ContextClassloaderLocal<TransformerFactory>() {
233 @Override
234 protected TransformerFactory initialValue() throws Exception {
235 return TransformerFactory.newInstance();
236 }
237 };
239 static final ContextClassloaderLocal<SAXParserFactory> saxParserFactory = new ContextClassloaderLocal<SAXParserFactory>() {
240 @Override
241 protected SAXParserFactory initialValue() throws Exception {
242 SAXParserFactory factory = SAXParserFactory.newInstance();
243 factory.setNamespaceAware(true);
244 return factory;
245 }
246 };
248 /**
249 * Creates a new identity transformer.
250 */
251 public static Transformer newTransformer() {
252 try {
253 return transformerFactory.get().newTransformer();
254 } catch (TransformerConfigurationException tex) {
255 throw new IllegalStateException("Unable to create a JAXP transformer");
256 }
257 }
259 /**
260 * Performs identity transformation.
261 */
262 public static <T extends Result>
263 T identityTransform(Source src, T result) throws TransformerException, SAXException, ParserConfigurationException, IOException {
264 if (src instanceof StreamSource) {
265 // work around a bug in JAXP in JDK6u4 and earlier where the namespace processing
266 // is not turned on by default
267 StreamSource ssrc = (StreamSource) src;
268 TransformerHandler th = ((SAXTransformerFactory) transformerFactory.get()).newTransformerHandler();
269 th.setResult(result);
270 XMLReader reader = saxParserFactory.get().newSAXParser().getXMLReader();
271 reader.setContentHandler(th);
272 reader.setProperty(LEXICAL_HANDLER_PROPERTY, th);
273 reader.parse(toInputSource(ssrc));
274 } else {
275 newTransformer().transform(src, result);
276 }
277 return result;
278 }
280 private static InputSource toInputSource(StreamSource src) {
281 InputSource is = new InputSource();
282 is.setByteStream(src.getInputStream());
283 is.setCharacterStream(src.getReader());
284 is.setPublicId(src.getPublicId());
285 is.setSystemId(src.getSystemId());
286 return is;
287 }
289 /*
290 * Gets an EntityResolver using XML catalog
291 */
292 public static EntityResolver createEntityResolver(@Nullable URL catalogUrl) {
293 // set up a manager
294 CatalogManager manager = new CatalogManager();
295 manager.setIgnoreMissingProperties(true);
296 // Using static catalog may result in to sharing of the catalog by multiple apps running in a container
297 manager.setUseStaticCatalog(false);
298 Catalog catalog = manager.getCatalog();
299 try {
300 if (catalogUrl != null) {
301 catalog.parseCatalog(catalogUrl);
302 }
303 } catch (IOException e) {
304 throw new ServerRtException("server.rt.err",e);
305 }
306 return workaroundCatalogResolver(catalog);
307 }
309 /**
310 * Gets a default EntityResolver for catalog at META-INF/jaxws-catalog.xml
311 */
312 public static EntityResolver createDefaultCatalogResolver() {
314 // set up a manager
315 CatalogManager manager = new CatalogManager();
316 manager.setIgnoreMissingProperties(true);
317 // Using static catalog may result in to sharing of the catalog by multiple apps running in a container
318 manager.setUseStaticCatalog(false);
319 // parse the catalog
320 ClassLoader cl = Thread.currentThread().getContextClassLoader();
321 Enumeration<URL> catalogEnum;
322 Catalog catalog = manager.getCatalog();
323 try {
324 if (cl == null) {
325 catalogEnum = ClassLoader.getSystemResources("META-INF/jax-ws-catalog.xml");
326 } else {
327 catalogEnum = cl.getResources("META-INF/jax-ws-catalog.xml");
328 }
330 while(catalogEnum.hasMoreElements()) {
331 URL url = catalogEnum.nextElement();
332 catalog.parseCatalog(url);
333 }
334 } catch (IOException e) {
335 throw new WebServiceException(e);
336 }
338 return workaroundCatalogResolver(catalog);
339 }
341 /**
342 * Default CatalogResolver implementation is broken as it depends on CatalogManager.getCatalog() which will always create a new one when
343 * useStaticCatalog is false.
344 * This returns a CatalogResolver that uses the catalog passed as parameter.
345 * @param catalog
346 * @return CatalogResolver
347 */
348 private static CatalogResolver workaroundCatalogResolver(final Catalog catalog) {
349 // set up a manager
350 CatalogManager manager = new CatalogManager() {
351 @Override
352 public Catalog getCatalog() {
353 return catalog;
354 }
355 };
356 manager.setIgnoreMissingProperties(true);
357 // Using static catalog may result in to sharing of the catalog by multiple apps running in a container
358 manager.setUseStaticCatalog(false);
360 return new CatalogResolver(manager);
361 }
363 /**
364 * {@link ErrorHandler} that always treat the error as fatal.
365 */
366 public static final ErrorHandler DRACONIAN_ERROR_HANDLER = new ErrorHandler() {
367 @Override
368 public void warning(SAXParseException exception) {
369 }
371 @Override
372 public void error(SAXParseException exception) throws SAXException {
373 throw exception;
374 }
376 @Override
377 public void fatalError(SAXParseException exception) throws SAXException {
378 throw exception;
379 }
380 };
382 public static DocumentBuilderFactory newDocumentBuilderFactory() {
383 return newDocumentBuilderFactory(false);
384 }
386 public static DocumentBuilderFactory newDocumentBuilderFactory(boolean disableSecurity) {
387 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
388 String featureToSet = XMLConstants.FEATURE_SECURE_PROCESSING;
389 try {
390 boolean securityOn = !isXMLSecurityDisabled(disableSecurity);
391 factory.setFeature(featureToSet, securityOn);
392 factory.setNamespaceAware(true);
393 if (securityOn) {
394 factory.setExpandEntityReferences(false);
395 featureToSet = DISALLOW_DOCTYPE_DECL;
396 factory.setFeature(featureToSet, true);
397 featureToSet = EXTERNAL_GE;
398 factory.setFeature(featureToSet, false);
399 featureToSet = EXTERNAL_PE;
400 factory.setFeature(featureToSet, false);
401 featureToSet = LOAD_EXTERNAL_DTD;
402 factory.setFeature(featureToSet, false);
403 }
404 } catch (ParserConfigurationException e) {
405 LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support "+featureToSet+" feature!", new Object[] {factory.getClass().getName()} );
406 }
407 return factory;
408 }
410 public static TransformerFactory newTransformerFactory(boolean secureXmlProcessingEnabled) {
411 TransformerFactory factory = TransformerFactory.newInstance();
412 try {
413 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, isXMLSecurityDisabled(secureXmlProcessingEnabled));
414 } catch (TransformerConfigurationException e) {
415 LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support secure xml processing!", new Object[]{factory.getClass().getName()});
416 }
417 return factory;
418 }
420 public static TransformerFactory newTransformerFactory() {
421 return newTransformerFactory(true);
422 }
424 public static SAXParserFactory newSAXParserFactory(boolean disableSecurity) {
425 SAXParserFactory factory = SAXParserFactory.newInstance();
426 String featureToSet = XMLConstants.FEATURE_SECURE_PROCESSING;
427 try {
428 boolean securityOn = !isXMLSecurityDisabled(disableSecurity);
429 factory.setFeature(featureToSet, securityOn);
430 factory.setNamespaceAware(true);
431 if (securityOn) {
432 featureToSet = DISALLOW_DOCTYPE_DECL;
433 factory.setFeature(featureToSet, true);
434 featureToSet = EXTERNAL_GE;
435 factory.setFeature(featureToSet, false);
436 featureToSet = EXTERNAL_PE;
437 factory.setFeature(featureToSet, false);
438 featureToSet = LOAD_EXTERNAL_DTD;
439 factory.setFeature(featureToSet, false);
440 }
441 } catch (ParserConfigurationException | SAXNotRecognizedException | SAXNotSupportedException e) {
442 LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support "+featureToSet+" feature!", new Object[]{factory.getClass().getName()});
443 }
444 return factory;
445 }
447 public static XPathFactory newXPathFactory(boolean secureXmlProcessingEnabled) {
448 XPathFactory factory = XPathFactory.newInstance();
449 try {
450 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, isXMLSecurityDisabled(secureXmlProcessingEnabled));
451 } catch (XPathFactoryConfigurationException e) {
452 LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support secure xml processing!", new Object[] { factory.getClass().getName() } );
453 }
454 return factory;
455 }
457 public static XMLInputFactory newXMLInputFactory(boolean secureXmlProcessingEnabled) {
458 XMLInputFactory factory = XMLInputFactory.newInstance();
459 if (isXMLSecurityDisabled(secureXmlProcessingEnabled)) {
460 // TODO-Miran: are those apppropriate defaults?
461 factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
462 factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
463 }
464 return factory;
465 }
467 private static boolean isXMLSecurityDisabled(boolean runtimeDisabled) {
468 return XML_SECURITY_DISABLED || runtimeDisabled;
469 }
471 public static SchemaFactory allowExternalAccess(SchemaFactory sf, String value, boolean disableSecureProcessing) {
473 // if xml security (feature secure processing) disabled, nothing to do, no restrictions applied
474 if (isXMLSecurityDisabled(disableSecureProcessing)) {
475 if (LOGGER.isLoggable(Level.FINE)) {
476 LOGGER.log(Level.FINE, "Xml Security disabled, no JAXP xsd external access configuration necessary.");
477 }
478 return sf;
479 }
481 if (System.getProperty("javax.xml.accessExternalSchema") != null) {
482 if (LOGGER.isLoggable(Level.FINE)) {
483 LOGGER.log(Level.FINE, "Detected explicitly JAXP configuration, no JAXP xsd external access configuration necessary.");
484 }
485 return sf;
486 }
488 try {
489 sf.setProperty(ACCESS_EXTERNAL_SCHEMA, value);
490 if (LOGGER.isLoggable(Level.FINE)) {
491 LOGGER.log(Level.FINE, "Property \"{0}\" is supported and has been successfully set by used JAXP implementation.", new Object[]{ACCESS_EXTERNAL_SCHEMA});
492 }
493 } catch (SAXException ignored) {
494 // nothing to do; support depends on version JDK or SAX implementation
495 if (LOGGER.isLoggable(Level.CONFIG)) {
496 LOGGER.log(Level.CONFIG, "Property \"{0}\" is not supported by used JAXP implementation.", new Object[]{ACCESS_EXTERNAL_SCHEMA});
497 }
498 }
499 return sf;
500 }
502 }