Skip to content

Commit

Permalink
Merge pull request #2236 from SanojPunchihewa/payload
Browse files Browse the repository at this point in the history
Add Inline synapse expression support for Payload Factory mediator
  • Loading branch information
SanojPunchihewa authored Dec 5, 2024
2 parents 30128ac + a53c728 commit eb71f70
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ private Constants() {
public static final String CTX_PROPERTY_INJECTING_NAME = "ctx";
public static final String AXIS2_PROPERTY_INJECTING_NAME = "axis2";
public static final String TRANSPORT_PROPERTY_INJECTING_NAME = "trp";
public static final String VARIABLE_INJECTING_NAME = "var";
public static final String JSON_TYPE = "json";
public static final String XML_TYPE = "xml";
public static final String TEXT_TYPE = "text";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
import static org.apache.synapse.mediators.transform.pfutils.Constants.PAYLOAD_INJECTING_NAME;
import static org.apache.synapse.mediators.transform.pfutils.Constants.TEXT_PAYLOAD_TYPE;
import static org.apache.synapse.mediators.transform.pfutils.Constants.TRANSPORT_PROPERTY_INJECTING_NAME;
import static org.apache.synapse.mediators.transform.pfutils.Constants.VARIABLE_INJECTING_NAME;
import static org.apache.synapse.mediators.transform.pfutils.Constants.XML_PAYLOAD_TYPE;
import static org.apache.synapse.util.PayloadHelper.TEXTELT;
import static org.apache.synapse.util.PayloadHelper.getXMLPayload;
Expand All @@ -88,6 +89,7 @@ public class FreeMarkerTemplateProcessor extends TemplateProcessor {
private boolean usingPropertyAxis2;
private boolean usingPropertyTransport;
private boolean usingArgs;
private boolean usingVariables;

private static final Log log = LogFactory.getLog(FreeMarkerTemplateProcessor.class);
private boolean templateLoaded = false;
Expand Down Expand Up @@ -218,6 +220,7 @@ private void findRequiredInjections(String templateString) {
usingPropertyCtx = templateString.contains(CTX_PROPERTY_INJECTING_NAME);
usingPropertyAxis2 = templateString.contains(AXIS2_PROPERTY_INJECTING_NAME);
usingPropertyTransport = templateString.contains(TRANSPORT_PROPERTY_INJECTING_NAME);
usingVariables = templateString.contains(VARIABLE_INJECTING_NAME);
}

/**
Expand Down Expand Up @@ -400,6 +403,7 @@ private void injectProperties(MessageContext synCtx, Map<String, Object> data) {
injectCtxProperties(synCtx, data);
injectAxis2Properties(synCtx, data);
injectTransportProperties(synCtx, data);
injectVariables(synCtx, data);
}

private void injectCtxProperties(MessageContext synCtx, Map<String, Object> data) {
Expand All @@ -420,6 +424,21 @@ private void injectCtxProperties(MessageContext synCtx, Map<String, Object> data
}
}

private void injectVariables(MessageContext synCtx, Map<String, Object> data) {

if (usingVariables) {
Map<String, String> variables = new HashMap<>();
for (Object o : synCtx.getVariableKeySet()) {
String varName = (String) o;
Object variable = synCtx.getVariable(varName);
if (variable != null) {
variables.put(varName, variable.toString());
}
}
data.put(VARIABLE_INJECTING_NAME, variables);
}
}

private void injectAxis2Properties(MessageContext synCtx, Map<String, Object> data) {

if (usingPropertyAxis2) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,40 @@

package org.apache.synapse.mediators.transform.pfutils;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMException;
import org.apache.axis2.AxisFault;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.synapse.MessageContext;
import org.apache.synapse.SynapseException;
import org.apache.synapse.commons.json.JsonUtil;
import org.apache.synapse.mediators.transform.ArgumentDetails;
import org.apache.synapse.util.xpath.SynapseExpression;
import org.jaxen.JaxenException;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.stream.XMLStreamException;

/**
* TemplateProcessor implementation for Regex based templates
*/
public class RegexTemplateProcessor extends TemplateProcessor {

private static final Log log = LogFactory.getLog(RegexTemplateProcessor.class);
private final Pattern pattern = Pattern.compile("\\$(\\d)+");
// Pattern matches "${...}" (quoted), ${...} (unquoted), and $n
private final Pattern pattern = Pattern.compile("\"\\$\\{(.+?)\\}\"|\\$\\{(.+?)\\}|\\$(\\d+)");

private final Gson gson = new Gson();

@Override
public String processTemplate(String template, String mediaType, MessageContext synCtx) {
Expand Down Expand Up @@ -72,25 +89,157 @@ private void replace(String format, StringBuffer result, String mediaType, Messa
}
try {
while (matcher.find()) {
String matchSeq = matcher.group();
replacement = getReplacementValue(argValues, matchSeq);
replacementEntry = replacement.entrySet().iterator().next();
replacementValue = prepareReplacementValue(mediaType, synCtx, replacementEntry);
matcher.appendReplacement(result, replacementValue);
if (matcher.group(1) != null) {
// Handle "${...}" pattern (with quotes)
String expression = matcher.group(1);
Object expressionResult = evaluateExpression(expression, synCtx);
if (expressionResult instanceof JsonPrimitive) {
replacementValue = prepareJSONPrimitiveReplacementValue(expressionResult, mediaType);
} else if (expressionResult instanceof JsonElement) {
// Escape JSON object and Arrays since we need to consider it as
replacementValue = escapeJson(Matcher.quoteReplacement(gson.toJson(expressionResult)));
if (XML_TYPE.equals(mediaType)) {
replacementValue = convertJsonToXML(replacementValue);
}
} else {
replacementValue = expressionResult.toString();
if (XML_TYPE.equals(mediaType)) {
replacementValue = StringEscapeUtils.escapeXml10(replacementValue);
} else if (JSON_TYPE.equals(mediaType)) {
if (isXML(replacementValue)) {
// consider the replacement value as a literal XML
replacementValue = Matcher.quoteReplacement(replacementValue);
} else {
replacementValue = escapeSpecialCharactersOfJson(replacementValue);
}
}
}
matcher.appendReplacement(result, "\"" + replacementValue + "\"");
} else if (matcher.group(2) != null) {
// Handle ${...} pattern (without quotes)
String expression = matcher.group(2);
Object expressionResult = evaluateExpression(expression, synCtx);
replacementValue = expressionResult.toString();
if (expressionResult instanceof JsonPrimitive) {
replacementValue = prepareJSONPrimitiveReplacementValue(expressionResult, mediaType);
} else if (expressionResult instanceof JsonElement) {
if (XML_TYPE.equals(mediaType)) {
replacementValue = convertJsonToXML(replacementValue);
replacementValue = Matcher.quoteReplacement(replacementValue);
} else {
replacementValue = Matcher.quoteReplacement(gson.toJson(expressionResult));
}
} else {
if (JSON_TYPE.equals(mediaType) && isXML(replacementValue)) {
replacementValue = convertXMLToJSON(replacementValue);
} else {
if (XML_TYPE.equals(mediaType) && !isXML(replacementValue)) {
replacementValue = StringEscapeUtils.escapeXml10(replacementValue);
}
replacementValue = Matcher.quoteReplacement(replacementValue);
}
}
matcher.appendReplacement(result, replacementValue);
} else if (matcher.group(3) != null) {
// Handle $n pattern
String matchSeq = matcher.group(3);
replacement = getReplacementValue(argValues, matchSeq);
replacementEntry = replacement.entrySet().iterator().next();
replacementValue = prepareReplacementValue(mediaType, synCtx, replacementEntry);
matcher.appendReplacement(result, replacementValue);
}
}
} catch (ArrayIndexOutOfBoundsException e) {
log.error("#replace. Mis-match detected between number of formatters and arguments", e);
} catch (JaxenException e) {
throw new SynapseException("Error evaluating expression" , e);
}
matcher.appendTail(result);
}

private String prepareJSONPrimitiveReplacementValue(Object expressionResult, String mediaType) {

String replacementValue = ((JsonPrimitive) expressionResult).getAsString();
replacementValue = escapeSpecialChars(Matcher.quoteReplacement(replacementValue));
if (XML_TYPE.equals(mediaType)) {
replacementValue = StringEscapeUtils.escapeXml10(replacementValue);
}
return replacementValue;
}

/**
* Evaluates the expression and returns the result as a string or an object.
* If the expression contains "xpath(", we meed to evaluate it as a string.
*
* @param expression expression to evaluate
* @param synCtx message context
* @return evaluated result
* @throws JaxenException if an error occurs while evaluating the expression
*/
private Object evaluateExpression(String expression, MessageContext synCtx) throws JaxenException {

if (expression.contains("xpath(")) {
return new SynapseExpression(expression).stringValueOf(synCtx);
}
return new SynapseExpression(expression).objectValueOf(synCtx);
}

private String escapeJson(String value) {
// Manual escape for JSON: escaping double quotes and backslashes
return value.replace("\"", "\\\"").replace("\\", "\\\\");
}

private String convertJsonToXML(String replacementValue) {

try {
// replacementValue = Matcher.quoteReplacement(replacementValue);
OMElement omXML = JsonUtil.toXml(IOUtils.toInputStream(replacementValue), 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();
}
} catch (AxisFault e) {
handleException(
"Error converting JSON to XML, please check your expressions return valid JSON: ");
}
return escapeSpecialCharactersOfXml(replacementValue);
}

private String convertXMLToJSON(String replacementValue) {

try {
replacementValue = "<jsonObject>" + replacementValue + "</jsonObject>";
OMElement omXML = convertStringToOM(replacementValue);
replacementValue = JsonUtil.toJsonString(omXML).toString();
replacementValue = escapeSpecialCharactersOfJson(replacementValue);
} catch (XMLStreamException e) {
handleException(
"Error parsing XML for JSON conversion, please check your expressions return valid XML: ");
} catch (AxisFault e) {
handleException("Error converting XML to JSON");
} catch (OMException e) {
// if the logic comes to this means, it was tried as a XML, which means it has
// "<" as starting element and ">" as end element, so basically if the logic comes here, that means
// value is a string value, that means No conversion required, as path evaluates to regular String.
replacementValue = escapeSpecialChars(replacementValue);

}
return replacementValue;
}

private HashMap<String, ArgumentDetails> getReplacementValue(HashMap<String, ArgumentDetails>[] argValues,
String matchSeq) {

HashMap<String, ArgumentDetails> replacement;
int argIndex;
try {
argIndex = Integer.parseInt(matchSeq.substring(1));
argIndex = Integer.parseInt(matchSeq);
} catch (NumberFormatException e) {
argIndex = Integer.parseInt(matchSeq.substring(2, matchSeq.length() - 1));
}
Expand Down

0 comments on commit eb71f70

Please sign in to comment.