org.apache.synapse.mediators.experimental.MockFactoryMediator.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.synapse.mediators.experimental.MockFactoryMediator.java

Source

/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF 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.apache.synapse.mediators.experimental;

import com.google.gson.Gson;
import com.jayway.jsonpath.InvalidJsonException;
import com.jayway.jsonpath.JsonPath;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMException;
import org.apache.axiom.om.OMText;
import org.apache.axiom.om.util.AXIOMUtil;
import org.apache.axiom.soap.SOAP11Constants;
import org.apache.axiom.soap.SOAP12Constants;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axis2.AxisFault;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.synapse.Mediator;
import org.apache.synapse.MessageContext;
import org.apache.synapse.commons.json.JsonUtil;
import org.apache.synapse.config.xml.SynapsePath;
import org.apache.synapse.core.axis2.Axis2MessageContext;
import org.apache.synapse.mediators.AbstractMediator;
import org.apache.synapse.mediators.Value;
import org.apache.synapse.mediators.transform.Argument;
import org.apache.synapse.util.AXIOMUtils;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MockFactoryMediator extends AbstractMediator {

    private static final Log logger = LogFactory.getLog(MockFactoryMediator.class);

    private Pattern pattern = Pattern.compile("\\$(\\d)+");
    private List<Argument> pathArgumentList = new ArrayList<Argument>();
    private Value formatKey = null;
    private Value mockDataKey = null;
    private boolean isFormatDynamic = false;
    private boolean isMockDataDynamic = false;
    private String formatRaw;
    private String fileLocation;
    private String mockDataRaw;

    private String mediaType = XML_TYPE;
    private final static String JSON_CONTENT_TYPE = "application/json";
    private final static String XML_CONTENT_TYPE = "application/xml";
    private final static String JSON_TYPE = "json";
    private final static String XML_TYPE = "xml";
    private final static String STRING_TYPE = "str";
    private final Gson gson = new Gson();

    /**
     * Contains 2 paths - one when JSON Streaming is in use (mediateJsonStreamPayload) and the other for regular
     * builders (mediatePayload).
     *
     * @param synCtx the current message for mediation
     * @return
     */
    public boolean mediate(MessageContext synCtx) {
        String format = formatRaw;
        for (Argument arg : pathArgumentList) {
            String argStrVal = "";
            if (arg.getValue() != null) {
                argStrVal = arg.getValue();
            } else if (arg.getExpression() != null) {
                argStrVal = arg.getExpression().stringValueOf(synCtx);
            }
            logger.debug("pathArgument: " + argStrVal);
        }
        logger.debug("format raw value: " + format);
        return mediate(synCtx, format);
    }

    public boolean mediate(MessageContext synCtx, String format) {

        if (!isDoingXml(synCtx) && !isDoingJson(synCtx)) {
            logger.error(
                    "#mediate. Could not identify the payload format of the existing payload prior to mediate.");
            return false;
        }

        org.apache.axis2.context.MessageContext axis2MessageContext = ((Axis2MessageContext) synCtx)
                .getAxis2MessageContext();
        StringBuffer result = new StringBuffer();
        getMockResponseFromQueryPath(result, synCtx, format);
        String out = result.toString().trim();
        if (log.isDebugEnabled()) {
            log.debug("#mediate. Transformed payload format>>> " + out);
        }
        if (mediaType.equals(XML_TYPE)) {
            try {
                JsonUtil.removeJsonPayload(axis2MessageContext);
                OMElement omXML = AXIOMUtil.stringToOM(out);
                if (!checkAndReplaceEnvelop(omXML, synCtx)) { // check if the target of the PF 'format' is the entire SOAP envelop, not just the body.
                    axis2MessageContext.getEnvelope().getBody().addChild(omXML.getFirstElement());
                }
            } catch (XMLStreamException e) {
                handleException("Error creating SOAP Envelope from source " + out, synCtx);
            }
        } else {
            JsonUtil.newJsonPayload(axis2MessageContext, out, true, true);
        }
        return true;
    }

    private void getMockResponseFromQueryPath(StringBuffer result, MessageContext synCtx, String format) {

        StringBuffer formatResult = new StringBuffer();
        if (isFormatDynamic()) {
            String key = formatKey.evaluateValue(synCtx);
            Object entry = synCtx.getEntry(key);
            String text = "";
            if (entry instanceof OMElement) {
                OMElement e = (OMElement) entry;
                removeIndentations(e);
                text = e.toString();
            } else if (entry instanceof OMText) {
                text = ((OMText) entry).getText();
            } else if (entry instanceof String) {
                text = (String) entry;
            }
            replace(text, formatResult, synCtx);
        } else {
            replace(format, formatResult, synCtx);
        }

        logger.debug("Query path: " + formatResult.toString());

        if (isMockDataDynamic()) {
            String key = formatKey.evaluateValue(synCtx);
            Object entry = synCtx.getEntry(key);
            String text = "";
            if (entry instanceof OMElement) {
                OMElement e = (OMElement) entry;
                removeIndentations(e);
                text = e.toString();
            } else if (entry instanceof OMText) {
                text = ((OMText) entry).getText();
            } else if (entry instanceof String) {
                text = (String) entry;
            }
            logger.debug("Dynamic mock data: " + text);

            if (getType().contains(JSON_TYPE)) {
                Object document = JsonPath.read(text, formatResult.toString());
                String jsonStr = gson.toJson(document);
                logger.debug("JsonPath query result from dynamic mock data: " + jsonStr);
                result.append(jsonStr);
                return;

            } else {

                // TODO
            }

        } else {
            // check if mockdata is file-based
            if (fileLocation != null) {
                logger.debug("Loading mock data for MockFactoryMediator from " + fileLocation);

                File mockDataFile = new File(fileLocation);
                if (mockDataFile.exists()) {
                    try {
                        if (getType().contains(JSON_TYPE)) {
                            InputStream jsonInputStream = new FileInputStream(mockDataFile);
                            Object document = JsonPath.read(jsonInputStream, formatResult.toString());
                            String jsonStr = gson.toJson(document);
                            logger.debug("JsonPath query result from file-based mock data: " + jsonStr);
                            result.append(jsonStr);
                            return;
                        } else {
                            // TODO handle XPath with XML media type case

                        }
                    } catch (IOException e) {
                        logger.error("IO error when reading JSON file : " + mockDataFile.getAbsolutePath(), e);
                        return;
                    } catch (InvalidJsonException e) {
                        logger.error("Invalid JSON format. " + e.getMessage(), e);
                        return;
                    } catch (Exception e) {
                        logger.error("Error while mediating MyDeqMockMediator. " + e.getMessage(), e);
                        return;
                    }
                } else {
                    log.error("Mock data file does not exist.");
                    return;
                }

            } else if (mockDataRaw != null) {
                if (getType().contains(JSON_TYPE)) {
                    Object document = JsonPath.read(mockDataRaw, formatResult.toString());
                    String jsonStr = gson.toJson(document);
                    logger.debug("JsonPath query result from in-line mock data: " + jsonStr);
                    result.append(jsonStr);
                    return;

                } else {

                    // TODO
                }
            } else {
                logger.error("Mock data not found!");
                return;
            }
        }
    }

    public void setFileLocation(String fileLocation) {
        this.fileLocation = fileLocation;
    }

    public String getFileLocation() {
        return fileLocation;
    }

    /**
     * Goes through SynapsePath argument list, evaluating each by calling stringValueOf and returns a HashMap String, String
     * array where each item will contain a hash map with key "evaluated expression" and value "SynapsePath type".
     *
     * @param synCtx
     * @return
     */
    private HashMap<String, String>[] getArgValues(MessageContext synCtx) {
        HashMap<String, String>[] argValues = new HashMap[pathArgumentList.size()];
        HashMap<String, String> valueMap;
        String value = "";
        for (int i = 0; i < pathArgumentList.size(); ++i) { /*ToDo use foreach*/
            Argument arg = pathArgumentList.get(i);
            if (arg.getValue() != null) {
                value = arg.getValue();
                if (!isXML(value)) {
                    value = StringEscapeUtils.escapeXml(value);
                }
                value = Matcher.quoteReplacement(value);
            } else if (arg.getExpression() != null) {
                value = arg.getExpression().stringValueOf(synCtx);
                if (value != null) {
                    // XML escape the result of an expression that produces a literal, if the target format
                    // of the payload is XML.
                    if (!isXML(value) && !arg.getExpression().getPathType().equals(SynapsePath.JSON_PATH)
                            && XML_TYPE.equals(getType())) {
                        value = StringEscapeUtils.escapeXml(value);
                    }
                    value = Matcher.quoteReplacement(value);
                } else {
                    value = "";
                }
            } else {
                handleException("Unexpected arg type detected", synCtx);
            }
            //value = value.replace(String.valueOf((char) 160), " ").trim();
            valueMap = new HashMap<String, String>();
            if (null != arg.getExpression()) {
                valueMap.put(value, arg.getExpression().getPathType());
            } else {
                valueMap.put(value, SynapsePath.X_PATH);
            }
            argValues[i] = valueMap;
        }
        return argValues;
    }

    private boolean checkAndReplaceEnvelop(OMElement resultElement, MessageContext synCtx) {
        OMElement firstChild = resultElement.getFirstElement();
        QName resultQName = firstChild.getQName();
        if (resultQName.getLocalPart().equals("Envelope")
                && (resultQName.getNamespaceURI().equals(SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI)
                        || resultQName.getNamespaceURI().equals(SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI))) {
            SOAPEnvelope soapEnvelope = AXIOMUtils.getSOAPEnvFromOM(resultElement.getFirstElement());
            if (soapEnvelope != null) {
                try {
                    synCtx.setEnvelope(soapEnvelope);
                } catch (AxisFault axisFault) {
                    handleException("Unable to attach SOAPEnvelope", axisFault, synCtx);
                }
            }
        } else {
            return false;
        }
        return true;
    }

    /**
     * Helper function to remove indentations.
     * @param element
     */
    private void removeIndentations(OMElement element) {
        List<OMText> removables = new ArrayList<OMText>();
        removeIndentations(element, removables);
        for (OMText node : removables) {
            node.detach();
        }
    }

    /**
     * Helper function to remove indentations.
     *
     * @param element
     * @param removables
     */
    private void removeIndentations(OMElement element, List<OMText> removables) {
        Iterator children = element.getChildren();
        while (children.hasNext()) {
            Object next = children.next();
            if (next instanceof OMText) {
                OMText text = (OMText) next;
                if (text.getText().trim().equals("")) {
                    removables.add(text);
                }
            } else if (next instanceof OMElement) {
                removeIndentations((OMElement) next, removables);
            }
        }
    }

    /**
     * Replaces the payload format with SynapsePath arguments which are evaluated using getArgValues().
     *
     * @param format
     * @param result
     * @param synCtx
     */
    private void replace(String format, StringBuffer result, MessageContext synCtx) {
        HashMap<String, String>[] argValues = getArgValues(synCtx);
        HashMap<String, String> replacement;
        Map.Entry<String, String> replacementEntry;
        String replacementValue = null;
        Matcher matcher;

        if (mediaType != null && mediaType.equals(JSON_TYPE)) {
            matcher = pattern.matcher(format);
        } else {
            matcher = pattern.matcher("<pfPadding>" + format + "</pfPadding>");
        }
        try {
            while (matcher.find()) {
                String matchSeq = matcher.group();
                int argIndex;
                try {
                    argIndex = Integer.parseInt(matchSeq.substring(1, matchSeq.length()));
                } catch (NumberFormatException e) {
                    argIndex = Integer.parseInt(matchSeq.substring(2, matchSeq.length() - 1));
                }
                replacement = argValues[argIndex - 1];
                replacementEntry = replacement.entrySet().iterator().next();
                if (mediaType.equals(JSON_TYPE) && inferReplacementType(replacementEntry).equals(XML_TYPE)) {
                    // XML to JSON conversion here
                    try {
                        replacementValue = "<jsonObject>" + replacementEntry.getKey() + "</jsonObject>";
                        OMElement omXML = AXIOMUtil.stringToOM(replacementValue);
                        replacementValue = JsonUtil.toJsonString(omXML).toString();
                    } catch (XMLStreamException e) {
                        handleException(
                                "Error parsing XML for JSON conversion, please check your xPath expressions return valid XML: ",
                                synCtx);
                    } catch (AxisFault e) {
                        handleException("Error converting XML to JSON", synCtx);
                    }
                } else if (mediaType.equals(XML_TYPE) && inferReplacementType(replacementEntry).equals(JSON_TYPE)) {
                    // JSON to XML conversion here
                    try {
                        OMElement omXML = JsonUtil.toXml(IOUtils.toInputStream(replacementEntry.getKey()), false);
                        if (JsonUtil.isAJsonPayloadElement(omXML)) { // remove <jsonObject/> from result.
                            Iterator children = omXML.getChildElements();
                            String childrenStr = "";
                            while (children.hasNext()) {
                                childrenStr += (children.next()).toString().trim();
                            }
                            replacementValue = childrenStr;
                        } else { ///~
                            replacementValue = omXML.toString();
                        }
                        //replacementValue = omXML.toString();
                    } catch (AxisFault e) {
                        handleException(
                                "Error converting JSON to XML, please check your JSON Path expressions return valid JSON: ",
                                synCtx);
                    }
                } else {
                    // No conversion required, as path evaluates to regular String.
                    replacementValue = replacementEntry.getKey();
                }
                matcher.appendReplacement(result, replacementValue);
            }
        } catch (ArrayIndexOutOfBoundsException e) {
            log.error("#replace. Mis-match detected between number of formatters and arguments", e);
        }
        matcher.appendTail(result);
    }

    /**
     * Helper function that returns true if value passed is of JSON type.
     *
     * @param value
     * @return
     */
    private boolean isJson(String value) {
        return !(value == null || value.isEmpty())
                && (value.trim().charAt(0) == '{' || value.trim().charAt(0) == '[');
    }

    /**
     * Helper function that takes a Map of String, String where key contains the value of an evaluated SynapsePath
     * expression and value contains the type of SynapsePath in use.
     * <p/>
     * It returns the type of conversion required (XML | JSON | String) based on the actual returned value and the path
     * type.
     *
     * @param entry
     * @return
     */
    private String inferReplacementType(Map.Entry<String, String> entry) {
        if (entry.getValue().equals(SynapsePath.X_PATH) && isXML(entry.getKey())) {
            return XML_TYPE;
        } else if (entry.getValue().equals(SynapsePath.X_PATH) && !isXML(entry.getKey())) {
            return STRING_TYPE;
        } else if (entry.getValue().equals(SynapsePath.JSON_PATH) && isJson(entry.getKey())) {
            return JSON_TYPE;
        } else if (entry.getValue().equals(SynapsePath.JSON_PATH) && !isJson((entry.getKey()))) {
            return STRING_TYPE;
        } else {
            return STRING_TYPE;
        }
    }

    public String getFormat() {
        return formatRaw;
    }

    public void setFormat(String format) {
        this.formatRaw = format;
    }

    public void addPathArgument(Argument arg) {
        pathArgumentList.add(arg);
    }

    public List<Argument> getPathArgumentList() {
        return pathArgumentList;
    }

    /**
     * Helper function that returns true if value passed is of XML Type.
     *
     * @param value
     * @return
     */
    private boolean isXML(String value) {
        try {
            AXIOMUtil.stringToOM(value);
        } catch (XMLStreamException ignore) {
            // means not a xml
            return false;
        } catch (OMException ignore) {
            // means not a xml
            return false;
        }
        return true;
    }

    public String getType() {
        return mediaType;
    }

    public void setType(String type) {
        this.mediaType = type;
    }

    /**
     * To get the key which is used to pick the format definition from the local registry
     *
     * @return return the key which is used to pick the format definition from the local registry
     */
    public Value getFormatKey() {
        return formatKey;
    }

    /**
     * To set the local registry key in order to pick the format definition
     *
     * @param key the local registry key
     */
    public void setFormatKey(Value key) {
        this.formatKey = key;
    }

    public void setFormatDynamic(boolean formatDynamic) {
        this.isFormatDynamic = formatDynamic;
    }

    public boolean isFormatDynamic() {
        return isFormatDynamic;
    }

    private boolean isDoingJson(MessageContext messageContext) {
        return JsonUtil.hasAJsonPayload(((Axis2MessageContext) messageContext).getAxis2MessageContext());
    }

    private boolean isDoingXml(MessageContext messageContext) {
        return !isDoingJson(messageContext);
    }

    public boolean isMockDataDynamic() {
        return isMockDataDynamic;
    }

    public void setMockDataDynamic(boolean isMockDataDynamic) {
        this.isMockDataDynamic = isMockDataDynamic;
    }

    public Value getMockDataKey() {
        return mockDataKey;
    }

    public void setMockDataKey(Value mockDataKey) {
        this.mockDataKey = mockDataKey;
    }

    public String getMockDataRaw() {
        return mockDataRaw;
    }

    public void setMockDataRaw(String mockDataRaw) {
        this.mockDataRaw = mockDataRaw;
    }
}