This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch ic
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 8aea4a572383d8a31cfc0db1265d60b62f3478ba
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Jan 30 13:06:37 2026 +0100

    CAMEL-22899: camel-core - Simple language - Add custom function in init 
block via chains
---
 .../org/apache/camel/catalog/languages/simple.json | 133 +++++++++++----------
 .../apache/camel/spi}/SimpleFunctionRegistry.java  |   7 +-
 .../camel/impl/engine/AbstractCamelContext.java    |   4 +
 .../engine}/DefaultSimpleFunctionRegistry.java     |  26 ++--
 .../camel/impl/engine/SimpleCamelContext.java      |   6 +
 .../org/apache/camel/language/simple/simple.json   | 133 +++++++++++----------
 .../modules/languages/pages/simple-language.adoc   |   1 +
 .../camel/language/simple/SimpleConstants.java     |   4 +
 .../language/simple/SimpleExpressionBuilder.java   |   7 +-
 .../language/simple/SimpleExpressionParser.java    |   2 +-
 .../camel/language/simple/SimpleLanguage.java      |   9 --
 .../language/simple/ast/InitBlockExpression.java   |   2 +-
 .../simple/ast/SimpleFunctionExpression.java       |  33 ++---
 .../language/simple/SimpleCustomFunctionTest.java  |  75 ++++++++++++
 .../org/apache/camel/support/PluginHelper.java     |  15 +++
 15 files changed, 277 insertions(+), 180 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 59830496f75b..217e99bad2b9 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
