You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2023/12/30 15:15:03 UTC
(camel) 06/25: CAMEL-19749: Add variables as concept to Camel
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch var
in repository https://gitbox.apache.org/repos/asf/camel.git
commit 77bea9c115b33bc7326b240217af919c2d0265e1
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Dec 28 13:43:07 2023 +0100
CAMEL-19749: Add variables as concept to Camel
---
.../modules/languages/pages/simple-language.adoc | 15 ++++-
.../language/simple/SimpleExpressionBuilder.java | 17 +++++
.../simple/ast/SimpleFunctionExpression.java | 71 ++++++++++++++++++++
.../apache/camel/language/simple/SimpleTest.java | 75 +++++++++++++++++++++-
.../org/apache/camel/support/LanguageHelper.java | 15 +++++
.../camel/support/builder/ExpressionBuilder.java | 2 +-
6 files changed, 192 insertions(+), 3 deletions(-)
diff --git a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc
index 4789404a6cb..9a663faf367 100644
--- a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc
+++ b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc
@@ -127,6 +127,18 @@ classname
|headers |Map |refer to the headers
+|variable.foo |Object |refer to the foo variable
+
+|variable[foo] |Object |refer to the foo variable
+
+|variable.foo.*OGNL* |Object |refer to the foo variable and invoke its
+value using a Camel OGNL expression.
+
+|variableAs(_key_,_type_) |Type |converts the variable to the given type determined by its
+classname
+
+|variables |Map |refer to the variables
+
|exchangeProperty.foo |Object |refer to the foo property on the exchange
|exchangeProperty[foo] |Object |refer to the foo property on the exchange
@@ -165,7 +177,8 @@ exceptions (`Exchange.EXCEPTION_CAUGHT`) if the Exchange has any.
|date:_command_ |Date |evaluates to a Date object.
Supported commands are: *now* for current timestamp,
*exchangeCreated* for the timestamp when the current exchange was created,
-*header.xxx* to use the Long/Date object header with the key xxx.
+*header.xxx* to use the Long/Date object in the header with the key xxx.
+*variable.xxx* to use the Long/Date in the variable with the key xxx.
*exchangeProperty.xxx* to use the Long/Date object in the exchange property with the key xxx.
*file* for the last modified timestamp of the file (available with a File consumer).
Command accepts offsets such as: *now-24h* or *header.xxx+1h* or even *now+1h30m-100*.
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
index 0be12c475a1..ea80baf72a8 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
@@ -76,6 +76,21 @@ public final class SimpleExpressionBuilder {
});
}
+ /**
+ * Returns the expression for the variable invoking methods defined in a simple OGNL
+ * notation
+ *
+ * @param ognl methods to invoke on the variable in a simple OGNL syntax
+ */
+ public static Expression variablesOgnlExpression(final String ognl) {
+ return new KeyedOgnlExpressionAdapter(
+ ognl, "variableOgnl(" + ognl + ")",
+ (exchange, exp) -> {
+ String text = exp.evaluate(exchange, String.class);
+ return exchange.getVariable(text);
+ });
+ }
+
/**
* Returns the message history (including exchange details or not)
*/
@@ -596,6 +611,8 @@ public final class SimpleExpressionBuilder {
date = LanguageHelper.dateFromExchangeCreated(exchange);
} else if (command.startsWith("header.")) {
date = LanguageHelper.dateFromHeader(exchange, command, (e, o) -> tryConvertingAsDate(e, o, command));
+ } else if (command.startsWith("variable.")) {
+ date = LanguageHelper.dateFromVariable(exchange, command, (e, o) -> tryConvertingAsDate(e, o, command));
} else if (command.startsWith("exchangeProperty.")) {
date = LanguageHelper.dateFromExchangeProperty(exchange, command, (e, o) -> tryConvertingAsDate(e, o, command));
} else if ("file".equals(command)) {
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
index ddc8fd13425..fb04db0eefa 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
@@ -88,6 +88,11 @@ public class SimpleFunctionExpression extends LiteralExpression {
if (answer != null) {
return answer;
}
+ // variables
+ answer = createSimpleExpressionVariables(function, strict);
+ if (answer != null) {
+ return answer;
+ }
// custom languages
answer = createSimpleCustomLanguage(function, strict);
if (answer != null) {
@@ -451,6 +456,63 @@ public class SimpleFunctionExpression extends LiteralExpression {
return null;
}
+ private Expression createSimpleExpressionVariables(String function, boolean strict) {
+ // variableAs
+ String remainder = ifStartsWithReturnRemainder("variableAs(", function);
+ if (remainder != null) {
+ String keyAndType = StringHelper.before(remainder, ")");
+ if (keyAndType == null) {
+ throw new SimpleParserException("Valid syntax: ${variableAs(key, type)} was: " + function, token.getIndex());
+ }
+
+ String key = StringHelper.before(keyAndType, ",");
+ String type = StringHelper.after(keyAndType, ",");
+ remainder = StringHelper.after(remainder, ")");
+ if (ObjectHelper.isEmpty(key) || ObjectHelper.isEmpty(type) || ObjectHelper.isNotEmpty(remainder)) {
+ throw new SimpleParserException("Valid syntax: ${variableAs(key, type)} was: " + function, token.getIndex());
+ }
+ key = StringHelper.removeQuotes(key);
+ type = StringHelper.removeQuotes(type);
+ return ExpressionBuilder.variableExpression(key, type);
+ }
+
+ // variables function
+ if ("variables".equals(function)) {
+ return ExpressionBuilder.variablesExpression();
+ }
+
+ // variable function
+ remainder = parseVariable(function);
+ if (remainder != null) {
+ // remove leading character (dot, colon or ?)
+ if (remainder.startsWith(".") || remainder.startsWith(":") || remainder.startsWith("?")) {
+ remainder = remainder.substring(1);
+ }
+ // remove starting and ending brackets
+ if (remainder.startsWith("[") && remainder.endsWith("]")) {
+ remainder = remainder.substring(1, remainder.length() - 1);
+ }
+ // remove quotes from key
+ String key = StringHelper.removeLeadingAndEndingQuotes(remainder);
+
+ // validate syntax
+ boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(key);
+ if (invalid) {
+ throw new SimpleParserException("Valid syntax: ${variable.name[key]} was: " + function, token.getIndex());
+ }
+
+ if (OgnlHelper.isValidOgnlExpression(key)) {
+ // ognl based variable
+ return SimpleExpressionBuilder.variablesOgnlExpression(key);
+ } else {
+ // regular variable
+ return ExpressionBuilder.variableExpression(key);
+ }
+ }
+
+ return null;
+ }
+
private Expression createSimpleCustomLanguage(String function, boolean strict) {
// jq
String remainder = ifStartsWithReturnRemainder("jq(", function);
@@ -1271,6 +1333,15 @@ public class SimpleFunctionExpression extends LiteralExpression {
return remainder;
}
+ private String parseVariable(String function) {
+ String remainder;
+ remainder = ifStartsWithReturnRemainder("variables", function);
+ if (remainder == null) {
+ remainder = ifStartsWithReturnRemainder("variable", function);
+ }
+ return remainder;
+ }
+
private String createCodeExchangeProperty(final String function) {
// exchangePropertyAsIndex
String remainder = ifStartsWithReturnRemainder("exchangePropertyAsIndex(", function);
diff --git a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java
index f6d5e1a8586..d0f672df846 100644
--- a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java
@@ -62,7 +62,6 @@ import static org.junit.jupiter.api.Assertions.fail;
public class SimpleTest extends LanguageTestSupport {
- private static final String JAVA8_INDEX_OUT_OF_BOUNDS_ERROR_MSG = "Index: 2, Size: 2";
private static final String INDEX_OUT_OF_BOUNDS_ERROR_MSG = "Index 2 out of bounds for length 2";
@Override
@@ -941,6 +940,80 @@ public class SimpleTest extends LanguageTestSupport {
}
}
+ @Test
+ public void testVariables() throws Exception {
+ exchange.getVariables().putAll(exchange.getMessage().getHeaders());
+ exchange.getMessage().removeHeaders("*");
+
+ Map<String, Object> variables = exchange.getVariables();
+ assertEquals(3, variables.size());
+
+ assertExpression("${variables}", variables);
+ }
+
+ @Test
+ public void testVariableKeyWithSpace() throws Exception {
+ exchange.getVariables().putAll(exchange.getMessage().getHeaders());
+ exchange.getMessage().removeHeaders("*");
+
+ Map<String, Object> variables = exchange.getVariables();
+ variables.put("some key", "Some Value");
+ assertEquals(4, variables.size());
+
+ assertExpression("${variableAs(foo,String)}", "abc");
+ assertExpression("${variableAs(some key,String)}", "Some Value");
+ assertExpression("${variableAs('some key',String)}", "Some Value");
+
+ assertExpression("${variable[foo]}", "abc");
+ assertExpression("${variable[cheese]}", "gauda");
+ assertExpression("${variable[some key]}", "Some Value");
+ assertExpression("${variable['some key']}", "Some Value");
+
+ assertExpression("${variables[foo]}", "abc");
+ assertExpression("${variables[cheese]}", "gauda");
+ assertExpression("${variables[some key]}", "Some Value");
+ assertExpression("${variables['some key']}", "Some Value");
+ }
+
+ @Test
+ public void testVariableAs() throws Exception {
+ exchange.getVariables().putAll(exchange.getMessage().getHeaders());
+ exchange.getMessage().removeHeaders("*");
+
+ assertExpression("${variableAs(foo,String)}", "abc");
+
+ assertExpression("${variableAs(bar,int)}", 123);
+ assertExpression("${variableAs(bar, int)}", 123);
+ assertExpression("${variableAs('bar', int)}", 123);
+ assertExpression("${variableAs('bar','int')}", 123);
+ assertExpression("${variableAs('bar','Integer')}", 123);
+ assertExpression("${variableAs('bar',\"int\")}", 123);
+ assertExpression("${variableAs(bar,String)}", "123");
+
+ assertExpression("${variableAs(unknown,String)}", null);
+
+ try {
+ assertExpression("${variableAs(unknown String)}", null);
+ fail("Should have thrown an exception");
+ } catch (ExpressionIllegalSyntaxException e) {
+ assertTrue(e.getMessage().startsWith("Valid syntax: ${variableAs(key, type)} was: variableAs(unknown String)"));
+ }
+
+ try {
+ assertExpression("${variableAs(fool,String).test}", null);
+ fail("Should have thrown an exception");
+ } catch (ExpressionIllegalSyntaxException e) {
+ assertTrue(e.getMessage().startsWith("Valid syntax: ${variableAs(key, type)} was: variableAs(fool,String).test"));
+ }
+
+ try {
+ assertExpression("${variableAs(bar,XXX)}", 123);
+ fail("Should have thrown an exception");
+ } catch (CamelExecutionException e) {
+ assertIsInstanceOf(ClassNotFoundException.class, e.getCause());
+ }
+ }
+
@Test
public void testIllegalSyntax() throws Exception {
try {
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/LanguageHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/LanguageHelper.java
index fdbb92ce501..7725136bb95 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/LanguageHelper.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/LanguageHelper.java
@@ -256,6 +256,21 @@ public final class LanguageHelper {
return null;
}
+ public static Date dateFromVariable(Exchange exchange, String command, BiFunction<Exchange, Object, Date> orElseFunction) {
+ final String key = command.substring(command.lastIndexOf('.') + 1);
+ final Object obj = exchange.getVariable(key);
+ if (obj instanceof Date) {
+ return (Date) obj;
+ } else if (obj instanceof Long) {
+ return new Date((Long) obj);
+ } else {
+ if (orElseFunction != null) {
+ return orElseFunction.apply(exchange, obj);
+ }
+ }
+ return null;
+ }
+
/**
* Extracts the creation date from an exchange
*
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java b/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java
index 2b00ee42e3b..b476a8c0e96 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java
@@ -232,7 +232,7 @@ public class ExpressionBuilder {
* @param typeName the type to convert to as a FQN class name
* @return an expression object which will return the header value
*/
- public static Expression varibleExpression(final String variableName, final String typeName) {
+ public static Expression variableExpression(final String variableName, final String typeName) {
return variableExpression(simpleExpression(variableName), simpleExpression(typeName));
}