From 7d04c0f1ca91b30011192025e981e8785f9750eb Mon Sep 17 00:00:00 2001 From: GDLMadushanka Date: Thu, 9 Jan 2025 10:03:05 +0530 Subject: [PATCH] Fix content aware logic Fix content aware logic --- .../util/xpath/SynapseExpressionUtils.java | 86 +++++++++++++----- .../expression/ContentAwarenessTest.java | 91 +++++++++++++++++++ 2 files changed, 156 insertions(+), 21 deletions(-) create mode 100644 modules/core/src/test/java/org/apache/synapse/util/synapse/expression/ContentAwarenessTest.java diff --git a/modules/core/src/main/java/org/apache/synapse/util/xpath/SynapseExpressionUtils.java b/modules/core/src/main/java/org/apache/synapse/util/xpath/SynapseExpressionUtils.java index 0ac4c212bf..d6712d3a08 100644 --- a/modules/core/src/main/java/org/apache/synapse/util/xpath/SynapseExpressionUtils.java +++ b/modules/core/src/main/java/org/apache/synapse/util/xpath/SynapseExpressionUtils.java @@ -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; @@ -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); diff --git a/modules/core/src/test/java/org/apache/synapse/util/synapse/expression/ContentAwarenessTest.java b/modules/core/src/test/java/org/apache/synapse/util/synapse/expression/ContentAwarenessTest.java new file mode 100644 index 0000000000..0138fb1722 --- /dev/null +++ b/modules/core/src/test/java/org/apache/synapse/util/synapse/expression/ContentAwarenessTest.java @@ -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()); + } +}