Skip to content

CAMEL-23239: Add camel-state-store component with pluggable key-value store#22158

Open
gnodet wants to merge 1 commit intoapache:mainfrom
gnodet:camel-state-store
Open

CAMEL-23239: Add camel-state-store component with pluggable key-value store#22158
gnodet wants to merge 1 commit intoapache:mainfrom
gnodet:camel-state-store

Conversation

@gnodet
Copy link
Contributor

@gnodet gnodet commented Mar 20, 2026

JIRA: CAMEL-23239

Motivation

Camel provides dedicated components for specific caching/store technologies (Caffeine, Redis, Infinispan, etc.), each with its own API surface. This works well when users need the full feature set of a specific technology, but it creates friction when the actual need is simple: store and retrieve key-value pairs.

The camel-state-store component follows the same "choose the problem, not the technology" pattern that Camel already uses successfully in other areas:

  • camel-sql / camel-jdbc — generic SQL over any JDBC database, without locking into a vendor
  • camel-jms — generic messaging over any JMS provider (with camel-activemq, camel-amqp as pre-configured variants)
  • camel-jcache — generic caching via JSR-107 over any compliant implementation

Similarly, camel-state-store provides a unified key-value API where:

  • Users choose the capability first ("I need a key-value store") rather than a specific technology
  • The backend is swappable without changing route logic — develop with in-memory, deploy with Redis or Infinispan
  • The API surface is intentionally minimal (put, get, delete, contains, keys, clear) — unlike the full-featured technology-specific components

Difference from existing cache components

camel-state-store camel-caffeine-cache / camel-infinispan / etc.
Focus Simple key-value store abstraction Full feature set of a specific technology
Backend Pluggable via StateStoreBackend interface Fixed to one technology
API Minimal: put, get, delete, contains, keys, clear Rich: queries, events, statistics, pub/sub, etc.
Use case Portability, simplicity, migration from MuleSoft Object Store Deep integration with a specific product

Summary

  • New camel-state-store component providing a simple, unified key-value store API with pluggable backends
  • Supports operations: put, putIfAbsent, get, delete, contains, keys, size, clear
  • Per-message TTL override via CamelStateStoreTtl header
  • Multi-module structure with pluggable backends:
    • camel-state-store: core + in-memory backend (ConcurrentHashMap, lazy TTL)
    • camel-state-store-caffeine: Caffeine cache with per-entry variable expiry
    • camel-state-store-redis: Redisson RMapCache with native TTL
    • camel-state-store-infinispan: Hot Rod client with lifespan TTL
  • Custom backends via StateStoreBackend interface and bean references

Test plan

  • Unit tests for all operations with in-memory backend (StateStoreTest - 8 tests)
  • TTL expiry tests (StateStoreTtlTest - 2 tests)
  • Caffeine backend unit tests (CaffeineStateStoreBackendTest - 12 tests)
  • Redis integration tests (RedisStateStoreBackendIT - 8 tests)
  • Infinispan integration tests (InfinispanStateStoreBackendIT - 7 tests)
  • Code formatted with formatter:format and impsort:sort
  • Catalog metadata, Endpoint DSL, and Component DSL regenerated

@github-actions
Copy link
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using build-all, build-dependents, skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@orpiske
Copy link
Contributor

orpiske commented Mar 21, 2026

Hm, this issue doesn't seem to exist on ASF Jira.

@gnodet gnodet marked this pull request as ready for review March 21, 2026 23:24
@claudio4j
Copy link
Contributor

The title refers to the wrong issue, CAMEL-23228 is "Add DataWeave to DataSonnet transpiler in camel-jbang".

@gnodet gnodet changed the title CAMEL-23228: Add camel-state-store component with pluggable key-value store CAMEL-23239: Add camel-state-store component with pluggable key-value store Mar 24, 2026
@apupier
Copy link
Contributor

apupier commented Mar 24, 2026

the commit message also needs to be updated with the correct jira issue number

Copy link
Contributor

@apupier apupier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate on the difference between these components and the existing Camel caffeine cache component? https://camel.apache.org/components/4.18.x/caffeine-cache-component.html

… store

- New state-store component providing a unified key-value store API
- Operations: put, putIfAbsent, get, delete, contains, keys, size, clear
- Per-message TTL override via CamelStateStoreTtl header
- Multi-module structure with pluggable backends:
  - camel-state-store: core + in-memory backend (ConcurrentHashMap, lazy TTL)
  - camel-state-store-caffeine: Caffeine cache with per-entry variable expiry
  - camel-state-store-redis: Redisson RMapCache with native TTL
  - camel-state-store-infinispan: Hot Rod client with lifespan TTL
- Registered in MojoHelper, parent BOM, allcomponents, catalog
- 22 unit tests (core + caffeine) and 15 integration tests (Redis + Infinispan)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gnodet gnodet force-pushed the camel-state-store branch from 1e1a769 to e3ba53c Compare March 25, 2026 16:29
@gnodet
Copy link
Contributor Author

gnodet commented Mar 25, 2026

Thanks for the reviews! I've pushed an update:

  • Fixed the JIRA reference: commit message now correctly references CAMEL-23239 (was CAMEL-23228, a copy-paste error)
  • Updated the PR description with a detailed motivation section explaining the rationale and how this relates to existing components

@apupier — regarding the difference with camel-caffeine-cache: the state-store component is not a replacement for the technology-specific components. It follows the same pattern as camel-sql (generic SQL over any JDBC database) or camel-jms (generic messaging over any JMS provider). The key benefit is ease of use and backend portability — users choose the capability ("I need a key-value store") and can swap backends without changing route logic. The technology-specific components remain the right choice when users need the full feature set of a particular product (queries, pub/sub, statistics, etc.). See the updated PR description for a detailed comparison table.

Claude Code on behalf of Guillaume Nodet

@gnodet
Copy link
Contributor Author

gnodet commented Mar 25, 2026

Design note: StateStoreBackend interface location

We considered moving the StateStoreBackend interface to camel-api so it could serve as a shared abstraction alongside the existing SPIs (IdempotentRepository, AggregationRepository, StateRepository). After analysis, we decided against it:

  • The existing camel-api SPIs are there because core EIPs consume them (idempotent consumer, aggregator, etc.). StateStoreBackend is only consumed by the camel-state-store component itself — nothing in core needs it.
  • The existing SPIs have different semantics that don't map cleanly to a generic key-value store: IdempotentRepository has two-phase confirm/rollback, AggregationRepository stores Exchange objects requiring serialization, and technology-specific implementations often leverage native features (JDBC transactions, Cassandra TTL, Kafka offsets).
  • Moving it to camel-api would add API surface with stricter stability guarantees for no practical benefit today.

If a future core EIP needs a generic key-value store abstraction, it can be promoted then.

Claude Code on behalf of Guillaume Nodet

[source,java]
----
@BindToRegistry("infinispanBackend")
public InfinispanStateStoreBackend infinispan() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gnodet is this kind of configuration doable only via java beans? would it be possible to provide this configuration via properties?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants