At the heart of Apache Camel's power lies the ability to intercept a message as it flows through a route and change it — its body, headers, properties, or structure — before it reaches its destination. In this lesson, you'll learn how to use processors and transformation components to reshape data mid-route, giving you fine-grained control over every message that passes through your integration. By the end, you'll be able to write clean, testable transformation logic and understand which tool to reach for in any given situation.
A processor is the most fundamental building block for custom logic in Apache Camel. It is a simple Java interface with a single method:
public interface Processor {
void process(Exchange exchange) throws Exception;
}
The Exchange object is Camel's envelope — it wraps the incoming message (exchange.getIn()) and optionally an outgoing message (exchange.getOut()). When you implement a Processor, you have full programmatic access to the message body, headers, and exchange properties.
Think of a processor like a customs officer at a border checkpoint. The message (like a traveler) arrives, the officer inspects it, may stamp it, modify the luggage, and then lets it continue on its way. The route itself doesn't care what the officer does internally — it just sees a message arrive and a (potentially different) message leave.
Here's the key distinction beginners often miss: you should almost always modify exchange.getIn() rather than exchange.getOut(). Setting getOut() causes Camel to create a brand-new message and discard all headers and properties from the incoming message — a common source of mysterious bugs where headers suddenly disappear.
// ✅ Correct: modifies the existing message, preserving headers
exchange.getIn().setBody("Modified: " + exchange.getIn().getBody(String.class));
// ⚠️ Risky: creates a new Out message, headers from In are lost
exchange.getOut().setBody("Modified: " + exchange.getIn().getBody(String.class));
Processors are best used when your transformation logic is complex enough to warrant its own class — they're easy to unit test in isolation and keep your route definition clean.
process() DSL Method and Inline LambdasIn modern Camel (3.x+), you rarely need to create a separate named class for simple processors. The Java DSL lets you inline a processor as a lambda directly in the route definition:
from("direct:start")
.process(exchange -> {
String body = exchange.getIn().getBody(String.class);
exchange.getIn().setBody(body.toUpperCase());
exchange.getIn().setHeader("processed", true);
})
.to("mock:result");
This is concise and readable for short transformations. However, keep a few guidelines in mind:
Processor class can be unit tested directly without spinning up a Camel context. Lambdas embedded in routes require an integration test.transform() and setBody() DSL MethodsWhen your transformation is purely about changing the message body using an expression, Camel provides dedicated DSL methods that are more declarative and self-documenting than a full processor.
setBody() replaces the message body with the result of an expression:
from("direct:greet")
.setBody(constant("Hello, World!"))
.to("mock:result");
transform() does the same thing but signals to the reader that this is a data transformation step (semantically equivalent to setBody(), but expresses intent more clearly):
from("direct:greet")
.transform(simple("Hello ${header.username}!"))
.to("mock:result");
The real power here comes from Camel's Simple expression language and constant(), body(), header() expression builders. These let you perform surprisingly rich transformations without writing a single line of Java logic.
Common expressions you'll use constantly:
| Expression | What it produces |
|---|---|
| constant("text") | A fixed string value |
| simple("Hello ${body}") | Body interpolated into a template |
| header("myHeader") | The value of a specific header |
| body() | The current message body (useful for type coercion) |
| exchangeProperty("key") | A value from exchange properties |
Important:
setBody()andtransform()are ideal for simple, stateless transformations. The moment you need to make decisions (if/else), call external services, or mutate multiple parts of the exchange at once, reach for aProcessorinstead.
setHeader() and removeHeader() MethodsMessages in Camel carry metadata in their headers — key/value pairs that travel alongside the body. Headers are extremely important in integration: they often carry routing hints, authentication tokens, content types, or correlation IDs.
setHeader(name, expression) adds or overwrites a header:
from("direct:start")
.setHeader("Content-Type", constant("application/json"))
.setHeader("X-Retry-Count", constant(0))
.to("http://api.example.com/ingest");
removeHeader(name) deletes a specific header. This is especially useful when forwarding messages to external systems — you don't want to accidentally leak internal Camel headers (like CamelHttpMethod or CamelFileName) to downstream services.
from("file:input")
.removeHeader("CamelFileAbsolutePath")
.removeHeader("CamelFileName")
.to("http://api.example.com/upload");
removeHeaders(pattern) removes all headers matching a wildcard pattern:
.removeHeaders("Camel*") // removes all internal Camel headers
A very common production pattern is to strip all Camel-internal headers before calling an external HTTP API, because some of those headers can alter how Camel's HTTP component behaves unexpectedly.
convertBodyTo() MethodOne of the most underappreciated features in Camel is its built-in type converter system. When you consume a file, the body is a File object. When you consume from HTTP, it might be an InputStream. When you call getBody(String.class), Camel automatically runs a converter to give you a String — no manual casting needed.
The convertBodyTo() DSL method explicitly triggers this conversion mid-route:
from("file:input?noop=true")
.convertBodyTo(String.class) // Now the body is definitely a String
.process(exchange -> {
String content = exchange.getIn().getBody(String.class);
// Safe to do string operations now
});
Why do this explicitly? Because some processors and components behave differently based on the body type. A JSON marshaller, for example, might fail if it receives an InputStream instead of a String or a Java object.
You can also convert to domain objects directly (if a converter is registered):
from("direct:xml-input")
.unmarshal().jaxb("com.example.model") // XML → Java object
.process(exchange -> {
Order order = exchange.getIn().getBody(Order.class);
order.setStatus("PROCESSED");
exchange.getIn().setBody(order);
})
.marshal().json(JsonLibrary.Jackson) // Java object → JSON
.to("direct:json-output");
This pattern — unmarshal → process → marshal — is one of the most common transformation pipelines in Camel. It gives you type-safe access to your domain model in the processor, and lets Camel handle the serialization boilerplate.
Tip: If
convertBodyTo(String.class)returns garbled text, check the character encoding. You can specify it explicitly:convertBodyTo(String.class, "UTF-8").
Exchange — always prefer exchange.getIn().setBody() over exchange.getOut() to avoid losing headerssetBody() and transform() with Camel expressions for simple, declarative body replacements; reserve full Processor classes for complex or reusable logicsetHeader(), removeHeader(), and removeHeaders(pattern) manage message metadata — strip internal Camel and JMS headers before calling external services to prevent protocol leakageconvertBodyTo() lets you trigger conversion explicitly when neededProcessors in Apache Camel give developers direct access to the Exchange object, allowing them to inspect and modify messages as they flow through a route. Imagine you are onboarding a new team member who has just written their first Camel processor, and they have chosen to use `exchange.getOut().setBody(...)` to update the message body. Explain to this new team member what the difference is between modifying `exchange.getIn()` versus `exchange.getOut()`, why their current approach could cause unexpected problems in a real integration, and what they should do instead to safely transform the message body while keeping existing headers and properties intact.