This is an automated email from the ASF dual-hosted git repository. fmariani pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel.git
commit a3aba7638ed241ca7d4f4ca7e60f3e31579a76f5 Author: Claus Ibsen <[email protected]> AuthorDate: Mon Jan 19 07:22:10 2026 +0100 CAMEL-22871: camel-core - Add simple function to assign a variable (#20865) --- .../org/apache/camel/catalog/languages/simple.json | 61 +++++---- .../language/csimple/joor/OriginalSimpleTest.java | 51 +++++++ .../jsonpath/JsonPathSimpleSetVariableTest.java | 99 ++++++++++++++ .../org/apache/camel/language/simple/simple.json | 61 +++++---- .../modules/languages/pages/simple-language.adoc | 121 ++++++++++++++++- .../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, 666 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 12c163c5398f..98ce8768f5ad 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,34 +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": "}" }, - "trim(exp)": { "index": 55, "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": 56, "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": 57, "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": 58, "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": 59, "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": 60, "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": 61, "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": 62, "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": 63, "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": 64, "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": 65, "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": 66, "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": 67, "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": 68, "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": 69, "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": 70, "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": 71, "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": 72, "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": 73, "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": 74, "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": 75, "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": 76, "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": 77, "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": 78, "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": 79, "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": 80, "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 485f1a20a4ea..5baf41eb3c38 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 @@ -2617,6 +2617,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 12c163c5398f..98ce8768f5ad 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,34 +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": "}" }, - "trim(exp)": { "index": 55, "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": 56, "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": 57, "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": 58, "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": 59, "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": 60, "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": 61, "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": 62, "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": 63, "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": 64, "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": 65, "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": 66, "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": 67, "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": 68, "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": 69, "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": 70, "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": 71, "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": 72, "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": 73, "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": 74, "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": 75, "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": 76, "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": 77, "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": 78, "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": 79, "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": 80, "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 80afa17b55e8..10c2374a50d6 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 @@ -59,8 +59,125 @@ include::partial$language-options.adoc[] == Variables [width="100%",cols="10%,10%,80%",options="header",] -|======================================================================= -|Variable |Type |Description +|==== +|Function |Response Type |Description +|`bean(name.method)` | `Object` | Invoking a bean expression using the xref:components::bean-component.adoc[Bean] language. Specifying a method name, you must use dot as the separator. We also support the ?method=methodname syntax that is used by the xref:components::bean-component.adoc[Bean] component. Camel will by default lookup a bean by the given name. However, if you need to refer to a bean class (such as calling a static method), then you can prefix with the type, such as `bean:ty [...] +|`body._OGNL_` | `Object` | The body invoked using a Camel _OGNL syntax_. For example to invoke the `getCountryCode` on the message body, you can use `${body.getCountryCode()}`. In the message body is a POJO then you can use a short-hand syntax `${body.countryCode}`. +|`bodyAs(type)._OGNL_` | `Object` | Converts the body to the given type determined by its classname and then invoke methods using a Camel _OGNL syntax_. +|`bodyAs(type)` | `<T>` | Converts the body to the given type determined by its classname. +|`bodyOneLine` | `String` | Converts the body to a String and removes all line-breaks, so the string is in one line. +|`bodyType` | `Class` | The message body class. +|`body` | `Object` | The message body +|`camelContext._OGNL_` | `Object` | The CamelContext invoked using Camel _OGNL syntax_. +|`camelId` | `String` | The name of the Camel application (ie `CamelContext`). +|`capitalize()` | `String` | Capitalizes the message body as a String value (upper case every words) +|`capitalize(exp)` | `String` | Capitalizes the expression as a String value (upper case every words) +|`collate(size)` | `List` | 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/batch the split sub message into a group of N sub lists. This method works similar to the collate method in Groovy. +|`concat(exp,exp,separator` | `String` | Performs a string concat using two expressions (message body as default) with optional separator (uses comma by default). +|`convertTo(exp,type)._OGNL_` | `Object` | Converts the expression to the specified type and then invoke methods using a Camel _OGNL syntax_. +|`convertTo(exp,type)` | `<T>` | Converts the expression to the specified type. +|`convertTo(type)` | `<T>` | Converts the message body to the specified type. +|`date-with-timezone:command:timezone:pattern` | `String` | Date formatting using `java.text.SimpleDateFormat` timezones and patterns. See `data:command` function for additional documentation on the commands. +|`date:command:pattern` | `String` | Date formatting using `java.text.SimpleDateFormat` patterns. See `data:command` function for additional documentation on the commands. +|`date:command` | `Date` | Evaluates to a `java.util.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 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 (on [...] +|`empty(kind)` | `<T>` | Creates a new empty object of the given kind. The `string` kind creates an empty `String` object. The `list` creates an empty `ArrayList`, and `map` creates an empty `LinkedHashMap` object. +|`env.key` | `String` | Refers to the OS system environment variable with the given key. For example `env.HOME` to refer to the home directory. +|`exception._OGNL_` | `Object` | Same as `exception` and then invoke Camel _OGNL syntax_. +|`exception.message` | `String` | The message from the `Exchange` object. See `exception` for more details. +|`exception.stacktrace` | `String` | The stacktrace from the `Exchange` object. See `exception` for more details. +|`exception` | `Throwable` | The `Exception` object on the exchange, is `null` if no exception is set on the `Exchange`. Will fallback and grab any caught exceptions stored as exchange property (`Exchange.EXCEPTION_CAUGHT`) +|`exchange._OGNL_` | `Object` | The current `Exchange` invoked using Camel _OGNL syntax_. +|`exchangeId` | `String` | The id of the current `Exchange`. +|`exchangeProperty.key._OGNL_` | `Object` | Same as `exchangeProperty.key` and then invoke Camel _OGNL syntax_. +|`exchangeProperty.key` | `Object` | Returns the value of the exchange property with the given key. Returns `null` if the property does not exists. +|`exchange` | `Exchange` | The current `Exchange` object. +|`fromRouteId` | `String` | Returns the original route id where this `Exchange` was created. +|`hash(exp,algorithm)` | `String` | Returns a hashed value (string in hex decimal) of the given expression. The algorithm can be `SHA-256` (default) or `SHA3-256`. +|`header.key._OGNL_` | `Object` | Same as `header.key` and then invoke Camel _OGNL syntax_. +|`header.key` | `Object` | The message header with the given key. +|`headerAs(key,type)` | `<T>` | The message header with the given key, converted to the given type. +|`header[key]._OGNL_` | `Object` | *Deprecated* Same as `header[key]` and then invoke Camel _OGNL syntax_. +|`header[key]` | `Object` | *Deprecated* The message header with the given key. +|`headers.key` | `Object` | *Deprecated* The message header with the given key. +|`headers.size` | `int` | The number of headers +|`headers:key` | `Object` | *Deprecated* The message header with the given key. +|`headers[key]` | `Object` | *Deprecated* The message header with the given key. +|`headers` | `Map` | All the message headers as a `java.util.Map`. +|`hostname` | `String` | Returns the local hostname (may be `null` if not possible to resolve). +|`id` | `String` | The message id +|`iif(predicate,trueExp,falseExp`) | `Object` | Evaluates the predicate expression and returns the value of _trueExp_ if the predicate is `true`, otherwise the value of `falseExp` is returned. This function is similar to the ternary operator in Java. +|`isEmpty(exp)` | `boolean` | Whether the expression is `null` or empty (list/map types are tested if they have 0 elements). +|`isEmpty()` | `boolean` | Whether the message body is `null` or empty (list/map types are tested if they have 0 elements). +|`join(separator,prefix,exp` | `String` | The join function iterates the message body (by default) 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. It is possible to refer to another source (simple language) such as a header via the exp parameter. For example `join('&','id=','$\{header.ids}')` +|`jq(exp)` | `Object` | When working with JSon data, then this allows using the JQ language, for example, to extract data from the message body (in JSon format). This requires having camel-jq JAR on the classpath. +|`jq(input,exp)` | `Object` | Same as `jp(exp)` but to use the _input_ expression as the source of the JSon document. +|`jsonpath(exp)` | `Object` | "When working with JSon data, then this allows using the JsonPath language, for example, to extract data from the message body (in JSon format). This requires having camel-jsonpath JAR on the classpath. +|`jsonpath(input,exp)` | `Object` | Same as `jsonpath(exp)` but to use the _input_ expression as the source of the JSon document. +|`length()` | `int` | The payload length (number of bytes) of the message body +|`length(exp)` | `int` | The payload length (number of bytes) of the expression. +|`list(val1,val2,...)` | `List` | The list function creates an `java.util.ArrayList` with the given set of values. +|`logExchange` | `String` | Dumps the exchange for logging purpose (uses `ExchangeFormatter` to format the output). +|`lowercase()` | `String` | Lowercases the message body +|`lowercase(exp)` | `String` | Lowercases the expression +|`mandatoryBodyAs(type)._OGNL_` | `Object` | Same as `mandatoryBodyAs(type)` and then invoke Camel _OGNL syntax_. +|`mandatoryBodyAs(type)` | `<T>` | Converts the message body to the given type determined by its classname. If the body is `null` then an exception is thrown. +|`map(key1,value1,...)` | `Map` | The map function creates a `java.util.LinkedHashMap` with the given set of pairs. +|`messageAs(type)._OGNL_` | `Object` | Same as `messageAs(type)` and then invoke Camel _OGNL syntax_. +|`messageAs(type)` | `<T>` | Converts the message to the given type determined by its classname. +|`messageHistory` | `String` | 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. +|`messageHistory(false)` | `String` | Same as `messageHistory` but without the exchange details (only includes the route stack-trace). This can be used if you do not want to log sensitive data from the message itself. +|`messageTimestamp` | `long` | The message timestamp (millis since epoc) that this message originates from. Some systems like JMS, Kafka, AWS have a timestamp on the event/message that Camel received. This method returns the timestamp if a timestamp exists. The message timestamp and exchange created are different. An exchange always has a created timestamp which is the local timestamp when Camel created the exchange. The message timestamp is only available in some Camel components when t [...] +|`normalizeWhitespace()` | `String` | Normalizes the whitespace in the message body by cleaning up excess whitespaces. +|`normalizeWhitespace(exp)` | `String` | Normalizes the whitespace in the expression by cleaning up excess whitespaces. +|`null` | `null` | Returns a `null` value. +|`originalBody` | `Object` | The original incoming message body (only available if Camel has been configured with `allowUseOriginalMessage=true`). +|`pretty(exp)` | `String` | Converts the expression to a `String`, and attempts to pretty print (if JSon or XML) otherwise return the value as-is. +|`prettyBody` | `String` | Converts the message body to a `String`, and attempts to pretty print (if JSon or XML) otherwise return the value as-is. +|`properties:key:default` | `String` | Lookup a property placeholder with the given key. If the key does not exist nor has a value, then an optional default value can be specified. +|`propertiesExist:key` | `boolean` | Checks whether a property placeholder with the given key exists or not. The result can be negated by prefixing the key with `!`. +|`random(max)` | `int` | Returns a random `Integer` between 0 (included) and max (excluded). +|`random(min,max)` | `int` | Returns a random `Integer` between min (included) and max (excluded), +|`ref:key` | `Object` | To look up a bean from the Camel xref:manual::registry.adoc[Registry] with the given key. +|`replace(from,to)` | `String` | Replace all the string values in the message body. 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. +|`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. +|`split(exp,separator)` | `String[]` | Splits the expression as a `String` value using the separator into a `String` array. The separator is comma by default. +|`split(separator)` | `String[]` | Splits the message body as a `String` value using the separator into a `String` array. The separator is comma by default. +|`stepId` | `String` | Returns the id of the current step the `Exchange` is being routed. Returns `null` if there are no steps. +|`substring(num1)` | `String` | Returns a substring of the message body. If the number is positive, then the returned string is clipped from the beginning. If the number is negative, then the returned string is clipped from the ending. +|`substring(num1,num2)` | `String` | Returns a substring of the message body. If the number is positive, then the returned string is clipped from the beginning. If the number is negative, then the returned string is clipped from the ending. +|`substring(num1,num2,exp)` | `String` | Returns a substring of the given expression. If the number is positive, then the returned string is clipped from the beginning. If the number is negative, then the returned string is clipped from the ending. +|`substringAfter(after)` | `String` | Returns a substring of the message body that comes after. Returns `null` if nothing comes after. +|`substringAfter(exp,after)` | `String` | Returns a substring of the expression that comes after. Returns `null` if nothing comes after. +|`substringBefore(before)` | `String` | Returns a substring of the message body that comes before. Returns `null` if nothing comes before. +|`substringBefore(exp,before)` | `String` | Returns a substring of the expression that comes before. Returns `null` if nothing comes before. +|`substringBetween(after,before)` | `String` | Returns a substring of the message body that are between before and after. Returns `null` if nothing comes between. +|`substringBetween(exp,after,before)` | `String` | Returns a substring of the expression that are between before and after. Returns `null` if nothing comes between. +|`sys.key` | `String` | *Deprecated* To lookup the JVM system property with the given key. +|`sysenv.key` | `String` | To lookup the JVM system property with the given key. +|`threadId` | `String` | Returns the id of the current thread. +|`threadName` | `String` | Returns the name of the current thread. +|`trim()` | `String` | The trim function trims the message body by removing all leading and trailing white spaces. +|`trim(exp)` | `String` | The trim function trims the expression by removing all leading and trailing white spaces. +|`type:name.field` | `Object` | To refer to a type or field by its fully qualified classname. For example: `type:org.apache.camel.Exchange.FILE_NAME`. +|`uppercase()` | `String` | Uppercases the message body +|`uppercase(exp)` | `String` | Uppercases the expression +|`uuid(kind)` | `String` | Returns a UUID using the Camel `UuidGenerator`. You can choose kind between `default`, `classic`, `short`, `simple` and `random` as the kind. If no kind is given, then `default` is used. It is also possible to use a custom `UuidGenerator` and bind the bean to the xref:manual::registry.adoc[Registry] with an id. For example `${uuid(myGenerator)}` where the id is `myGenerator`. +|`variable.key._OGNL_` | `Object` | To look up the variable with the given key and then invoke Camel _OGNL syntax_. +|`variable.key` | `Object` | To look up the variable with the given key. +|`variableAs(key,type)` | `<T>` | To look up the variable with the given key, and convert the value to the given type determined by its classname +|`variable[key]` | `Object` | *Deprecated* To look up the variable with the given key. +|`variables.size` | `int` | The number of `Exchange` variables +|`variables` | `Map` | All the variables from the current `Exchange` as a `java.util.Map`. +|`xpath(exp)` | `Object` | When working with XML data, then this allows using the XPath language, for example, to extract data from the message body (in XML format). This requires having camel-xpath JAR on the classpath. +|`xpath(input,exp)` | `Object` | When working with XML data, then this allows using the XPath language, for example, to extract data from the message body (in XML format). This requires having camel-xpath JAR on the classpath. For input you can choose `header:key`, `exchangeProperty:key` or `variable:key` to use as input for the JSon payload instead of the message body. +|==== |camelId |String |the CamelContext name 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 5c1e240c2a75..850d72d40480 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 @@ -958,4 +958,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 325deb308034..569ac086a221 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 362e499a58d8..616d3ef42ee8 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 @@ -824,6 +824,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 76fc383aaf0f..c197cadf638c 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) { @@ -2086,6 +2141,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 cf87eba403be..a43191c1c61d 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 @@ -2861,6 +2861,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);
