Skip to content

Commit

Permalink
Merge pull request #2278 from GDLMadushanka/caware
Browse files Browse the repository at this point in the history
Fix content aware logic
  • Loading branch information
GDLMadushanka authored Jan 9, 2025
2 parents 57fb9b8 + 7d04c0f commit cba8eb7
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

package org.apache.synapse.util.xpath;

import org.apache.synapse.util.synapse.expression.constants.ExpressionConstants;
import org.jaxen.JaxenException;

import java.util.regex.Matcher;
Expand All @@ -38,35 +39,78 @@ public class SynapseExpressionUtils {
* @return true if the synapse expression is content aware, false otherwise
*/
public static boolean isSynapseExpressionContentAware(String synapseExpression) {

// TODO : Need to improve the content aware detection logic
if (synapseExpression.equals("payload") || synapseExpression.equals("$")
|| synapseExpression.contains("payload.") || synapseExpression.contains("$.")) {
boolean isContentAware = false;
if (synapseExpression.equals("payload") || synapseExpression.equals("$")) {
return true;
} else if (synapseExpression.contains("xpath(")) {
// TODO change the regex to support xpath + variable syntax
Pattern pattern = Pattern.compile("xpath\\(['\"](.*?)['\"]\\s*(,\\s*['\"](.*?)['\"])?\\)?");
Matcher matcher = pattern.matcher(synapseExpression);
// Find all matches
while (matcher.find()) {
if (matcher.group(2) != null) {
// evaluating xpath on a variable so not content aware
continue;
}
String xpath = matcher.group(1);
try {
SynapseXPath synapseXPath = new SynapseXPath(xpath);
if (synapseXPath.isContentAware()) {
return true;
} else {
if (synapseExpression.contains(ExpressionConstants.PAYLOAD_$)
|| synapseExpression.contains(ExpressionConstants.PAYLOAD)) {
isContentAware = checkForPayloadReference(synapseExpression);
}
if (!isContentAware && synapseExpression.contains("xpath(")) {
// TODO change the regex to support xpath + variable syntax
Pattern pattern = Pattern.compile("xpath\\(['\"](.*?)['\"]\\s*(,\\s*['\"](.*?)['\"])?\\)?");
Matcher matcher = pattern.matcher(synapseExpression);
// Find all matches
while (matcher.find()) {
if (matcher.group(2) != null) {
// evaluating xpath on a variable so not content aware
continue;
}
String xpath = matcher.group(1);
try {
SynapseXPath synapseXPath = new SynapseXPath(xpath);
if (synapseXPath.isContentAware()) {
isContentAware = true;
}
} catch (JaxenException e) {
// Ignore the exception and continue
}
} catch (JaxenException e) {
// Ignore the exception and continue
}
}
return isContentAware;
}
}

private static boolean checkForPayloadReference(String expr) {
if (expr == null || expr.isEmpty()) {
return false;
}
return findKeyword(expr, ExpressionConstants.PAYLOAD)
|| findKeyword(expr, ExpressionConstants.PAYLOAD_$);
}

private static boolean findKeyword(String expr, String keyword) {
int index = expr.indexOf(keyword);
while (index != -1) {
if (isValidCharBeforeKeyword(expr, index)
&& isValidCharAfterKeyword(expr, index, keyword.length())) {
return true;
}
index = expr.indexOf(keyword, index + 1);
}
return false;
}

private static boolean isValidCharBeforeKeyword(String expr, int startIndex) {
if (startIndex > 0) {
char prev = expr.charAt(startIndex - 1);
return !Character.isLetterOrDigit(prev) && prev != '_' && prev != '.'
&& prev != '"' && prev != '\'';
}
return true;
}

private static boolean isValidCharAfterKeyword(String expr, int startIndex, int keywordLen) {
int afterIndex = startIndex + keywordLen;
if (afterIndex < expr.length()) {
char next = expr.charAt(afterIndex);
return !Character.isLetterOrDigit(next) && next != '_'
&& next != '"' && next != '\'';
}
return true;
}

public static boolean isVariableXPathExpression(String synapseExpression) {

Matcher matcher = pattern.matcher(synapseExpression);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright (c) 2025, WSO2 LLC. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 LLC. 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.util.synapse.expression;

import org.apache.synapse.util.xpath.SynapseExpression;
import org.jaxen.JaxenException;
import org.junit.Assert;
import org.junit.Test;

/**
* Test class for content awareness in the Synapse Expressions.
*/
public class ContentAwarenessTest {

@Test
public void testPayloadAccess() throws JaxenException {
SynapseExpression synapsePath = new SynapseExpression("payload");
Assert.assertTrue(synapsePath.isContentAware());
synapsePath = new SynapseExpression("$");
Assert.assertTrue(synapsePath.isContentAware());
synapsePath = new SynapseExpression("payload.name");
Assert.assertTrue(synapsePath.isContentAware());
synapsePath = new SynapseExpression("$.name");
Assert.assertTrue(synapsePath.isContentAware());
}

@Test
public void testComplexUsages() throws JaxenException {
SynapseExpression synapsePath = new SynapseExpression("length(payload)");
Assert.assertTrue(synapsePath.isContentAware());
synapsePath = new SynapseExpression("5 + length(payload)");
Assert.assertTrue(synapsePath.isContentAware());
synapsePath = new SynapseExpression("5 + length(payload[\"payload\"])");
Assert.assertTrue(synapsePath.isContentAware());
synapsePath = new SynapseExpression("$..book[?(@.category==payload.category)]");
Assert.assertTrue(synapsePath.isContentAware());
synapsePath = new SynapseExpression("$..book[?(@.category==\"payload.category\")]");
Assert.assertTrue(synapsePath.isContentAware());
synapsePath = new SynapseExpression("var.books[?(@.category==payload.category)]");
Assert.assertTrue(synapsePath.isContentAware());
synapsePath = new SynapseExpression("var.books[?(@.category==$.category)]");
Assert.assertTrue(synapsePath.isContentAware());
}

@Test
public void testWithXpath() throws JaxenException {
SynapseExpression synapsePath = new SynapseExpression("xpath(\"$ctx:name\")");
Assert.assertFalse(synapsePath.isContentAware());
synapsePath = new SynapseExpression("xpath(\"//student\")");
Assert.assertTrue(synapsePath.isContentAware());
synapsePath = new SynapseExpression("xpath(\"/student\")");
Assert.assertTrue(synapsePath.isContentAware());
synapsePath = new SynapseExpression("xpath(\"//*\") + var.a$bc");
Assert.assertTrue(synapsePath.isContentAware());
synapsePath = new SynapseExpression("xpath(\"$ctx:bla\") + $.age");
Assert.assertTrue(synapsePath.isContentAware());
synapsePath = new SynapseExpression("var.num1 + var.[\"payload\"] + xpath(\"//num3\")");
Assert.assertTrue(synapsePath.isContentAware());
synapsePath = new SynapseExpression("var.num1 + var.[\"payload\"] + xpath(\"$ctx:num3\")");
Assert.assertFalse(synapsePath.isContentAware());
}

@Test
public void testNegativeCases() throws JaxenException {
SynapseExpression synapsePath = new SynapseExpression("length(var.abc)");
Assert.assertFalse(synapsePath.isContentAware());
synapsePath = new SynapseExpression("var[\"payload\"]");
Assert.assertFalse(synapsePath.isContentAware());
synapsePath = new SynapseExpression("5 + var[\"payload\"].age");
Assert.assertFalse(synapsePath.isContentAware());
synapsePath = new SynapseExpression("var.a$.bc");
Assert.assertFalse(synapsePath.isContentAware());
synapsePath = new SynapseExpression("var.books[?(@.category==\"payload.category\")]");
Assert.assertFalse(synapsePath.isContentAware());
}
}

0 comments on commit cba8eb7

Please sign in to comment.