KeyValueRepository Backends
KeyValueRepository is the unified storage SPI used by the Cache EIP, Idempotent Consumer, and Aggregator patterns. It provides a simple get / put / delete / clear contract — plus an optional atomic putIfAbsent — that Camel maps onto many storage technologies through pluggable backend implementations.
Choosing the right backend is typically a matter of:
-
Scope — in-process memory, distributed in-memory, durable on-disk, or remote service.
-
Atomicity — whether the backend natively provides an atomic Compare-And-Swap (CAS) operation (required for exactly-once idempotency under concurrent load).
-
TTL — whether expiration is handled natively by the store or requires client-side logic.
-
Operational footprint — whether extra infrastructure (Redis, Kafka, Cassandra …) is acceptable.
Available Backends
| Backend class | Module | Key features | Atomic CAS | Native TTL |
|---|---|---|---|---|
|
| JVM-local | ✓ | ✗ |
|
| High-throughput in-process cache; configurable size; near-cache for distributed setups | ✓ | ✗ (client-side via Cache EIP |
|
| Ehcache 3; heap + off-heap + disk tiers; native TTL per cache config | ✓ | ✓ (Ehcache XML config) |
|
| JSR-107 provider-agnostic; works with Ehcache, Hazelcast, Infinispan, … | ✓ (provider-dependent) | ✓ (via |
|
| Distributed, partitioned | ✓ | ✓ (map TTL config) |
|
| Redis | ✓ | ✓ (native |
|
| Hot Rod client to remote Infinispan/Data Grid cluster; distributed, transactional | ✓ | ✓ (per-entry lifespan) |
|
| Any JDBC DataSource; portable; optional auto-DDL; ACID via DataSource transactions | ✗ (non-atomic) | ✗ (client-side) |
|
| JPA entity-backed; works with any JPA 2 provider (Hibernate, EclipseLink…) | ✗ (non-atomic) | ✗ (client-side) |
|
| Cassandra wide-column store; configurable consistency levels; naturally distributed | ✗ (non-atomic) | ✓ (Cassandra TTL) |
|
| Kafka topic as a compacted key-value log; state rebuilt on startup; eventually consistent | ✗ (non-atomic) | ✗ (topic retention) |
Atomic CAS means putIfAbsent is implemented without an external lock — required for safe concurrent use as an Idempotent Repository. Non-atomic backends can still be used with the Cache EIP and Aggregator when access is serialized or best-effort semantics are acceptable. |
Backend Configuration
Each backend is registered as a named bean in the Camel registry and then referenced from a route or EIP by name (or auto-discovered when only one bean is present).
MemoryKeyValueRepository
Provided by camel-support (always on the classpath). No extra dependencies.
-
Java DSL
-
YAML DSL
import org.apache.camel.support.MemoryKeyValueRepository;
@BindToRegistry("myMemoryKvr")
public KeyValueRepository myMemoryKvr() {
return new MemoryKeyValueRepository();
} - beans:
- name: myMemoryKvr
type: org.apache.camel.support.MemoryKeyValueRepository Because MemoryKeyValueRepository has no required properties, registering it by type alone is enough. When no KeyValueRepository bean is registered at all, the Cache EIP auto-creates a private MemoryKeyValueRepository per block — no explicit bean needed.
CaffeineKeyValueRepository
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-caffeine</artifactId>
</dependency> Optional property:
-
maximumSize— maximum number of entries before eviction (default: unbounded).
-
Java DSL
-
YAML DSL
import org.apache.camel.component.caffeine.cache.CaffeineKeyValueRepository;
@BindToRegistry("caffeineKvr")
public KeyValueRepository caffeineKvr() {
CaffeineKeyValueRepository kvr = new CaffeineKeyValueRepository();
kvr.setMaximumSize(10_000);
return kvr;
} - beans:
- name: caffeineKvr
type: org.apache.camel.component.caffeine.cache.CaffeineKeyValueRepository
properties:
maximumSize: 10000 TTL is not natively managed by Caffeine in this adapter. Use the Cache EIP ttl option for time-based expiration, which wraps values with an expiry timestamp stored in the entry.
EhcacheKeyValueRepository
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-ehcache</artifactId>
</dependency> Required property:
-
cacheManager— a configuredorg.ehcache.CacheManagerbean.
Optional property:
-
cacheName— logical cache name within theCacheManager(default:"camel-kvr").
-
Java DSL
-
YAML DSL
import org.apache.camel.component.ehcache.EhcacheKeyValueRepository;
import org.ehcache.CacheManager;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.expiry.ExpiryPolicy;
import java.time.Duration;
@BindToRegistry("ehcacheManager")
public CacheManager ehcacheManager() {
return CacheManagerBuilder.newCacheManagerBuilder()
.withCache("products",
CacheConfigurationBuilder
.newCacheConfigurationBuilder(String.class, Object.class,
ResourcePoolsBuilder.heap(10_000))
.withExpiry(ExpiryPolicy.BASE_EXPIRY // or a custom ExpiryPolicy
.timeToLiveExpiration(Duration.ofMinutes(30)))
)
.build(true); // true = init on build
}
@BindToRegistry("ehcacheKvr")
public KeyValueRepository ehcacheKvr(CacheManager ehcacheManager) {
EhcacheKeyValueRepository kvr = new EhcacheKeyValueRepository();
kvr.setCacheManager(ehcacheManager);
kvr.setCacheName("products");
return kvr;
} - beans:
- name: ehcacheKvr
type: org.apache.camel.component.ehcache.EhcacheKeyValueRepository
properties:
cacheManager: "#bean:ehcacheManager"
cacheName: products Native TTL is configured on the CacheManager / CacheConfiguration, not on the KeyValueRepository bean itself.
JCacheKeyValueRepository
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jcache</artifactId>
</dependency>
<!-- Plus a JSR-107 provider, e.g.: -->
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency> Optional properties:
-
configuration— ajavax.cache.configuration.Configuration(orMutableConfiguration) bean. -
cacheName— name of the JCache cache (default:"camel-kvr").
-
Java DSL
-
YAML DSL
import org.apache.camel.component.jcache.JCacheKeyValueRepository;
import javax.cache.configuration.MutableConfiguration;
import javax.cache.expiry.CreatedExpiryPolicy;
import javax.cache.expiry.Duration;
@BindToRegistry("jcacheConfig")
public MutableConfiguration<String, Object> jcacheConfig() {
return new MutableConfiguration<String, Object>()
.setTypes(String.class, Object.class)
.setExpiryPolicyFactory(
CreatedExpiryPolicy.factoryOf(new Duration(java.util.concurrent.TimeUnit.MINUTES, 30)))
.setStatisticsEnabled(true);
}
@BindToRegistry("jcacheKvr")
public KeyValueRepository jcacheKvr(MutableConfiguration<String, Object> jcacheConfig) {
JCacheKeyValueRepository kvr = new JCacheKeyValueRepository();
kvr.setConfiguration(jcacheConfig);
kvr.setCacheName("products");
return kvr;
} - beans:
- name: jcacheKvr
type: org.apache.camel.component.jcache.JCacheKeyValueRepository
properties:
cacheName: products
configuration: "#bean:jcacheConfig" Whether putIfAbsent is truly atomic depends on the JSR-107 provider. Most production providers (Ehcache 3, Hazelcast, Infinispan) implement it atomically.
HazelcastKeyValueRepository
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-hazelcast</artifactId>
</dependency> Optional properties:
-
hazelcastInstance— an existingHazelcastInstancebean (default: auto-created). -
mapName— name of the distributedIMap(default:"camel-kvr").
-
Java DSL
-
YAML DSL
import org.apache.camel.component.hazelcast.HazelcastKeyValueRepository;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.config.Config;
import com.hazelcast.config.MapConfig;
@BindToRegistry("hazelcastInstance")
public HazelcastInstance hazelcastInstance() {
Config config = new Config();
config.addMapConfig(new MapConfig("camel-products")
.setTimeToLiveSeconds(1800)); // 30-minute TTL
return Hazelcast.newHazelcastInstance(config);
}
@BindToRegistry("hazelcastKvr")
public KeyValueRepository hazelcastKvr(HazelcastInstance hazelcastInstance) {
HazelcastKeyValueRepository kvr = new HazelcastKeyValueRepository();
kvr.setHazelcastInstance(hazelcastInstance);
kvr.setMapName("camel-products");
return kvr;
} - beans:
- name: hazelcastKvr
type: org.apache.camel.component.hazelcast.HazelcastKeyValueRepository
properties:
hazelcastInstance: "#bean:hazelcastInstance"
mapName: camel-products TTL is configured on the Hazelcast MapConfig, not on the KeyValueRepository. putIfAbsent maps to IMap.putIfAbsent, which is atomic across the cluster.
RedisKeyValueRepository
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-redis</artifactId>
</dependency> Required property:
-
endpoint— acamel-redisendpoint URI string (e.g."redis://localhost:6379").
Optional properties:
-
keyPrefix— a string prefix applied to all keys (useful to namespace multiple stores in one Redis DB). -
redisson— a pre-configuredRedissonClientbean (alternative toendpoint).
-
Java DSL
-
With a custom
RedissonClient -
YAML DSL
import org.apache.camel.component.redis.RedisKeyValueRepository;
@BindToRegistry("redisKvr")
public KeyValueRepository redisKvr() {
RedisKeyValueRepository kvr = new RedisKeyValueRepository();
kvr.setEndpoint("redis://localhost:6379");
kvr.setKeyPrefix("myapp:");
return kvr;
} import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
@BindToRegistry("redissonClient")
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer()
.setAddress("redis://localhost:6379")
.setPassword("secret");
return Redisson.create(config);
}
@BindToRegistry("redisKvr")
public KeyValueRepository redisKvr(RedissonClient redissonClient) {
RedisKeyValueRepository kvr = new RedisKeyValueRepository();
kvr.setRedisson(redissonClient);
kvr.setKeyPrefix("myapp:");
return kvr;
} - beans:
- name: redisKvr
type: org.apache.camel.component.redis.RedisKeyValueRepository
properties:
endpoint: "redis://localhost:6379"
keyPrefix: "myapp:" Redis provides native TTL via EXPIRE / SET … EX. putIfAbsent maps to SET NX, which is atomic. The Cache EIP ttl option is respected and propagated to the underlying EXPIRE call.
InfinispanRemoteKeyValueRepository
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-infinispan</artifactId>
</dependency> Required property:
-
cacheName— the remote Infinispan cache name.
Optional properties:
-
configuration— aorg.infinispan.client.hotrod.configuration.Configurationbean. -
cacheContainer— a pre-builtRemoteCacheManagerbean (takes priority overconfiguration).
-
Java DSL
-
YAML DSL
import org.apache.camel.component.infinispan.remote.InfinispanRemoteKeyValueRepository;
import org.infinispan.client.hotrod.RemoteCacheManager;
import org.infinispan.client.hotrod.configuration.ConfigurationBuilder;
@BindToRegistry("infinispanManager")
public RemoteCacheManager infinispanManager() {
return new RemoteCacheManager(
new ConfigurationBuilder()
.addServer().host("infinispan-host").port(11222)
.security().authentication()
.username("camel").password("secret")
.build());
}
@BindToRegistry("infinispanKvr")
public KeyValueRepository infinispanKvr(RemoteCacheManager infinispanManager) {
InfinispanRemoteKeyValueRepository kvr = new InfinispanRemoteKeyValueRepository();
kvr.setCacheContainer(infinispanManager);
kvr.setCacheName("products");
return kvr;
} - beans:
- name: infinispanKvr
type: org.apache.camel.component.infinispan.remote.InfinispanRemoteKeyValueRepository
properties:
cacheContainer: "#bean:infinispanManager"
cacheName: products Infinispan supports per-entry lifespan and max-idle TTL natively. putIfAbsent is implemented via the PUTIFABSENT Hot Rod operation, which is atomic.
JdbcKeyValueRepository
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-sql</artifactId>
</dependency> Required property:
-
dataSource— ajavax.sql.DataSourcebean.
Optional properties:
-
tableName— the table used for storage (default:"camel_kvr"). -
createTableIfNotExists— auto-create the table at startup if absent (default:true).
The auto-created table schema:
CREATE TABLE camel_kvr (
kvr_key VARCHAR(255) NOT NULL PRIMARY KEY,
kvr_value TEXT
); -
Java DSL
-
YAML DSL
import org.apache.camel.component.sql.JdbcKeyValueRepository;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import javax.sql.DataSource;
@BindToRegistry("myDataSource")
public DataSource myDataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("org.postgresql.Driver");
ds.setUrl("jdbc:postgresql://localhost:5432/mydb");
ds.setUsername("camel");
ds.setPassword("secret");
return ds;
}
@BindToRegistry("jdbcKvr")
public KeyValueRepository jdbcKvr(DataSource myDataSource) {
JdbcKeyValueRepository kvr = new JdbcKeyValueRepository();
kvr.setDataSource(myDataSource);
kvr.setTableName("product_cache");
kvr.setCreateTableIfNotExists(true);
return kvr;
} - beans:
- name: jdbcKvr
type: org.apache.camel.component.sql.JdbcKeyValueRepository
properties:
dataSource: "#bean:myDataSource"
tableName: product_cache
createTableIfNotExists: true JdbcKeyValueRepository does not implement atomic putIfAbsent — concurrent inserts may produce duplicate-key exceptions that are caught and treated as a "key already exists" condition. This is safe for idempotency at low concurrency, but for high-throughput exactly-once semantics, prefer a backend with native CAS. |
JpaKeyValueRepository
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jpa</artifactId>
</dependency> Required property:
-
entityManagerFactory— ajavax.persistence.EntityManagerFactorybean.
Optional properties:
-
joinTransaction— whether to participate in an existing JTA transaction (default:true). -
sharedEntityManager— use a shared/thread-boundEntityManager(Spring integration, default:false).
-
Java DSL
-
YAML DSL
import org.apache.camel.component.jpa.JpaKeyValueRepository;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
@BindToRegistry("emf")
public EntityManagerFactory emf() {
return Persistence.createEntityManagerFactory("myPersistenceUnit");
}
@BindToRegistry("jpaKvr")
public KeyValueRepository jpaKvr(EntityManagerFactory emf) {
JpaKeyValueRepository kvr = new JpaKeyValueRepository();
kvr.setEntityManagerFactory(emf);
kvr.setJoinTransaction(false); // standalone, no JTA
return kvr;
} - beans:
- name: jpaKvr
type: org.apache.camel.component.jpa.JpaKeyValueRepository
properties:
entityManagerFactory: "#bean:emf"
joinTransaction: false Like JdbcKeyValueRepository, the JPA backend does not provide atomic putIfAbsent. Idempotency under concurrent load relies on unique-constraint violations being caught at the database level. |
CassandraKeyValueRepository
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-cassandraql</artifactId>
</dependency> Required property:
-
session— acom.datastax.oss.driver.api.core.CqlSessionbean.
Optional properties:
-
table— Cassandra table name (default:"camel_kvr"). -
readConsistencyLevel—ConsistencyLevelfor reads (default:LOCAL_ONE). -
writeConsistencyLevel—ConsistencyLevelfor writes (default:LOCAL_ONE).
-
Java DSL
-
YAML DSL
import org.apache.camel.component.cassandra.CassandraKeyValueRepository;
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.ConsistencyLevel;
import java.net.InetSocketAddress;
@BindToRegistry("cassandraSession")
public CqlSession cassandraSession() {
return CqlSession.builder()
.addContactPoint(new InetSocketAddress("cassandra-host", 9042))
.withLocalDatacenter("datacenter1")
.withKeyspace("myapp")
.build();
}
@BindToRegistry("cassandraKvr")
public KeyValueRepository cassandraKvr(CqlSession cassandraSession) {
CassandraKeyValueRepository kvr = new CassandraKeyValueRepository();
kvr.setSession(cassandraSession);
kvr.setTable("product_cache");
kvr.setReadConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM);
kvr.setWriteConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM);
return kvr;
} - beans:
- name: cassandraKvr
type: org.apache.camel.component.cassandra.CassandraKeyValueRepository
properties:
session: "#bean:cassandraSession"
table: product_cache
readConsistencyLevel: LOCAL_QUORUM
writeConsistencyLevel: LOCAL_QUORUM Cassandra supports native TTL via the USING TTL clause on INSERT/UPDATE. The Cache EIP ttl option is forwarded to the Cassandra write operation as a native TTL, so entries expire automatically at the storage layer.
putIfAbsent uses Cassandra’s lightweight transaction (INSERT … IF NOT EXISTS). Although this is CAS at the Cassandra level, it is not classified as fully atomic in the table above because it is subject to Paxos latency and only provides linearizability within a single partition.
KafkaKeyValueRepository
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-kafka</artifactId>
</dependency> Required properties:
-
topic— the compacted Kafka topic used as the key-value store. -
bootstrapServers— comma-separated list of Kafka broker addresses.
Optional properties:
-
maxCacheSize— maximum number of entries to hold in the local in-memory replica (default: unlimited). -
pollDurationMs— poll timeout in milliseconds when rebuilding state at startup (default:100). -
startupOnly— iftrue, the consumer only reads the topic once at startup and does not keep polling (default:false). -
groupId— Kafka consumer group ID (default: auto-generated).
-
Java DSL
-
YAML DSL
import org.apache.camel.component.kafka.KafkaKeyValueRepository;
@BindToRegistry("kafkaKvr")
public KeyValueRepository kafkaKvr() {
KafkaKeyValueRepository kvr = new KafkaKeyValueRepository();
kvr.setTopic("camel-kvr-products");
kvr.setBootstrapServers("kafka1:9092,kafka2:9092");
kvr.setMaxCacheSize(50_000);
kvr.setGroupId("camel-kvr-consumer");
return kvr;
} - beans:
- name: kafkaKvr
type: org.apache.camel.component.kafka.KafkaKeyValueRepository
properties:
topic: camel-kvr-products
bootstrapServers: "kafka1:9092,kafka2:9092"
maxCacheSize: 50000
groupId: camel-kvr-consumer The Kafka backend reconstructs its state by replaying the topic log on startup. This means startup time scales with topic size. Use a log-compacted topic (cleanup.policy=compact) to keep the log bounded. |
TTL is not natively supported — entry retention is governed by topic-level retention.ms / retention.bytes, which affects all entries equally, not individual ones. putIfAbsent is not atomic: reads and writes go through the local in-memory replica with no distributed locking.
Atomicity Guarantees
The putIfAbsent(key, value) method is the critical operation for the Idempotent Consumer — it must return true only for the first caller and false for all subsequent callers with the same key.
| Backend | Atomic CAS | Mechanism |
|---|---|---|
| ✓ |
|
| ✓ | Caffeine’s internal lock-free |
| ✓ |
|
| ✓ (provider) | Delegates to the JSR-107 provider’s |
| ✓ |
|
| ✓ |
|
| ✓ | Hot Rod |
| ✓ |
|
| ✗ | Optimistic INSERT + constraint catch; |
| ✓ | Lightweight transactions: |
| ✗ | In-memory replica check — no distributed coordination |
For non-atomic backends, use the Cache EIP (where idempotency is not required) or accept at-least-once semantics in idempotency-sensitive flows. Alternatively, front a non-atomic backend with a distributed lock (e.g. via camel-hazelcast ILock).
TTL Behavior
| Backend | Native TTL | Notes |
|---|---|---|
| ✗ | Cache EIP wraps values with an expiry timestamp; expired entries are evicted on read |
| ✗ | Same as Memory — client-side expiry envelope |
| ✓ |
|
| ✓ (provider) |
|
| ✓ |
|
| ✓ |
|
| ✓ | Per-entry |
| ✗ | Client-side expiry envelope; a periodic cleanup query is needed to purge expired rows |
| ✗ | Client-side expiry envelope; schedule a |
| ✓ |
|
| ✗ | Topic-level retention only; all entries expire at the same time based on |
| For backends without native TTL, the Cache EIP stores an expiry timestamp inside the serialized value and skips (evicts on read) entries whose timestamp has passed. This means the storage layer may accumulate stale entries that are never explicitly deleted — size them accordingly or run periodic cleanup jobs. |
Distributed Deployment
When running multiple Camel instances (e.g. behind a load balancer or in a Kubernetes cluster), the choice of backend determines whether state is shared and whether CAS operations are safe across nodes.
| Backend | Shared state | Distributed CAS | Distributed-safe | Notes |
|---|---|---|---|---|
| ✗ | ✗ | ❌ | JVM-local only. Each node has its own isolated store. |
| ✗ | ✗ | ❌ | JVM-local only. Suitable as a near-cache in front of a distributed backend. |
| ✗ (unless clustered) | ✗ | ❌ | JVM-local by default. Ehcache clustering requires additional configuration outside of this integration. |
| Depends on provider | Depends on provider | ⚠️ | With a distributed provider (Hazelcast, Infinispan), state is shared. CAS atomicity depends on the provider. |
| ✓ | ✓ | ✅ | Distributed |
| ✓ | ✓ | ✅ | Shared Redis server. |
| ✓ | ✓ | ✅ | Remote cache with version-based optimistic CAS via |
| ✓ | ✓ | ✅ | Shared database. |
| ✓ | ⚠️ | ⚠️ | Shared database. |
| ✓ | ✓ | ✅ | Shared cluster. Full lightweight transaction (LWT) support: |
| ✓ (eventually) | ✗ | ⚠️ | State is shared via the topic, but CAS is local-only. Suitable for best-effort deduplication, not strict exactly-once across nodes. |
Recommendations:
-
Idempotent Consumer in a cluster — use Hazelcast, Redis, Infinispan, JDBC, or Cassandra for guaranteed exactly-once deduplication.
-
Aggregator in a cluster — use any shared-state backend (recovery requires the same backend to be visible from the recovering node).
-
Cache EIP in a cluster — any backend works. JVM-local backends give each node its own cache (fine for read-through caching); distributed backends share cached values.
-
Single-node deployment — any backend works, including Memory and Caffeine.
Using Adapters: Idempotent Consumer and Aggregator
A KeyValueRepository can be adapted to the specialized repository interfaces required by the Idempotent Consumer and Aggregator EIPs via two adapter classes.
KeyValueIdempotentRepository
KeyValueIdempotentRepository wraps any KeyValueRepository as a org.apache.camel.spi.IdempotentRepository, making it usable with Idempotent Consumer.
-
Java DSL
-
XML DSL
-
YAML DSL
import org.apache.camel.support.KeyValueIdempotentRepository;
import org.apache.camel.component.redis.RedisKeyValueRepository;
@BindToRegistry("redisKvr")
public KeyValueRepository redisKvr() {
RedisKeyValueRepository kvr = new RedisKeyValueRepository();
kvr.setEndpoint("redis://localhost:6379");
kvr.setKeyPrefix("idempotent:");
return kvr;
}
@BindToRegistry("idempotentRepo")
public IdempotentRepository idempotentRepo(KeyValueRepository redisKvr) {
return new KeyValueIdempotentRepository(redisKvr);
} Then reference it in a route:
from("jms:queue:orders")
.idempotentConsumer(header("JMSMessageID"))
.idempotentRepository("idempotentRepo")
.to("direct:processOrder"); <route>
<from uri="jms:queue:orders"/>
<idempotentConsumer idempotentRepository="idempotentRepo">
<header>JMSMessageID</header>
<to uri="direct:processOrder"/>
</idempotentConsumer>
</route> - from:
uri: jms:queue:orders
steps:
- idempotentConsumer:
header: JMSMessageID
idempotentRepository: idempotentRepo
steps:
- to: direct:processOrder KeyValueAggregationRepository
KeyValueAggregationRepository wraps any KeyValueRepository as a org.apache.camel.spi.AggregationRepository, making it usable with Aggregator.
-
Java DSL
-
XML DSL
-
YAML DSL
import org.apache.camel.support.KeyValueAggregationRepository;
import org.apache.camel.component.hazelcast.HazelcastKeyValueRepository;
@BindToRegistry("hazelcastKvr")
public KeyValueRepository hazelcastKvr() {
HazelcastKeyValueRepository kvr = new HazelcastKeyValueRepository();
kvr.setMapName("aggregation-store");
return kvr;
}
@BindToRegistry("aggregationRepo")
public AggregationRepository aggregationRepo(KeyValueRepository hazelcastKvr) {
return new KeyValueAggregationRepository(hazelcastKvr);
} Then reference it in a route:
from("direct:start")
.aggregate(header("orderId"), new GroupedBodyAggregationStrategy())
.aggregationRepository("aggregationRepo")
.completionSize(10)
.to("direct:process"); <route>
<from uri="direct:start"/>
<aggregate strategyRef="groupedBodyStrategy"
aggregationRepositoryRef="aggregationRepo"
completionSize="10">
<correlationExpression>
<header>orderId</header>
</correlationExpression>
<to uri="direct:process"/>
</aggregate>
</route> - from:
uri: direct:start
steps:
- aggregate:
correlationExpression:
header: orderId
strategyRef: groupedBodyStrategy
aggregationRepository: aggregationRepo
completionSize: 10
steps:
- to: direct:process KeyValueAggregationRepository serializes the entire Exchange to the backing store. For backends without native TTL (JDBC, JPA, Kafka), aggregation-in-progress entries persist until completion. Use a backend with durable storage (JDBC, JPA, Infinispan) when crash recovery of in-flight aggregations is required. |