Federico Mariani created CAMEL-24757:
----------------------------------------
Summary: camel-spring-boot: Spring Cache-backed KeyValueRepository
adapter for the State Store component
Key: CAMEL-24757
URL: https://issues.apache.org/jira/browse/CAMEL-24757
Project: Camel
Issue Type: New Feature
Components: camel-spring-boot
Reporter: Federico Mariani
h2. Background
The State Store component (camel-state-store, since 4.23) provides a unified
key-value API (put/get/putIfAbsent/delete/contains/keys/size/clear) backed by
the pluggable {{KeyValueRepository}} SPI from camel-api. When no backend bean
is configured, it falls back to the in-memory {{MemoryKeyValueRepository}}; for
anything persistent/distributed, camel-api already ships implementations for
Caffeine, Cassandra, Ehcache, Hazelcast, Infinispan, JCache, JDBC, JPA, Kafka
and Redis.
h2. Problem
Those implementations don't line up with what Spring Boot users already have
auto-configured:
* {{RedisKeyValueRepository}} (camel-redis) is built on *Redisson*
({{RedissonClient}}), not Spring Data Redis (Lettuce/{{RedisTemplate}}), which
is what {{spring-boot-starter-data-redis}} actually auto-configures. A Spring
Boot user has to add Redisson separately and hand-wire a {{RedissonClient}}
bean just to get a {{KeyValueRepository}}.
* The same friction applies to Caffeine/Ehcache/Hazelcast: Camel's
implementations expect Camel-flavored construction, not the {{CacheManager}}
bean that {{spring-boot-starter-cache}} already auto-configures from
{{spring.cache.type}}.
So in a Spring Boot application, using the State Store component with a real
backend currently means adding a second, Camel-specific client/dependency and
manually wiring a bean, even when Spring Boot already manages an equivalent
cache/client for you.
h2. Proposal
Add a {{KeyValueRepository}} implementation in *camel-spring-boot* (core
auto-configuration module, not the generated starter) backed by Spring's cache
abstraction ({{org.springframework.cache.Cache}} / {{CacheManager}}).
Auto-configure it with {{@ConditionalOnBean(CacheManager.class)}} and
{{@ConditionalOnMissingBean(KeyValueRepository.class)}}, so it only kicks in
when the application already has a Spring-managed {{CacheManager}} and no
explicit Camel {{KeyValueRepository}} bean.
One adapter then covers every backend Spring Boot's cache abstraction already
supports (Caffeine, Redis via Lettuce, Ehcache, Hazelcast, JCache, Couchbase,
etc.) via the standard {{spring.cache.*}} properties, with no extra
Camel-specific dependency or bean.
The State Store endpoint's {{storeName}} would map 1:1 to the Spring {{Cache}}
name looked up via {{CacheManager.getCache(storeName)}}.
h2. Example usage
Caffeine, using only spring-boot-starter-cache (no camel-caffeine dependency
needed):
{code:yaml}
spring:
cache:
type: caffeine
cache-names: myStore
caffeine:
spec: maximumSize=10000,expireAfterWrite=600s
{code}
{code:java}
from("direct:store")
.setHeader("CamelStateStoreKey", constant("myKey"))
.to("state-store:myStore?operation=put");
from("direct:retrieve")
.setHeader("CamelStateStoreKey", constant("myKey"))
.to("state-store:myStore?operation=get");
{code}
Redis via Spring Data Redis/Lettuce (no camel-redis/Redisson dependency needed):
{code:yaml}
spring:
cache:
type: redis
cache-names: myStore
data:
redis:
host: localhost
port: 6379
{code}
Same route code as above - only the Spring Boot configuration changes to switch
backend.
h2. Limitations
* *No {{keys()}} support* - Spring's {{Cache}} SPI has no portable key
enumeration across backends, so the adapter would throw
{{UnsupportedOperationException}} for the {{keys}} operation.
* *No {{size()}} support* - same reason as above; {{size}} would also throw
{{UnsupportedOperationException}}.
* *No per-entry TTL* - Spring's {{Cache.put}} has no TTL parameter; TTL is
fixed per cache/CacheManager configuration (e.g. the Caffeine spec or Redis
entry-ttl config), not per message. The State Store {{ttl}} endpoint option and
{{CamelStateStoreTtl}} header would have no effect against this backend; the
adapter should log a WARN if a TTL is supplied so the mismatch isn't silent.
* *Spring Boot only* - this lives in camel-spring-boot, not camel-api/core, so
it is not available to other runtimes (Quarkus, camel-main, etc.); those keep
using the existing backend-specific KeyValueRepository implementations.
* *Cache pre-declaration* - some {{CacheManager}} implementations require cache
names to be pre-declared (e.g. via {{spring.cache.cache-names}}); a
{{storeName}} without a matching configured cache would fail to resolve, unlike
the always-available in-memory default.
_Claude Code on behalf of Federico Mariani_
--
This message was sent by Atlassian Jira
(v8.20.10#820010)