This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch setvar in repository https://gitbox.apache.org/repos/asf/camel.git
commit cbdda1ebead74f9344c3f22f6cb3319f5949aa25 Author: Claus Ibsen <[email protected]> AuthorDate: Sun Jan 18 18:32:14 2026 +0100 CAMEL-22871: camel-core - Add simple function to assign a variable --- .../org/apache/camel/catalog/languages/simple.json | 62 ++++----- .../language/csimple/joor/OriginalSimpleTest.java | 51 +++++++ .../jsonpath/JsonPathSimpleSetVariableTest.java | 99 ++++++++++++++ .../org/apache/camel/language/simple/simple.json | 62 ++++----- .../modules/languages/pages/simple-language.adoc | 2 + .../camel/language/csimple/CSimpleHelper.java | 24 ++++ .../camel/language/simple/SimpleConstants.java | 6 + .../language/simple/SimpleExpressionBuilder.java | 84 ++++++++++++ .../simple/ast/SimpleFunctionExpression.java | 146 +++++++++++++++++++++ .../apache/camel/language/simple/SimpleTest.java | 73 +++++++++++ 10 files changed, 549 insertions(+), 60 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json index a6ab10fed80d..1653301ef4f9 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json @@ -75,35 +75,37 @@ "substringBetween(exp,after,before)": { "index": 49, "kind": "function", "displayName": "Substring Between", "group": "function", "label": "function", "required": false, "javaType": "int", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Returns a random number between min (included) and max (excluded).", "ognl": false, "suffix": "}" }, "split(exp,separator)": { "index": 50, "kind": "function", "displayName": "Split String Values", "group": "function", "label": "function", "required": false, "javaType": "String[]", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Splits the message body\/expression as a String value using the separator into a String array", "ognl": false, "suffix": "}" }, "capitalize(exp)": { "index": 51, "kind": "function", "displayName": "Capitalize String Values", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Capitalizes the message body\/expression as a String value (upper case every words)", "ognl": false, "suffix": "}" }, - "random(min,max)": { "index": 52, "kind": "function", "displayName": "Random", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Returns a substring of the message body\/expression that are between after and before. Returns null if nothing comes between.", "ognl": false, "suffix": "}" }, - "skip(num)": { "index": 53, "kind": "function", "displayName": "Skip First Items from the Message Body", "group": "function", "label": "function", "required": false, "javaType": "java.util.Iterator", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The skip function iterates the message body and skips the first number of items. This can be used with the Splitter EIP to split a message body and skip the first N number of [...] - "convertTo(exp,type)": { "index": 54, "kind": "function", "displayName": "Convert To", "group": "function", "label": "function", "required": false, "javaType": "", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Converts the message body (or expression) to the specified type.", "ognl": true, "suffix": "}" }, - "isEmpty(exp)": { "index": 55, "kind": "function", "displayName": "Is Empty", "group": "function", "label": "function", "required": false, "javaType": "boolean", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Whether the message body (or expression) is null or empty (list\/map types are tested if they have 0 elements).", "ognl": false, "suffix": "}" }, - "trim(exp)": { "index": 56, "kind": "function", "displayName": "Trim", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The trim function trims the message body (or expression) by removing all leading and trailing white spaces.", "ognl": false, "suffix": "}" }, - "normalizeWhitespace(exp)": { "index": 57, "kind": "function", "displayName": "Normalize Whitspace", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Normalizes the whitespace in the message body (or expression) by cleaning up excess whitespaces.", "ognl": false, "suffix": "}" }, - "length(exp)": { "index": 58, "kind": "function", "displayName": "Length", "group": "function", "label": "function", "required": false, "javaType": "int", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The payload length (number of bytes) of the message body (or expression).", "ognl": false, "suffix": "}" }, - "size(exp)": { "index": 59, "kind": "function", "displayName": "Size", "group": "function", "label": "function", "required": false, "javaType": "int", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The size of the message body (or expression). If the payload is java.util.Collection or java.util.Map based then the size is the number of elements; otherwise the payload size in bytes.", "ognl": false, "suffix": "}" }, - "uppercase(exp)": { "index": 60, "kind": "function", "displayName": "Uppercase", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Uppercases the message body (or expression)", "ognl": false, "suffix": "}" }, - "lowercase(exp)": { "index": 61, "kind": "function", "displayName": "Lowercase", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Lowercases the message body (or expression)", "ognl": false, "suffix": "}" }, - "concat(exp,exp,separator)": { "index": 62, "kind": "function", "displayName": "Concat", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Performs a string concat using two expressions (message body as default) with optional separator", "ognl": false, "suffix": "}" }, - "collate(num)": { "index": 63, "kind": "function", "displayName": "Group Message Body into Sub Lists", "group": "function", "label": "function", "required": false, "javaType": "java.util.Iterator", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The collate function iterates the message body and groups the data into sub lists of specified size. This can be used with the Splitter EIP to split a message body and group\/ba [...] - "join(separator,prefix,exp)": { "index": 64, "kind": "function", "displayName": "Join", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The join function iterates the message body\/expression and joins the data into a string. The separator is by default a comma. The prefix is optional. The join uses the message body as source by default. [...] - "messageHistory(boolean)": { "index": 65, "kind": "function", "displayName": "Print Message History", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The message history of the current exchange (how it has been routed). This is similar to the route stack-trace message history the error handler logs in case of an unhandled exception. The b [...] - "uuid(type)": { "index": 66, "kind": "function", "displayName": "Generate UUID", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Returns a UUID using the Camel `UuidGenerator`. You can choose between `default`, `classic`, `short` and `simple` as the type. If no type is given, the default is used. It is also possible to use a custom `UuidG [...] - "hash(exp,algorithm)": { "index": 67, "kind": "function", "displayName": "Compute Hash Value", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Returns a hashed value (string in hex decimal) of the message body\/expression using JDK MessageDigest. The algorithm can be SHA-256 (default) or SHA3-256.", "ognl": false, "suffix": "}" }, - "empty(type)": { "index": 68, "kind": "function", "displayName": "Create Empty Object", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Creates a new empty object (decided by type). Use `string` to create an empty String. Use `list` to create an empty `java.util.ArrayList`. Use `map` to create an empty `java.util.LinkedHashMap`.", "ognl": [...] - "iif(predicate,trueExp,falseExp)": { "index": 69, "kind": "function", "displayName": "If Then Else", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Evaluates the predicate and returns the value of trueExp or falseExp. This function is similar to the ternary operator in Java.", "ognl": false, "suffix": "}" }, - "list(val...)": { "index": 70, "kind": "function", "displayName": "Create List of values", "group": "function", "label": "function", "required": false, "javaType": "java.util.ArrayList", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The list function creates an ArrayList with the given set of values.", "ognl": false, "suffix": "}" }, - "map(key1,value1,...)": { "index": 71, "kind": "function", "displayName": "Create Map of pairs", "group": "function", "label": "function", "required": false, "javaType": "java.util.LinkedHashMap", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The map function creates a LinkedHashMap with the given set of pairs.", "ognl": false, "suffix": "}" }, - "attachments": { "index": 72, "kind": "function", "displayName": "Attachments", "group": "function", "label": "function", "required": false, "javaType": "java.util.Map", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "All the attachments as a Map<String,DataHandler.", "ognl": false, "suffix": "}" }, - "attachments.size": { "index": 73, "kind": "function", "displayName": "Attachments", "group": "function", "label": "function", "required": false, "javaType": "int", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The number of attachments. Is 0 if there are no attachments.", "ognl": false, "suffix": "}" }, - "attachmentContentAsText": { "index": 74, "kind": "function", "displayName": "Attachment Content As Text", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The content of the attachment as text (ie String).", "ognl": false, "suffix": "}" }, - "attachmentContent": { "index": 75, "kind": "function", "displayName": "Attachment Content", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The content of the attachment", "ognl": false, "suffix": "}" }, - "attachmentContentAs(type)": { "index": 76, "kind": "function", "displayName": "Attachment Content As", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The content of the attachment, converted to the given type.", "ognl": false, "suffix": "}" }, - "attachmentHeader(key,name)": { "index": 77, "kind": "function", "displayName": "Attachment Header", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The attachment header with the given name.", "ognl": false, "suffix": "}" }, - "attachmentHeader(key,name,type)": { "index": 78, "kind": "function", "displayName": "Attachment Header", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The attachment header with the given name, converted to the given type.", "ognl": false, "suffix": "}" }, - "attachment(key)": { "index": 79, "kind": "function", "displayName": "Attachment", "group": "function", "label": "function", "required": false, "javaType": "jakarta.activation.DataHandler", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The DataHandler for the given attachment.", "ognl": true, "suffix": "}" }, - "base64Encode(exp)": { "index": 80, "kind": "function", "displayName": "Base64 Encode", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Base64 encodes the message body (or expression)", "ognl": false, "suffix": "}" }, - "base64Decode(exp)": { "index": 81, "kind": "function", "displayName": "Base64 Decode", "group": "function", "label": "function", "required": false, "javaType": "byte[]", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Base64 decodes the message body (or expression)", "ognl": false, "suffix": "}" } + "setHeader(name,type,exp)": { "index": 52, "kind": "function", "displayName": "Set Header", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Sets a message header with the given expression (optional converting to the given type)", "ognl": false, "suffix": "}" }, + "setVariable(name,type,exp)": { "index": 53, "kind": "function", "displayName": "Set Variable", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Sets a variable with the given expression (optional converting to the given type)", "ognl": false, "suffix": "}" }, + "random(min,max)": { "index": 54, "kind": "function", "displayName": "Random", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Returns a substring of the message body\/expression that are between after and before. Returns null if nothing comes between.", "ognl": false, "suffix": "}" }, + "skip(num)": { "index": 55, "kind": "function", "displayName": "Skip First Items from the Message Body", "group": "function", "label": "function", "required": false, "javaType": "java.util.Iterator", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The skip function iterates the message body and skips the first number of items. This can be used with the Splitter EIP to split a message body and skip the first N number of [...] + "convertTo(exp,type)": { "index": 56, "kind": "function", "displayName": "Convert To", "group": "function", "label": "function", "required": false, "javaType": "", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Converts the message body (or expression) to the specified type.", "ognl": true, "suffix": "}" }, + "isEmpty(exp)": { "index": 57, "kind": "function", "displayName": "Is Empty", "group": "function", "label": "function", "required": false, "javaType": "boolean", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Whether the message body (or expression) is null or empty (list\/map types are tested if they have 0 elements).", "ognl": false, "suffix": "}" }, + "trim(exp)": { "index": 58, "kind": "function", "displayName": "Trim", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The trim function trims the message body (or expression) by removing all leading and trailing white spaces.", "ognl": false, "suffix": "}" }, + "normalizeWhitespace(exp)": { "index": 59, "kind": "function", "displayName": "Normalize Whitspace", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Normalizes the whitespace in the message body (or expression) by cleaning up excess whitespaces.", "ognl": false, "suffix": "}" }, + "length(exp)": { "index": 60, "kind": "function", "displayName": "Length", "group": "function", "label": "function", "required": false, "javaType": "int", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The payload length (number of bytes) of the message body (or expression).", "ognl": false, "suffix": "}" }, + "size(exp)": { "index": 61, "kind": "function", "displayName": "Size", "group": "function", "label": "function", "required": false, "javaType": "int", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The size of the message body (or expression). If the payload is java.util.Collection or java.util.Map based then the size is the number of elements; otherwise the payload size in bytes.", "ognl": false, "suffix": "}" }, + "uppercase(exp)": { "index": 62, "kind": "function", "displayName": "Uppercase", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Uppercases the message body (or expression)", "ognl": false, "suffix": "}" }, + "lowercase(exp)": { "index": 63, "kind": "function", "displayName": "Lowercase", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Lowercases the message body (or expression)", "ognl": false, "suffix": "}" }, + "concat(exp,exp,separator)": { "index": 64, "kind": "function", "displayName": "Concat", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Performs a string concat using two expressions (message body as default) with optional separator", "ognl": false, "suffix": "}" }, + "collate(num)": { "index": 65, "kind": "function", "displayName": "Group Message Body into Sub Lists", "group": "function", "label": "function", "required": false, "javaType": "java.util.Iterator", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The collate function iterates the message body and groups the data into sub lists of specified size. This can be used with the Splitter EIP to split a message body and group\/ba [...] + "join(separator,prefix,exp)": { "index": 66, "kind": "function", "displayName": "Join", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The join function iterates the message body\/expression and joins the data into a string. The separator is by default a comma. The prefix is optional. The join uses the message body as source by default. [...] + "messageHistory(boolean)": { "index": 67, "kind": "function", "displayName": "Print Message History", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The message history of the current exchange (how it has been routed). This is similar to the route stack-trace message history the error handler logs in case of an unhandled exception. The b [...] + "uuid(type)": { "index": 68, "kind": "function", "displayName": "Generate UUID", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Returns a UUID using the Camel `UuidGenerator`. You can choose between `default`, `classic`, `short` and `simple` as the type. If no type is given, the default is used. It is also possible to use a custom `UuidG [...] + "hash(exp,algorithm)": { "index": 69, "kind": "function", "displayName": "Compute Hash Value", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Returns a hashed value (string in hex decimal) of the message body\/expression using JDK MessageDigest. The algorithm can be SHA-256 (default) or SHA3-256.", "ognl": false, "suffix": "}" }, + "empty(type)": { "index": 70, "kind": "function", "displayName": "Create Empty Object", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Creates a new empty object (decided by type). Use `string` to create an empty String. Use `list` to create an empty `java.util.ArrayList`. Use `map` to create an empty `java.util.LinkedHashMap`.", "ognl": [...] + "iif(predicate,trueExp,falseExp)": { "index": 71, "kind": "function", "displayName": "If Then Else", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Evaluates the predicate and returns the value of trueExp or falseExp. This function is similar to the ternary operator in Java.", "ognl": false, "suffix": "}" }, + "list(val...)": { "index": 72, "kind": "function", "displayName": "Create List of values", "group": "function", "label": "function", "required": false, "javaType": "java.util.ArrayList", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The list function creates an ArrayList with the given set of values.", "ognl": false, "suffix": "}" }, + "map(key1,value1,...)": { "index": 73, "kind": "function", "displayName": "Create Map of pairs", "group": "function", "label": "function", "required": false, "javaType": "java.util.LinkedHashMap", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The map function creates a LinkedHashMap with the given set of pairs.", "ognl": false, "suffix": "}" }, + "attachments": { "index": 74, "kind": "function", "displayName": "Attachments", "group": "function", "label": "function", "required": false, "javaType": "java.util.Map", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "All the attachments as a Map<String,DataHandler.", "ognl": false, "suffix": "}" }, + "attachments.size": { "index": 75, "kind": "function", "displayName": "Attachments", "group": "function", "label": "function", "required": false, "javaType": "int", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The number of attachments. Is 0 if there are no attachments.", "ognl": false, "suffix": "}" }, + "attachmentContentAsText": { "index": 76, "kind": "function", "displayName": "Attachment Content As Text", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The content of the attachment as text (ie String).", "ognl": false, "suffix": "}" }, + "attachmentContent": { "index": 77, "kind": "function", "displayName": "Attachment Content", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The content of the attachment", "ognl": false, "suffix": "}" }, + "attachmentContentAs(type)": { "index": 78, "kind": "function", "displayName": "Attachment Content As", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The content of the attachment, converted to the given type.", "ognl": false, "suffix": "}" }, + "attachmentHeader(key,name)": { "index": 79, "kind": "function", "displayName": "Attachment Header", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The attachment header with the given name.", "ognl": false, "suffix": "}" }, + "attachmentHeader(key,name,type)": { "index": 80, "kind": "function", "displayName": "Attachment Header", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The attachment header with the given name, converted to the given type.", "ognl": false, "suffix": "}" }, + "attachment(key)": { "index": 81, "kind": "function", "displayName": "Attachment", "group": "function", "label": "function", "required": false, "javaType": "jakarta.activation.DataHandler", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The DataHandler for the given attachment.", "ognl": true, "suffix": "}" }, + "base64Encode(exp)": { "index": 82, "kind": "function", "displayName": "Base64 Encode", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Base64 encodes the message body (or expression)", "ognl": false, "suffix": "}" }, + "base64Decode(exp)": { "index": 83, "kind": "function", "displayName": "Base64 Decode", "group": "function", "label": "function", "required": false, "javaType": "byte[]", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Base64 decodes the message body (or expression)", "ognl": false, "suffix": "}" } } } diff --git a/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/OriginalSimpleTest.java b/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/OriginalSimpleTest.java index bafd03803a88..86946fd940a1 100644 --- a/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/OriginalSimpleTest.java +++ b/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/OriginalSimpleTest.java @@ -2686,6 +2686,57 @@ public class OriginalSimpleTest extends LanguageTestSupport { assertEquals("carlsberg", s); } + @Test + public void testSetHeader() { + exchange.getMessage().setBody("Hello World"); + + Expression expression = context.resolveLanguage("csimple").createExpression("${setHeader(foo,${body})}"); + Object s = expression.evaluate(exchange, String.class); + assertNull(s); + assertEquals("Hello World", exchange.getMessage().getHeader("foo")); + + exchange.getMessage().setBody("123"); + expression = context.resolveLanguage("csimple").createExpression("${setHeader(bar,int,${body})}"); + s = expression.evaluate(exchange, String.class); + assertNull(s); + assertIsInstanceOf(Integer.class, exchange.getMessage().getHeader("bar")); + assertEquals(123, exchange.getMessage().getHeader("bar")); + + // null should remove the variable + expression = context.resolveLanguage("csimple").createExpression("${setHeader(bar,${null})}"); + s = expression.evaluate(exchange, String.class); + assertNull(s); + assertNull(exchange.getMessage().getHeader("bar")); + } + + @Test + public void testSetVariable() { + exchange.getVariables().clear(); + assertEquals(0, exchange.getVariables().size()); + exchange.getMessage().setBody("Hello World"); + + Expression expression = context.resolveLanguage("csimple").createExpression("${setVariable(foo,${body})}"); + Object s = expression.evaluate(exchange, String.class); + assertNull(s); + assertEquals("Hello World", exchange.getVariable("foo")); + assertEquals(1, exchange.getVariables().size()); + + exchange.getMessage().setBody("123"); + expression = context.resolveLanguage("csimple").createExpression("${setVariable(bar,int,${body})}"); + s = expression.evaluate(exchange, String.class); + assertNull(s); + assertIsInstanceOf(Integer.class, exchange.getVariable("bar")); + assertEquals(123, exchange.getVariable("bar")); + assertEquals(2, exchange.getVariables().size()); + + // null should remove the variable + expression = context.resolveLanguage("csimple").createExpression("${setVariable(bar,${null})}"); + s = expression.evaluate(exchange, String.class); + assertNull(s); + assertNull(exchange.getVariable("bar")); + assertEquals(1, exchange.getVariables().size()); + } + @Override protected String getLanguageName() { return "csimple"; diff --git a/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathSimpleSetVariableTest.java b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathSimpleSetVariableTest.java new file mode 100644 index 000000000000..a3a4afaf652f --- /dev/null +++ b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathSimpleSetVariableTest.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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.camel.jsonpath; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit5.CamelTestSupport; +import org.junit.jupiter.api.Test; + +public class JsonPathSimpleSetVariableTest extends CamelTestSupport { + + private final String MAPPING = """ + { + "id": "${jsonpath($.id)}", + "type": "${header.type}", + "amount": ${jsonpath($.amount)}, + "status": "${iif(${jsonpath($.amount)} > 100,HIGH,LOW)}" + } + """; + + private final String MAPPING2 = """ + ${setVariable(myVar,${jsonpath($.amount)})} + { + "id": "${jsonpath($.id)}", + "type": "${header.type}", + "amount": ${jsonpath($.amount)}, + "status": "${iif(${variable.myVar} > 100,HIGH,LOW)}" + } + """; + + private final String EXPECTED = """ + { + "id": "123", + "type": "silver", + "amount": 444, + "status": "HIGH" + }"""; + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("direct:start") + .transform().simple(MAPPING) + .log("${body}") + .to("mock:order"); + + from("direct:start2") + .transform().simple(MAPPING2).transform().simple("${trim()}") + .log("${body}") + .to("mock:order"); + } + }; + } + + @Test + public void testMapping() throws Exception { + getMockEndpoint("mock:order").expectedBodiesReceived(EXPECTED); + + template.sendBodyAndHeader("direct:start", """ + { + "id": 123, + "amount": 444 + } + """, "type", "silver"); + + MockEndpoint.assertIsSatisfied(context); + } + + @Test + public void testMapping2() throws Exception { + getMockEndpoint("mock:order").expectedBodiesReceived(EXPECTED); + + template.sendBodyAndHeader("direct:start2", """ + { + "id": 123, + "amount": 444 + } + """, "type", "silver"); + + MockEndpoint.assertIsSatisfied(context); + } + +} diff --git a/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json b/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json index a6ab10fed80d..1653301ef4f9 100644 --- a/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json +++ b/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json @@ -75,35 +75,37 @@ "substringBetween(exp,after,before)": { "index": 49, "kind": "function", "displayName": "Substring Between", "group": "function", "label": "function", "required": false, "javaType": "int", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Returns a random number between min (included) and max (excluded).", "ognl": false, "suffix": "}" }, "split(exp,separator)": { "index": 50, "kind": "function", "displayName": "Split String Values", "group": "function", "label": "function", "required": false, "javaType": "String[]", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Splits the message body\/expression as a String value using the separator into a String array", "ognl": false, "suffix": "}" }, "capitalize(exp)": { "index": 51, "kind": "function", "displayName": "Capitalize String Values", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Capitalizes the message body\/expression as a String value (upper case every words)", "ognl": false, "suffix": "}" }, - "random(min,max)": { "index": 52, "kind": "function", "displayName": "Random", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Returns a substring of the message body\/expression that are between after and before. Returns null if nothing comes between.", "ognl": false, "suffix": "}" }, - "skip(num)": { "index": 53, "kind": "function", "displayName": "Skip First Items from the Message Body", "group": "function", "label": "function", "required": false, "javaType": "java.util.Iterator", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The skip function iterates the message body and skips the first number of items. This can be used with the Splitter EIP to split a message body and skip the first N number of [...] - "convertTo(exp,type)": { "index": 54, "kind": "function", "displayName": "Convert To", "group": "function", "label": "function", "required": false, "javaType": "", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Converts the message body (or expression) to the specified type.", "ognl": true, "suffix": "}" }, - "isEmpty(exp)": { "index": 55, "kind": "function", "displayName": "Is Empty", "group": "function", "label": "function", "required": false, "javaType": "boolean", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Whether the message body (or expression) is null or empty (list\/map types are tested if they have 0 elements).", "ognl": false, "suffix": "}" }, - "trim(exp)": { "index": 56, "kind": "function", "displayName": "Trim", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The trim function trims the message body (or expression) by removing all leading and trailing white spaces.", "ognl": false, "suffix": "}" }, - "normalizeWhitespace(exp)": { "index": 57, "kind": "function", "displayName": "Normalize Whitspace", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Normalizes the whitespace in the message body (or expression) by cleaning up excess whitespaces.", "ognl": false, "suffix": "}" }, - "length(exp)": { "index": 58, "kind": "function", "displayName": "Length", "group": "function", "label": "function", "required": false, "javaType": "int", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The payload length (number of bytes) of the message body (or expression).", "ognl": false, "suffix": "}" }, - "size(exp)": { "index": 59, "kind": "function", "displayName": "Size", "group": "function", "label": "function", "required": false, "javaType": "int", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The size of the message body (or expression). If the payload is java.util.Collection or java.util.Map based then the size is the number of elements; otherwise the payload size in bytes.", "ognl": false, "suffix": "}" }, - "uppercase(exp)": { "index": 60, "kind": "function", "displayName": "Uppercase", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Uppercases the message body (or expression)", "ognl": false, "suffix": "}" }, - "lowercase(exp)": { "index": 61, "kind": "function", "displayName": "Lowercase", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Lowercases the message body (or expression)", "ognl": false, "suffix": "}" }, - "concat(exp,exp,separator)": { "index": 62, "kind": "function", "displayName": "Concat", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Performs a string concat using two expressions (message body as default) with optional separator", "ognl": false, "suffix": "}" }, - "collate(num)": { "index": 63, "kind": "function", "displayName": "Group Message Body into Sub Lists", "group": "function", "label": "function", "required": false, "javaType": "java.util.Iterator", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The collate function iterates the message body and groups the data into sub lists of specified size. This can be used with the Splitter EIP to split a message body and group\/ba [...] - "join(separator,prefix,exp)": { "index": 64, "kind": "function", "displayName": "Join", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The join function iterates the message body\/expression and joins the data into a string. The separator is by default a comma. The prefix is optional. The join uses the message body as source by default. [...] - "messageHistory(boolean)": { "index": 65, "kind": "function", "displayName": "Print Message History", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The message history of the current exchange (how it has been routed). This is similar to the route stack-trace message history the error handler logs in case of an unhandled exception. The b [...] - "uuid(type)": { "index": 66, "kind": "function", "displayName": "Generate UUID", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Returns a UUID using the Camel `UuidGenerator`. You can choose between `default`, `classic`, `short` and `simple` as the type. If no type is given, the default is used. It is also possible to use a custom `UuidG [...] - "hash(exp,algorithm)": { "index": 67, "kind": "function", "displayName": "Compute Hash Value", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Returns a hashed value (string in hex decimal) of the message body\/expression using JDK MessageDigest. The algorithm can be SHA-256 (default) or SHA3-256.", "ognl": false, "suffix": "}" }, - "empty(type)": { "index": 68, "kind": "function", "displayName": "Create Empty Object", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Creates a new empty object (decided by type). Use `string` to create an empty String. Use `list` to create an empty `java.util.ArrayList`. Use `map` to create an empty `java.util.LinkedHashMap`.", "ognl": [...] - "iif(predicate,trueExp,falseExp)": { "index": 69, "kind": "function", "displayName": "If Then Else", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Evaluates the predicate and returns the value of trueExp or falseExp. This function is similar to the ternary operator in Java.", "ognl": false, "suffix": "}" }, - "list(val...)": { "index": 70, "kind": "function", "displayName": "Create List of values", "group": "function", "label": "function", "required": false, "javaType": "java.util.ArrayList", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The list function creates an ArrayList with the given set of values.", "ognl": false, "suffix": "}" }, - "map(key1,value1,...)": { "index": 71, "kind": "function", "displayName": "Create Map of pairs", "group": "function", "label": "function", "required": false, "javaType": "java.util.LinkedHashMap", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The map function creates a LinkedHashMap with the given set of pairs.", "ognl": false, "suffix": "}" }, - "attachments": { "index": 72, "kind": "function", "displayName": "Attachments", "group": "function", "label": "function", "required": false, "javaType": "java.util.Map", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "All the attachments as a Map<String,DataHandler.", "ognl": false, "suffix": "}" }, - "attachments.size": { "index": 73, "kind": "function", "displayName": "Attachments", "group": "function", "label": "function", "required": false, "javaType": "int", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The number of attachments. Is 0 if there are no attachments.", "ognl": false, "suffix": "}" }, - "attachmentContentAsText": { "index": 74, "kind": "function", "displayName": "Attachment Content As Text", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The content of the attachment as text (ie String).", "ognl": false, "suffix": "}" }, - "attachmentContent": { "index": 75, "kind": "function", "displayName": "Attachment Content", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The content of the attachment", "ognl": false, "suffix": "}" }, - "attachmentContentAs(type)": { "index": 76, "kind": "function", "displayName": "Attachment Content As", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The content of the attachment, converted to the given type.", "ognl": false, "suffix": "}" }, - "attachmentHeader(key,name)": { "index": 77, "kind": "function", "displayName": "Attachment Header", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The attachment header with the given name.", "ognl": false, "suffix": "}" }, - "attachmentHeader(key,name,type)": { "index": 78, "kind": "function", "displayName": "Attachment Header", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The attachment header with the given name, converted to the given type.", "ognl": false, "suffix": "}" }, - "attachment(key)": { "index": 79, "kind": "function", "displayName": "Attachment", "group": "function", "label": "function", "required": false, "javaType": "jakarta.activation.DataHandler", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The DataHandler for the given attachment.", "ognl": true, "suffix": "}" }, - "base64Encode(exp)": { "index": 80, "kind": "function", "displayName": "Base64 Encode", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Base64 encodes the message body (or expression)", "ognl": false, "suffix": "}" }, - "base64Decode(exp)": { "index": 81, "kind": "function", "displayName": "Base64 Decode", "group": "function", "label": "function", "required": false, "javaType": "byte[]", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Base64 decodes the message body (or expression)", "ognl": false, "suffix": "}" } + "setHeader(name,type,exp)": { "index": 52, "kind": "function", "displayName": "Set Header", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Sets a message header with the given expression (optional converting to the given type)", "ognl": false, "suffix": "}" }, + "setVariable(name,type,exp)": { "index": 53, "kind": "function", "displayName": "Set Variable", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Sets a variable with the given expression (optional converting to the given type)", "ognl": false, "suffix": "}" }, + "random(min,max)": { "index": 54, "kind": "function", "displayName": "Random", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Returns a substring of the message body\/expression that are between after and before. Returns null if nothing comes between.", "ognl": false, "suffix": "}" }, + "skip(num)": { "index": 55, "kind": "function", "displayName": "Skip First Items from the Message Body", "group": "function", "label": "function", "required": false, "javaType": "java.util.Iterator", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The skip function iterates the message body and skips the first number of items. This can be used with the Splitter EIP to split a message body and skip the first N number of [...] + "convertTo(exp,type)": { "index": 56, "kind": "function", "displayName": "Convert To", "group": "function", "label": "function", "required": false, "javaType": "", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Converts the message body (or expression) to the specified type.", "ognl": true, "suffix": "}" }, + "isEmpty(exp)": { "index": 57, "kind": "function", "displayName": "Is Empty", "group": "function", "label": "function", "required": false, "javaType": "boolean", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Whether the message body (or expression) is null or empty (list\/map types are tested if they have 0 elements).", "ognl": false, "suffix": "}" }, + "trim(exp)": { "index": 58, "kind": "function", "displayName": "Trim", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The trim function trims the message body (or expression) by removing all leading and trailing white spaces.", "ognl": false, "suffix": "}" }, + "normalizeWhitespace(exp)": { "index": 59, "kind": "function", "displayName": "Normalize Whitspace", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Normalizes the whitespace in the message body (or expression) by cleaning up excess whitespaces.", "ognl": false, "suffix": "}" }, + "length(exp)": { "index": 60, "kind": "function", "displayName": "Length", "group": "function", "label": "function", "required": false, "javaType": "int", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The payload length (number of bytes) of the message body (or expression).", "ognl": false, "suffix": "}" }, + "size(exp)": { "index": 61, "kind": "function", "displayName": "Size", "group": "function", "label": "function", "required": false, "javaType": "int", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The size of the message body (or expression). If the payload is java.util.Collection or java.util.Map based then the size is the number of elements; otherwise the payload size in bytes.", "ognl": false, "suffix": "}" }, + "uppercase(exp)": { "index": 62, "kind": "function", "displayName": "Uppercase", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Uppercases the message body (or expression)", "ognl": false, "suffix": "}" }, + "lowercase(exp)": { "index": 63, "kind": "function", "displayName": "Lowercase", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Lowercases the message body (or expression)", "ognl": false, "suffix": "}" }, + "concat(exp,exp,separator)": { "index": 64, "kind": "function", "displayName": "Concat", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Performs a string concat using two expressions (message body as default) with optional separator", "ognl": false, "suffix": "}" }, + "collate(num)": { "index": 65, "kind": "function", "displayName": "Group Message Body into Sub Lists", "group": "function", "label": "function", "required": false, "javaType": "java.util.Iterator", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The collate function iterates the message body and groups the data into sub lists of specified size. This can be used with the Splitter EIP to split a message body and group\/ba [...] + "join(separator,prefix,exp)": { "index": 66, "kind": "function", "displayName": "Join", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The join function iterates the message body\/expression and joins the data into a string. The separator is by default a comma. The prefix is optional. The join uses the message body as source by default. [...] + "messageHistory(boolean)": { "index": 67, "kind": "function", "displayName": "Print Message History", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The message history of the current exchange (how it has been routed). This is similar to the route stack-trace message history the error handler logs in case of an unhandled exception. The b [...] + "uuid(type)": { "index": 68, "kind": "function", "displayName": "Generate UUID", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Returns a UUID using the Camel `UuidGenerator`. You can choose between `default`, `classic`, `short` and `simple` as the type. If no type is given, the default is used. It is also possible to use a custom `UuidG [...] + "hash(exp,algorithm)": { "index": 69, "kind": "function", "displayName": "Compute Hash Value", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Returns a hashed value (string in hex decimal) of the message body\/expression using JDK MessageDigest. The algorithm can be SHA-256 (default) or SHA3-256.", "ognl": false, "suffix": "}" }, + "empty(type)": { "index": 70, "kind": "function", "displayName": "Create Empty Object", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Creates a new empty object (decided by type). Use `string` to create an empty String. Use `list` to create an empty `java.util.ArrayList`. Use `map` to create an empty `java.util.LinkedHashMap`.", "ognl": [...] + "iif(predicate,trueExp,falseExp)": { "index": 71, "kind": "function", "displayName": "If Then Else", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Evaluates the predicate and returns the value of trueExp or falseExp. This function is similar to the ternary operator in Java.", "ognl": false, "suffix": "}" }, + "list(val...)": { "index": 72, "kind": "function", "displayName": "Create List of values", "group": "function", "label": "function", "required": false, "javaType": "java.util.ArrayList", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The list function creates an ArrayList with the given set of values.", "ognl": false, "suffix": "}" }, + "map(key1,value1,...)": { "index": 73, "kind": "function", "displayName": "Create Map of pairs", "group": "function", "label": "function", "required": false, "javaType": "java.util.LinkedHashMap", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The map function creates a LinkedHashMap with the given set of pairs.", "ognl": false, "suffix": "}" }, + "attachments": { "index": 74, "kind": "function", "displayName": "Attachments", "group": "function", "label": "function", "required": false, "javaType": "java.util.Map", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "All the attachments as a Map<String,DataHandler.", "ognl": false, "suffix": "}" }, + "attachments.size": { "index": 75, "kind": "function", "displayName": "Attachments", "group": "function", "label": "function", "required": false, "javaType": "int", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The number of attachments. Is 0 if there are no attachments.", "ognl": false, "suffix": "}" }, + "attachmentContentAsText": { "index": 76, "kind": "function", "displayName": "Attachment Content As Text", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The content of the attachment as text (ie String).", "ognl": false, "suffix": "}" }, + "attachmentContent": { "index": 77, "kind": "function", "displayName": "Attachment Content", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The content of the attachment", "ognl": false, "suffix": "}" }, + "attachmentContentAs(type)": { "index": 78, "kind": "function", "displayName": "Attachment Content As", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The content of the attachment, converted to the given type.", "ognl": false, "suffix": "}" }, + "attachmentHeader(key,name)": { "index": 79, "kind": "function", "displayName": "Attachment Header", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The attachment header with the given name.", "ognl": false, "suffix": "}" }, + "attachmentHeader(key,name,type)": { "index": 80, "kind": "function", "displayName": "Attachment Header", "group": "function", "label": "function", "required": false, "javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The attachment header with the given name, converted to the given type.", "ognl": false, "suffix": "}" }, + "attachment(key)": { "index": 81, "kind": "function", "displayName": "Attachment", "group": "function", "label": "function", "required": false, "javaType": "jakarta.activation.DataHandler", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The DataHandler for the given attachment.", "ognl": true, "suffix": "}" }, + "base64Encode(exp)": { "index": 82, "kind": "function", "displayName": "Base64 Encode", "group": "function", "label": "function", "required": false, "javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Base64 encodes the message body (or expression)", "ognl": false, "suffix": "}" }, + "base64Decode(exp)": { "index": 83, "kind": "function", "displayName": "Base64 Decode", "group": "function", "label": "function", "required": false, "javaType": "byte[]", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Base64 decodes the message body (or expression)", "ognl": false, "suffix": "}" } } } 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 c50f5303b6a9..f65eae9c1d02 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 @@ -204,6 +204,8 @@ The Simple language has many built-in functions which allows access to various p |`replace(from,to,ex[])` | `String` | Replace all the string values in the given expression. To make it easier to replace single and double quotes, then you can use XML escaped values `\"` as double quote, `\'` as single quote, and `\∅` as empty value. |`routeGroup` | `String` | Returns the route group of the current route the `Exchange` is being routed. Not all routes have a group assigned, so this may be `null`. |`routeId` | `String` | Returns the route id of the current route the `Exchange` is being routed. +|`setHeader(name,type,exp)` | `null` | Sets a message header with the given expression (optional converting to the given type). +|`setVariable(name,type,exp)` | `null` | Sets a variable with the given expression (optional converting to the given type). |`size()` | `int` | The size of the message body. If the payload is `java.util.Collection` or `java.util.Map` based then the size is the number of elements; otherwise the payload size in bytes. |`size(exp)` | `int` | The size of the expression. If the payload is `java.util.Collection` or `java.util.Map` based then the size is the number of elements; otherwise the payload size in bytes. |`skip(number)` | `Iterator` | The skip function iterates the message body and skips the first number of items. This can be used with the Splitter EIP to split a message body and skip the first N number of items. diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleHelper.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleHelper.java index 2d5fe8a2178d..4d205e4c488a 100644 --- a/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleHelper.java +++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleHelper.java @@ -987,4 +987,28 @@ public final class CSimpleHelper { } } + public static Object setHeader(Exchange exchange, String name, Class<?> type, Object value) { + if (type != null && value != null) { + value = convertTo(exchange, type, value); + } + if (value != null) { + exchange.getMessage().setHeader(name, value); + } else { + exchange.getMessage().removeHeader(name); + } + return null; + } + + public static Object setVariable(Exchange exchange, String name, Class<?> type, Object value) { + if (type != null && value != null) { + value = convertTo(exchange, type, value); + } + if (value != null) { + exchange.setVariable(name, value); + } else { + exchange.removeVariable(name); + } + return null; + } + } diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java index ab20fab19918..d9a5bf6253d0 100644 --- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java +++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java @@ -192,6 +192,12 @@ public final class SimpleConstants { label = "function", javaType = "String", displayName = "Capitalize String Values") public static final String CAPITALIZE = "capitalize(exp)"; + @Metadata(description = "Sets a message header with the given expression (optional converting to the given type)", + label = "function", javaType = "Object") + public static final String SET_HEADER = "setHeader(name,type,exp)"; + @Metadata(description = "Sets a variable with the given expression (optional converting to the given type)", + label = "function", javaType = "Object") + public static final String SET_VARIABLE = "setVariable(name,type,exp)"; @Metadata(description = "Returns a substring of the message body/expression that are between after and before. Returns null if nothing comes between.", label = "function", javaType = "String") public static final String RANDOM = "random(min,max)"; 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 10a4cb88cb58..3cd569fda828 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 @@ -913,6 +913,90 @@ public final class SimpleExpressionBuilder { }; } + /** + * Sets the message header with the given expression value + */ + public static Expression setHeaderExpression(final String name, final String type, final String expression) { + return new ExpressionAdapter() { + private ClassResolver classResolver; + private Expression exp; + + @Override + public void init(CamelContext context) { + classResolver = context.getClassResolver(); + exp = context.resolveLanguage("simple").createExpression(expression); + exp.init(context); + } + + @Override + public Object evaluate(Exchange exchange) { + Class<?> clazz = Object.class; + if (type != null) { + try { + clazz = classResolver.resolveMandatoryClass(type); + } catch (ClassNotFoundException e) { + throw CamelExecutionException.wrapCamelExecutionException(exchange, e); + } + } + Object value = exp.evaluate(exchange, clazz); + if (value != null) { + exchange.getMessage().setHeader(name, value); + } else { + exchange.getMessage().removeHeader(name); + } + // does not return anything + return null; + } + + @Override + public String toString() { + return "setHeader(" + name + "," + expression + ")"; + } + }; + } + + /** + * Sets the variable with the given expression value + */ + public static Expression setVariableExpression(final String name, final String type, final String expression) { + return new ExpressionAdapter() { + private ClassResolver classResolver; + private Expression exp; + + @Override + public void init(CamelContext context) { + classResolver = context.getClassResolver(); + exp = context.resolveLanguage("simple").createExpression(expression); + exp.init(context); + } + + @Override + public Object evaluate(Exchange exchange) { + Class<?> clazz = Object.class; + if (type != null) { + try { + clazz = classResolver.resolveMandatoryClass(type); + } catch (ClassNotFoundException e) { + throw CamelExecutionException.wrapCamelExecutionException(exchange, e); + } + } + Object value = exp.evaluate(exchange, clazz); + if (value != null) { + exchange.setVariable(name, value); + } else { + exchange.removeVariable(name); + } + // does not return anything + return null; + } + + @Override + public String toString() { + return "setVariable(" + name + "," + expression + ")"; + } + }; + } + /** * Replaces string values from the expression */ 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 664a033675c3..edc0cce1c067 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 @@ -748,6 +748,61 @@ public class SimpleFunctionExpression extends LiteralExpression { private Expression createSimpleExpressionMisc(String function) { String remainder; + // setHeader function + remainder = ifStartsWithReturnRemainder("setHeader(", function); + if (remainder != null) { + String values = StringHelper.beforeLast(remainder, ")"); + if (values == null || ObjectHelper.isEmpty(values)) { + throw new SimpleParserException( + "Valid syntax: ${setHeader(name,exp)} or ${setHeader(name,type,exp)} was: " + function, + token.getIndex()); + } + String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', false); + if (tokens.length < 2 || tokens.length > 3) { + throw new SimpleParserException( + "Valid syntax: ${setHeader(name,exp)} or ${setHeader(name,type,exp)} was: " + function, + token.getIndex()); + } + String name = tokens[0]; + String exp; + String type = null; + if (tokens.length == 3) { + type = tokens[1]; + exp = tokens[2]; + } else { + exp = tokens[1]; + } + type = StringHelper.removeQuotes(type); + return SimpleExpressionBuilder.setHeaderExpression(name, type, exp); + } + // setVariable function + remainder = ifStartsWithReturnRemainder("setVariable(", function); + if (remainder != null) { + String values = StringHelper.beforeLast(remainder, ")"); + if (values == null || ObjectHelper.isEmpty(values)) { + throw new SimpleParserException( + "Valid syntax: ${setVariable(name,exp)} or ${setVariable(name,type,exp)} was: " + function, + token.getIndex()); + } + String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', false); + if (tokens.length < 2 || tokens.length > 3) { + throw new SimpleParserException( + "Valid syntax: ${setVariable(name,exp)} or ${setVariable(name,type,exp)} was: " + function, + token.getIndex()); + } + String name = tokens[0]; + String exp; + String type = null; + if (tokens.length == 3) { + type = tokens[1]; + exp = tokens[2]; + } else { + exp = tokens[1]; + } + type = StringHelper.removeQuotes(type); + return SimpleExpressionBuilder.setVariableExpression(name, type, exp); + } + // replace function remainder = ifStartsWithReturnRemainder("replace(", function); if (remainder != null) { @@ -2108,6 +2163,97 @@ public class SimpleFunctionExpression extends LiteralExpression { private String createCodeExpressionMisc(String function) { String remainder; + // setHeader function + remainder = ifStartsWithReturnRemainder("setHeader(", function); + if (remainder != null) { + String values = StringHelper.beforeLast(remainder, ")"); + if (values == null || ObjectHelper.isEmpty(values)) { + throw new SimpleParserException( + "Valid syntax: ${setHeader(name,exp)} or ${setHeader(name,type,exp)} was: " + function, + token.getIndex()); + } + String[] tokens = codeSplitSafe(values, ',', true, true); + if (tokens.length < 2 || tokens.length > 3) { + throw new SimpleParserException( + "Valid syntax: ${setHeader(name,exp)} or ${setHeader(name,type,exp)} was: " + function, + token.getIndex()); + } + // single quotes should be double quotes + for (int i = 1; i < tokens.length; i++) { + String s = tokens[i]; + if (StringHelper.isSingleQuoted(s)) { + s = StringHelper.removeLeadingAndEndingQuotes(s); + s = StringQuoteHelper.doubleQuote(s); + tokens[i] = s; + } + } + // name must be in double quote as its a string value + String name = StringHelper.removeLeadingAndEndingQuotes(tokens[0]); + name = StringQuoteHelper.doubleQuote(name); + String exp; + String type = null; + if (tokens.length == 3) { + type = tokens[1]; + exp = tokens[2]; + } else { + exp = tokens[1]; + } + if (type != null) { + type = StringHelper.removeQuotes(type); + type = type.trim(); + type = appendClass(type); + type = type.replace('$', '.'); + } else { + type = "null"; + } + return "Object value = " + exp + ";\n return setHeader(exchange, " + name + ", " + type + ", value);"; + } + // setVariable function + remainder = ifStartsWithReturnRemainder("setVariable(", function); + if (remainder != null) { + String values = StringHelper.beforeLast(remainder, ")"); + if (values == null || ObjectHelper.isEmpty(values)) { + throw new SimpleParserException( + "Valid syntax: ${setVariable(name,exp)} or ${setVariable(name,type,exp)} was: " + function, + token.getIndex()); + } + String[] tokens = codeSplitSafe(values, ',', true, true); + if (tokens.length < 2 || tokens.length > 3) { + throw new SimpleParserException( + "Valid syntax: ${setVariable(name,exp)} or ${setVariable(name,type,exp)} was: " + function, + token.getIndex()); + } + // single quotes should be double quotes + for (int i = 1; i < tokens.length; i++) { + String s = tokens[i]; + if (StringHelper.isSingleQuoted(s)) { + s = StringHelper.removeLeadingAndEndingQuotes(s); + s = StringQuoteHelper.doubleQuote(s); + tokens[i] = s; + } + } + // name must be in double quote as its a string value + String name = StringHelper.removeLeadingAndEndingQuotes(tokens[0]); + name = StringQuoteHelper.doubleQuote(name); + String exp; + String type = null; + if (tokens.length == 3) { + type = tokens[1]; + exp = tokens[2]; + } else { + exp = tokens[1]; + } + if (type != null) { + type = StringHelper.removeQuotes(type); + type = type.trim(); + type = appendClass(type); + type = type.replace('$', '.'); + } else { + type = "null"; + } + return "Object value = " + exp + ";\n return setVariable(exchange, " + name + ", " + type + ", value);"; + } + // substring function remainder = ifStartsWithReturnRemainder("substring(", function); if (remainder != null) { 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 5ba8c279c4b6..8868c78171da 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 @@ -2930,6 +2930,79 @@ public class SimpleTest extends LanguageTestSupport { assertExpression(exchange, "${pretty(${body})}", "{\n\t\"name\": \"Jack\",\n\t\"id\": 123\n}\n"); } + @Test + public void testSetHeader() { + exchange.getMessage().setBody("Hello World"); + + Expression expression = context.resolveLanguage("simple").createExpression("${setHeader(foo,${body})}"); + Object s = expression.evaluate(exchange, String.class); + assertNull(s); + assertEquals("Hello World", exchange.getMessage().getHeader("foo")); + + exchange.getMessage().setBody("123"); + expression = context.resolveLanguage("simple").createExpression("${setHeader(bar,int,${body})}"); + s = expression.evaluate(exchange, String.class); + assertNull(s); + assertIsInstanceOf(Integer.class, exchange.getMessage().getHeader("bar")); + assertEquals(123, exchange.getMessage().getHeader("bar")); + + // null should remove the variable + expression = context.resolveLanguage("simple").createExpression("${setHeader(bar,${null})}"); + s = expression.evaluate(exchange, String.class); + assertNull(s); + assertNull(exchange.getMessage().getHeader("bar")); + } + + @Test + public void testSetVariable() { + exchange.getVariables().clear(); + assertEquals(0, exchange.getVariables().size()); + exchange.getMessage().setBody("Hello World"); + + Expression expression = context.resolveLanguage("simple").createExpression("${setVariable(foo,${body})}"); + Object s = expression.evaluate(exchange, String.class); + assertNull(s); + assertEquals("Hello World", exchange.getVariable("foo")); + assertEquals(1, exchange.getVariables().size()); + + exchange.getMessage().setBody("123"); + expression = context.resolveLanguage("simple").createExpression("${setVariable(bar,int,${body})}"); + s = expression.evaluate(exchange, String.class); + assertNull(s); + assertIsInstanceOf(Integer.class, exchange.getVariable("bar")); + assertEquals(123, exchange.getVariable("bar")); + assertEquals(2, exchange.getVariables().size()); + + // null should remove the variable + expression = context.resolveLanguage("simple").createExpression("${setVariable(bar,${null})}"); + s = expression.evaluate(exchange, String.class); + assertNull(s); + assertNull(exchange.getVariable("bar")); + assertEquals(1, exchange.getVariables().size()); + } + + @Test + public void testSetVariableMapping() { + exchange.getVariables().clear(); + assertEquals(0, exchange.getVariables().size()); + exchange.getMessage().setBody("Hello World"); + + String map = """ + ${setVariable(count,${body.length})} + Input: ${body} + Bytes: ${variable.count} + """; + String exp = """ + + Input: Hello World + Bytes: 11 + """; + + Expression expression = context.resolveLanguage("simple").createExpression(map); + String out = expression.evaluate(exchange, String.class); + assertEquals(exp, out); + } + private void assertExpressionCreateNewEmpty( String type, Class<?> expectedClass, java.util.function.Predicate<Object> isEmptyAssertion) { Object value = evaluateExpression("${empty(%s)}".formatted(type), null);
