io.cloudslang.content.xml.utils.XmlUtils.java Source code

Java tutorial

Introduction

Here is the source code for io.cloudslang.content.xml.utils.XmlUtils.java

Source

/*******************************************************************************
 * (c) Copyright 2017 Hewlett-Packard Development Company, L.P.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Apache License v2.0 which accompany this distribution.
 *
 * The Apache License is available at
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 *******************************************************************************/
package io.cloudslang.content.xml.utils;

import io.cloudslang.content.httpclient.CSHttpClient;
import io.cloudslang.content.httpclient.HttpClientInputs;
import io.cloudslang.content.httpclient.build.auth.AuthTypes;
import io.cloudslang.content.xml.entities.SimpleNamespaceContext;
import io.cloudslang.content.xml.entities.inputs.CommonInputs;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.HttpGet;
import org.jdom2.input.SAXBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

import static org.apache.commons.lang3.StringUtils.EMPTY;

/**
 * Created by markowis on 23/02/2016.
 */
public class XmlUtils {
    private static final String OK_STATUS_CODE = "200";

    private XmlUtils() {
    }

    public static String nodeToString(Node node) throws TransformerException {

        if (node == null) {
            return EMPTY;
        } else if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
            return node.toString();
        } else {
            return transformElementNode(node);
        }
    }

    /**
     * Returns the Namespaces context from an xml.
     *
     * @param xmlString   xml as string
     * @param xmlFilePath path to xml file
     * @return the Namespaces context from an xml.
     * @throws IOException        file reading exception
     * @throws XMLStreamException parsing exception
     */
    public static NamespaceContext getNamespaceContext(String xmlString, String xmlFilePath) throws Exception {
        InputStream inputXML = getStream(xmlString, xmlFilePath);
        XMLInputFactory inputFactory = XMLInputFactory.newInstance();
        XMLStreamReader reader = inputFactory.createXMLStreamReader(inputXML);
        Map<String, String> namespaces = new HashMap<>();
        while (reader.hasNext()) {
            int evt = reader.next();
            if (evt == XMLStreamConstants.START_ELEMENT) {
                QName qName = reader.getName();
                if (qName != null) {
                    if (qName.getPrefix() != null && qName.getPrefix().compareTo("") != 0)
                        namespaces.put(qName.getPrefix(), qName.getNamespaceURI());
                }
            }
        }
        return new SimpleNamespaceContext(namespaces);
    }

    public static Document parseXmlStringSecurely(String xmlDocument, boolean secure) throws Exception {
        DocumentBuilder builder = getDocumentBuilder(secure);

        return builder.parse(new InputSource(new StringReader(xmlDocument)));
    }

    public static DocumentBuilder getDocumentBuilder(boolean secure) throws ParserConfigurationException {
        String feature;
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

        feature = "http://xml.org/sax/features/external-general-entities";
        factory.setFeature(feature, false);
        feature = "http://xml.org/sax/features/external-parameter-entities";
        factory.setFeature(feature, false);
        feature = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
        factory.setFeature(feature, false);
        feature = "http://apache.org/xml/features/disallow-doctype-decl";
        factory.setFeature(feature, true);
        factory.setXIncludeAware(false);
        factory.setExpandEntityReferences(false);
        factory.setNamespaceAware(true);
        factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, secure);
        return factory.newDocumentBuilder();
    }

    /**
     * This method creates an XML document from a given path which respects the encoding specified in the xml header. Otherwise it defaults to UTF-8.
     *
     * @param path   xml given path
     * @param secure secure
     * @return created xml document form the given path
     * @throws IOException
     * @throws ParserConfigurationException
     * @throws SAXException
     */
    public static Document createDocumentFromFile(String path, boolean secure)
            throws IOException, ParserConfigurationException, SAXException {
        DocumentBuilder builder = XmlUtils.getDocumentBuilder(secure);
        File initialFile = new File(path);
        InputStream targetStream = new FileInputStream(initialFile);
        InputSource is = new InputSource(targetStream);
        return builder.parse(is);
    }

    public static void parseXmlString(String xml, String features) throws Exception {
        parseXmlInputStream(getStream(xml, EMPTY), features);
    }

    public static void parseXmlFile(String xmlFile, String features) throws Exception {
        parseXmlInputStream(getStream(EMPTY, xmlFile), features);
    }

    /**
     * Transforms a String or File provided by path to a Document object.
     *
     * @param xml      xml the xml as String
     * @param filePath the path/remote path to the file
     * @param features parsing features to set on the document builder
     * @return a  Document representation of the String or file
     * @throws Exception in case something goes wrong
     */
    public static Document createDocument(String xml, String filePath, String features) throws Exception {
        try {
            return parseXmlInputStream(getStream(xml, filePath), features);
        } catch (MalformedURLException e) {
            throw new Exception((new StringBuilder("Unable to open remote file requested, file path["))
                    .append(filePath).append("], error[").append(e.getMessage()).append("]").toString(), e);
        } catch (IOException e) {
            throw new Exception((new StringBuilder("Unable to open file requested, filename[")).append(filePath)
                    .append("], error[").append(e.getMessage()).append("]").toString(), e);
        }
    }

    public static Document parseXmlInputStream(InputStream inputStream, String features) throws Exception {
        Document xmlDocument;
        DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
        XmlUtils.setFeatures(builderFactory, features);

        builderFactory.setNamespaceAware(true);
        DocumentBuilder builder = builderFactory.newDocumentBuilder();
        xmlDocument = builder.parse(inputStream);
        return xmlDocument;
    }

    /**
     * Return all the nodes in an Document for a given XPath.
     *
     * @param doc        the document to read from
     * @param pathToNode the XPath to find
     * @return a NodeList object
     * @throws XPathExpressionException if  xpath exception occurred
     */
    public static NodeList readNode(Document doc, String pathToNode, NamespaceContext ctx)
            throws XPathExpressionException {
        XPath xPath = createXpath();
        xPath.setNamespaceContext(ctx);
        return (NodeList) xPath.evaluate(pathToNode, doc, XPathConstants.NODESET);
    }

    /**
     * Transforms a string representation of an XML Node to an Node
     *
     * @param value    the node as String
     * @param features parsing features to set on the document builder
     * @return the Node object
     * @throws Exception in case the String can't be represented as a Node
     */
    public static Node stringToNode(String value, String encoding, String features) throws Exception {
        Node node;
        if (StringUtils.isEmpty(encoding)) {
            encoding = "UTF-8";
        }
        try (InputStream inputStream = new ByteArrayInputStream(value.getBytes(encoding))) {
            // check if input value is a Node
            Document docNew = DocumentUtils.createDocumentBuilder(features).parse(inputStream);
            node = docNew.getDocumentElement();
        } catch (SAXException se) {
            throw new Exception("Value " + value + "is not valid XML element : " + se.getMessage());
        }
        return node;
    }

    /**
     * Returns a new instance of XPath.
     *
     * @return XPath object
     */
    private static XPath createXpath() {
        return XPathFactory.newInstance().newXPath();
    }

    /**
     * Returns the InputStream representation of a file or string.
     *
     * @param xml      xml the xml as String
     * @param filePath he path/remote path to the file
     * @return the InputStream representation of a file or string
     * @throws IOException Exception in case something goes wrong
     */
    public static InputStream getStream(String xml, String filePath) throws IOException {
        InputStream inputXML;
        if (StringUtils.isEmpty(filePath)) {
            inputXML = new ByteArrayInputStream(xml.getBytes());
        } else {
            if (filePath.startsWith(Constants.Inputs.HTTP_PREFIX_STRING)
                    || filePath.startsWith(Constants.Inputs.HTTPS_PREFIX_STRING)) {
                inputXML = new java.net.URL(filePath).openStream();
            } else {
                inputXML = new FileInputStream(new File(filePath));
            }
        }
        return inputXML;
    }

    public static StringWriter getStringWriter(Document doc) throws TransformerException {
        DOMSource domSource = new DOMSource(doc);
        StringWriter writer = new StringWriter();
        StreamResult streamResult = new StreamResult(writer);
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        transformer.transform(domSource, streamResult);
        return writer;
    }

    public static NodeList evaluateXPathQuery(Document doc, NamespaceContext context, String xPathQuery)
            throws XPathExpressionException {
        XPathExpression expr = createXPathExpression(context, xPathQuery);
        return (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
    }

    public static XPathExpression createXPathExpression(NamespaceContext context, String xPathQuery)
            throws XPathExpressionException {
        XPath xpath = XPathFactory.newInstance().newXPath();
        xpath.setNamespaceContext(context);
        return xpath.compile(xPathQuery);
    }

    public static void validateNodeList(NodeList nodeList) throws Exception {
        if (nodeList.getLength() == 0) {
            throw new Exception(Constants.ErrorMessages.ELEMENT_NOT_FOUND);
        }
    }

    private static String transformElementNode(Node node) throws TransformerException {
        StringWriter stringWriter = new StringWriter();

        Transformer transformer = TransformerFactory.newInstance().newTransformer();
        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, Constants.YES);
        transformer.setOutputProperty(OutputKeys.INDENT, Constants.YES);
        transformer.transform(new DOMSource(node), new StreamResult(stringWriter));

        return stringWriter.toString().trim();
    }

    public static Document getDocument(CommonInputs commonInputs) throws Exception {
        Document doc;
        if (Constants.XML_PATH.equalsIgnoreCase(commonInputs.getXmlDocumentSource())) {
            doc = XmlUtils.createDocumentFromFile(commonInputs.getXmlDocument(),
                    commonInputs.getSecureProcessing());
        } else {
            doc = XmlUtils.parseXmlStringSecurely(commonInputs.getXmlDocument(),
                    commonInputs.getSecureProcessing());
        }
        return doc;
    }

    public static NamespaceContext getNamespaceContext(CommonInputs commonInputs, Document doc) throws Exception {
        NamespaceContext context;
        if (Constants.XML_PATH.equalsIgnoreCase(commonInputs.getXmlDocumentSource())) {
            StringWriter writer = XmlUtils.getStringWriter(doc);
            context = XmlUtils.getNamespaceContext(writer.toString(), "");
        } else {
            context = XmlUtils.getNamespaceContext(commonInputs.getXmlDocument(), "");
        }
        return context;
    }

    public static String createXmlDocumentFromUrl(CommonInputs commonInputs)
            throws ParserConfigurationException, SAXException, IOException {
        CSHttpClient scoreHttpClient = new CSHttpClient();
        HttpClientInputs httpClientInputs = new HttpClientInputs();
        httpClientInputs.setMethod(HttpGet.METHOD_NAME);
        httpClientInputs.setUrl(commonInputs.getXmlDocument());

        if (commonInputs.getUsername().isEmpty()) {
            httpClientInputs.setAuthType(AuthTypes.ANONYMOUS);
        } else {
            httpClientInputs.setAuthType(AuthTypes.BASIC);
            httpClientInputs.setUsername(commonInputs.getUsername());
            httpClientInputs.setPassword(commonInputs.getPassword());
        }
        httpClientInputs.setRequestCharacterSet(StandardCharsets.UTF_8.toString());
        httpClientInputs.setResponseCharacterSet(StandardCharsets.UTF_8.toString());
        httpClientInputs.setTrustAllRoots(commonInputs.getTrustAllRoots());
        httpClientInputs.setKeystore(commonInputs.getKeystore());
        httpClientInputs.setKeystorePassword(commonInputs.getKeystorePassword());
        httpClientInputs.setTrustKeystore(commonInputs.getTrustKeystore());
        httpClientInputs.setTrustPassword(commonInputs.getTrustPassword());
        httpClientInputs.setX509HostnameVerifier(commonInputs.getX509Hostnameverifier());
        httpClientInputs.setProxyHost(commonInputs.getProxyHost());
        httpClientInputs.setProxyPort(commonInputs.getProxyPort());
        httpClientInputs.setProxyUsername(commonInputs.getProxyUsername());
        httpClientInputs.setProxyPassword(commonInputs.getProxyPassword());

        Map<String, String> requestResponse = scoreHttpClient.execute(httpClientInputs);
        if (!OK_STATUS_CODE.equals(requestResponse.get(CSHttpClient.STATUS_CODE))) {
            throw new RuntimeException("Http request to specified URL: " + commonInputs.getXmlDocument()
                    + " failed with status code: " + requestResponse.get(CSHttpClient.STATUS_CODE)
                    + ". Request response is: " + requestResponse.get(Constants.Outputs.RETURN_RESULT));
        }
        return requestResponse.get(Constants.Outputs.RETURN_RESULT);
    }

    public static void setFeatures(DocumentBuilderFactory reader, String features)
            throws ParserConfigurationException {
        if (StringUtils.isNotBlank(features)) {
            Map featuresMap = parseFeatures(features);

            for (Object o : featuresMap.keySet()) {
                String key = (String) o;
                reader.setFeature(key, (Boolean) featuresMap.get(key));
            }
        }

    }

    public static void setFeatures(SAXBuilder reader, String features) throws SAXException {
        if (!StringUtils.isEmpty(features)) {
            Map<String, Boolean> featuresMap = parseFeatures(features);
            for (String key : featuresMap.keySet()) {
                reader.setFeature(key, featuresMap.get(key));
            }
        }
    }

    private static Map<String, Boolean> parseFeatures(String features) {
        Map<String, Boolean> map = new HashMap<>();
        String[] featuresList = features.split("\\n");
        int len$ = featuresList.length;

        for (int i$ = 0; i$ < len$; ++i$) {
            String element = featuresList[i$];
            String[] keyValue = element.split("\\s");
            if (keyValue.length != 2) {
                throw new IllegalArgumentException("Wrong format for \'features\' input field!");
            }

            map.put(keyValue[0], Boolean.valueOf(keyValue[1]));
        }

        return map;
    }
}