Apache Camel's Transformer EIP enables declarative, type-safe message conversion between POJOs in routes. Below is a fully self-contained, single-file example using nested classes and Camel's Main class.
Complete Single-File Example
import org.apache.camel.Message;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.main.Main;
import org.apache.camel.spi.DataType;
import org.apache.camel.spi.Transformer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Apache Camel Transformer EIP example demonstrating declarative type-safe message conversion.
* All classes (Lead, Customer, LeadToCustomerTransformer) are in this single file.
*/
public class TransformerDemo {
private static final Logger LOG = LoggerFactory.getLogger(TransformerDemo.class);
/**
* Lead POJO - Input model
*/
public static class Lead {
private String name;
private String company;
private String city;
public Lead() {}
public Lead(String name, String company, String city) {
this.name = name;
this.company = company;
this.city = city;
}
// Getters and setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getCompany() { return company; }
public void setCompany(String company) { this.company = company; }
public String getCity() { return city; }
public void setCity(String city) { this.city = city; }
@Override
public String toString() {
return "Lead{name='" + name + "', company='" + company + "', city='" + city + "'}";
}
}
/**
* Customer POJO - Output model
*/
public static class Customer {
private String fullName;
private String organization;
private String location;
public Customer() {}
public Customer(String fullName, String organization, String location) {
this.fullName = fullName;
this.organization = organization;
this.location = location;
}
// Getters and setters
public String getFullName() { return fullName; }
public void setFullName(String fullName) { this.fullName = fullName; }
public String getOrganization() { return organization; }
public void setOrganization(String organization) { this.organization = organization; }
public String getLocation() { return location; }
public void setLocation(String location) { this.location = location; }
@Override
public String toString() {
return "Customer{fullName='" + fullName + "', organization='" + organization + "', location='" + location + "'}";
}
}
/**
* Transformer implementation for converting Lead to Customer
*/
public static class LeadToCustomerTransformer extends Transformer {
@Override
public void transform(Message message, DataType fromType, DataType toType) throws Exception {
Lead lead = message.getMandatoryBody(Lead.class);
Customer customer = new Customer(
lead.getName().toUpperCase(),
lead.getCompany(),
lead.getCity()
);
message.setBody(customer);
}
}
public static void main(String[] args) throws Exception {
Main main = new Main();
main.configure().addRoutesBuilder(new RouteBuilder() {
@Override
public void configure() {
// Register the transformer
transformer()
.fromType("java:TransformerDemo$Lead")
.toType("java:TransformerDemo$Customer")
.withJava(LeadToCustomerTransformer.class);
// Main route with automatic transformation
from("direct:start")
.inputType("java:TransformerDemo$Lead")
.log("INPUT Lead: ${body}")
.to("direct:process") // Triggers transformer automatically
.log("OUTPUT Customer: ${body}");
from("direct:process")
.inputType("java:TransformerDemo$Customer")
.log("PROCESSED Customer: ${body}")
.to("log:final?showAll=true");
}
});
// Add a startup listener to send test message after Camel starts
main.addMainListener(new org.apache.camel.main.MainListenerSupport() {
@Override
public void afterStart(org.apache.camel.main.BaseMainSupport mainSupport) {
try {
LOG.info("Sending test Lead message...");
mainSupport.getCamelContext().createProducerTemplate()
.sendBody("direct:start", new Lead("John Doe", "Acme Corp", "Auckland"));
LOG.info("Test message sent successfully!");
} catch (Exception e) {
LOG.error("Failed to send test message", e);
}
}
});
// Run Camel (will run until Ctrl+C is pressed)
main.run(args);
}
}
Expected Console Output
Sending test Lead message...
INPUT Lead: Lead{name='John Doe', company='Acme Corp', city='Auckland'}
PROCESSED Customer: Customer{fullName='JOHN DOE', organization='Acme Corp', location='Auckland'}
Exchange[Id: D0B1A9A95984A67-0000000000000000, RouteGroup: null, RouteId: route2, ExchangePattern: InOnly, Properties: {CamelToEndpoint=log://final?showAll=true}, Headers: {}, BodyType: TransformerDemo.Customer, Body: Customer{fullName='JOHN DOE', organization='Acme Corp', location='Auckland'}]
OUTPUT Customer: Customer{fullName='JOHN DOE', organization='Acme Corp', location='Auckland'}
Test message sent successfully!
Key Implementation Details
Nested Class References: Uses TransformerDemo$Lead syntax to reference nested classes in data type strings.
Transformer Method Signature: Uses transform(Message message, DataType fromType, DataType toType)—the modern Camel 4.x+ API.
Automatic Startup Test: MainListenerSupport.afterStart() sends test message automatically when Camel context starts.
Single-File Design: All POJOs, transformer, and routes contained in one compilable .java file.
How the Transformation Triggers
- Input validation →
inputType("java:TransformerDemo$Lead")expects Lead - Route boundary →
to("direct:process")requires Customer input type - Type mismatch → Camel matches
Lead→Customertransformer automatically - Conversion executes →
LeadToCustomerTransformer.transform()runs - Type contract satisfied → Route continues with Customer
Leave a Reply