Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix content aware logic #2278

Merged
merged 1 commit into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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());
}
}
Loading