This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-website.git
The following commit(s) were added to refs/heads/main by this push:
new 937e924b Add blog post Extending Apache Camel Simple with Your Own
Functions (#1780)
937e924b is described below
commit 937e924ba44f792a68b8cab4c4eb05a240e2b646
Author: Simon Hartl <[email protected]>
AuthorDate: Sun Sep 20 14:38:13 2026 +0200
Add blog post Extending Apache Camel Simple with Your Own Functions (#1780)
* Add blog post Extending Apache Camel Simple with Your Own Functions
* Updated blog post Extending Apache Camel Simple with Your Own Functions
Change the way how it must be run as Camel 4.22.1 is already released.
---
.../09/camel-simple-customfunction/featured.jpg | Bin 0 -> 158724 bytes
.../2026/09/camel-simple-customfunction/index.md | 891 +++++++++++++++++++++
2 files changed, 891 insertions(+)
diff --git a/content/blog/2026/09/camel-simple-customfunction/featured.jpg
b/content/blog/2026/09/camel-simple-customfunction/featured.jpg
new file mode 100644
index 00000000..5bc2f104
Binary files /dev/null and
b/content/blog/2026/09/camel-simple-customfunction/featured.jpg differ
diff --git a/content/blog/2026/09/camel-simple-customfunction/index.md
b/content/blog/2026/09/camel-simple-customfunction/index.md
new file mode 100644
index 00000000..b7bc5018
--- /dev/null
+++ b/content/blog/2026/09/camel-simple-customfunction/index.md
@@ -0,0 +1,891 @@
+---
+title: "Extending Apache Camel Simple with Your Own Functions"
+date: 2026-09-20
+draft: false
+authors: [SiHartl]
+categories: ["Howtos"]
+keywords: ["apache camel", "simple language", "YAML"]
+preview: "Apache Camel’s Simple language already covers much of the logic
needed in integration routes. Functions can be composed directly in Simple,
while custom Java functions provide a clean extension point for
application-specific logic."
+---
+
+Apache Camel's Simple language is surprisingly powerful. It can access message
bodies, headers, variables and exchange properties, work with strings and
numbers, evaluate conditions, process dates, access JSON and XML data, and
combine multiple functions into transformation pipelines. ([Apache Camel –
Simple Language][1])
+
+For a large part of the logic required inside an integration route, this means
that no Java code is necessary. Simple goes another step further: existing
functions can even be composed into local custom functions directly inside an
expression. ([Apache Camel – Simple Advanced Features][2])
+
+There will still be situations where application-specific logic does not fit
naturally into an expression. For those cases, Camel allows us to extend Simple
with functions implemented in Java without moving the complete route into Java.
+
+In this article, we will use both approaches in one runnable example:
+
+```text
+cleanName → composed entirely from Simple functions
+maskEmail → implemented as a Java SimpleFunction
+```
+
+The complete example consists of only two files:
+
+```text
+custom-simple-function/
+├── route.camel.yaml
+└── MaskEmailFunction.java
+```
+
+The examples below have been tested with Camel 4.22.1 and can be run using:
+
+```shell
+camel run route.camel.yaml MaskEmailFunction.java
+```
+
+The important part is that the integration flow remains a regular YAML route.
We use Simple for the logic it already handles well and add a small piece of
Java only where it provides additional value.
+
+***NOTE for Camel 4.22.0:***
+
+Camel 4.22.0 contains a bug, [CAMEL-24486][4], that affects custom functions
declared in `$init{}` blocks when Camel is running with the `dev` profile. The
issue is fixed in Camel 4.22.1. To run the example with Camel 4.22.0, use the
`test` profile instead: ([Apache Camel – Using Profiles][5])
+
+```shell
+camel run --profile test route.camel.yaml MaskEmailFunction.java
+```
+
+
+## Quick start: custom functions in a few steps
+
+If you already know Camel and just want the short version, there are two ways
to add functionality to Simple. If you prefer a detailed, step-by-step
walkthrough, you can [skip ahead to What we are going to
build](#what-we-are-going-to-build).
+
+### 1. Compose existing Simple functions
+
+If the functionality can already be expressed with Simple, define a local
function inside an `$init{}` block:
+
+```text
+$init{
+ $cleanName ~:= ${trim()} ~> ${normalizeWhitespace()} ~> ${uppercase()};
+}init$
+
+${cleanName(${header.customerName})}
+```
+
+No Java is required. The function is local to the Simple expression and can
accept the current body or a single explicit input value. ([Apache Camel –
Simple Advanced Features][2])
+
+### 2. Implement application-specific logic in Java
+
+For functionality that is easier to express in Java, implement
`SimpleFunction` and make it available through Camel's Registry:
+
+```java
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.Exchange;
+import org.apache.camel.spi.SimpleFunction;
+
+@BindToRegistry("mask-email-function")
+public class MaskEmailFunction implements SimpleFunction {
+
+ @Override
+ public String getName() {
+ return "maskEmail";
+ }
+
+ @Override
+ public Object apply(Exchange exchange, Object input) throws Exception {
+ String email = input.toString().trim();
+
+ int at = email.indexOf('@');
+
+ if (at <= 0 || at == email.length() - 1) {
+ return "***";
+ }
+
+ String localPart = email.substring(0, at);
+ String domain = email.substring(at);
+
+ return localPart.substring(0, 1) + "***" + domain;
+ }
+}
+```
+
+The name returned by `getName()` becomes the function name available to
Simple. For standalone Camel, `@BindToRegistry` makes the implementation
discoverable by Camel. ([Apache Camel – Simple Advanced Features][2]) ([Apache
Camel – Java Beans][3])
+
+### 3. Call the function from the YAML route
+
+The Java implementation can then be used like another Simple function:
+
+```yaml
+- setBody:
+ simple: |
+ Customer: ${cleanName(${header.customerName})}
+ Contact: ${maskEmail(${header.customerEmail})}
+```
+
+From the route developer's perspective, the two functions look almost
identical even though one is composed entirely in Simple and the other is
implemented in Java.
+
+### 4. Run the route and Java file together
+
+Put the route and Java source file in the same directory:
+
+```text
+route.camel.yaml
+MaskEmailFunction.java
+```
+
+and run them with Camel CLI:
+
+```shell
+camel run route.camel.yaml MaskEmailFunction.java
+```
+
+That's the short version. The rest of the article walks through the example
step by step, explains how both types of functions work, and discusses when
each approach is useful.
+
+## What we are going to build
+
+Imagine an integration that receives some simple customer data. The customer
name arrives with inconsistent whitespace:
+
+```text
+ John Doe
+```
+
+We want to normalize it to:
+
+```text
+JOHN DOE
+```
+
+This transformation can already be expressed by combining existing Simple
functions, so there is little reason to implement it in Java. We will create a
local `cleanName` function that performs the following operations:
+
+```text
+trim
+ → normalizeWhitespace
+ → uppercase
+```
+
+The same customer also has the following email address:
+
+```text
[email protected]
+```
+
+Before writing that address to another message, we want to mask it:
+
+```text
+j***@example.com
+```
+
+For the purpose of this example, we will treat the masking rule as
application-specific logic and implement it as a reusable Java function:
+
+```text
+${maskEmail(${header.customerEmail})}
+```
+
+The final route therefore demonstrates two different ways of extending what we
can do from a Simple expression. `cleanName` is composed entirely from existing
Simple functions, while `maskEmail` is implemented in Java and exposed back to
the route as a Simple function.
+
+## Step 1: Create the example directory
+
+Create a directory for the example and change into it:
+
+```shell
+mkdir custom-simple-function
+cd custom-simple-function
+```
+
+Inside this directory, we will create the following two files:
+
+```text
+route.camel.yaml
+MaskEmailFunction.java
+```
+
+The complete integration flow stays in `route.camel.yaml`. The Java file
contains only the small piece of functionality that we want to add to Simple.
+
+## Before using Java: composing functions with Simple
+
+Before implementing our Java function, it is worth looking at what Simple
itself can already do. In Camel 4.22, a Simple expression can contain an
initialization block using `$init{}`. ([Apache Camel – Simple Advanced
Features][2])
+
+```text
+$init{
+ ...
+}init$
+```
+
+Inside this block, we can define a local function by composing existing Simple
functions with the `~:=` operator. For example, our `cleanName` function can be
defined like this:
+
+```text
+$init{
+ $cleanName ~:= ${trim()} ~> ${normalizeWhitespace()} ~> ${uppercase()};
+}init$
+
+${cleanName(${header.customerName})}
+```
+
+The function definition combines three existing Simple functions using Camel's
chain operator `~>`:
+
+```text
+${trim()}
+ ~> ${normalizeWhitespace()}
+ ~> ${uppercase()}
+```
+
+The result of each function becomes the input of the next one. A value such as:
+
+```text
+ John Doe
+```
+
+therefore moves through the chain like this:
+
+```text
+input
+ │
+ ▼
+trim()
+ │
+ ▼
+normalizeWhitespace()
+ │
+ ▼
+uppercase()
+ │
+ ▼
+JOHN DOE
+```
+
+Once defined, the complete transformation can be called through the much more
meaningful expression:
+
+```text
+${cleanName(${header.customerName})}
+```
+
+There is no Java implementation behind `cleanName`; the entire function is
composed from functionality that Simple already provides. Functions defined in
an `$init{}` block are local to the Simple expression in which they are
declared, which makes them particularly useful for structuring larger
transformations and giving them meaningful names. ([Apache Camel – Simple
Advanced Features][2])
+
+If the required logic can be expressed clearly by combining existing Simple
operations, this is often all we need. Our email masking requirement is
deliberately different: instead of composing existing operations, we want to
implement our own application-specific algorithm and expose it as a reusable
function.
+
+For that, we will create a Java-based `SimpleFunction`.
+
+## Step 2: Create the Java Simple function
+
+Create a file called:
+
+```text
+MaskEmailFunction.java
+```
+
+and add the following code:
+
+```java
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.Exchange;
+import org.apache.camel.spi.SimpleFunction;
+
+@BindToRegistry("mask-email-function")
+public class MaskEmailFunction implements SimpleFunction {
+
+ @Override
+ public String getName() {
+ return "maskEmail";
+ }
+
+ @Override
+ public Object apply(Exchange exchange, Object input) throws Exception {
+ String email = input.toString().trim();
+
+ int at = email.indexOf('@');
+
+ if (at <= 0 || at == email.length() - 1) {
+ return "***";
+ }
+
+ String localPart = email.substring(0, at);
+ String domain = email.substring(at);
+
+ return localPart.substring(0, 1) + "***" + domain;
+ }
+}
+```
+
+That is the complete Java implementation. To understand how Camel makes this
available to Simple, there are three parts worth looking at.
+
+### Implement `SimpleFunction`
+
+The class implements:
+
+```java
+org.apache.camel.spi.SimpleFunction
+```
+
+The actual function logic is contained in the `apply()` method:
+
+```java
+public Object apply(Exchange exchange, Object input)
+```
+
+`input` contains the value passed to the function. In our example, the input
will be:
+
+```text
[email protected]
+```
+
+and the function returns:
+
+```text
+j***@example.com
+```
+
+The method also receives the current Camel `Exchange`. We do not need it for
this simple example, but it gives more advanced functions access to information
associated with the current message. ([Apache Camel – Simple Advanced
Features][2])
+
+### Give the function a name
+
+The name exposed to the Simple language comes from:
+
+```java
+@Override
+public String getName() {
+ return "maskEmail";
+}
+```
+
+This means that the function can later be called from a Simple expression
using:
+
+```text
+${maskEmail(...)}
+```
+
+The Java class name therefore does not become part of the route. The route
developer only needs to know the meaningful function name `maskEmail`.
+
+### Make the function available to Camel
+
+The class is annotated with:
+
+```java
+@BindToRegistry("mask-email-function")
+```
+
+When running this example with Camel CLI, the annotation makes an instance
available through Camel's Registry so that the `SimpleFunction` can be
discovered. Camel CLI can run regular Java source files alongside route DSL
files, which makes this particularly convenient for small standalone examples.
([Apache Camel – Java Beans][3])
+
+The registry bean name and the Simple function name are two different things:
+
+```text
+Registry bean: mask-email-function
+Simple function: maskEmail
+```
+
+For somebody writing the route, `maskEmail` is the relevant name.
+
+## Step 3: Create the YAML route
+
+Now create the second file:
+
+```text
+route.camel.yaml
+```
+
+with the following content:
+
+```yaml
+- route:
+ id: custom-simple-function
+ from:
+ uri: "timer:demo?repeatCount=1"
+ steps:
+ - setHeader:
+ name: customerName
+ constant: " John Doe "
+
+ - setHeader:
+ name: customerEmail
+ constant: "[email protected]"
+
+ - log:
+ message: |
+ Original customer:
+ Name: ${header.customerName}
+ Email: ${header.customerEmail}
+
+ - setBody:
+ simple: |
+ $init{
+ $cleanName ~:= ${trim()} ~> ${normalizeWhitespace()} ~>
${uppercase()};
+ }init$
+
+ Customer: ${cleanName(${header.customerName})}
+ Contact: ${maskEmail(${header.customerEmail})}
+
+ - log:
+ message: |
+ Processed customer:
+ ${body}
+```
+
+This route now demonstrates both approaches in the same Simple expression.
+
+The first function is defined directly inside the `$init{}` block:
+
+```text
+$cleanName ~:= ${trim()} ~> ${normalizeWhitespace()} ~> ${uppercase()};
+```
+
+and then called with:
+
+```text
+${cleanName(${header.customerName})}
+```
+
+There is no Java implementation involved. Camel passes the header value
through `trim()`, `normalizeWhitespace()` and `uppercase()`, resulting in `JOHN
DOE`.
+
+Immediately below it, we call:
+
+```text
+${maskEmail(${header.customerEmail})}
+```
+
+From the perspective of somebody reading the YAML route, this looks very
similar to `cleanName`. The difference is that the implementation comes from
`MaskEmailFunction.java`.
+
+That is an important property of this approach: the route does not have to
change its programming model just because one operation requires Java. Both
Simple-composed logic and application-specific Java functionality can be used
naturally inside the same expression.
+
+## Step 4: Run the integration
+
+At this point, the directory should look like this:
+
+```text
+custom-simple-function/
+├── route.camel.yaml
+└── MaskEmailFunction.java
+```
+
+Camel CLI can run multiple files together, including YAML route definitions
and regular Java source files. Both files can therefore simply be started
using: ([Apache Camel – Java Beans][3])
+
+```shell
+camel run route.camel.yaml MaskEmailFunction.java
+```
+
+## Expected result
+
+The route executes once and should produce output similar to:
+
+```text
+Original customer:
+Name: John Doe
+Email: [email protected]
+```
+
+followed by:
+
+```text
+Processed customer:
+Customer: JOHN DOE
+Contact: j***@example.com
+```
+
+Both extension mechanisms are now active in the same route. The customer name
is transformed entirely through Simple, while the email address passes through
our Java implementation:
+
+```text
+customerName
+ │
+ ▼
+cleanName
+ │
+ ├── trim()
+ ├── normalizeWhitespace()
+ └── uppercase()
+ │
+ ▼
+JOHN DOE
+
+
+customerEmail
+ │
+ ▼
+maskEmail
+ │
+ └── MaskEmailFunction.java
+ │
+ ▼
+j***@example.com
+```
+
+## Using the current message body as input
+
+A custom function does not necessarily need an explicit argument. Suppose the
current message body already contains:
+
+```text
[email protected]
+```
+
+Our Java function can then be called using:
+
+```text
+${maskEmail}
+```
+
+or:
+
+```text
+${maskEmail()}
+```
+
+Camel uses the current message body as the function input. This behavior is
supported by custom Simple functions and allows them to fit naturally into
transformation pipelines. ([Apache Camel – Simple Advanced Features][2])
+
+A simple route fragment could therefore look like this:
+
+```yaml
+- setBody:
+ constant: "[email protected]"
+
+- setBody:
+ simple: "${maskEmail}"
+
+- log:
+ message: "Masked email: ${body}"
+```
+
+The same principle applies to a function composed inside `$init{}`:
+
+```text
+$init{
+ $cleanName ~:= ${trim()} ~> ${normalizeWhitespace()} ~> ${uppercase()};
+}init$
+
+${cleanName()}
+```
+
+Without an explicit argument, the current message body passes through the
complete function chain. This becomes particularly useful when building
transformation pipelines.
+
+## Combining custom and built-in functions
+
+Simple's chain operator allows built-in and custom functions to be combined.
The operator:
+
+```text
+~>
+```
+
+passes the result of the expression on its left to the function on its right.
([Apache Camel – Simple Advanced Features][2])
+
+Instead of writing:
+
+```text
+${maskEmail(${header.customerEmail})}
+```
+
+we can therefore write:
+
+```text
+${header.customerEmail} ~> ${maskEmail}
+```
+
+We can also add built-in operations before our custom function:
+
+```text
+${header.customerEmail}
+ ~> ${trim()}
+ ~> ${maskEmail}
+```
+
+The email address is first trimmed and the resulting value is then passed to
the Java-based `maskEmail` function. Application-specific functionality
therefore becomes another building block in the same transformation pipeline as
Camel's built-in functions.
+
+## Simple already avoids a lot of Java
+
+A custom Java function should not be the first solution for every
transformation. Simple itself already handles a considerable amount of
route-level logic involving message bodies, headers, variables, exchange
properties, strings, numbers, dates, collections, JSON, XML, predicates,
conditions and type conversions. ([Apache Camel – Simple Language][1])
+
+For example:
+
+```text
+${header.customerName}
+ ~> ${trim()}
+ ~> ${normalizeWhitespace()}
+ ~> ${uppercase()}
+```
+
+is short enough to remain directly inside a route. Creating a Java processor
or helper class for such a transformation would add complexity without adding
much value.
+
+If the expression becomes larger or is easier to understand under a meaningful
name, `$init{}` gives us another option:
+
+```text
+$init{
+ $cleanName ~:= ${trim()} ~> ${normalizeWhitespace()} ~> ${uppercase()};
+}init$
+
+${cleanName(${header.customerName})}
+```
+
+We still have not written any Java. Simple should therefore normally be the
first place to look when implementing small pieces of route-level
transformation or decision logic.
+
+## When Java is useful
+
+There will still be cases where an expression language is no longer the right
place for the implementation. Company-specific material-number normalization,
proprietary naming rules, complex classifications, special validation
algorithms, application-specific identifier generation or the reuse of existing
Java libraries are all examples where a Java implementation may be clearer and
easier to maintain.
+
+In such cases, a `SimpleFunction` provides a small extension point without
changing the architecture of the route. For example, we could implement:
+
+```java
+public class MaterialNumberFunction implements SimpleFunction {
+ // company-specific implementation
+}
+```
+
+and expose that functionality to route developers as:
+
+```text
+${normalizeMaterialNumber(...)}
+```
+
+The same principle could be applied to functions such as:
+
+```text
+${calculateCustomerClass(...)}
+${sanitizeForLogging(...)}
+```
+
+The implementation complexity stays behind the function, while the route only
sees a concise and meaningful operation.
+
+## Local Simple functions or Java functions?
+
+The two approaches serve slightly different purposes. A function declared
inside `$init{}` is local to the Simple expression and is a good fit when
existing Simple functionality already does what we need, but we want to compose
several operations and give the result a meaningful name. ([Apache Camel –
Simple Advanced Features][2])
+
+A Java `SimpleFunction`, on the other hand, is registered with Camel and is
better suited to application-specific functionality that should be encapsulated
independently of one particular expression.
+
+A useful way to think about the decision is:
+
+```text
+Can Simple already do it?
+ │
+ ├── Yes
+ │ │
+ │ ▼
+ │ Use built-in Simple
+ │
+ ▼
+Is the expression becoming harder to read?
+ │
+ ├── Yes
+ │ │
+ │ ▼
+ │ Compose a local function with $init{}
+ │
+ ▼
+Does it require genuinely custom logic?
+ │
+ ├── Yes
+ │ │
+ │ ▼
+ │ Implement a Java SimpleFunction
+ │
+ ▼
+Use it from the YAML route like another function
+```
+
+This keeps the amount of Java in an integration proportional to the actual
complexity of the problem rather than making Java the default choice for every
transformation.
+
+## Accessing the Camel Exchange
+
+A Java custom function receives not only its input value but also the current
Camel `Exchange`:
+
+```java
+public Object apply(Exchange exchange, Object input)
+```
+
+This means a function can do more than simply transform its argument. When
required, it can access headers, variables, exchange properties, the Camel
context or other information associated with the current message. ([Apache
Camel – Simple Advanced Features][2])
+
+That makes `SimpleFunction` a lightweight bridge between declarative route
expressions and more advanced application-specific functionality. For simple
transformations such as our email masking function, however, the explicit input
value is all we need.
+
+## Handling null values
+
+There is one implementation detail worth knowing when implementing
`SimpleFunction`. By default, Camel does not call `apply()` when the resolved
function input is `null`. ([Apache Camel – Simple Advanced Features][2])
+
+For example:
+
+```text
+${maskEmail(${header.customerEmail})}
+```
+
+will resolve to a `null` input if the `customerEmail` header does not exist.
The default implementation of:
+
+```java
+allowNull()
+```
+
+returns `false`, so Camel does not invoke the function in that case.
+
+If handling a missing value should be part of the function itself, override
`allowNull()`:
+
+```java
+@Override
+public boolean allowNull() {
+ return true;
+}
+```
+
+The implementation can then decide how to deal with the missing input:
+
+```java
+@Override
+public Object apply(Exchange exchange, Object input) throws Exception {
+ if (input == null) {
+ return "not provided";
+ }
+
+ String email = input.toString();
+
+ // ...
+}
+```
+
+For many transformation functions, Camel's default behavior is appropriate.
Overriding `allowNull()` is only necessary when handling the missing value is
explicitly part of the function's responsibility.
+
+## Simple first, Java where useful
+
+The main point of custom Simple functions is not that every transformation
should eventually be implemented in Java. Quite the opposite: Simple already
provides a powerful toolbox that can keep a significant amount of route logic
directly inside the YAML definition. ([Apache Camel – Simple Language][1])
+
+For straightforward transformations, use Simple directly:
+
+```text
+${header.customerName} ~> ${trim()} ~> ${uppercase()}
+```
+
+If several existing operations form a larger transformation, compose them into
a local function:
+
+```text
+$init{
+ $cleanName ~:= ${trim()} ~> ${normalizeWhitespace()} ~> ${uppercase()};
+}init$
+
+${cleanName(${header.customerName})}
+```
+
+Only when the requirement becomes genuinely application-specific do we add the
missing functionality in Java:
+
+```text
+${maskEmail(${header.customerEmail})}
+```
+
+The resulting architecture can remain simple:
+
+```text
+YAML route
+ │
+ ▼
+Simple expressions
+ │
+ ▼
+Built-in functions
+ │
+ ▼
+Composed local functions with $init{}
+ │
+ ▼
+Custom Java functions where required
+```
+
+Java remains available whenever we need its full capabilities, but we do not
have to use it for every small piece of route logic. Even when Java is
necessary, `SimpleFunction` lets us encapsulate the implementation behind a
concise expression while the integration flow itself remains declarative.
+
+## Complete runnable example
+
+For reference, the complete example consists of these two files:
+
+```text
+custom-simple-function/
+├── route.camel.yaml
+└── MaskEmailFunction.java
+```
+
+### `MaskEmailFunction.java`
+
+```java
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.Exchange;
+import org.apache.camel.spi.SimpleFunction;
+
+@BindToRegistry("mask-email-function")
+public class MaskEmailFunction implements SimpleFunction {
+
+ @Override
+ public String getName() {
+ return "maskEmail";
+ }
+
+ @Override
+ public Object apply(Exchange exchange, Object input) throws Exception {
+ String email = input.toString().trim();
+
+ int at = email.indexOf('@');
+
+ if (at <= 0 || at == email.length() - 1) {
+ return "***";
+ }
+
+ String localPart = email.substring(0, at);
+ String domain = email.substring(at);
+
+ return localPart.substring(0, 1) + "***" + domain;
+ }
+}
+```
+
+### `route.camel.yaml`
+
+```yaml
+- route:
+ id: custom-simple-function
+ from:
+ uri: "timer:demo?repeatCount=1"
+ steps:
+ - setHeader:
+ name: customerName
+ constant: " John Doe "
+
+ - setHeader:
+ name: customerEmail
+ constant: "[email protected]"
+
+ - log:
+ message: |
+ Original customer:
+ Name: ${header.customerName}
+ Email: ${header.customerEmail}
+
+ - setBody:
+ simple: |
+ $init{
+ $cleanName ~:= ${trim()} ~> ${normalizeWhitespace()} ~>
${uppercase()};
+ }init$
+
+ Customer: ${cleanName(${header.customerName})}
+ Contact: ${maskEmail(${header.customerEmail})}
+
+ - log:
+ message: |
+ Processed customer:
+ ${body}
+```
+
+### Run with Camel CLI
+
+```shell
+camel run route.camel.yaml MaskEmailFunction.java
+```
+
+The output should look similar to:
+
+```text
+Original customer:
+Name: John Doe
+Email: [email protected]
+
+Processed customer:
+Customer: JOHN DOE
+Contact: j***@example.com
+```
+
+With two files and one command, the example demonstrates both extension
mechanisms: reusable logic composed directly from Simple functions and
application-specific Java logic exposed back to the route as another Simple
function.
+
+## Conclusion
+
+Apache Camel's Simple language can handle far more than just accessing a
message body or header. For many integrations, its built-in functions and
operators cover a significant part of the transformation and decision logic
required directly inside a route. ([Apache Camel – Simple Language][1])
+
+When several existing operations belong together, `$init{}` allows us to
compose them into meaningful local functions without writing Java. And when
something genuinely application-specific is missing, a Java `SimpleFunction`
provides a clean extension mechanism without forcing the integration route
itself into Java. ([Apache Camel – Simple Advanced Features][2])
+
+The result is a useful balance: the route remains YAML, specialized
implementations stay encapsulated, and both approaches can be consumed through
concise Simple expressions such as:
+
+```text
+${cleanName(...)}
+${maskEmail(...)}
+```
+
+**Use Simple wherever it keeps the route clear. Compose what you can. Add Java
where it actually provides value.**
+
+[1]: https://camel.apache.org/components/languages/simple-language.html
"Apache Camel - Simple Language"
+[2]: https://camel.apache.org/components/languages/simple-advanced.html
"Apache Camel - Simple Advanced Features"
+[3]: https://camel.apache.org/manual/camel-jbang-beans.html "Apache Camel -
Java Beans"
+[4]: https://issues.apache.org/jira/browse/CAMEL-24486 "CAMEL-24486"
+[5]: https://camel.apache.org/manual/camel-jbang-running.html#_using_profiles
"Apache Camel CLI - Using Profiles"