These are the Enterprise Integration Patterns (EIPs) you reach for most often when building a connector. They come from Apache Camel’s standard DSL.
Content-based router (choice)
Routes the message to a different sub-route based on a predicate against the body or a header.
.choice()
.when(simple("${header.entityType} == 'TYPE_A'"))
.to("direct:handleTypeA")
.when(simple("${header.entityType} == 'TYPE_B'"))
.to("direct:handleTypeB")
.otherwise()
.to("direct:handleDefault")
.end();
For body-driven routing, use the JSONPath filter form $[?(@.field)] so missing fields don’t throw:
.choice()
.when().jsonpath("$[?(@.fieldName)]")
.log(LoggingLevel.DEBUG, "Field present")
.otherwise()
.process(exchange -> /* fill in default */)
.end();
Parallel fan-out and aggregation with multicast
Sends the same exchange to multiple routes in parallel, then combines the results with an aggregation strategy.
.multicast()
.aggregationStrategy(AggregationStrategies.groupedBody())
.parallelProcessing()
.executorService("customThreadPoolExecutor")
.stopOnException()
.to("direct:routeA", "direct:routeB")
.end();
// Body is now List<?>; access the responses by index
Register a custom thread pool when you parallelize:
getContext().getExecutorServiceManager()
.registerThreadPoolProfile(
new CustomThreadPoolProfile(getCamelContext()).get("customThreadPoolExecutor"));
Splitter for collection iteration
Processes every element of a collection independently, optionally in parallel.
.split(exchangeProperty("items"), AggregationStrategies.groupedBody())
.parallelProcessing()
.executorService("customThreadPoolExecutor")
.stopOnException()
// per-item processing here
.end();
Error recovery with doTry and doCatch
Use this for optional data extraction or any step that might throw but shouldn’t fail the whole route.
.doTry()
.setProperty("optionalData", jsonpath("$.optionalField"))
.doCatch(Exception.class)
.log(LoggingLevel.DEBUG, "Optional field not present; skipping")
.end();
Dead letter channel
Catch-all error redelivery for the whole connector. Add this before any route definitions in configure().
errorHandler(deadLetterChannel("direct:errorHandler")
.maximumRedeliveries(3)
.redeliveryDelay(1000)
.useExponentialBackOff());
Push and pop body
Use this when you need the body both as input to a transformation and as input to a later step. The pattern shows up often in multi-step REST flows.
from("direct:start")
.setBody(constant("Original Body"))
// Push the current body ("Original Body") onto the stack
.claimCheck(ClaimCheckOperation.Push)
.setBody(constant("New Temporary Body"))
.to("mock:transformed")
// Pop the "Original Body" back
.claimCheck(ClaimCheckOperation.Pop)
.to("mock:result");
Use Claim Check when you want to temporarily “park” a large or sensitive message (or part of it) while you do intermediate steps, then restore it later. Do not use with parallel work (for example, split().parallelProcessing(), multicast().parallelProcessing(), threads(), wireTap(), or async/SEDA).
Dynamic endpoint with toD
Routes to a dynamically computed endpoint, resolving the URL or route name at runtime.
// Dynamic route name
.toD("${exchangeProperty.resolvedRoute}");
// Dynamic XSLT file
.toD("xslt:{{?resourcesBasePath}}${exchangeProperty.xsltFile}-transformation.xslt");
Next step
To shape the data flowing through these EIPs, see Transform and validate data.