ohair@286: /* mkos@515: * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. ohair@286: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ohair@286: * ohair@286: * This code is free software; you can redistribute it and/or modify it ohair@286: * under the terms of the GNU General Public License version 2 only, as ohair@286: * published by the Free Software Foundation. Oracle designates this ohair@286: * particular file as subject to the "Classpath" exception as provided ohair@286: * by Oracle in the LICENSE file that accompanied this code. ohair@286: * ohair@286: * This code is distributed in the hope that it will be useful, but WITHOUT ohair@286: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ohair@286: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ohair@286: * version 2 for more details (a copy is included in the LICENSE file that ohair@286: * accompanied this code). ohair@286: * ohair@286: * You should have received a copy of the GNU General Public License version ohair@286: * 2 along with this work; if not, write to the Free Software Foundation, ohair@286: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ohair@286: * ohair@286: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@286: * or visit www.oracle.com if you need additional information or have any ohair@286: * questions. ohair@286: */ ohair@286: ohair@286: package com.sun.xml.internal.ws.util.xml; ohair@286: ohair@286: import com.sun.istack.internal.Nullable; ohair@286: import com.sun.org.apache.xml.internal.resolver.Catalog; ohair@286: import com.sun.org.apache.xml.internal.resolver.CatalogManager; ohair@286: import com.sun.org.apache.xml.internal.resolver.tools.CatalogResolver; ohair@286: import com.sun.xml.internal.ws.server.ServerRtException; ohair@286: import com.sun.xml.internal.ws.util.ByteArrayBuffer; ohair@286: import org.w3c.dom.Attr; ohair@286: import org.w3c.dom.Element; ohair@286: import org.w3c.dom.EntityReference; ohair@286: import org.w3c.dom.Node; ohair@286: import org.w3c.dom.NodeList; ohair@286: import org.w3c.dom.Text; alanb@368: import org.xml.sax.*; ohair@286: alanb@368: import javax.xml.XMLConstants; ohair@286: import javax.xml.namespace.QName; alanb@368: import javax.xml.parsers.DocumentBuilderFactory; ohair@286: import javax.xml.parsers.ParserConfigurationException; ohair@286: import javax.xml.parsers.SAXParserFactory; alanb@368: import javax.xml.stream.XMLInputFactory; ohair@286: import javax.xml.transform.Result; ohair@286: import javax.xml.transform.Source; ohair@286: import javax.xml.transform.Transformer; ohair@286: import javax.xml.transform.TransformerConfigurationException; ohair@286: import javax.xml.transform.TransformerException; ohair@286: import javax.xml.transform.TransformerFactory; ohair@286: import javax.xml.transform.sax.SAXTransformerFactory; ohair@286: import javax.xml.transform.sax.TransformerHandler; ohair@286: import javax.xml.transform.stream.StreamSource; mkos@397: import javax.xml.validation.SchemaFactory; ohair@286: import javax.xml.ws.WebServiceException; alanb@368: import javax.xml.xpath.XPathFactory; alanb@368: import javax.xml.xpath.XPathFactoryConfigurationException; ohair@286: import java.io.IOException; ohair@286: import java.io.InputStream; ohair@286: import java.io.OutputStreamWriter; ohair@286: import java.io.Writer; ohair@286: import java.net.URL; ohair@286: import java.util.ArrayList; ohair@286: import java.util.Enumeration; ohair@286: import java.util.Iterator; ohair@286: import java.util.List; ohair@286: import java.util.StringTokenizer; alanb@368: import java.util.logging.Level; alanb@368: import java.util.logging.Logger; ohair@286: ohair@286: /** ohair@286: * @author WS Development Team ohair@286: */ ohair@286: public class XmlUtil { mkos@397: mkos@397: // not in older JDK, so must be duplicated here, otherwise javax.xml.XMLConstants should be used mkos@397: private static final String ACCESS_EXTERNAL_SCHEMA = "http://javax.xml.XMLConstants/property/accessExternalSchema"; mkos@397: ohair@286: private final static String LEXICAL_HANDLER_PROPERTY = ohair@286: "http://xml.org/sax/properties/lexical-handler"; ohair@286: alanb@368: private static final Logger LOGGER = Logger.getLogger(XmlUtil.class.getName()); alanb@368: mkos@408: private static boolean XML_SECURITY_DISABLED; alanb@368: alanb@368: static { mkos@408: String disableXmlSecurity = System.getProperty("com.sun.xml.internal.ws.disableXmlSecurity"); mkos@408: XML_SECURITY_DISABLED = disableXmlSecurity == null || !Boolean.valueOf(disableXmlSecurity); alanb@368: } alanb@368: ohair@286: public static String getPrefix(String s) { ohair@286: int i = s.indexOf(':'); ohair@286: if (i == -1) ohair@286: return null; ohair@286: return s.substring(0, i); ohair@286: } ohair@286: ohair@286: public static String getLocalPart(String s) { ohair@286: int i = s.indexOf(':'); ohair@286: if (i == -1) ohair@286: return s; ohair@286: return s.substring(i + 1); ohair@286: } ohair@286: ohair@286: ohair@286: ohair@286: public static String getAttributeOrNull(Element e, String name) { ohair@286: Attr a = e.getAttributeNode(name); ohair@286: if (a == null) ohair@286: return null; ohair@286: return a.getValue(); ohair@286: } ohair@286: ohair@286: public static String getAttributeNSOrNull( ohair@286: Element e, ohair@286: String name, ohair@286: String nsURI) { ohair@286: Attr a = e.getAttributeNodeNS(nsURI, name); ohair@286: if (a == null) ohair@286: return null; ohair@286: return a.getValue(); ohair@286: } ohair@286: ohair@286: public static String getAttributeNSOrNull( ohair@286: Element e, ohair@286: QName name) { ohair@286: Attr a = e.getAttributeNodeNS(name.getNamespaceURI(), name.getLocalPart()); ohair@286: if (a == null) ohair@286: return null; ohair@286: return a.getValue(); ohair@286: } ohair@286: ohair@286: /* public static boolean matchesTagNS(Element e, String tag, String nsURI) { ohair@286: try { ohair@286: return e.getLocalName().equals(tag) ohair@286: && e.getNamespaceURI().equals(nsURI); ohair@286: } catch (NullPointerException npe) { ohair@286: ohair@286: // localname not null since parsing would fail before here ohair@286: throw new WSDLParseException( ohair@286: "null.namespace.found", ohair@286: e.getLocalName()); ohair@286: } ohair@286: } ohair@286: ohair@286: public static boolean matchesTagNS( ohair@286: Element e, ohair@286: javax.xml.namespace.QName name) { ohair@286: try { ohair@286: return e.getLocalName().equals(name.getLocalPart()) ohair@286: && e.getNamespaceURI().equals(name.getNamespaceURI()); ohair@286: } catch (NullPointerException npe) { ohair@286: ohair@286: // localname not null since parsing would fail before here ohair@286: throw new WSDLParseException( ohair@286: "null.namespace.found", ohair@286: e.getLocalName()); ohair@286: } ohair@286: }*/ ohair@286: ohair@286: public static Iterator getAllChildren(Element element) { ohair@286: return new NodeListIterator(element.getChildNodes()); ohair@286: } ohair@286: ohair@286: public static Iterator getAllAttributes(Element element) { ohair@286: return new NamedNodeMapIterator(element.getAttributes()); ohair@286: } ohair@286: ohair@286: public static List parseTokenList(String tokenList) { ohair@286: List result = new ArrayList(); ohair@286: StringTokenizer tokenizer = new StringTokenizer(tokenList, " "); ohair@286: while (tokenizer.hasMoreTokens()) { ohair@286: result.add(tokenizer.nextToken()); ohair@286: } ohair@286: return result; ohair@286: } ohair@286: ohair@286: public static String getTextForNode(Node node) { alanb@368: StringBuilder sb = new StringBuilder(); ohair@286: ohair@286: NodeList children = node.getChildNodes(); ohair@286: if (children.getLength() == 0) ohair@286: return null; ohair@286: ohair@286: for (int i = 0; i < children.getLength(); ++i) { ohair@286: Node n = children.item(i); ohair@286: ohair@286: if (n instanceof Text) ohair@286: sb.append(n.getNodeValue()); ohair@286: else if (n instanceof EntityReference) { ohair@286: String s = getTextForNode(n); ohair@286: if (s == null) ohair@286: return null; ohair@286: else ohair@286: sb.append(s); ohair@286: } else ohair@286: return null; ohair@286: } ohair@286: ohair@286: return sb.toString(); ohair@286: } ohair@286: ohair@286: public static InputStream getUTF8Stream(String s) { ohair@286: try { ohair@286: ByteArrayBuffer bab = new ByteArrayBuffer(); ohair@286: Writer w = new OutputStreamWriter(bab, "utf-8"); ohair@286: w.write(s); ohair@286: w.close(); ohair@286: return bab.newInputStream(); ohair@286: } catch (IOException e) { ohair@286: throw new RuntimeException("should not happen"); ohair@286: } ohair@286: } ohair@286: mkos@515: static final ContextClassloaderLocal transformerFactory = new ContextClassloaderLocal() { mkos@515: @Override mkos@515: protected TransformerFactory initialValue() throws Exception { mkos@515: return TransformerFactory.newInstance(); mkos@515: } mkos@515: }; ohair@286: mkos@515: static final ContextClassloaderLocal saxParserFactory = new ContextClassloaderLocal() { mkos@515: @Override mkos@515: protected SAXParserFactory initialValue() throws Exception { mkos@515: SAXParserFactory factory = SAXParserFactory.newInstance(); mkos@515: factory.setNamespaceAware(true); mkos@515: return factory; mkos@515: } mkos@515: }; ohair@286: ohair@286: /** ohair@286: * Creates a new identity transformer. ohair@286: */ ohair@286: public static Transformer newTransformer() { ohair@286: try { mkos@515: return transformerFactory.get().newTransformer(); ohair@286: } catch (TransformerConfigurationException tex) { ohair@286: throw new IllegalStateException("Unable to create a JAXP transformer"); ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * Performs identity transformation. ohair@286: */ ohair@286: public static ohair@286: T identityTransform(Source src, T result) throws TransformerException, SAXException, ParserConfigurationException, IOException { ohair@286: if (src instanceof StreamSource) { ohair@286: // work around a bug in JAXP in JDK6u4 and earlier where the namespace processing ohair@286: // is not turned on by default ohair@286: StreamSource ssrc = (StreamSource) src; mkos@515: TransformerHandler th = ((SAXTransformerFactory) transformerFactory.get()).newTransformerHandler(); ohair@286: th.setResult(result); mkos@515: XMLReader reader = saxParserFactory.get().newSAXParser().getXMLReader(); ohair@286: reader.setContentHandler(th); ohair@286: reader.setProperty(LEXICAL_HANDLER_PROPERTY, th); ohair@286: reader.parse(toInputSource(ssrc)); ohair@286: } else { ohair@286: newTransformer().transform(src, result); ohair@286: } ohair@286: return result; ohair@286: } ohair@286: ohair@286: private static InputSource toInputSource(StreamSource src) { ohair@286: InputSource is = new InputSource(); ohair@286: is.setByteStream(src.getInputStream()); ohair@286: is.setCharacterStream(src.getReader()); ohair@286: is.setPublicId(src.getPublicId()); ohair@286: is.setSystemId(src.getSystemId()); ohair@286: return is; ohair@286: } ohair@286: ohair@286: /* ohair@286: * Gets an EntityResolver using XML catalog ohair@286: */ ohair@286: public static EntityResolver createEntityResolver(@Nullable URL catalogUrl) { ohair@286: // set up a manager ohair@286: CatalogManager manager = new CatalogManager(); ohair@286: manager.setIgnoreMissingProperties(true); ohair@286: // Using static catalog may result in to sharing of the catalog by multiple apps running in a container ohair@286: manager.setUseStaticCatalog(false); ohair@286: Catalog catalog = manager.getCatalog(); ohair@286: try { ohair@286: if (catalogUrl != null) { ohair@286: catalog.parseCatalog(catalogUrl); ohair@286: } ohair@286: } catch (IOException e) { ohair@286: throw new ServerRtException("server.rt.err",e); ohair@286: } ohair@286: return workaroundCatalogResolver(catalog); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Gets a default EntityResolver for catalog at META-INF/jaxws-catalog.xml ohair@286: */ ohair@286: public static EntityResolver createDefaultCatalogResolver() { ohair@286: ohair@286: // set up a manager ohair@286: CatalogManager manager = new CatalogManager(); ohair@286: manager.setIgnoreMissingProperties(true); ohair@286: // Using static catalog may result in to sharing of the catalog by multiple apps running in a container ohair@286: manager.setUseStaticCatalog(false); ohair@286: // parse the catalog ohair@286: ClassLoader cl = Thread.currentThread().getContextClassLoader(); ohair@286: Enumeration catalogEnum; ohair@286: Catalog catalog = manager.getCatalog(); ohair@286: try { ohair@286: if (cl == null) { ohair@286: catalogEnum = ClassLoader.getSystemResources("META-INF/jax-ws-catalog.xml"); ohair@286: } else { ohair@286: catalogEnum = cl.getResources("META-INF/jax-ws-catalog.xml"); ohair@286: } ohair@286: ohair@286: while(catalogEnum.hasMoreElements()) { ohair@286: URL url = catalogEnum.nextElement(); ohair@286: catalog.parseCatalog(url); ohair@286: } ohair@286: } catch (IOException e) { ohair@286: throw new WebServiceException(e); ohair@286: } ohair@286: ohair@286: return workaroundCatalogResolver(catalog); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Default CatalogResolver implementation is broken as it depends on CatalogManager.getCatalog() which will always create a new one when ohair@286: * useStaticCatalog is false. ohair@286: * This returns a CatalogResolver that uses the catalog passed as parameter. ohair@286: * @param catalog ohair@286: * @return CatalogResolver ohair@286: */ ohair@286: private static CatalogResolver workaroundCatalogResolver(final Catalog catalog) { ohair@286: // set up a manager ohair@286: CatalogManager manager = new CatalogManager() { ohair@286: @Override ohair@286: public Catalog getCatalog() { ohair@286: return catalog; ohair@286: } ohair@286: }; ohair@286: manager.setIgnoreMissingProperties(true); ohair@286: // Using static catalog may result in to sharing of the catalog by multiple apps running in a container ohair@286: manager.setUseStaticCatalog(false); ohair@286: ohair@286: return new CatalogResolver(manager); ohair@286: } ohair@286: ohair@286: /** ohair@286: * {@link ErrorHandler} that always treat the error as fatal. ohair@286: */ ohair@286: public static final ErrorHandler DRACONIAN_ERROR_HANDLER = new ErrorHandler() { alanb@368: @Override ohair@286: public void warning(SAXParseException exception) { ohair@286: } ohair@286: alanb@368: @Override ohair@286: public void error(SAXParseException exception) throws SAXException { ohair@286: throw exception; ohair@286: } ohair@286: alanb@368: @Override ohair@286: public void fatalError(SAXParseException exception) throws SAXException { ohair@286: throw exception; ohair@286: } ohair@286: }; alanb@368: alanb@368: public static DocumentBuilderFactory newDocumentBuilderFactory() { alanb@368: return newDocumentBuilderFactory(true); alanb@368: } alanb@368: alanb@368: public static DocumentBuilderFactory newDocumentBuilderFactory(boolean secureXmlProcessing) { alanb@368: DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); alanb@368: try { mkos@408: factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, isXMLSecurityDisabled(secureXmlProcessing)); alanb@368: } catch (ParserConfigurationException e) { mkos@408: LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support secure xml processing!", new Object[] { factory.getClass().getName() } ); alanb@368: } alanb@368: return factory; alanb@368: } alanb@368: alanb@368: public static TransformerFactory newTransformerFactory(boolean secureXmlProcessingEnabled) { alanb@368: TransformerFactory factory = TransformerFactory.newInstance(); alanb@368: try { mkos@408: factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, isXMLSecurityDisabled(secureXmlProcessingEnabled)); alanb@368: } catch (TransformerConfigurationException e) { mkos@408: LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support secure xml processing!", new Object[]{factory.getClass().getName()}); alanb@368: } alanb@368: return factory; alanb@368: } alanb@368: alanb@368: public static TransformerFactory newTransformerFactory() { alanb@368: return newTransformerFactory(true); alanb@368: } alanb@368: alanb@368: public static SAXParserFactory newSAXParserFactory(boolean secureXmlProcessingEnabled) { alanb@368: SAXParserFactory factory = SAXParserFactory.newInstance(); alanb@368: try { mkos@408: factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, isXMLSecurityDisabled(secureXmlProcessingEnabled)); alanb@368: } catch (Exception e) { mkos@408: LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support secure xml processing!", new Object[]{factory.getClass().getName()}); alanb@368: } alanb@368: return factory; alanb@368: } alanb@368: alanb@368: public static XPathFactory newXPathFactory(boolean secureXmlProcessingEnabled) { alanb@368: XPathFactory factory = XPathFactory.newInstance(); alanb@368: try { mkos@408: factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, isXMLSecurityDisabled(secureXmlProcessingEnabled)); alanb@368: } catch (XPathFactoryConfigurationException e) { mkos@408: LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support secure xml processing!", new Object[] { factory.getClass().getName() } ); alanb@368: } alanb@368: return factory; alanb@368: } alanb@368: alanb@368: public static XMLInputFactory newXMLInputFactory(boolean secureXmlProcessingEnabled) { alanb@368: XMLInputFactory factory = XMLInputFactory.newInstance(); mkos@408: if (isXMLSecurityDisabled(secureXmlProcessingEnabled)) { alanb@368: // TODO-Miran: are those apppropriate defaults? alanb@368: factory.setProperty(XMLInputFactory.SUPPORT_DTD, false); alanb@368: factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); alanb@368: } alanb@368: return factory; alanb@368: } alanb@368: mkos@408: private static boolean isXMLSecurityDisabled(boolean runtimeDisabled) { mkos@408: return XML_SECURITY_DISABLED || runtimeDisabled; alanb@368: } alanb@368: mkos@408: public static SchemaFactory allowExternalAccess(SchemaFactory sf, String value, boolean disableSecureProcessing) { mkos@397: mkos@408: // if xml security (feature secure processing) disabled, nothing to do, no restrictions applied mkos@408: if (isXMLSecurityDisabled(disableSecureProcessing)) { mkos@408: if (LOGGER.isLoggable(Level.FINE)) { mkos@408: LOGGER.log(Level.FINE, "Xml Security disabled, no JAXP xsd external access configuration necessary."); mkos@408: } mkos@408: return sf; mkos@408: } mkos@408: mkos@408: if (System.getProperty("javax.xml.accessExternalSchema") != null) { mkos@408: if (LOGGER.isLoggable(Level.FINE)) { mkos@408: LOGGER.log(Level.FINE, "Detected explicitly JAXP configuration, no JAXP xsd external access configuration necessary."); mkos@408: } mkos@397: return sf; mkos@397: } mkos@397: mkos@397: try { mkos@408: sf.setProperty(ACCESS_EXTERNAL_SCHEMA, value); mkos@408: if (LOGGER.isLoggable(Level.FINE)) { mkos@408: LOGGER.log(Level.FINE, "Property \"{0}\" is supported and has been successfully set by used JAXP implementation.", new Object[]{ACCESS_EXTERNAL_SCHEMA}); mkos@408: } mkos@397: } catch (SAXException ignored) { mkos@408: // nothing to do; support depends on version JDK or SAX implementation mkos@408: if (LOGGER.isLoggable(Level.CONFIG)) { mkos@408: LOGGER.log(Level.CONFIG, "Property \"{0}\" is not supported by used JAXP implementation.", new Object[]{ACCESS_EXTERNAL_SCHEMA}); mkos@408: } mkos@397: } mkos@397: return sf; mkos@397: } mkos@408: ohair@286: }