Cache
The Cache EIP provides a transparent read-through cache for a block of processing steps.
On cache hit (key found), the cached value is set as the message body and the nested steps are skipped entirely. On cache miss, the nested steps execute normally and the resulting message body is stored in the cache for subsequent requests with the same key.
Cache EIP vs Cache Components
Camel includes several cache components (camel-caffeine, camel-ehcache, camel-jcache, camel-infinispan, etc.) that provide endpoint-level cache operations (explicit GET/PUT/INVALIDATE). Those are useful for fine-grained cache management (bulk ops, invalidation, event listening).
The Cache EIP covers the most common use case — "cache the result of this expensive block by key with a TTL" — without manual plumbing. It is analogous to @Cacheable in Spring:
// Without Cache EIP: 7+ lines of plumbing, tied to one cache technology
from("direct:start")
.setHeader("CamelCaffeineAction", constant("GET"))
.setHeader("CamelCaffeineKey", header("productId"))
.to("caffeine-cache:products")
.choice()
.when(body().isNull())
.to("http://expensive-service")
.setHeader("CamelCaffeineAction", constant("PUT"))
.to("caffeine-cache:products")
.end();
// With Cache EIP: 2 lines, backend-agnostic
from("direct:start")
.cache(simple("${header.productId}"))
.to("http://expensive-service")
.end(); Options
The Cache eip supports the following options which are listed below.
| Name | Description | Default | Type |
|---|---|---|---|
note | The note for this node. | String | |
description | The description for this node. | String | |
disabled | Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime. | false | Boolean |
expression | Required Expression to compute the cache key. Messages with the same key share the cached result. | ExpressionDefinition | |
keyValueRepository | Sets the reference name of the KeyValueRepository to use as the cache backing store. If not set, a MemoryKeyValueRepository is auto-created. | KeyValueRepository | |
ttl | Sets the time-to-live for cached entries. Supports duration syntax (e.g. 10m, 1h) or milliseconds. Default: -1 (no expiration). | -1 | String |
cacheNull | Whether to cache null results. By default, null message bodies are not cached. | false | Boolean |
outputs | Required | List |
Error Handling
Cache errors are handled gracefully:
-
If the cache read fails, the nested steps execute normally (graceful degradation).
-
If the cache write fails after a successful execution, the error is logged but does not propagate to the exchange.
-
Failed exchanges (those with an exception) are never cached.
Examples
Java DSL
Cache with an auto-created in-memory store (zero config):
from("direct:start")
.cache(simple("${header.productId}"))
.to("http://product-service/api/product")
.unmarshal().json()
.end()
.to("direct:continue"); Cache with TTL and an explicit KeyValueRepository:
from("direct:start")
.cache(simple("${header.productId}"))
.ttl("10m")
.keyValueRepository("myRedisKvr")
.to("http://product-service/api/product")
.end()
.to("direct:continue"); Expression clause form:
from("direct:start")
.cache().simple("${header.productId}")
.ttl("5m")
.to("http://product-service/api/product")
.end()
.to("direct:continue"); Using with KeyValueRepository
The Cache EIP uses the org.apache.camel.spi.KeyValueRepository SPI as its backing store. If no repository is configured, a MemoryKeyValueRepository is auto-created.
Any KeyValueRepository implementation can be used — the same SPI that backs the Idempotent Consumer and Aggregator patterns.
To use a specific store, register a KeyValueRepository bean in the Camel registry:
@BindToRegistry("myCache")
public KeyValueRepository myCache() {
return new MemoryKeyValueRepository();
} Then reference it in the route:
from("direct:start")
.cache(simple("${header.key}"))
.keyValueRepository("myCache")
.to("http://service")
.end(); If a single KeyValueRepository bean exists in the registry, it is auto-discovered — no explicit reference needed.
When no KeyValueRepository bean is registered, each .cache() block auto-creates its own isolated in-memory store — no risk of key collisions. However, if exactly one KeyValueRepository bean exists in the registry, all .cache() blocks will auto-discover and share it. In that case, ensure cache keys are unique across blocks (e.g. include a domain prefix: simple("product:${header.productId}") vs simple("order:${header.orderId}")), or reference distinct named repositories explicitly via .keyValueRepository("name"). |
Cache Invalidation
The Cache EIP uses TTL-based expiration by default — entries expire automatically after the configured ttl. For scenarios where you need to invalidate a specific cache entry on demand (e.g. when a foreign system notifies you that data has changed), you can call delete(key) on the underlying KeyValueRepository directly.
Invalidating from Another Route
Register a named KeyValueRepository and share it between the caching route and an invalidation route:
// Shared cache store
@BindToRegistry("productCache")
public KeyValueRepository productCache() {
return new MemoryKeyValueRepository();
} // Route that caches product lookups
from("direct:getProduct")
.cache(simple("${header.productId}"))
.keyValueRepository("productCache")
.ttl("30m")
.to("http://product-service/api/product")
.end();
// Route that invalidates on external change events
from("jms:topic:product.changes")
.process(exchange -> {
String productId = exchange.getIn().getHeader("productId", String.class);
KeyValueRepository cache = exchange.getContext().getRegistry()
.lookupByNameAndType("productCache", KeyValueRepository.class);
cache.delete(productId);
})
.log("Invalidated cache for product ${header.productId}"); XML DSL
<route>
<from uri="direct:getProduct"/>
<cache keyValueRepository="productCache" ttl="30m">
<simple>${header.productId}</simple>
<to uri="http://product-service/api/product"/>
</cache>
</route>
<!-- Invalidation route -->
<route>
<from uri="jms:topic:product.changes"/>
<bean ref="productCache" method="delete(${header.productId})"/>
<log message="Invalidated cache for product ${header.productId}"/>
</route>