@@ -65,71 +65,72 @@
     "forEach(exp,fun)": { "index": 38, "kind": "function", "displayName": "For 
Each call Function", "group": "function", "label": "function", "required": 
false, "javaType": "List", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Returns a List containing the values returned by the function when applied to 
each value from the input expression", "ognl": false, "suffix": "}" },
     "floor(exp)": { "index": 39, "kind": "function", "displayName": "Floor 
Number", "group": "function", "label": "function", "required": false, 
"javaType": "Integer", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Converts the message 
body (or expression) to a floating number and return the floor value (rounded 
down to nearest integer).", "ognl": false, "suffix": "}" },
     "fromRouteId": { "index": 40, "kind": "function", "displayName": "From 
Route Id", "group": "function", "label": "function", "required": false, 
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "The original route id 
where this exchange was created.", "ognl": false, "suffix": "}" },
-    "hash(exp,algorithm)": { "index": 41, "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": "}" },
-    "header.name": { "index": 42, "kind": "function", "displayName": "Header", 
"group": "function", "label": "function", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The message header with 
the given name", "ognl": true, "suffix": "}" },
-    "headerAs(key,type)": { "index": 43, "kind": "function", "displayName": 
"Header As", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Converts the message 
header to the given type (classname).", "ognl": false, "suffix": "}" },
-    "headers": { "index": 44, "kind": "function", "displayName": "Headers", 
"group": "function", "label": "function", "required": false, "javaType": 
"java.util.Map", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns all the message 
headers in a Map", "ognl": false, "suffix": "}" },
-    "hostName": { "index": 45, "kind": "function", "displayName": "Host Name", 
"group": "function", "label": "function", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the local hostname 
(may be empty if not possible to resolve).", "ognl": false, "suffix": "}" },
-    "id": { "index": 46, "kind": "function", "displayName": "Id", "group": 
"function", "label": "function", "required": false, "javaType": "String", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The message id", "ognl": false, "suffix": "}" 
},
-    "iif(predicate,trueExp,falseExp)": { "index": 47, "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": 
"}" },
-    "isAlpha(exp)": { "index": 48, "kind": "function", "displayName": "Is 
Alphabetic Value", "group": "function", "label": "function", "required": false, 
"javaType": "boolean", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Whether the message 
body (or expression) is alphabetic value (A..Z). For more advanced checks use 
the `regex` operator.", "ognl": false, "suffix": "}" },
-    "isAlphaNumeric(exp)": { "index": 49, "kind": "function", "displayName": 
"Is Alphabetic-Numeric Value", "group": "function", "label": "function", 
"required": false, "javaType": "boolean", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Whether the message body (or expression) is alphanumeric value (A..Z0-9). For 
more advanced checks use the `regex` operator.", "ognl": false, "suffix": "}" },
-    "isEmpty(exp)": { "index": 50, "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": "}" },
-    "isNumeric(exp)": { "index": 51, "kind": "function", "displayName": "Is 
Numeric Value", "group": "function", "label": "function", "required": false, 
"javaType": "boolean", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Whether the message 
body (or expression) is numeric value (0..9). For more advanced checks use the 
`regex` operator.", "ognl": false, "suffix": "}" },
-    "jq(input,exp)": { "index": 52, "kind": "function", "displayName": "JQ", 
"group": "function", "label": "function", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "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. For input (optional), you can [...]
-    "jsonpath(input,exp)": { "index": 53, "kind": "function", "displayName": 
"JSonPath", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "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. For i [...]
-    "join(separator,prefix,exp)": { "index": 54, "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.  [...]
-    "length(exp)": { "index": 55, "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": "}" },
-    "list(val...)": { "index": 56, "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": "}" },
-    "lowercase(exp)": { "index": 57, "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": "}" },
-    "mandatoryBodyAs(type)": { "index": 58, "kind": "function", "displayName": 
"Mandatory Body As", "group": "function", "label": "function", "required": 
false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Converts the message body to the given type (classname). If the body is null 
then the function will fail with an exception", "ognl": true, "suffix": "}" },
-    "map(key1,value1,...)": { "index": 59, "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": "}" },
-    "max(val...)": { "index": 60, "kind": "function", "displayName": "Maximum 
Number", "group": "function", "label": "function", "required": false, 
"javaType": "long", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the maximum number 
from all the values (integral numbers only).", "ognl": false, "suffix": "}" },
-    "messageAs(type)": { "index": 61, "kind": "function", "displayName": 
"Message As", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Converts the message 
to the given type (classname).", "ognl": true, "suffix": "}" },
-    "messageHistory(boolean)": { "index": 62, "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 [...]
-    "messageTimestamp": { "index": 63, "kind": "function", "displayName": 
"Message Timestamp", "group": "function", "label": "function", "required": 
false, "javaType": "long", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "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 timestam [...]
-    "min(val...)": { "index": 64, "kind": "function", "displayName": "Minimum 
Number", "group": "function", "label": "function", "required": false, 
"javaType": "long", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the minimum number 
from all the values (integral numbers only).", "ognl": false, "suffix": "}" },
-    "newEmpty(type)": { "index": 65, "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`. Use `s [...]
-    "normalizeWhitespace(exp)": { "index": 66, "kind": "function", 
"displayName": "Normalize Whitespace", "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": "}" },
-    "not": { "index": 67, "kind": "function", "displayName": "Not", "group": 
"function", "label": "function", "required": false, "javaType": "boolean", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Evaluates the predicate and returns the 
opposite.", "ognl": false, "suffix": "}" },
-    "null": { "index": 68, "kind": "function", "displayName": "Null", "group": 
"function", "label": "function", "required": false, "javaType": "Object", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Represents a null value", "ognl": false, 
"suffix": "}" },
-    "originalBody": { "index": 69, "kind": "function", "displayName": 
"Original Body", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "The original incoming 
body (only available if allowUseOriginalMessage=true).", "ognl": false, 
"suffix": "}" },
-    "pad(exp,width,separator)": { "index": 70, "kind": "function", 
"displayName": "Pad String", "group": "function", "label": "function", 
"required": false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Pads the expression with extra padding if necessary, according the the total 
width. The separator is by default a space. If the width is negative then 
padding to the right, otherwise to the left.", "o [...]
-    "pretty(exp)": { "index": 71, "kind": "function", "displayName": "Pretty 
Print", "group": "function", "label": "function", "required": false, 
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Converts the 
expression to a String, and attempts to pretty print if JSon or XML, otherwise 
the expression is returned as the String value.", "ognl": false, "suffix": "}" 
},
-    "prettyBody": { "index": 72, "kind": "function", "displayName": "Pretty 
Body", "group": "function", "label": "function", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Converts the body to a 
String, and attempts to pretty print if JSon or XML; otherwise the body is 
returned as the String value.", "ognl": false, "suffix": "}" },
-    "properties:key:default": { "index": 73, "kind": "function", 
"displayName": "Property Placeholder", "group": "function", "label": 
"function", "required": false, "javaType": "String", "prefix": "${", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "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.", "ognl": false, "suffix": "}" },
-    "propertiesExist:key": { "index": 74, "kind": "function", "displayName": 
"Property Placeholder Exists", "group": "function", "label": "function", 
"required": false, "javaType": "boolean", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Checks whether a property placeholder with the given key exists or not. The 
result can be negated by prefixing the key with !", "ognl": false, "suffix": 
"}" },
-    "random(min,max)": { "index": 75, "kind": "function", "displayName": 
"Random", "group": "function", "label": "function", "required": false, 
"javaType": "int", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns a random number 
between min and max (exclusive)", "ognl": false, "suffix": "}" },
-    "ref:name": { "index": 76, "kind": "function", "displayName": "Bean By 
Id", "group": "function", "label": "function", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "To look up a bean from the 
Registry with the given name.", "ognl": false, "suffix": "}" },
-    "replace(from,to,exp)": { "index": 77, "kind": "function", "displayName": 
"Replace String Values", "group": "function", "label": "function", "required": 
false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Replace all the string values in the message body\/expression. To make it 
easier to replace single and double quotes, then you can use XML escaped values 
`\\&quot;` as double quote, `\\&apos;`  [...]
-    "reverse(val...)": { "index": 78, "kind": "function", "displayName": 
"Reverse Values", "group": "function", "label": "function", "required": false, 
"javaType": "List", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns a list of all the 
values, but in reverse order", "ognl": false, "suffix": "}" },
-    "routeGroup": { "index": 79, "kind": "function", "displayName": "Route 
Group", "group": "function", "label": "function", "required": false, 
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "The route group of the 
current route the Exchange is being routed. Not all routes have a group 
assigned, so this may be null.", "ognl": false, "suffix": "}" },
-    "routeId": { "index": 80, "kind": "function", "displayName": "Route Id", 
"group": "function", "label": "function", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The route id of the 
current route the Exchange is being routed", "ognl": false, "suffix": "}" },
-    "setVariable(key,exp)": { "index": 81, "kind": "function", "displayName": 
"Set Variable", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Sets an attachment 
with payload from the message body\/expression.", "ognl": false, "suffix": "}" 
},
-    "setHeader(name,type,exp)": { "index": 82, "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": 83, "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": "}" },
-    "shuffle(val...)": { "index": 84, "kind": "function", "displayName": 
"Shuffle Values", "group": "function", "label": "function", "required": false, 
"javaType": "List", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns a list of all the 
values shuffled in random order", "ognl": false, "suffix": "}" },
-    "size(exp)": { "index": 85, "kind": "function", "displayName": "Size", 
"group": "function", "label": "function", "required": false, "javaType": "int", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Returns the number of elements in collection 
or array based payloads. If the value is null then 0 is returned, otherwise 
1.", "ognl": false, "suffix": "}" },
-    "skip(num)": { "index": 86, "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  [...]
-    "split(exp,separator)": { "index": 87, "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": "}" },
-    "stepId": { "index": 88, "kind": "function", "displayName": "Step Id", 
"group": "function", "label": "function", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the id of the 
current step the Exchange is being routed.", "ognl": false, "suffix": "}" },
-    "substring(head,tail)": { "index": 89, "kind": "function", "displayName": 
"Substring", "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. If only one positive number, then the returned 
string is clipped from the beginning. If only one negative number, then the 
returned string is clipped fr [...]
-    "substringAfter(exp,before)": { "index": 90, "kind": "function", 
"displayName": "Substring After", "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 comes after. Returns 
null if nothing comes after.", "ognl": false, "suffix": "}" },
-    "substringBefore(exp,before)": { "index": 91, "kind": "function", 
"displayName": "Substring Before", "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 comes before. Returns 
null if nothing comes before.", "ognl": false, "suffix": "}" },
-    "substringBetween(exp,after,before)": { "index": 92, "kind": "function", 
"displayName": "Substring Between", "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": "}" },
-    "sum(val...)": { "index": 93, "kind": "function", "displayName": 
"Calculate Sum Number", "group": "function", "label": "function", "required": 
false, "javaType": "long", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Sums together all the values as integral numbers. This function can also be 
used to subtract by using negative numbers.", "ognl": false, "suffix": "}" },
-    "sys.name": { "index": 94, "kind": "function", "displayName": "JVM System 
Property", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "The JVM system 
property with the given name", "ognl": false, "suffix": "}" },
-    "threadId": { "index": 95, "kind": "function", "displayName": "Thread Id", 
"group": "function", "label": "function", "required": false, "javaType": 
"long", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the id of the 
current thread. Can be used for logging.", "ognl": false, "suffix": "}" },
-    "threadName": { "index": 96, "kind": "function", "displayName": "Thread 
Name", "group": "function", "label": "function", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the name of the 
current thread. Can be used for logging.", "ognl": false, "suffix": "}" },
-    "throwException(type,msg)": { "index": 97, "kind": "function", 
"displayName": "Throw Exception", "group": "function", "label": "function", 
"required": false, "javaType": "java.lang.Exception", "prefix": "${", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "Deliberately throws an error. Uses 
IllegalArgumentException by default if no type is specified (use fully 
qualified classname).", "ognl": false, "suffix": "}" },
-    "trim(exp)": { "index": 98, "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": "}" },
-    "type:name.field": { "index": 99, "kind": "function", "displayName": "Java 
Field Value", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "To refer to a type or 
field by its classname. To refer to a field, you can append .FIELD_NAME. For 
example, you can refer to the constant field from Exchange as: 
`org.apache.camel.Exchange.FILE_NAME`", " [...]
-    "kindOfType(exp)": { "index": 100, "kind": "function", "displayName": 
"Kind of Type", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "What kind of type is 
the value (null,number,string,boolean,array,object)", "ognl": false, "suffix": 
"}" },
-    "uppercase(exp)": { "index": 101, "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": "}" },
-    "uuid(type)": { "index": 102, "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 `Uuid [...]
-    "variable.name": { "index": 103, "kind": "function", "displayName": 
"Variable", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "The variable with the 
given name", "ognl": true, "suffix": "}" },
-    "variableAs(key,type)": { "index": 104, "kind": "function", "displayName": 
"Variable As", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Converts the variable 
to the given type (classname).", "ognl": false, "suffix": "}" },
-    "variables": { "index": 105, "kind": "function", "displayName": 
"Variables", "group": "function", "label": "function", "required": false, 
"javaType": "java.util.Map", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Returns all the variables from the current Exchange in a Map", "ognl": false, 
"suffix": "}" },
-    "xpath(input,exp)": { "index": 106, "kind": "function", "displayName": 
"XPath", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "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 (optiona [...]
+    "function(name,exp)": { "index": 41, "kind": "function", "displayName": 
"For Each call Function", "group": "function", "label": "function", "required": 
false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Invokes a custom function with the given name using the message body (or 
expression) as input parameter.", "ognl": false, "suffix": "}" },
+    "hash(exp,algorithm)": { "index": 42, "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": "}" },
+    "header.name": { "index": 43, "kind": "function", "displayName": "Header", 
"group": "function", "label": "function", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The message header with 
the given name", "ognl": true, "suffix": "}" },
+    "headerAs(key,type)": { "index": 44, "kind": "function", "displayName": 
"Header As", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Converts the message 
header to the given type (classname).", "ognl": false, "suffix": "}" },
+    "headers": { "index": 45, "kind": "function", "displayName": "Headers", 
"group": "function", "label": "function", "required": false, "javaType": 
"java.util.Map", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns all the message 
headers in a Map", "ognl": false, "suffix": "}" },
+    "hostName": { "index": 46, "kind": "function", "displayName": "Host Name", 
"group": "function", "label": "function", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the local hostname 
(may be empty if not possible to resolve).", "ognl": false, "suffix": "}" },
+    "id": { "index": 47, "kind": "function", "displayName": "Id", "group": 
"function", "label": "function", "required": false, "javaType": "String", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The message id", "ognl": false, "suffix": "}" 
},
+    "iif(predicate,trueExp,falseExp)": { "index": 48, "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": 
"}" },
+    "isAlpha(exp)": { "index": 49, "kind": "function", "displayName": "Is 
Alphabetic Value", "group": "function", "label": "function", "required": false, 
"javaType": "boolean", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Whether the message 
body (or expression) is alphabetic value (A..Z). For more advanced checks use 
the `regex` operator.", "ognl": false, "suffix": "}" },
+    "isAlphaNumeric(exp)": { "index": 50, "kind": "function", "displayName": 
"Is Alphabetic-Numeric Value", "group": "function", "label": "function", 
"required": false, "javaType": "boolean", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Whether the message body (or expression) is alphanumeric value (A..Z0-9). For 
more advanced checks use the `regex` operator.", "ognl": false, "suffix": "}" },
+    "isEmpty(exp)": { "index": 51, "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": "}" },
+    "isNumeric(exp)": { "index": 52, "kind": "function", "displayName": "Is 
Numeric Value", "group": "function", "label": "function", "required": false, 
"javaType": "boolean", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Whether the message 
body (or expression) is numeric value (0..9). For more advanced checks use the 
`regex` operator.", "ognl": false, "suffix": "}" },
+    "jq(input,exp)": { "index": 53, "kind": "function", "displayName": "JQ", 
"group": "function", "label": "function", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "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. For input (optional), you can [...]
+    "jsonpath(input,exp)": { "index": 54, "kind": "function", "displayName": 
"JSonPath", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "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. For i [...]
+    "join(separator,prefix,exp)": { "index": 55, "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.  [...]
+    "length(exp)": { "index": 56, "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": "}" },
+    "list(val...)": { "index": 57, "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": "}" },
+    "lowercase(exp)": { "index": 58, "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": "}" },
+    "mandatoryBodyAs(type)": { "index": 59, "kind": "function", "displayName": 
"Mandatory Body As", "group": "function", "label": "function", "required": 
false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Converts the message body to the given type (classname). If the body is null 
then the function will fail with an exception", "ognl": true, "suffix": "}" },
+    "map(key1,value1,...)": { "index": 60, "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": "}" },
+    "max(val...)": { "index": 61, "kind": "function", "displayName": "Maximum 
Number", "group": "function", "label": "function", "required": false, 
"javaType": "long", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the maximum number 
from all the values (integral numbers only).", "ognl": false, "suffix": "}" },
+    "messageAs(type)": { "index": 62, "kind": "function", "displayName": 
"Message As", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Converts the message 
to the given type (classname).", "ognl": true, "suffix": "}" },
+    "messageHistory(boolean)": { "index": 63, "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 [...]
+    "messageTimestamp": { "index": 64, "kind": "function", "displayName": 
"Message Timestamp", "group": "function", "label": "function", "required": 
false, "javaType": "long", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "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 timestam [...]
+    "min(val...)": { "index": 65, "kind": "function", "displayName": "Minimum 
Number", "group": "function", "label": "function", "required": false, 
"javaType": "long", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the minimum number 
from all the values (integral numbers only).", "ognl": false, "suffix": "}" },
+    "newEmpty(type)": { "index": 66, "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`. Use `s [...]
+    "normalizeWhitespace(exp)": { "index": 67, "kind": "function", 
"displayName": "Normalize Whitespace", "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": "}" },
+    "not": { "index": 68, "kind": "function", "displayName": "Not", "group": 
"function", "label": "function", "required": false, "javaType": "boolean", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Evaluates the predicate and returns the 
opposite.", "ognl": false, "suffix": "}" },
+    "null": { "index": 69, "kind": "function", "displayName": "Null", "group": 
"function", "label": "function", "required": false, "javaType": "Object", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Represents a null value", "ognl": false, 
"suffix": "}" },
+    "originalBody": { "index": 70, "kind": "function", "displayName": 
"Original Body", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "The original incoming 
body (only available if allowUseOriginalMessage=true).", "ognl": false, 
"suffix": "}" },
+    "pad(exp,width,separator)": { "index": 71, "kind": "function", 
"displayName": "Pad String", "group": "function", "label": "function", 
"required": false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Pads the expression with extra padding if necessary, according the the total 
width. The separator is by default a space. If the width is negative then 
padding to the right, otherwise to the left.", "o [...]
+    "pretty(exp)": { "index": 72, "kind": "function", "displayName": "Pretty 
Print", "group": "function", "label": "function", "required": false, 
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Converts the 
expression to a String, and attempts to pretty print if JSon or XML, otherwise 
the expression is returned as the String value.", "ognl": false, "suffix": "}" 
},
+    "prettyBody": { "index": 73, "kind": "function", "displayName": "Pretty 
Body", "group": "function", "label": "function", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Converts the body to a 
String, and attempts to pretty print if JSon or XML; otherwise the body is 
returned as the String value.", "ognl": false, "suffix": "}" },
+    "properties:key:default": { "index": 74, "kind": "function", 
"displayName": "Property Placeholder", "group": "function", "label": 
"function", "required": false, "javaType": "String", "prefix": "${", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "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.", "ognl": false, "suffix": "}" },
+    "propertiesExist:key": { "index": 75, "kind": "function", "displayName": 
"Property Placeholder Exists", "group": "function", "label": "function", 
"required": false, "javaType": "boolean", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Checks whether a property placeholder with the given key exists or not. The 
result can be negated by prefixing the key with !", "ognl": false, "suffix": 
"}" },
+    "random(min,max)": { "index": 76, "kind": "function", "displayName": 
"Random", "group": "function", "label": "function", "required": false, 
"javaType": "int", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns a random number 
between min and max (exclusive)", "ognl": false, "suffix": "}" },
+    "ref:name": { "index": 77, "kind": "function", "displayName": "Bean By 
Id", "group": "function", "label": "function", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "To look up a bean from the 
Registry with the given name.", "ognl": false, "suffix": "}" },
+    "replace(from,to,exp)": { "index": 78, "kind": "function", "displayName": 
"Replace String Values", "group": "function", "label": "function", "required": 
false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Replace all the string values in the message body\/expression. To make it 
easier to replace single and double quotes, then you can use XML escaped values 
`\\&quot;` as double quote, `\\&apos;`  [...]
+    "reverse(val...)": { "index": 79, "kind": "function", "displayName": 
"Reverse Values", "group": "function", "label": "function", "required": false, 
"javaType": "List", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns a list of all the 
values, but in reverse order", "ognl": false, "suffix": "}" },
+    "routeGroup": { "index": 80, "kind": "function", "displayName": "Route 
Group", "group": "function", "label": "function", "required": false, 
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "The route group of the 
current route the Exchange is being routed. Not all routes have a group 
assigned, so this may be null.", "ognl": false, "suffix": "}" },
+    "routeId": { "index": 81, "kind": "function", "displayName": "Route Id", 
"group": "function", "label": "function", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The route id of the 
current route the Exchange is being routed", "ognl": false, "suffix": "}" },
+    "setVariable(key,exp)": { "index": 82, "kind": "function", "displayName": 
"Set Variable", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Sets an attachment 
with payload from the message body\/expression.", "ognl": false, "suffix": "}" 
},
+    "setHeader(name,type,exp)": { "index": 83, "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": 84, "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": "}" },
+    "shuffle(val...)": { "index": 85, "kind": "function", "displayName": 
"Shuffle Values", "group": "function", "label": "function", "required": false, 
"javaType": "List", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns a list of all the 
values shuffled in random order", "ognl": false, "suffix": "}" },
+    "size(exp)": { "index": 86, "kind": "function", "displayName": "Size", 
"group": "function", "label": "function", "required": false, "javaType": "int", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Returns the number of elements in collection 
or array based payloads. If the value is null then 0 is returned, otherwise 
1.", "ognl": false, "suffix": "}" },
+    "skip(num)": { "index": 87, "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  [...]
+    "split(exp,separator)": { "index": 88, "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": "}" },
+    "stepId": { "index": 89, "kind": "function", "displayName": "Step Id", 
"group": "function", "label": "function", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the id of the 
current step the Exchange is being routed.", "ognl": false, "suffix": "}" },
+    "substring(head,tail)": { "index": 90, "kind": "function", "displayName": 
"Substring", "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. If only one positive number, then the returned 
string is clipped from the beginning. If only one negative number, then the 
returned string is clipped fr [...]
+    "substringAfter(exp,before)": { "index": 91, "kind": "function", 
"displayName": "Substring After", "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 comes after. Returns 
null if nothing comes after.", "ognl": false, "suffix": "}" },
+    "substringBefore(exp,before)": { "index": 92, "kind": "function", 
"displayName": "Substring Before", "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 comes before. Returns 
null if nothing comes before.", "ognl": false, "suffix": "}" },
+    "substringBetween(exp,after,before)": { "index": 93, "kind": "function", 
"displayName": "Substring Between", "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": "}" },
+    "sum(val...)": { "index": 94, "kind": "function", "displayName": 
"Calculate Sum Number", "group": "function", "label": "function", "required": 
false, "javaType": "long", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Sums together all the values as integral numbers. This function can also be 
used to subtract by using negative numbers.", "ognl": false, "suffix": "}" },
+    "sys.name": { "index": 95, "kind": "function", "displayName": "JVM System 
Property", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "The JVM system 
property with the given name", "ognl": false, "suffix": "}" },
+    "threadId": { "index": 96, "kind": "function", "displayName": "Thread Id", 
"group": "function", "label": "function", "required": false, "javaType": 
"long", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the id of the 
current thread. Can be used for logging.", "ognl": false, "suffix": "}" },
+    "threadName": { "index": 97, "kind": "function", "displayName": "Thread 
Name", "group": "function", "label": "function", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the name of the 
current thread. Can be used for logging.", "ognl": false, "suffix": "}" },
+    "throwException(type,msg)": { "index": 98, "kind": "function", 
"displayName": "Throw Exception", "group": "function", "label": "function", 
"required": false, "javaType": "java.lang.Exception", "prefix": "${", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "Deliberately throws an error. Uses 
IllegalArgumentException by default if no type is specified (use fully 
qualified classname).", "ognl": false, "suffix": "}" },
+    "trim(exp)": { "index": 99, "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": "}" },
+    "type:name.field": { "index": 100, "kind": "function", "displayName": 
"Java Field Value", "group": "function", "label": "function", "required": 
false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "To 
refer to a type or field by its classname. To refer to a field, you can append 
.FIELD_NAME. For example, you can refer to the constant field from Exchange as: 
`org.apache.camel.Exchange.FILE_NAME`",  [...]
+    "kindOfType(exp)": { "index": 101, "kind": "function", "displayName": 
"Kind of Type", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "What kind of type is 
the value (null,number,string,boolean,array,object)", "ognl": false, "suffix": 
"}" },
+    "uppercase(exp)": { "index": 102, "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": "}" },
+    "uuid(type)": { "index": 103, "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 `Uuid [...]
+    "variable.name": { "index": 104, "kind": "function", "displayName": 
"Variable", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "The variable with the 
given name", "ognl": true, "suffix": "}" },
+    "variableAs(key,type)": { "index": 105, "kind": "function", "displayName": 
"Variable As", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Converts the variable 
to the given type (classname).", "ognl": false, "suffix": "}" },
+    "variables": { "index": 106, "kind": "function", "displayName": 
"Variables", "group": "function", "label": "function", "required": false, 
"javaType": "java.util.Map", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Returns all the variables from the current Exchange in a Map", "ognl": false, 
"suffix": "}" },
+    "xpath(input,exp)": { "index": 107, "kind": "function", "displayName": 
"XPath", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "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 (optiona [...]
   }
 }
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionRegistry.java
 b/core/camel-api/src/main/java/org/apache/camel/spi/SimpleFunctionRegistry.java
similarity index 93%
rename from 
core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionRegistry.java
rename to 
core/camel-api/src/main/java/org/apache/camel/spi/SimpleFunctionRegistry.java
index 06c0f17644ff..d0bcc1d061db 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleFunctionRegistry.java
+++ 
b/core/camel-api/src/main/java/org/apache/camel/spi/SimpleFunctionRegistry.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.language.simple;
+package org.apache.camel.spi;
 
 import org.apache.camel.Expression;
 
@@ -46,4 +46,9 @@ public interface SimpleFunctionRegistry {
      */
     Expression getFunction(String name);
 
+    /**
+     * Number of custom functions
+     */
+    int size();
+
 }
diff --git 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
index 5b1e7da577c3..77510a1632a5 100644
--- 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
+++ 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
@@ -167,6 +167,7 @@ import org.apache.camel.spi.RouteTemplateParameterSource;
 import org.apache.camel.spi.RoutesLoader;
 import org.apache.camel.spi.RuntimeEndpointRegistry;
 import org.apache.camel.spi.ShutdownStrategy;
+import org.apache.camel.spi.SimpleFunctionRegistry;
 import org.apache.camel.spi.StartupConditionStrategy;
 import org.apache.camel.spi.StartupStepRecorder;
 import org.apache.camel.spi.StreamCachingStrategy;
@@ -402,6 +403,7 @@ public abstract class AbstractCamelContext extends 
BaseService
         camelContextExtension.lazyAddContextPlugin(DumpRoutesStrategy.class, 
this::createDumpRoutesStrategy);
         camelContextExtension.lazyAddContextPlugin(BackOffTimerFactory.class, 
this::createBackOffTimerFactory);
         camelContextExtension.lazyAddContextPlugin(GroovyScriptCompiler.class, 
this::createGroovyScriptCompiler);
+        
camelContextExtension.lazyAddContextPlugin(SimpleFunctionRegistry.class, 
this::createSimpleFunctionRegistry);
     }
 
     protected static <T> T lookup(CamelContext context, String ref, Class<T> 
type) {
@@ -4451,6 +4453,8 @@ public abstract class AbstractCamelContext extends 
BaseService
 
     protected abstract GroovyScriptCompiler createGroovyScriptCompiler();
 
+    protected abstract SimpleFunctionRegistry createSimpleFunctionRegistry();
+
     protected abstract BeanProxyFactory createBeanProxyFactory();
 
     protected abstract AnnotationBasedProcessorFactory 
createAnnotationBasedProcessorFactory();
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/DefaultSimpleFunctionRegistry.java
 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultSimpleFunctionRegistry.java
similarity index 80%
rename from 
core/camel-core-languages/src/main/java/org/apache/camel/language/simple/DefaultSimpleFunctionRegistry.java
rename to 
core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultSimpleFunctionRegistry.java
index 904b58d7db96..33030f5471de 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/DefaultSimpleFunctionRegistry.java
+++ 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultSimpleFunctionRegistry.java
@@ -14,30 +14,26 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.language.simple;
+package org.apache.camel.impl.engine;
 
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.camel.CamelContext;
-import org.apache.camel.CamelContextAware;
 import org.apache.camel.Expression;
-import org.apache.camel.NonManagedService;
+import org.apache.camel.StaticService;
+import org.apache.camel.spi.SimpleFunctionRegistry;
 import org.apache.camel.support.service.ServiceSupport;
 
-public class DefaultSimpleFunctionRegistry extends ServiceSupport
-        implements CamelContextAware, SimpleFunctionRegistry, 
NonManagedService {
+/**
+ * Default {@link SimpleFunctionRegistry}.
+ */
+public class DefaultSimpleFunctionRegistry extends ServiceSupport implements 
SimpleFunctionRegistry, StaticService {
 
     private final Map<String, Expression> functions = new 
ConcurrentHashMap<>();
-    private CamelContext camelContext;
-
-    @Override
-    public CamelContext getCamelContext() {
-        return camelContext;
-    }
+    private final CamelContext camelContext;
 
-    @Override
-    public void setCamelContext(CamelContext camelContext) {
+    public DefaultSimpleFunctionRegistry(CamelContext camelContext) {
         this.camelContext = camelContext;
     }
 
@@ -63,4 +59,8 @@ public class DefaultSimpleFunctionRegistry extends 
ServiceSupport
         functions.clear();
     }
 
+    @Override
+    public int size() {
+        return functions.size();
+    }
 }
diff --git 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java
 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java
index d96b9658a92e..cd5884d4ccd1 100644
--- 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java
+++ 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java
@@ -91,6 +91,7 @@ import org.apache.camel.spi.RouteController;
 import org.apache.camel.spi.RouteFactory;
 import org.apache.camel.spi.RoutesLoader;
 import org.apache.camel.spi.ShutdownStrategy;
+import org.apache.camel.spi.SimpleFunctionRegistry;
 import org.apache.camel.spi.StartupConditionStrategy;
 import org.apache.camel.spi.StreamCachingStrategy;
 import org.apache.camel.spi.Tracer;
@@ -485,6 +486,11 @@ public class SimpleCamelContext extends 
AbstractCamelContext {
         return result.orElse(null);
     }
 
+    @Override
+    protected SimpleFunctionRegistry createSimpleFunctionRegistry() {
+        return new DefaultSimpleFunctionRegistry(getCamelContextReference());
+    }
+
     private CliConnectorFactory createCliConnectorFactory() {
         // lookup in registry first
         CliConnectorFactory ccf = 
getCamelContextReference().getRegistry().findSingleByType(CliConnectorFactory.class);
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 59830496f75b..217e99bad2b9 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
@@ -65,71 +65,72 @@
     "forEach(exp,fun)": { "index": 38, "kind": "function", "displayName": "For 
Each call Function", "group": "function", "label": "function", "required": 
false, "javaType": "List", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Returns a List containing the values returned by the function when applied to 
each value from the input expression", "ognl": false, "suffix": "}" },
     "floor(exp)": { "index": 39, "kind": "function", "displayName": "Floor 
Number", "group": "function", "label": "function", "required": false, 
"javaType": "Integer", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Converts the message 
body (or expression) to a floating number and return the floor value (rounded 
down to nearest integer).", "ognl": false, "suffix": "}" },
     "fromRouteId": { "index": 40, "kind": "function", "displayName": "From 
Route Id", "group": "function", "label": "function", "required": false, 
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "The original route id 
where this exchange was created.", "ognl": false, "suffix": "}" },
-    "hash(exp,algorithm)": { "index": 41, "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": "}" },
-    "header.name": { "index": 42, "kind": "function", "displayName": "Header", 
"group": "function", "label": "function", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The message header with 
the given name", "ognl": true, "suffix": "}" },
-    "headerAs(key,type)": { "index": 43, "kind": "function", "displayName": 
"Header As", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Converts the message 
header to the given type (classname).", "ognl": false, "suffix": "}" },
-    "headers": { "index": 44, "kind": "function", "displayName": "Headers", 
"group": "function", "label": "function", "required": false, "javaType": 
"java.util.Map", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns all the message 
headers in a Map", "ognl": false, "suffix": "}" },
-    "hostName": { "index": 45, "kind": "function", "displayName": "Host Name", 
"group": "function", "label": "function", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the local hostname 
(may be empty if not possible to resolve).", "ognl": false, "suffix": "}" },
-    "id": { "index": 46, "kind": "function", "displayName": "Id", "group": 
"function", "label": "function", "required": false, "javaType": "String", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The message id", "ognl": false, "suffix": "}" 
},
-    "iif(predicate,trueExp,falseExp)": { "index": 47, "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": 
"}" },
-    "isAlpha(exp)": { "index": 48, "kind": "function", "displayName": "Is 
Alphabetic Value", "group": "function", "label": "function", "required": false, 
"javaType": "boolean", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Whether the message 
body (or expression) is alphabetic value (A..Z). For more advanced checks use 
the `regex` operator.", "ognl": false, "suffix": "}" },
-    "isAlphaNumeric(exp)": { "index": 49, "kind": "function", "displayName": 
"Is Alphabetic-Numeric Value", "group": "function", "label": "function", 
"required": false, "javaType": "boolean", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Whether the message body (or expression) is alphanumeric value (A..Z0-9). For 
more advanced checks use the `regex` operator.", "ognl": false, "suffix": "}" },
-    "isEmpty(exp)": { "index": 50, "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": "}" },
-    "isNumeric(exp)": { "index": 51, "kind": "function", "displayName": "Is 
Numeric Value", "group": "function", "label": "function", "required": false, 
"javaType": "boolean", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Whether the message 
body (or expression) is numeric value (0..9). For more advanced checks use the 
`regex` operator.", "ognl": false, "suffix": "}" },
-    "jq(input,exp)": { "index": 52, "kind": "function", "displayName": "JQ", 
"group": "function", "label": "function", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "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. For input (optional), you can [...]
-    "jsonpath(input,exp)": { "index": 53, "kind": "function", "displayName": 
"JSonPath", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "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. For i [...]
-    "join(separator,prefix,exp)": { "index": 54, "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.  [...]
-    "length(exp)": { "index": 55, "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": "}" },
-    "list(val...)": { "index": 56, "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": "}" },
-    "lowercase(exp)": { "index": 57, "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": "}" },
-    "mandatoryBodyAs(type)": { "index": 58, "kind": "function", "displayName": 
"Mandatory Body As", "group": "function", "label": "function", "required": 
false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Converts the message body to the given type (classname). If the body is null 
then the function will fail with an exception", "ognl": true, "suffix": "}" },
-    "map(key1,value1,...)": { "index": 59, "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": "}" },
-    "max(val...)": { "index": 60, "kind": "function", "displayName": "Maximum 
Number", "group": "function", "label": "function", "required": false, 
"javaType": "long", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the maximum number 
from all the values (integral numbers only).", "ognl": false, "suffix": "}" },
-    "messageAs(type)": { "index": 61, "kind": "function", "displayName": 
"Message As", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Converts the message 
to the given type (classname).", "ognl": true, "suffix": "}" },
-    "messageHistory(boolean)": { "index": 62, "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 [...]
-    "messageTimestamp": { "index": 63, "kind": "function", "displayName": 
"Message Timestamp", "group": "function", "label": "function", "required": 
false, "javaType": "long", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "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 timestam [...]
-    "min(val...)": { "index": 64, "kind": "function", "displayName": "Minimum 
Number", "group": "function", "label": "function", "required": false, 
"javaType": "long", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the minimum number 
from all the values (integral numbers only).", "ognl": false, "suffix": "}" },
-    "newEmpty(type)": { "index": 65, "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`. Use `s [...]
-    "normalizeWhitespace(exp)": { "index": 66, "kind": "function", 
"displayName": "Normalize Whitespace", "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": "}" },
-    "not": { "index": 67, "kind": "function", "displayName": "Not", "group": 
"function", "label": "function", "required": false, "javaType": "boolean", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Evaluates the predicate and returns the 
opposite.", "ognl": false, "suffix": "}" },
-    "null": { "index": 68, "kind": "function", "displayName": "Null", "group": 
"function", "label": "function", "required": false, "javaType": "Object", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Represents a null value", "ognl": false, 
"suffix": "}" },
-    "originalBody": { "index": 69, "kind": "function", "displayName": 
"Original Body", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "The original incoming 
body (only available if allowUseOriginalMessage=true).", "ognl": false, 
"suffix": "}" },
-    "pad(exp,width,separator)": { "index": 70, "kind": "function", 
"displayName": "Pad String", "group": "function", "label": "function", 
"required": false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Pads the expression with extra padding if necessary, according the the total 
width. The separator is by default a space. If the width is negative then 
padding to the right, otherwise to the left.", "o [...]
-    "pretty(exp)": { "index": 71, "kind": "function", "displayName": "Pretty 
Print", "group": "function", "label": "function", "required": false, 
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Converts the 
expression to a String, and attempts to pretty print if JSon or XML, otherwise 
the expression is returned as the String value.", "ognl": false, "suffix": "}" 
},
-    "prettyBody": { "index": 72, "kind": "function", "displayName": "Pretty 
Body", "group": "function", "label": "function", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Converts the body to a 
String, and attempts to pretty print if JSon or XML; otherwise the body is 
returned as the String value.", "ognl": false, "suffix": "}" },
-    "properties:key:default": { "index": 73, "kind": "function", 
"displayName": "Property Placeholder", "group": "function", "label": 
"function", "required": false, "javaType": "String", "prefix": "${", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "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.", "ognl": false, "suffix": "}" },
-    "propertiesExist:key": { "index": 74, "kind": "function", "displayName": 
"Property Placeholder Exists", "group": "function", "label": "function", 
"required": false, "javaType": "boolean", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Checks whether a property placeholder with the given key exists or not. The 
result can be negated by prefixing the key with !", "ognl": false, "suffix": 
"}" },
-    "random(min,max)": { "index": 75, "kind": "function", "displayName": 
"Random", "group": "function", "label": "function", "required": false, 
"javaType": "int", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns a random number 
between min and max (exclusive)", "ognl": false, "suffix": "}" },
-    "ref:name": { "index": 76, "kind": "function", "displayName": "Bean By 
Id", "group": "function", "label": "function", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "To look up a bean from the 
Registry with the given name.", "ognl": false, "suffix": "}" },
-    "replace(from,to,exp)": { "index": 77, "kind": "function", "displayName": 
"Replace String Values", "group": "function", "label": "function", "required": 
false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Replace all the string values in the message body\/expression. To make it 
easier to replace single and double quotes, then you can use XML escaped values 
`\\&quot;` as double quote, `\\&apos;`  [...]
-    "reverse(val...)": { "index": 78, "kind": "function", "displayName": 
"Reverse Values", "group": "function", "label": "function", "required": false, 
"javaType": "List", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns a list of all the 
values, but in reverse order", "ognl": false, "suffix": "}" },
-    "routeGroup": { "index": 79, "kind": "function", "displayName": "Route 
Group", "group": "function", "label": "function", "required": false, 
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "The route group of the 
current route the Exchange is being routed. Not all routes have a group 
assigned, so this may be null.", "ognl": false, "suffix": "}" },
-    "routeId": { "index": 80, "kind": "function", "displayName": "Route Id", 
"group": "function", "label": "function", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The route id of the 
current route the Exchange is being routed", "ognl": false, "suffix": "}" },
-    "setVariable(key,exp)": { "index": 81, "kind": "function", "displayName": 
"Set Variable", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Sets an attachment 
with payload from the message body\/expression.", "ognl": false, "suffix": "}" 
},
-    "setHeader(name,type,exp)": { "index": 82, "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": 83, "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": "}" },
-    "shuffle(val...)": { "index": 84, "kind": "function", "displayName": 
"Shuffle Values", "group": "function", "label": "function", "required": false, 
"javaType": "List", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns a list of all the 
values shuffled in random order", "ognl": false, "suffix": "}" },
-    "size(exp)": { "index": 85, "kind": "function", "displayName": "Size", 
"group": "function", "label": "function", "required": false, "javaType": "int", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Returns the number of elements in collection 
or array based payloads. If the value is null then 0 is returned, otherwise 
1.", "ognl": false, "suffix": "}" },
-    "skip(num)": { "index": 86, "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  [...]
-    "split(exp,separator)": { "index": 87, "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": "}" },
-    "stepId": { "index": 88, "kind": "function", "displayName": "Step Id", 
"group": "function", "label": "function", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the id of the 
current step the Exchange is being routed.", "ognl": false, "suffix": "}" },
-    "substring(head,tail)": { "index": 89, "kind": "function", "displayName": 
"Substring", "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. If only one positive number, then the returned 
string is clipped from the beginning. If only one negative number, then the 
returned string is clipped fr [...]
-    "substringAfter(exp,before)": { "index": 90, "kind": "function", 
"displayName": "Substring After", "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 comes after. Returns 
null if nothing comes after.", "ognl": false, "suffix": "}" },
-    "substringBefore(exp,before)": { "index": 91, "kind": "function", 
"displayName": "Substring Before", "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 comes before. Returns 
null if nothing comes before.", "ognl": false, "suffix": "}" },
-    "substringBetween(exp,after,before)": { "index": 92, "kind": "function", 
"displayName": "Substring Between", "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": "}" },
-    "sum(val...)": { "index": 93, "kind": "function", "displayName": 
"Calculate Sum Number", "group": "function", "label": "function", "required": 
false, "javaType": "long", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Sums together all the values as integral numbers. This function can also be 
used to subtract by using negative numbers.", "ognl": false, "suffix": "}" },
-    "sys.name": { "index": 94, "kind": "function", "displayName": "JVM System 
Property", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "The JVM system 
property with the given name", "ognl": false, "suffix": "}" },
-    "threadId": { "index": 95, "kind": "function", "displayName": "Thread Id", 
"group": "function", "label": "function", "required": false, "javaType": 
"long", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the id of the 
current thread. Can be used for logging.", "ognl": false, "suffix": "}" },
-    "threadName": { "index": 96, "kind": "function", "displayName": "Thread 
Name", "group": "function", "label": "function", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the name of the 
current thread. Can be used for logging.", "ognl": false, "suffix": "}" },
-    "throwException(type,msg)": { "index": 97, "kind": "function", 
"displayName": "Throw Exception", "group": "function", "label": "function", 
"required": false, "javaType": "java.lang.Exception", "prefix": "${", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "Deliberately throws an error. Uses 
IllegalArgumentException by default if no type is specified (use fully 
qualified classname).", "ognl": false, "suffix": "}" },
-    "trim(exp)": { "index": 98, "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": "}" },
-    "type:name.field": { "index": 99, "kind": "function", "displayName": "Java 
Field Value", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "To refer to a type or 
field by its classname. To refer to a field, you can append .FIELD_NAME. For 
example, you can refer to the constant field from Exchange as: 
`org.apache.camel.Exchange.FILE_NAME`", " [...]
-    "kindOfType(exp)": { "index": 100, "kind": "function", "displayName": 
"Kind of Type", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "What kind of type is 
the value (null,number,string,boolean,array,object)", "ognl": false, "suffix": 
"}" },
-    "uppercase(exp)": { "index": 101, "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": "}" },
-    "uuid(type)": { "index": 102, "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 `Uuid [...]
-    "variable.name": { "index": 103, "kind": "function", "displayName": 
"Variable", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "The variable with the 
given name", "ognl": true, "suffix": "}" },
-    "variableAs(key,type)": { "index": 104, "kind": "function", "displayName": 
"Variable As", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Converts the variable 
to the given type (classname).", "ognl": false, "suffix": "}" },
-    "variables": { "index": 105, "kind": "function", "displayName": 
"Variables", "group": "function", "label": "function", "required": false, 
"javaType": "java.util.Map", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Returns all the variables from the current Exchange in a Map", "ognl": false, 
"suffix": "}" },
-    "xpath(input,exp)": { "index": 106, "kind": "function", "displayName": 
"XPath", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "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 (optiona [...]
+    "function(name,exp)": { "index": 41, "kind": "function", "displayName": 
"For Each call Function", "group": "function", "label": "function", "required": 
false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Invokes a custom function with the given name using the message body (or 
expression) as input parameter.", "ognl": false, "suffix": "}" },
+    "hash(exp,algorithm)": { "index": 42, "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": "}" },
+    "header.name": { "index": 43, "kind": "function", "displayName": "Header", 
"group": "function", "label": "function", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The message header with 
the given name", "ognl": true, "suffix": "}" },
+    "headerAs(key,type)": { "index": 44, "kind": "function", "displayName": 
"Header As", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Converts the message 
header to the given type (classname).", "ognl": false, "suffix": "}" },
+    "headers": { "index": 45, "kind": "function", "displayName": "Headers", 
"group": "function", "label": "function", "required": false, "javaType": 
"java.util.Map", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns all the message 
headers in a Map", "ognl": false, "suffix": "}" },
+    "hostName": { "index": 46, "kind": "function", "displayName": "Host Name", 
"group": "function", "label": "function", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the local hostname 
(may be empty if not possible to resolve).", "ognl": false, "suffix": "}" },
+    "id": { "index": 47, "kind": "function", "displayName": "Id", "group": 
"function", "label": "function", "required": false, "javaType": "String", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The message id", "ognl": false, "suffix": "}" 
},
+    "iif(predicate,trueExp,falseExp)": { "index": 48, "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": 
"}" },
+    "isAlpha(exp)": { "index": 49, "kind": "function", "displayName": "Is 
Alphabetic Value", "group": "function", "label": "function", "required": false, 
"javaType": "boolean", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Whether the message 
body (or expression) is alphabetic value (A..Z). For more advanced checks use 
the `regex` operator.", "ognl": false, "suffix": "}" },
+    "isAlphaNumeric(exp)": { "index": 50, "kind": "function", "displayName": 
"Is Alphabetic-Numeric Value", "group": "function", "label": "function", 
"required": false, "javaType": "boolean", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Whether the message body (or expression) is alphanumeric value (A..Z0-9). For 
more advanced checks use the `regex` operator.", "ognl": false, "suffix": "}" },
+    "isEmpty(exp)": { "index": 51, "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": "}" },
+    "isNumeric(exp)": { "index": 52, "kind": "function", "displayName": "Is 
Numeric Value", "group": "function", "label": "function", "required": false, 
"javaType": "boolean", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Whether the message 
body (or expression) is numeric value (0..9). For more advanced checks use the 
`regex` operator.", "ognl": false, "suffix": "}" },
+    "jq(input,exp)": { "index": 53, "kind": "function", "displayName": "JQ", 
"group": "function", "label": "function", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "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. For input (optional), you can [...]
+    "jsonpath(input,exp)": { "index": 54, "kind": "function", "displayName": 
"JSonPath", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "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. For i [...]
+    "join(separator,prefix,exp)": { "index": 55, "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.  [...]
+    "length(exp)": { "index": 56, "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": "}" },
+    "list(val...)": { "index": 57, "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": "}" },
+    "lowercase(exp)": { "index": 58, "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": "}" },
+    "mandatoryBodyAs(type)": { "index": 59, "kind": "function", "displayName": 
"Mandatory Body As", "group": "function", "label": "function", "required": 
false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Converts the message body to the given type (classname). If the body is null 
then the function will fail with an exception", "ognl": true, "suffix": "}" },
+    "map(key1,value1,...)": { "index": 60, "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": "}" },
+    "max(val...)": { "index": 61, "kind": "function", "displayName": "Maximum 
Number", "group": "function", "label": "function", "required": false, 
"javaType": "long", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the maximum number 
from all the values (integral numbers only).", "ognl": false, "suffix": "}" },
+    "messageAs(type)": { "index": 62, "kind": "function", "displayName": 
"Message As", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Converts the message 
to the given type (classname).", "ognl": true, "suffix": "}" },
+    "messageHistory(boolean)": { "index": 63, "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 [...]
+    "messageTimestamp": { "index": 64, "kind": "function", "displayName": 
"Message Timestamp", "group": "function", "label": "function", "required": 
false, "javaType": "long", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "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 timestam [...]
+    "min(val...)": { "index": 65, "kind": "function", "displayName": "Minimum 
Number", "group": "function", "label": "function", "required": false, 
"javaType": "long", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the minimum number 
from all the values (integral numbers only).", "ognl": false, "suffix": "}" },
+    "newEmpty(type)": { "index": 66, "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`. Use `s [...]
+    "normalizeWhitespace(exp)": { "index": 67, "kind": "function", 
"displayName": "Normalize Whitespace", "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": "}" },
+    "not": { "index": 68, "kind": "function", "displayName": "Not", "group": 
"function", "label": "function", "required": false, "javaType": "boolean", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Evaluates the predicate and returns the 
opposite.", "ognl": false, "suffix": "}" },
+    "null": { "index": 69, "kind": "function", "displayName": "Null", "group": 
"function", "label": "function", "required": false, "javaType": "Object", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Represents a null value", "ognl": false, 
"suffix": "}" },
+    "originalBody": { "index": 70, "kind": "function", "displayName": 
"Original Body", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "The original incoming 
body (only available if allowUseOriginalMessage=true).", "ognl": false, 
"suffix": "}" },
+    "pad(exp,width,separator)": { "index": 71, "kind": "function", 
"displayName": "Pad String", "group": "function", "label": "function", 
"required": false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Pads the expression with extra padding if necessary, according the the total 
width. The separator is by default a space. If the width is negative then 
padding to the right, otherwise to the left.", "o [...]
+    "pretty(exp)": { "index": 72, "kind": "function", "displayName": "Pretty 
Print", "group": "function", "label": "function", "required": false, 
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Converts the 
expression to a String, and attempts to pretty print if JSon or XML, otherwise 
the expression is returned as the String value.", "ognl": false, "suffix": "}" 
},
+    "prettyBody": { "index": 73, "kind": "function", "displayName": "Pretty 
Body", "group": "function", "label": "function", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Converts the body to a 
String, and attempts to pretty print if JSon or XML; otherwise the body is 
returned as the String value.", "ognl": false, "suffix": "}" },
+    "properties:key:default": { "index": 74, "kind": "function", 
"displayName": "Property Placeholder", "group": "function", "label": 
"function", "required": false, "javaType": "String", "prefix": "${", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "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.", "ognl": false, "suffix": "}" },
+    "propertiesExist:key": { "index": 75, "kind": "function", "displayName": 
"Property Placeholder Exists", "group": "function", "label": "function", 
"required": false, "javaType": "boolean", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Checks whether a property placeholder with the given key exists or not. The 
result can be negated by prefixing the key with !", "ognl": false, "suffix": 
"}" },
+    "random(min,max)": { "index": 76, "kind": "function", "displayName": 
"Random", "group": "function", "label": "function", "required": false, 
"javaType": "int", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns a random number 
between min and max (exclusive)", "ognl": false, "suffix": "}" },
+    "ref:name": { "index": 77, "kind": "function", "displayName": "Bean By 
Id", "group": "function", "label": "function", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "To look up a bean from the 
Registry with the given name.", "ognl": false, "suffix": "}" },
+    "replace(from,to,exp)": { "index": 78, "kind": "function", "displayName": 
"Replace String Values", "group": "function", "label": "function", "required": 
false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Replace all the string values in the message body\/expression. To make it 
easier to replace single and double quotes, then you can use XML escaped values 
`\\&quot;` as double quote, `\\&apos;`  [...]
+    "reverse(val...)": { "index": 79, "kind": "function", "displayName": 
"Reverse Values", "group": "function", "label": "function", "required": false, 
"javaType": "List", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns a list of all the 
values, but in reverse order", "ognl": false, "suffix": "}" },
+    "routeGroup": { "index": 80, "kind": "function", "displayName": "Route 
Group", "group": "function", "label": "function", "required": false, 
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "The route group of the 
current route the Exchange is being routed. Not all routes have a group 
assigned, so this may be null.", "ognl": false, "suffix": "}" },
+    "routeId": { "index": 81, "kind": "function", "displayName": "Route Id", 
"group": "function", "label": "function", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The route id of the 
current route the Exchange is being routed", "ognl": false, "suffix": "}" },
+    "setVariable(key,exp)": { "index": 82, "kind": "function", "displayName": 
"Set Variable", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Sets an attachment 
with payload from the message body\/expression.", "ognl": false, "suffix": "}" 
},
+    "setHeader(name,type,exp)": { "index": 83, "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": 84, "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": "}" },
+    "shuffle(val...)": { "index": 85, "kind": "function", "displayName": 
"Shuffle Values", "group": "function", "label": "function", "required": false, 
"javaType": "List", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns a list of all the 
values shuffled in random order", "ognl": false, "suffix": "}" },
+    "size(exp)": { "index": 86, "kind": "function", "displayName": "Size", 
"group": "function", "label": "function", "required": false, "javaType": "int", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Returns the number of elements in collection 
or array based payloads. If the value is null then 0 is returned, otherwise 
1.", "ognl": false, "suffix": "}" },
+    "skip(num)": { "index": 87, "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  [...]
+    "split(exp,separator)": { "index": 88, "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": "}" },
+    "stepId": { "index": 89, "kind": "function", "displayName": "Step Id", 
"group": "function", "label": "function", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the id of the 
current step the Exchange is being routed.", "ognl": false, "suffix": "}" },
+    "substring(head,tail)": { "index": 90, "kind": "function", "displayName": 
"Substring", "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. If only one positive number, then the returned 
string is clipped from the beginning. If only one negative number, then the 
returned string is clipped fr [...]
+    "substringAfter(exp,before)": { "index": 91, "kind": "function", 
"displayName": "Substring After", "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 comes after. Returns 
null if nothing comes after.", "ognl": false, "suffix": "}" },
+    "substringBefore(exp,before)": { "index": 92, "kind": "function", 
"displayName": "Substring Before", "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 comes before. Returns 
null if nothing comes before.", "ognl": false, "suffix": "}" },
+    "substringBetween(exp,after,before)": { "index": 93, "kind": "function", 
"displayName": "Substring Between", "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": "}" },
+    "sum(val...)": { "index": 94, "kind": "function", "displayName": 
"Calculate Sum Number", "group": "function", "label": "function", "required": 
false, "javaType": "long", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Sums together all the values as integral numbers. This function can also be 
used to subtract by using negative numbers.", "ognl": false, "suffix": "}" },
+    "sys.name": { "index": 95, "kind": "function", "displayName": "JVM System 
Property", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "The JVM system 
property with the given name", "ognl": false, "suffix": "}" },
+    "threadId": { "index": 96, "kind": "function", "displayName": "Thread Id", 
"group": "function", "label": "function", "required": false, "javaType": 
"long", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the id of the 
current thread. Can be used for logging.", "ognl": false, "suffix": "}" },
+    "threadName": { "index": 97, "kind": "function", "displayName": "Thread 
Name", "group": "function", "label": "function", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the name of the 
current thread. Can be used for logging.", "ognl": false, "suffix": "}" },
+    "throwException(type,msg)": { "index": 98, "kind": "function", 
"displayName": "Throw Exception", "group": "function", "label": "function", 
"required": false, "javaType": "java.lang.Exception", "prefix": "${", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "Deliberately throws an error. Uses 
IllegalArgumentException by default if no type is specified (use fully 
qualified classname).", "ognl": false, "suffix": "}" },
+    "trim(exp)": { "index": 99, "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": "}" },
+    "type:name.field": { "index": 100, "kind": "function", "displayName": 
"Java Field Value", "group": "function", "label": "function", "required": 
false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "To 
refer to a type or field by its classname. To refer to a field, you can append 
.FIELD_NAME. For example, you can refer to the constant field from Exchange as: 
`org.apache.camel.Exchange.FILE_NAME`",  [...]
+    "kindOfType(exp)": { "index": 101, "kind": "function", "displayName": 
"Kind of Type", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "What kind of type is 
the value (null,number,string,boolean,array,object)", "ognl": false, "suffix": 
"}" },
+    "uppercase(exp)": { "index": 102, "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": "}" },
+    "uuid(type)": { "index": 103, "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 `Uuid [...]
+    "variable.name": { "index": 104, "kind": "function", "displayName": 
"Variable", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "The variable with the 
given name", "ognl": true, "suffix": "}" },
+    "variableAs(key,type)": { "index": 105, "kind": "function", "displayName": 
"Variable As", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Converts the variable 
to the given type (classname).", "ognl": false, "suffix": "}" },
+    "variables": { "index": 106, "kind": "function", "displayName": 
"Variables", "group": "function", "label": "function", "required": false, 
"javaType": "java.util.Map", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Returns all the variables from the current Exchange in a Map", "ognl": false, 
"suffix": "}" },
+    "xpath(input,exp)": { "index": 107, "kind": "function", "displayName": 
"XPath", "group": "function", "label": "function", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "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 (optiona [...]
   }
 }
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 ef750015a187..217baa48d76f 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
@@ -149,6 +149,7 @@ NOTE: Some functions take 1 or more arguments enclosed in 
parentheses, and argum
 |`exchangeProperty.key._OGNL_` | `Object` | Same as `exchangeProperty.key` and 
then invoke Camel _OGNL syntax_.
 |`exchange` | `Exchange` | The current `Exchange` object.
 |`fromRouteId` | `String` | Returns the original route id where this 
`Exchange` was created.
+|`function(name,exp)` | `Object` | Invokes a custom function with the given 
name using the message body (or expression) as input parameter.
 |`header.key` | `Object` | The message header with the given key.
 |`header.key._OGNL_` | `Object` | Same as `header.key` and then invoke Camel 
_OGNL syntax_.
 |`header[key]` | `Object` | *Deprecated* The message header with the given key.
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 6553b6d5fd9c..0e9e6faa5fb9 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
@@ -173,6 +173,10 @@ public final class SimpleConstants {
     @Metadata(description = "The original route id where this exchange was 
created.", javaType = "String", label = "function")
     public static final String FROM_ROUTE_ID = "fromRouteId";
 
+    @Metadata(description = "Invokes a custom function with the given name 
using the message body (or expression) as input parameter.",
+              label = "function", javaType = "Object", displayName = "For Each 
call Function")
+    public static final String FUNCTION = "function(name,exp)";
+
     @Metadata(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.",
               label = "function", javaType = "String", displayName = "Compute 
Hash Value")
     public static final String HASH = "hash(exp,algorithm)";
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 e132ce47751f..fb96a600cf69 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
@@ -50,6 +50,7 @@ import org.apache.camel.StreamCache;
 import org.apache.camel.spi.ClassResolver;
 import org.apache.camel.spi.ExchangeFormatter;
 import org.apache.camel.spi.Language;
+import org.apache.camel.spi.SimpleFunctionRegistry;
 import org.apache.camel.spi.UuidGenerator;
 import org.apache.camel.support.CamelContextHelper;
 import org.apache.camel.support.ClassicUuidGenerator;
@@ -2878,21 +2879,23 @@ public final class SimpleExpressionBuilder {
                     throw new IllegalArgumentException("No custom simple 
function with name: " + name);
                 }
                 exp = ExpressionBuilder.simpleExpression(parameter);
+                exp.init(context);
             }
 
             @Override
             public Object evaluate(Exchange exchange) {
+                Object answer = null;
                 final Object originalBody = exchange.getMessage().getBody();
                 try {
                     Object input = exp.evaluate(exchange, Object.class);
                     if (input != null) {
                         exchange.getMessage().setBody(input);
-                        return func.evaluate(exchange, Object.class);
+                        answer = func.evaluate(exchange, Object.class);
                     }
-                    return null;
                 } finally {
                     exchange.getMessage().setBody(originalBody);
                 }
+                return answer;
             }
 
             public String toString() {
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java
index c0771c6de995..cc902f066a5a 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java
@@ -96,7 +96,7 @@ public class SimpleExpressionParser extends BaseSimpleParser {
                 // use $$key() as local function in the expression afterwards
                 for (String key : initParser.getInitFunctions()) {
                     // no-arg functions
-                    this.expression = this.expression.replace("$" + key + 
"()", "${function." + key + "}");
+                    this.expression = this.expression.replace("$" + key + 
"()", "${function(" + key + ")}");
                     // arg functions
                     this.expression = this.expression.replace("${" + key + 
"(", "${function(" + key + ",");
                 }
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleLanguage.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleLanguage.java
index 88e8305b9f26..b5bfc7e23cf8 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleLanguage.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleLanguage.java
@@ -30,7 +30,6 @@ import org.apache.camel.support.LanguageSupport;
 import org.apache.camel.support.PredicateToExpressionAdapter;
 import org.apache.camel.support.ScriptHelper;
 import org.apache.camel.support.builder.ExpressionBuilder;
-import org.apache.camel.support.service.ServiceHelper;
 import org.apache.camel.util.ObjectHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -49,8 +48,6 @@ public class SimpleLanguage extends LanguageSupport 
implements StaticService {
     // a special prefix to avoid cache clash
     private static final String CACHE_KEY_PREFIX = "@SIMPLE@";
 
-    private SimpleFunctionRegistry registry;
-
     boolean allowEscape = true;
     boolean skipFileFunctions;
 
@@ -84,10 +81,6 @@ public class SimpleLanguage extends LanguageSupport 
implements StaticService {
                 LOG.debug("Simple language disabled predicate/expression 
cache");
             }
         }
-        registry = new DefaultSimpleFunctionRegistry();
-        ServiceHelper.initService(registry);
-        // register so we can obtain it during parsing
-        
getCamelContext().getCamelContextExtension().addContextPlugin(SimpleFunctionRegistry.class,
 registry);
     }
 
     @Override
@@ -95,7 +88,6 @@ public class SimpleLanguage extends LanguageSupport 
implements StaticService {
         if (getCamelContext() != null) {
             SIMPLE.setCamelContext(getCamelContext());
         }
-        ServiceHelper.startService(registry);
     }
 
     @Override
@@ -114,7 +106,6 @@ public class SimpleLanguage extends LanguageSupport 
implements StaticService {
             }
             cacheExpression.clear();
         }
-        ServiceHelper.stopService(registry);
     }
 
     @Override
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/InitBlockExpression.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/InitBlockExpression.java
index a54b14a1bfb0..a46041a25c36 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/InitBlockExpression.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/InitBlockExpression.java
@@ -20,10 +20,10 @@ import org.apache.camel.CamelContext;
 import org.apache.camel.Exchange;
 import org.apache.camel.Expression;
 import org.apache.camel.language.simple.BaseSimpleParser;
-import org.apache.camel.language.simple.SimpleFunctionRegistry;
 import org.apache.camel.language.simple.types.InitOperatorType;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.language.simple.types.SimpleToken;
+import org.apache.camel.spi.SimpleFunctionRegistry;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.StringHelper;
 
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 5df606606e8e..c509f87cb651 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
@@ -30,7 +30,6 @@ import org.apache.camel.Expression;
 import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.language.simple.BaseSimpleParser;
 import org.apache.camel.language.simple.SimpleExpressionBuilder;
-import org.apache.camel.language.simple.SimpleFunctionRegistry;
 import org.apache.camel.language.simple.SimplePredicateParser;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.language.simple.types.SimpleToken;
@@ -607,39 +606,31 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
     }
 
     private Expression createSimpleCustomFunction(CamelContext camelContext, 
String function, boolean strict) {
-        String remainder = ifStartsWithReturnRemainder("function.", function);
-        if (remainder != null) {
-            String key = StringHelper.removeLeadingAndEndingQuotes(remainder);
-            key = key.trim();
-            SimpleFunctionRegistry registry
-                    = 
camelContext.getCamelContextExtension().getContextPlugin(SimpleFunctionRegistry.class);
-            Expression answer = registry.getFunction(key);
-            if (answer == null) {
-                throw new IllegalArgumentException("No custom simple function 
with name: " + key);
-            }
-            return answer;
-        }
-
-        remainder = ifStartsWithReturnRemainder("function(", function);
+        String remainder = ifStartsWithReturnRemainder("function(", function);
         if (remainder != null) {
             String key;
-            String param;
+            String param = null;
             String values = StringHelper.beforeLast(remainder, ")");
             if (values == null || ObjectHelper.isEmpty(values)) {
                 throw new SimpleParserException(
-                        "Valid syntax: ${function(key,exp)} was: " + function,
+                        "Valid syntax: ${function(name)} or 
${function(name,exp)} was: " + function,
                         token.getIndex());
             }
             String[] tokens = StringQuoteHelper.splitSafeQuote(values, ',', 
true, true);
-            if (tokens.length != 2) {
+            if (tokens.length < 1 || tokens.length > 2) {
                 throw new SimpleParserException(
-                        "Valid syntax: ${function(key,exp)} was: " + function,
+                        "Valid syntax: ${function(name)} or 
${function(name,exp)} was: " + function,
                         token.getIndex());
             }
             key = StringHelper.removeQuotes(tokens[0]);
             key = key.trim();
-            param = tokens[1];
-            param = StringHelper.removeLeadingAndEndingQuotes(param.trim());
+            if (tokens.length == 2) {
+                param = tokens[1];
+                param = 
StringHelper.removeLeadingAndEndingQuotes(param.trim());
+            }
+            if (param == null) {
+                param = "${body}";
+            }
             return SimpleExpressionBuilder.customFunction(key, param);
         }
 
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleCustomFunctionTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleCustomFunctionTest.java
new file mode 100644
index 000000000000..917ab106a9e8
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleCustomFunctionTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.language.simple;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Exchange;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.spi.SimpleFunctionRegistry;
+import org.apache.camel.support.ExpressionAdapter;
+import org.apache.camel.support.PluginHelper;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class SimpleCustomFunctionTest extends ContextTestSupport {
+
+    @Test
+    public void testCustomFunctionWithBody() throws Exception {
+        SimpleFunctionRegistry reg = 
PluginHelper.getSimpleFunctionRegistry(context);
+        Assertions.assertEquals(1, reg.size());
+
+        getMockEndpoint("mock:result").expectedBodiesReceived("Hello I was 
here World");
+        template.sendBody("direct:start", "World");
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testCustomFunctionWithExp() throws Exception {
+        SimpleFunctionRegistry reg = 
PluginHelper.getSimpleFunctionRegistry(context);
+        Assertions.assertEquals(1, reg.size());
+
+        getMockEndpoint("mock:result").expectedBodiesReceived("Bye I was here 
Moon");
+        template.sendBody("direct:start2", "World");
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                SimpleFunctionRegistry reg = 
PluginHelper.getSimpleFunctionRegistry(getCamelContext());
+                reg.addFunction("foo", new ExpressionAdapter() {
+                    @Override
+                    public Object evaluate(Exchange exchange) {
+                        return "I was here " + exchange.getMessage().getBody();
+                    }
+                });
+
+                from("direct:start")
+                        .setBody(simple("Hello ${function(foo)}"))
+                        .to("mock:result");
+
+                from("direct:start2")
+                        .setVariable("msg", constant("Moon"))
+                        .setBody(simple("Bye 
${function(foo,${variable.msg})}"))
+                        .to("mock:result");
+            }
+        };
+    }
+}
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/PluginHelper.java 
b/core/camel-support/src/main/java/org/apache/camel/support/PluginHelper.java
index d69f86035151..9373bb004666 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/PluginHelper.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/PluginHelper.java
@@ -55,6 +55,7 @@ import 
org.apache.camel.spi.RestBindingJacksonXmlDataFormatFactory;
 import org.apache.camel.spi.RestBindingJaxbDataFormatFactory;
 import org.apache.camel.spi.RouteFactory;
 import org.apache.camel.spi.RoutesLoader;
+import org.apache.camel.spi.SimpleFunctionRegistry;
 import org.apache.camel.spi.UnitOfWorkFactory;
 import org.apache.camel.spi.UriFactoryResolver;
 import org.apache.camel.support.task.TaskManagerRegistry;
@@ -624,4 +625,18 @@ public final class PluginHelper {
         return 
extendedCamelContext.getContextPlugin(TaskManagerRegistry.class);
     }
 
+    /**
+     * Gets the {@link SimpleFunctionRegistry} to use.
+     */
+    public static SimpleFunctionRegistry 
getSimpleFunctionRegistry(CamelContext camelContext) {
+        return 
camelContext.getCamelContextExtension().getContextPlugin(SimpleFunctionRegistry.class);
+    }
+
+    /**
+     * Gets the {@link SimpleFunctionRegistry} to use.
+     */
+    public static SimpleFunctionRegistry 
getSimpleFunctionRegistry(ExtendedCamelContext extendedCamelContext) {
+        return 
extendedCamelContext.getContextPlugin(SimpleFunctionRegistry.class);
+    }
+
 }


Reply via email to