Skip to content

Commit eb71f70

Browse files
Merge pull request #2236 from SanojPunchihewa/payload
Add Inline synapse expression support for Payload Factory mediator
2 parents 30128ac + a53c728 commit eb71f70

File tree

3 files changed

+176
-7
lines changed

3 files changed

+176
-7
lines changed

modules/core/src/main/java/org/apache/synapse/mediators/transform/pfutils/Constants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ private Constants() {
4040
public static final String CTX_PROPERTY_INJECTING_NAME = "ctx";
4141
public static final String AXIS2_PROPERTY_INJECTING_NAME = "axis2";
4242
public static final String TRANSPORT_PROPERTY_INJECTING_NAME = "trp";
43+
public static final String VARIABLE_INJECTING_NAME = "var";
4344
public static final String JSON_TYPE = "json";
4445
public static final String XML_TYPE = "xml";
4546
public static final String TEXT_TYPE = "text";

modules/core/src/main/java/org/apache/synapse/mediators/transform/pfutils/FreeMarkerTemplateProcessor.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
import static org.apache.synapse.mediators.transform.pfutils.Constants.PAYLOAD_INJECTING_NAME;
7171
import static org.apache.synapse.mediators.transform.pfutils.Constants.TEXT_PAYLOAD_TYPE;
7272
import static org.apache.synapse.mediators.transform.pfutils.Constants.TRANSPORT_PROPERTY_INJECTING_NAME;
73+
import static org.apache.synapse.mediators.transform.pfutils.Constants.VARIABLE_INJECTING_NAME;
7374
import static org.apache.synapse.mediators.transform.pfutils.Constants.XML_PAYLOAD_TYPE;
7475
import static org.apache.synapse.util.PayloadHelper.TEXTELT;
7576
import static org.apache.synapse.util.PayloadHelper.getXMLPayload;
@@ -88,6 +89,7 @@ public class FreeMarkerTemplateProcessor extends TemplateProcessor {
8889
private boolean usingPropertyAxis2;
8990
private boolean usingPropertyTransport;
9091
private boolean usingArgs;
92+
private boolean usingVariables;
9193

9294
private static final Log log = LogFactory.getLog(FreeMarkerTemplateProcessor.class);
9395
private boolean templateLoaded = false;
@@ -218,6 +220,7 @@ private void findRequiredInjections(String templateString) {
218220
usingPropertyCtx = templateString.contains(CTX_PROPERTY_INJECTING_NAME);
219221
usingPropertyAxis2 = templateString.contains(AXIS2_PROPERTY_INJECTING_NAME);
220222
usingPropertyTransport = templateString.contains(TRANSPORT_PROPERTY_INJECTING_NAME);
223+
usingVariables = templateString.contains(VARIABLE_INJECTING_NAME);
221224
}
222225

223226
/**
@@ -400,6 +403,7 @@ private void injectProperties(MessageContext synCtx, Map<String, Object> data) {
400403
injectCtxProperties(synCtx, data);
401404
injectAxis2Properties(synCtx, data);
402405
injectTransportProperties(synCtx, data);
406+
injectVariables(synCtx, data);
403407
}
404408

405409
private void injectCtxProperties(MessageContext synCtx, Map<String, Object> data) {
@@ -420,6 +424,21 @@ private void injectCtxProperties(MessageContext synCtx, Map<String, Object> data
420424
}
421425
}
422426

427+
private void injectVariables(MessageContext synCtx, Map<String, Object> data) {
428+
429+
if (usingVariables) {
430+
Map<String, String> variables = new HashMap<>();
431+
for (Object o : synCtx.getVariableKeySet()) {
432+
String varName = (String) o;
433+
Object variable = synCtx.getVariable(varName);
434+
if (variable != null) {
435+
variables.put(varName, variable.toString());
436+
}
437+
}
438+
data.put(VARIABLE_INJECTING_NAME, variables);
439+
}
440+
}
441+
423442
private void injectAxis2Properties(MessageContext synCtx, Map<String, Object> data) {
424443

425444
if (usingPropertyAxis2) {

modules/core/src/main/java/org/apache/synapse/mediators/transform/pfutils/RegexTemplateProcessor.java

Lines changed: 156 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,40 @@
1919

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

22+
import com.google.gson.Gson;
23+
import com.google.gson.JsonElement;
24+
import com.google.gson.JsonPrimitive;
25+
import org.apache.axiom.om.OMElement;
26+
import org.apache.axiom.om.OMException;
27+
import org.apache.axis2.AxisFault;
28+
import org.apache.commons.io.IOUtils;
2229
import org.apache.commons.logging.Log;
2330
import org.apache.commons.logging.LogFactory;
31+
import org.apache.commons.text.StringEscapeUtils;
2432
import org.apache.synapse.MessageContext;
33+
import org.apache.synapse.SynapseException;
34+
import org.apache.synapse.commons.json.JsonUtil;
2535
import org.apache.synapse.mediators.transform.ArgumentDetails;
36+
import org.apache.synapse.util.xpath.SynapseExpression;
37+
import org.jaxen.JaxenException;
2638

2739
import java.util.HashMap;
40+
import java.util.Iterator;
2841
import java.util.Map;
2942
import java.util.regex.Matcher;
3043
import java.util.regex.Pattern;
44+
import javax.xml.stream.XMLStreamException;
3145

3246
/**
3347
* TemplateProcessor implementation for Regex based templates
3448
*/
3549
public class RegexTemplateProcessor extends TemplateProcessor {
3650

3751
private static final Log log = LogFactory.getLog(RegexTemplateProcessor.class);
38-
private final Pattern pattern = Pattern.compile("\\$(\\d)+");
52+
// Pattern matches "${...}" (quoted), ${...} (unquoted), and $n
53+
private final Pattern pattern = Pattern.compile("\"\\$\\{(.+?)\\}\"|\\$\\{(.+?)\\}|\\$(\\d+)");
54+
55+
private final Gson gson = new Gson();
3956

4057
@Override
4158
public String processTemplate(String template, String mediaType, MessageContext synCtx) {
@@ -72,25 +89,157 @@ private void replace(String format, StringBuffer result, String mediaType, Messa
7289
}
7390
try {
7491
while (matcher.find()) {
75-
String matchSeq = matcher.group();
76-
replacement = getReplacementValue(argValues, matchSeq);
77-
replacementEntry = replacement.entrySet().iterator().next();
78-
replacementValue = prepareReplacementValue(mediaType, synCtx, replacementEntry);
79-
matcher.appendReplacement(result, replacementValue);
92+
if (matcher.group(1) != null) {
93+
// Handle "${...}" pattern (with quotes)
94+
String expression = matcher.group(1);
95+
Object expressionResult = evaluateExpression(expression, synCtx);
96+
if (expressionResult instanceof JsonPrimitive) {
97+
replacementValue = prepareJSONPrimitiveReplacementValue(expressionResult, mediaType);
98+
} else if (expressionResult instanceof JsonElement) {
99+
// Escape JSON object and Arrays since we need to consider it as
100+
replacementValue = escapeJson(Matcher.quoteReplacement(gson.toJson(expressionResult)));
101+
if (XML_TYPE.equals(mediaType)) {
102+
replacementValue = convertJsonToXML(replacementValue);
103+
}
104+
} else {
105+
replacementValue = expressionResult.toString();
106+
if (XML_TYPE.equals(mediaType)) {
107+
replacementValue = StringEscapeUtils.escapeXml10(replacementValue);
108+
} else if (JSON_TYPE.equals(mediaType)) {
109+
if (isXML(replacementValue)) {
110+
// consider the replacement value as a literal XML
111+
replacementValue = Matcher.quoteReplacement(replacementValue);
112+
} else {
113+
replacementValue = escapeSpecialCharactersOfJson(replacementValue);
114+
}
115+
}
116+
}
117+
matcher.appendReplacement(result, "\"" + replacementValue + "\"");
118+
} else if (matcher.group(2) != null) {
119+
// Handle ${...} pattern (without quotes)
120+
String expression = matcher.group(2);
121+
Object expressionResult = evaluateExpression(expression, synCtx);
122+
replacementValue = expressionResult.toString();
123+
if (expressionResult instanceof JsonPrimitive) {
124+
replacementValue = prepareJSONPrimitiveReplacementValue(expressionResult, mediaType);
125+
} else if (expressionResult instanceof JsonElement) {
126+
if (XML_TYPE.equals(mediaType)) {
127+
replacementValue = convertJsonToXML(replacementValue);
128+
replacementValue = Matcher.quoteReplacement(replacementValue);
129+
} else {
130+
replacementValue = Matcher.quoteReplacement(gson.toJson(expressionResult));
131+
}
132+
} else {
133+
if (JSON_TYPE.equals(mediaType) && isXML(replacementValue)) {
134+
replacementValue = convertXMLToJSON(replacementValue);
135+
} else {
136+
if (XML_TYPE.equals(mediaType) && !isXML(replacementValue)) {
137+
replacementValue = StringEscapeUtils.escapeXml10(replacementValue);
138+
}
139+
replacementValue = Matcher.quoteReplacement(replacementValue);
140+
}
141+
}
142+
matcher.appendReplacement(result, replacementValue);
143+
} else if (matcher.group(3) != null) {
144+
// Handle $n pattern
145+
String matchSeq = matcher.group(3);
146+
replacement = getReplacementValue(argValues, matchSeq);
147+
replacementEntry = replacement.entrySet().iterator().next();
148+
replacementValue = prepareReplacementValue(mediaType, synCtx, replacementEntry);
149+
matcher.appendReplacement(result, replacementValue);
150+
}
80151
}
81152
} catch (ArrayIndexOutOfBoundsException e) {
82153
log.error("#replace. Mis-match detected between number of formatters and arguments", e);
154+
} catch (JaxenException e) {
155+
throw new SynapseException("Error evaluating expression" , e);
83156
}
84157
matcher.appendTail(result);
85158
}
86159

160+
private String prepareJSONPrimitiveReplacementValue(Object expressionResult, String mediaType) {
161+
162+
String replacementValue = ((JsonPrimitive) expressionResult).getAsString();
163+
replacementValue = escapeSpecialChars(Matcher.quoteReplacement(replacementValue));
164+
if (XML_TYPE.equals(mediaType)) {
165+
replacementValue = StringEscapeUtils.escapeXml10(replacementValue);
166+
}
167+
return replacementValue;
168+
}
169+
170+
/**
171+
* Evaluates the expression and returns the result as a string or an object.
172+
* If the expression contains "xpath(", we meed to evaluate it as a string.
173+
*
174+
* @param expression expression to evaluate
175+
* @param synCtx message context
176+
* @return evaluated result
177+
* @throws JaxenException if an error occurs while evaluating the expression
178+
*/
179+
private Object evaluateExpression(String expression, MessageContext synCtx) throws JaxenException {
180+
181+
if (expression.contains("xpath(")) {
182+
return new SynapseExpression(expression).stringValueOf(synCtx);
183+
}
184+
return new SynapseExpression(expression).objectValueOf(synCtx);
185+
}
186+
187+
private String escapeJson(String value) {
188+
// Manual escape for JSON: escaping double quotes and backslashes
189+
return value.replace("\"", "\\\"").replace("\\", "\\\\");
190+
}
191+
192+
private String convertJsonToXML(String replacementValue) {
193+
194+
try {
195+
// replacementValue = Matcher.quoteReplacement(replacementValue);
196+
OMElement omXML = JsonUtil.toXml(IOUtils.toInputStream(replacementValue), false);
197+
if (JsonUtil.isAJsonPayloadElement(omXML)) { // remove <jsonObject/> from result.
198+
Iterator children = omXML.getChildElements();
199+
String childrenStr = "";
200+
while (children.hasNext()) {
201+
childrenStr += (children.next()).toString().trim();
202+
}
203+
replacementValue = childrenStr;
204+
} else {
205+
replacementValue = omXML.toString();
206+
}
207+
} catch (AxisFault e) {
208+
handleException(
209+
"Error converting JSON to XML, please check your expressions return valid JSON: ");
210+
}
211+
return escapeSpecialCharactersOfXml(replacementValue);
212+
}
213+
214+
private String convertXMLToJSON(String replacementValue) {
215+
216+
try {
217+
replacementValue = "<jsonObject>" + replacementValue + "</jsonObject>";
218+
OMElement omXML = convertStringToOM(replacementValue);
219+
replacementValue = JsonUtil.toJsonString(omXML).toString();
220+
replacementValue = escapeSpecialCharactersOfJson(replacementValue);
221+
} catch (XMLStreamException e) {
222+
handleException(
223+
"Error parsing XML for JSON conversion, please check your expressions return valid XML: ");
224+
} catch (AxisFault e) {
225+
handleException("Error converting XML to JSON");
226+
} catch (OMException e) {
227+
// if the logic comes to this means, it was tried as a XML, which means it has
228+
// "<" as starting element and ">" as end element, so basically if the logic comes here, that means
229+
// value is a string value, that means No conversion required, as path evaluates to regular String.
230+
replacementValue = escapeSpecialChars(replacementValue);
231+
232+
}
233+
return replacementValue;
234+
}
235+
87236
private HashMap<String, ArgumentDetails> getReplacementValue(HashMap<String, ArgumentDetails>[] argValues,
88237
String matchSeq) {
89238

90239
HashMap<String, ArgumentDetails> replacement;
91240
int argIndex;
92241
try {
93-
argIndex = Integer.parseInt(matchSeq.substring(1));
242+
argIndex = Integer.parseInt(matchSeq);
94243
} catch (NumberFormatException e) {
95244
argIndex = Integer.parseInt(matchSeq.substring(2, matchSeq.length() - 1));
96245
}

0 commit comments

Comments
 (0)