/* * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.carbon.governance.comparator.utils; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonParser; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.xerces.util.SecurityManager; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.governance.comparator.Comparison; import org.wso2.carbon.governance.comparator.ComparisonException; import org.wso2.carbon.governance.comparator.DiffGenerator; import org.wso2.carbon.governance.comparator.DiffGeneratorFactory; import org.wso2.carbon.governance.comparator.GovernanceDiffGeneratorFactory; import org.wso2.carbon.governance.comparator.TextDiffGeneratorFactory; import org.wso2.carbon.registry.common.services.RegistryAbstractAdmin; import org.wso2.carbon.registry.core.Registry; import org.wso2.carbon.registry.core.Resource; import org.wso2.carbon.registry.core.exceptions.RegistryException; import org.wso2.carbon.registry.core.internal.RegistryCoreServiceComponent; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import javax.wsdl.Definition; import javax.wsdl.WSDLException; import javax.wsdl.factory.WSDLFactory; import javax.wsdl.xml.WSDLReader; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; 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.XPathExpressionException; import javax.xml.xpath.XPathFactory; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.StringWriter; import java.io.UnsupportedEncodingException; /** * This Util class includes Comparator util methods. */ public class ComparatorUtils extends RegistryAbstractAdmin { private static final Log log = LogFactory.getLog(ComparatorUtils.class); private static final int ENTITY_EXPANSION_LIMIT = 0; /** * This method is used to get the text difference of two strings. * * @param resourcePathOne resource path one. * @param resourcePathTwo resource path two. * @return Comparison object which includes the difference parameters. * @throws ComparisonException * @throws WSDLException * @throws RegistryException * @throws UnsupportedEncodingException */ public Comparison getArtifactTextDiff(String resourcePathOne, String resourcePathTwo) throws ComparisonException, WSDLException, RegistryException, UnsupportedEncodingException { String username = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername(); int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); Registry registry = RegistryCoreServiceComponent.getRegistryService().getRegistry(username, tenantId); Resource resourceOne = registry.get(resourcePathOne); Resource resourceTwo = registry.get(resourcePathTwo); DiffGeneratorFactory factory = new TextDiffGeneratorFactory(); DiffGenerator flow = factory.getDiffGenerator(); String resourceOneText = new String((byte[]) resourceOne.getContent(), "UTF-8"); String resourceTwoText = new String((byte[]) resourceTwo.getContent(), "UTF-8"); String resourceOneFormattedText = prettyFormatText(resourceOneText, resourceOne.getMediaType()); String resourceTwoFormattedText = prettyFormatText(resourceTwoText, resourceTwo.getMediaType()); return flow.compare(resourceOneFormattedText, resourceTwoFormattedText, ComparatorConstants .TEXT_PLAIN_MEDIA_TYPE); } /** * This method is used to get a details difference of two resource while considering the media type. * * @param resourcePathOne resource path one * @param resourcePathTwo resource path two * @param mediaType media type * @return Comparison object which includes the difference parameters. * @throws ComparisonException * @throws WSDLException * @throws RegistryException * @throws UnsupportedEncodingException */ public Comparison getArtifactDetailDiff(String resourcePathOne, String resourcePathTwo, String mediaType) throws ComparisonException, WSDLException, RegistryException, UnsupportedEncodingException { String username = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername(); int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); Registry registry = RegistryCoreServiceComponent.getRegistryService().getRegistry(username, tenantId); Resource resourceOne = registry.get(resourcePathOne); Resource resourceTwo = registry.get(resourcePathTwo); switch (mediaType) { case ComparatorConstants.WSDL_MEDIA_TYPE: return getWSDLComparison(resourceOne, resourceTwo); default: return getArtifactTextDiff(resourcePathOne, resourcePathTwo); } } /** * This method is used to get wsdl difference comparison. * * @param WSDLOne wsdl one. * @param WSDLTwo wsdl two. * @return Comparison object which includes the difference parameters. * @throws ComparisonException * @throws WSDLException * @throws RegistryException * @throws UnsupportedEncodingException */ private Comparison getWSDLComparison(Resource WSDLOne, Resource WSDLTwo) throws ComparisonException, WSDLException, RegistryException, UnsupportedEncodingException { GovernanceDiffGeneratorFactory diffGeneratorFactory = new GovernanceDiffGeneratorFactory(); DiffGenerator flow = diffGeneratorFactory.getDiffGenerator(); if (flow != null) { WSDLReader wsdlReader = WSDLFactory.newInstance().newWSDLReader(); InputSource inputSourceOne = new InputSource(new ByteArrayInputStream((byte[]) WSDLOne.getContent())); Definition originalWSDL = wsdlReader.readWSDL(null, inputSourceOne); InputSource inputSourceTwo = new InputSource(new ByteArrayInputStream((byte[]) WSDLTwo.getContent())); Definition changedWSDL = wsdlReader.readWSDL(null, inputSourceTwo); return flow.compare(originalWSDL, changedWSDL, ComparatorConstants.WSDL_MEDIA_TYPE); } else { return null; } } /** * This method is used to format Texts. * * @param input input String * @param mediaType input resource mediaType * @return formatted text * @throws ComparisonException Exception will occur if an error happens when formatting the text. */ public String prettyFormatText(String input, String mediaType) throws ComparisonException { if (mediaType.contains(ComparatorConstants.XML)) { return prettyFormatXML(input); } else if (mediaType.contains(ComparatorConstants.JSON)) { return prettyFormatJSON(input); } else { return input; } } /** * This method is used to format XML Strings * @param input input xml String * @return formatted xml String */ private String prettyFormatXML(String input) { try { Document document = getSecuredDocumentBuilder().newDocumentBuilder() .parse(new InputSource(new ByteArrayInputStream(input.getBytes(ComparatorConstants.UTF_8)))); XPath xPath = XPathFactory.newInstance().newXPath(); NodeList nodeList = (NodeList) xPath .evaluate("//text()[normalize-space()='']", document, XPathConstants.NODESET); for (int i = 0; i < nodeList.getLength(); ++i) { Node node = nodeList.item(i); node.getParentNode().removeChild(node); } Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.setOutputProperty(OutputKeys.ENCODING, ComparatorConstants.UTF_8); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, ComparatorConstants.YES); transformer.setOutputProperty(OutputKeys.INDENT, ComparatorConstants.YES); transformer.setOutputProperty(ComparatorConstants.XML_INDENT_AMOUNT, ComparatorConstants.TWO); StringWriter stringWriter = new StringWriter(); StreamResult streamResult = new StreamResult(stringWriter); transformer.transform(new DOMSource(document), streamResult); return stringWriter.toString(); } catch (TransformerException | SAXException | ParserConfigurationException | XPathExpressionException | IOException e) { log.warn("Error occurred while formatting the xml content.", e); return input; } } /** * This method is used to format JSON Strings. * * @param input JSON Strong * @return Formatted JSON Strong */ private String prettyFormatJSON(String input) { JsonParser parser = new JsonParser(); Gson gson = new GsonBuilder().setPrettyPrinting().create(); JsonElement jsonElement = parser.parse(input); return gson.toJson(jsonElement); } /** * Returns a secured DocumentBuilderFactory instance * @return DocumentBuilderFactory */ public static DocumentBuilderFactory getSecuredDocumentBuilder() { org.apache.xerces.impl.Constants Constants = null; DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); dbf.setXIncludeAware(false); dbf.setExpandEntityReferences(false); try { dbf.setFeature(Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_GENERAL_ENTITIES_FEATURE, false); dbf.setFeature(Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE, false); dbf.setFeature(Constants.XERCES_FEATURE_PREFIX + Constants.LOAD_EXTERNAL_DTD_FEATURE, false); } catch (ParserConfigurationException e) { log.error( "Failed to load XML Processor Feature " + Constants.EXTERNAL_GENERAL_ENTITIES_FEATURE + " or " + Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE + " or " + Constants.LOAD_EXTERNAL_DTD_FEATURE); } org.apache.xerces.util.SecurityManager securityManager = new SecurityManager(); securityManager.setEntityExpansionLimit(ENTITY_EXPANSION_LIMIT); dbf.setAttribute(Constants.XERCES_PROPERTY_PREFIX + Constants.SECURITY_MANAGER_PROPERTY, securityManager); return dbf; } }