keys() {
+ return store.entrySet().stream()
+ .filter(e -> !e.getValue().isExpired())
+ .map(java.util.Map.Entry::getKey)
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public void clear() {
+ store.clear();
+ }
+
+ @Override
+ public void start() {
+ // no-op
+ }
+
+ @Override
+ public void stop() {
+ store.clear();
+ }
+
+ private record Entry(Object value, long expiresAt) {
+
+ boolean isExpired() {
+ return expiresAt > 0 && System.currentTimeMillis() > expiresAt;
+ }
+ }
+}
diff --git a/components/camel-state-store/camel-state-store/src/main/java/org/apache/camel/component/statestore/StateStoreBackend.java b/components/camel-state-store/camel-state-store/src/main/java/org/apache/camel/component/statestore/StateStoreBackend.java
new file mode 100644
index 0000000000000..a8ee12d852e9b
--- /dev/null
+++ b/components/camel-state-store/camel-state-store/src/main/java/org/apache/camel/component/statestore/StateStoreBackend.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.statestore;
+
+import java.util.Set;
+
+/**
+ * Interface for pluggable state store backends.
+ */
+public interface StateStoreBackend {
+
+ /**
+ * Store a value with the given key and optional TTL.
+ *
+ * @param key the key
+ * @param value the value to store
+ * @param ttlMillis time-to-live in milliseconds (0 = no expiry)
+ * @return the previous value associated with the key, or null
+ */
+ Object put(String key, Object value, long ttlMillis);
+
+ /**
+ * Store a value only if the key does not already exist.
+ *
+ * Note: The default implementation is not atomic. Implementations that require thread safety should override
+ * this method with an atomic version (e.g., using the backing store's native putIfAbsent).
+ *
+ * @param key the key
+ * @param value the value to store
+ * @param ttlMillis time-to-live in milliseconds (0 = no expiry)
+ * @return the existing value if the key already exists, or null if the value was stored
+ */
+ default Object putIfAbsent(String key, Object value, long ttlMillis) {
+ if (contains(key)) {
+ return get(key);
+ }
+ put(key, value, ttlMillis);
+ return null;
+ }
+
+ /**
+ * Retrieve the value associated with the given key.
+ *
+ * @param key the key
+ * @return the value, or null if not found or expired
+ */
+ Object get(String key);
+
+ /**
+ * Remove the value associated with the given key.
+ *
+ * @param key the key
+ * @return the removed value, or null if not found
+ */
+ Object delete(String key);
+
+ /**
+ * Check if a key exists in the store.
+ *
+ * @param key the key
+ * @return true if the key exists and has not expired
+ */
+ boolean contains(String key);
+
+ /**
+ * Return all keys in the store.
+ *
+ * @return a set of all keys
+ */
+ Set keys();
+
+ /**
+ * Return the number of entries in the store.
+ *
+ * @return the number of entries
+ */
+ default int size() {
+ return keys().size();
+ }
+
+ /**
+ * Remove all entries from the store.
+ */
+ void clear();
+
+ /**
+ * Start the backend (lifecycle).
+ */
+ void start();
+
+ /**
+ * Stop the backend (lifecycle).
+ */
+ void stop();
+}
diff --git a/components/camel-state-store/camel-state-store/src/main/java/org/apache/camel/component/statestore/StateStoreComponent.java b/components/camel-state-store/camel-state-store/src/main/java/org/apache/camel/component/statestore/StateStoreComponent.java
new file mode 100644
index 0000000000000..3caf756e14c8d
--- /dev/null
+++ b/components/camel-state-store/camel-state-store/src/main/java/org/apache/camel/component/statestore/StateStoreComponent.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.statestore;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.camel.Endpoint;
+import org.apache.camel.support.DefaultComponent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The State Store component provides a simple, unified key-value store API with pluggable backends.
+ */
+@org.apache.camel.spi.annotations.Component("state-store")
+public class StateStoreComponent extends DefaultComponent {
+
+ private static final Logger LOG = LoggerFactory.getLogger(StateStoreComponent.class);
+
+ private final ConcurrentHashMap backends = new ConcurrentHashMap<>();
+
+ @Override
+ protected Endpoint createEndpoint(String uri, String remaining, Map parameters) throws Exception {
+ StateStoreEndpoint endpoint = new StateStoreEndpoint(uri, this);
+ endpoint.setStoreName(remaining);
+ setProperties(endpoint, parameters);
+ return endpoint;
+ }
+
+ /**
+ * Returns the backend for the given store name, creating one if needed. Uses first-one-wins semantics: if a backend
+ * already exists for this store name, subsequent calls return the existing one regardless of the explicit backend
+ * passed.
+ */
+ StateStoreBackend getOrCreateBackend(String storeName, StateStoreBackend explicitBackend) {
+ if (explicitBackend != null) {
+ return backends.computeIfAbsent(storeName, k -> {
+ explicitBackend.start();
+ return explicitBackend;
+ });
+ }
+ return backends.computeIfAbsent(storeName, k -> {
+ // Auto-discover a StateStoreBackend from the registry
+ Set found = getCamelContext().getRegistry().findByType(StateStoreBackend.class);
+ StateStoreBackend backend;
+ if (found.size() == 1) {
+ backend = found.iterator().next();
+ } else {
+ if (found.size() > 1) {
+ LOG.warn(
+ "Found {} StateStoreBackend instances in the registry for store '{}'. "
+ + "Cannot auto-select — falling back to InMemoryStateStoreBackend. "
+ + "Use an explicit backend=# reference to choose one.",
+ found.size(), storeName);
+ }
+ backend = new InMemoryStateStoreBackend();
+ }
+ backend.start();
+ return backend;
+ });
+ }
+
+ @Override
+ protected void doStop() throws Exception {
+ for (StateStoreBackend backend : backends.values()) {
+ backend.stop();
+ }
+ backends.clear();
+ super.doStop();
+ }
+}
diff --git a/components/camel-state-store/camel-state-store/src/main/java/org/apache/camel/component/statestore/StateStoreConstants.java b/components/camel-state-store/camel-state-store/src/main/java/org/apache/camel/component/statestore/StateStoreConstants.java
new file mode 100644
index 0000000000000..77906a95cec80
--- /dev/null
+++ b/components/camel-state-store/camel-state-store/src/main/java/org/apache/camel/component/statestore/StateStoreConstants.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.statestore;
+
+import org.apache.camel.spi.Metadata;
+
+/**
+ * Constants for the State Store component.
+ */
+public final class StateStoreConstants {
+
+ @Metadata(description = "The operation to perform", javaType = "org.apache.camel.component.statestore.StateStoreOperations",
+ label = "producer")
+ public static final String OPERATION = "CamelStateStoreOperation";
+
+ @Metadata(description = "The key to use for the operation", javaType = "String", label = "producer")
+ public static final String KEY = "CamelStateStoreKey";
+
+ @Metadata(description = "Per-message TTL override in milliseconds. Takes precedence over the endpoint ttl option.",
+ javaType = "Long", label = "producer")
+ public static final String TTL = "CamelStateStoreTtl";
+
+ private StateStoreConstants() {
+ }
+}
diff --git a/components/camel-state-store/camel-state-store/src/main/java/org/apache/camel/component/statestore/StateStoreEndpoint.java b/components/camel-state-store/camel-state-store/src/main/java/org/apache/camel/component/statestore/StateStoreEndpoint.java
new file mode 100644
index 0000000000000..cacb0a39f5d46
--- /dev/null
+++ b/components/camel-state-store/camel-state-store/src/main/java/org/apache/camel/component/statestore/StateStoreEndpoint.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.statestore;
+
+import org.apache.camel.Category;
+import org.apache.camel.Consumer;
+import org.apache.camel.Processor;
+import org.apache.camel.Producer;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.UriEndpoint;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriPath;
+import org.apache.camel.support.DefaultEndpoint;
+
+/**
+ * Perform key-value operations against a pluggable state store backend.
+ */
+@UriEndpoint(firstVersion = "4.19.0", scheme = "state-store", title = "State Store",
+ syntax = "state-store:storeName", producerOnly = true,
+ category = { Category.CACHE },
+ headersClass = StateStoreConstants.class)
+public class StateStoreEndpoint extends DefaultEndpoint {
+
+ @UriPath(description = "The name of the state store")
+ @Metadata(required = true)
+ private String storeName;
+
+ @UriParam(description = "The default operation to perform", enums = "put,putIfAbsent,get,delete,contains,keys,size,clear")
+ private StateStoreOperations operation;
+
+ @UriParam(description = "The backend to use. If not set, auto-discovers a single StateStoreBackend from the registry, or falls back to an in-memory store.",
+ label = "advanced")
+ private StateStoreBackend backend;
+
+ @UriParam(description = "Time-to-live in milliseconds for entries. 0 means no expiry.", defaultValue = "0")
+ private long ttl;
+
+ public StateStoreEndpoint(String endpointUri, StateStoreComponent component) {
+ super(endpointUri, component);
+ }
+
+ @Override
+ public Producer createProducer() {
+ return new StateStoreProducer(this);
+ }
+
+ @Override
+ public Consumer createConsumer(Processor processor) {
+ throw new UnsupportedOperationException("The state-store component does not support consumers");
+ }
+
+ @Override
+ protected void doStart() throws Exception {
+ super.doStart();
+ StateStoreComponent comp = (StateStoreComponent) getComponent();
+ backend = comp.getOrCreateBackend(storeName, backend);
+ }
+
+ public String getStoreName() {
+ return storeName;
+ }
+
+ public void setStoreName(String storeName) {
+ this.storeName = storeName;
+ }
+
+ public StateStoreOperations getOperation() {
+ return operation;
+ }
+
+ public void setOperation(StateStoreOperations operation) {
+ this.operation = operation;
+ }
+
+ public StateStoreBackend getBackend() {
+ return backend;
+ }
+
+ public void setBackend(StateStoreBackend backend) {
+ this.backend = backend;
+ }
+
+ public long getTtl() {
+ return ttl;
+ }
+
+ public void setTtl(long ttl) {
+ this.ttl = ttl;
+ }
+}
diff --git a/components/camel-state-store/camel-state-store/src/main/java/org/apache/camel/component/statestore/StateStoreOperations.java b/components/camel-state-store/camel-state-store/src/main/java/org/apache/camel/component/statestore/StateStoreOperations.java
new file mode 100644
index 0000000000000..0e275cc3ea003
--- /dev/null
+++ b/components/camel-state-store/camel-state-store/src/main/java/org/apache/camel/component/statestore/StateStoreOperations.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.statestore;
+
+/**
+ * Operations supported by the State Store component.
+ */
+public enum StateStoreOperations {
+
+ put,
+ putIfAbsent,
+ get,
+ delete,
+ contains,
+ keys,
+ size,
+ clear;
+
+}
diff --git a/components/camel-state-store/camel-state-store/src/main/java/org/apache/camel/component/statestore/StateStoreProducer.java b/components/camel-state-store/camel-state-store/src/main/java/org/apache/camel/component/statestore/StateStoreProducer.java
new file mode 100644
index 0000000000000..da0741b354954
--- /dev/null
+++ b/components/camel-state-store/camel-state-store/src/main/java/org/apache/camel/component/statestore/StateStoreProducer.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.statestore;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.support.DefaultProducer;
+
+/**
+ * Producer for the State Store component that performs key-value operations.
+ */
+public class StateStoreProducer extends DefaultProducer {
+
+ private final StateStoreEndpoint endpoint;
+
+ public StateStoreProducer(StateStoreEndpoint endpoint) {
+ super(endpoint);
+ this.endpoint = endpoint;
+ }
+
+ @Override
+ public void process(Exchange exchange) throws Exception {
+ StateStoreOperations op = determineOperation(exchange);
+ if (op == null) {
+ throw new IllegalArgumentException(
+ "No operation specified. Set the operation via URI option or header " + StateStoreConstants.OPERATION);
+ }
+
+ StateStoreBackend backend = endpoint.getBackend();
+ long ttl = determineTtl(exchange);
+ Message message = exchange.getMessage();
+
+ switch (op) {
+ case put -> {
+ String key = requireKey(message);
+ Object value = message.getBody();
+ Object previous = backend.put(key, value, ttl);
+ message.setBody(previous);
+ }
+ case putIfAbsent -> {
+ String key = requireKey(message);
+ Object value = message.getBody();
+ Object existing = backend.putIfAbsent(key, value, ttl);
+ message.setBody(existing);
+ }
+ case get -> {
+ String key = requireKey(message);
+ Object value = backend.get(key);
+ message.setBody(value);
+ }
+ case delete -> {
+ String key = requireKey(message);
+ Object removed = backend.delete(key);
+ message.setBody(removed);
+ }
+ case contains -> {
+ String key = requireKey(message);
+ boolean exists = backend.contains(key);
+ message.setBody(exists);
+ }
+ case keys -> {
+ message.setBody(backend.keys());
+ }
+ case size -> {
+ message.setBody(backend.size());
+ }
+ case clear -> {
+ backend.clear();
+ message.setBody(null);
+ }
+ default -> throw new IllegalArgumentException("Unsupported operation: " + op);
+ }
+ }
+
+ private StateStoreOperations determineOperation(Exchange exchange) {
+ Object headerOp = exchange.getMessage().getHeader(StateStoreConstants.OPERATION);
+ if (headerOp != null) {
+ if (headerOp instanceof StateStoreOperations sso) {
+ return sso;
+ }
+ return StateStoreOperations.valueOf(headerOp.toString());
+ }
+ return endpoint.getOperation();
+ }
+
+ private long determineTtl(Exchange exchange) {
+ Long headerTtl = exchange.getMessage().getHeader(StateStoreConstants.TTL, Long.class);
+ if (headerTtl != null) {
+ return headerTtl;
+ }
+ return endpoint.getTtl();
+ }
+
+ private String requireKey(Message message) {
+ String key = message.getHeader(StateStoreConstants.KEY, String.class);
+ if (key == null) {
+ throw new IllegalArgumentException("Header " + StateStoreConstants.KEY + " is required for this operation");
+ }
+ return key;
+ }
+}
diff --git a/components/camel-state-store/camel-state-store/src/test/java/org/apache/camel/component/statestore/StateStoreAutoDiscoveryTest.java b/components/camel-state-store/camel-state-store/src/test/java/org/apache/camel/component/statestore/StateStoreAutoDiscoveryTest.java
new file mode 100644
index 0000000000000..34641ec9a61dc
--- /dev/null
+++ b/components/camel-state-store/camel-state-store/src/test/java/org/apache/camel/component/statestore/StateStoreAutoDiscoveryTest.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.statestore;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+
+/**
+ * Tests that a single {@link StateStoreBackend} in the registry is auto-discovered when no explicit backend is
+ * specified on the endpoint.
+ */
+class StateStoreAutoDiscoveryTest extends CamelTestSupport {
+
+ private final TrackingInMemoryBackend trackingBackend = new TrackingInMemoryBackend();
+
+ @BindToRegistry("myBackend")
+ public StateStoreBackend getBackend() {
+ return trackingBackend;
+ }
+
+ @Test
+ void testAutoDiscoveryFromRegistry() {
+ // No backend=#myBackend in the URI, should auto-discover from registry
+ template.requestBodyAndHeaders(
+ "direct:put", "hello",
+ Map.of(StateStoreConstants.KEY, "key1"));
+
+ Object result = template.requestBodyAndHeaders(
+ "direct:get", null,
+ Map.of(StateStoreConstants.KEY, "key1"));
+ assertEquals("hello", result);
+
+ // Verify our tracking backend was used, not the default InMemoryStateStoreBackend
+ assertInstanceOf(TrackingInMemoryBackend.class, trackingBackend);
+ assertEquals(Set.of("key1"), trackingBackend.keys());
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ // No backend=# reference — relies on auto-discovery
+ from("direct:put").to("state-store:myStore?operation=put");
+ from("direct:get").to("state-store:myStore?operation=get");
+ }
+ };
+ }
+
+ /**
+ * A simple wrapper around InMemoryStateStoreBackend to verify it was used instead of a fresh default instance.
+ */
+ static class TrackingInMemoryBackend extends InMemoryStateStoreBackend {
+ }
+}
diff --git a/components/camel-state-store/camel-state-store/src/test/java/org/apache/camel/component/statestore/StateStoreMultiBackendFallbackTest.java b/components/camel-state-store/camel-state-store/src/test/java/org/apache/camel/component/statestore/StateStoreMultiBackendFallbackTest.java
new file mode 100644
index 0000000000000..547d6792a8777
--- /dev/null
+++ b/components/camel-state-store/camel-state-store/src/test/java/org/apache/camel/component/statestore/StateStoreMultiBackendFallbackTest.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.statestore;
+
+import java.util.Map;
+
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * Tests that when multiple {@link StateStoreBackend} instances are in the registry and no explicit backend is
+ * specified, the component falls back to {@link InMemoryStateStoreBackend} and logs a warning.
+ */
+class StateStoreMultiBackendFallbackTest extends CamelTestSupport {
+
+ @BindToRegistry("backend1")
+ private final InMemoryStateStoreBackend backendOne = new InMemoryStateStoreBackend();
+
+ @BindToRegistry("backend2")
+ private final InMemoryStateStoreBackend backendTwo = new InMemoryStateStoreBackend();
+
+ @Test
+ void testFallsBackToInMemoryWhenMultipleBackends() {
+ // With two backends in the registry and no explicit reference,
+ // auto-discovery should fall back to a fresh InMemoryStateStoreBackend
+ Object previous = template.requestBodyAndHeaders(
+ "direct:put", "hello",
+ Map.of(StateStoreConstants.KEY, "key1"));
+ assertNull(previous);
+
+ Object result = template.requestBodyAndHeaders(
+ "direct:get", null,
+ Map.of(StateStoreConstants.KEY, "key1"));
+ assertEquals("hello", result);
+
+ // Verify neither registered backend was used
+ assertEquals(0, backendOne.size());
+ assertEquals(0, backendTwo.size());
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:put").to("state-store:myStore?operation=put");
+ from("direct:get").to("state-store:myStore?operation=get");
+ }
+ };
+ }
+}
diff --git a/components/camel-state-store/camel-state-store/src/test/java/org/apache/camel/component/statestore/StateStoreTest.java b/components/camel-state-store/camel-state-store/src/test/java/org/apache/camel/component/statestore/StateStoreTest.java
new file mode 100644
index 0000000000000..e612edf53fff2
--- /dev/null
+++ b/components/camel-state-store/camel-state-store/src/test/java/org/apache/camel/component/statestore/StateStoreTest.java
@@ -0,0 +1,236 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.statestore;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class StateStoreTest extends CamelTestSupport {
+
+ @Test
+ void testPutAndGet() {
+ Object previous = template.requestBodyAndHeaders(
+ "direct:put", "hello",
+ Map.of(StateStoreConstants.KEY, "key1"));
+ assertNull(previous);
+
+ Object result = template.requestBodyAndHeaders(
+ "direct:get", null,
+ Map.of(StateStoreConstants.KEY, "key1"));
+ assertEquals("hello", result);
+ }
+
+ @Test
+ void testPutReturnsOldValue() {
+ template.requestBodyAndHeaders(
+ "direct:put", "first",
+ Map.of(StateStoreConstants.KEY, "key1"));
+
+ Object previous = template.requestBodyAndHeaders(
+ "direct:put", "second",
+ Map.of(StateStoreConstants.KEY, "key1"));
+ assertEquals("first", previous);
+ }
+
+ @Test
+ void testGetNonExistent() {
+ Object result = template.requestBodyAndHeaders(
+ "direct:get", null,
+ Map.of(StateStoreConstants.KEY, "missing"));
+ assertNull(result);
+ }
+
+ @Test
+ void testDelete() {
+ template.requestBodyAndHeaders(
+ "direct:put", "value",
+ Map.of(StateStoreConstants.KEY, "key1"));
+
+ Object removed = template.requestBodyAndHeaders(
+ "direct:delete", null,
+ Map.of(StateStoreConstants.KEY, "key1"));
+ assertEquals("value", removed);
+
+ Object result = template.requestBodyAndHeaders(
+ "direct:get", null,
+ Map.of(StateStoreConstants.KEY, "key1"));
+ assertNull(result);
+ }
+
+ @Test
+ void testContains() {
+ template.requestBodyAndHeaders(
+ "direct:put", "value",
+ Map.of(StateStoreConstants.KEY, "key1"));
+
+ Object exists = template.requestBodyAndHeaders(
+ "direct:contains", null,
+ Map.of(StateStoreConstants.KEY, "key1"));
+ assertEquals(true, exists);
+
+ Object notExists = template.requestBodyAndHeaders(
+ "direct:contains", null,
+ Map.of(StateStoreConstants.KEY, "missing"));
+ assertEquals(false, notExists);
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void testKeys() {
+ template.requestBodyAndHeaders(
+ "direct:put", "v1",
+ Map.of(StateStoreConstants.KEY, "a"));
+ template.requestBodyAndHeaders(
+ "direct:put", "v2",
+ Map.of(StateStoreConstants.KEY, "b"));
+
+ Set keys = (Set) template.requestBody("direct:keys", (Object) null);
+ assertEquals(Set.of("a", "b"), keys);
+ }
+
+ @Test
+ void testSize() {
+ template.requestBodyAndHeaders(
+ "direct:put", "v1",
+ Map.of(StateStoreConstants.KEY, "a"));
+ template.requestBodyAndHeaders(
+ "direct:put", "v2",
+ Map.of(StateStoreConstants.KEY, "b"));
+
+ Object size = template.requestBody("direct:size", (Object) null);
+ assertEquals(2, size);
+ }
+
+ @Test
+ void testClear() {
+ template.requestBodyAndHeaders(
+ "direct:put", "v1",
+ Map.of(StateStoreConstants.KEY, "a"));
+
+ template.requestBody("direct:clear", (Object) null);
+
+ Object result = template.requestBodyAndHeaders(
+ "direct:get", null,
+ Map.of(StateStoreConstants.KEY, "a"));
+ assertNull(result);
+ }
+
+ @Test
+ void testPutIfAbsent() {
+ // first put should succeed
+ Object result = template.requestBodyAndHeaders(
+ "direct:putIfAbsent", "first",
+ Map.of(StateStoreConstants.KEY, "key1"));
+ assertNull(result);
+
+ // second put should return existing value
+ result = template.requestBodyAndHeaders(
+ "direct:putIfAbsent", "second",
+ Map.of(StateStoreConstants.KEY, "key1"));
+ assertEquals("first", result);
+
+ // verify original value is still there
+ Object value = template.requestBodyAndHeaders(
+ "direct:get", null,
+ Map.of(StateStoreConstants.KEY, "key1"));
+ assertEquals("first", value);
+ }
+
+ @Test
+ void testOperationViaHeader() {
+ template.requestBodyAndHeaders(
+ "direct:dynamic", "hello",
+ Map.of(
+ StateStoreConstants.OPERATION, StateStoreOperations.put,
+ StateStoreConstants.KEY, "key1"));
+
+ Object result = template.requestBodyAndHeaders(
+ "direct:dynamic", null,
+ Map.of(
+ StateStoreConstants.OPERATION, "get",
+ StateStoreConstants.KEY, "key1"));
+ assertEquals("hello", result);
+ }
+
+ @Test
+ void testPerMessageTtlHeader() throws Exception {
+ // put with per-message TTL of 200ms (no TTL on endpoint)
+ template.requestBodyAndHeaders(
+ "direct:put", "expiring",
+ Map.of(
+ StateStoreConstants.KEY, "ttlKey",
+ StateStoreConstants.TTL, 200L));
+
+ // should be there immediately
+ Object result = template.requestBodyAndHeaders(
+ "direct:get", null,
+ Map.of(StateStoreConstants.KEY, "ttlKey"));
+ assertEquals("expiring", result);
+
+ Thread.sleep(1000);
+
+ // should be expired
+ result = template.requestBodyAndHeaders(
+ "direct:get", null,
+ Map.of(StateStoreConstants.KEY, "ttlKey"));
+ assertNull(result);
+ }
+
+ @Test
+ void testMissingKeyHeaderThrows() {
+ CamelExecutionException ex = assertThrows(CamelExecutionException.class, () -> {
+ template.requestBody("direct:get", (Object) null);
+ });
+ assertInstanceOf(IllegalArgumentException.class, ex.getCause());
+ }
+
+ @Test
+ void testMissingOperationThrows() {
+ CamelExecutionException ex = assertThrows(CamelExecutionException.class, () -> {
+ template.requestBodyAndHeaders("direct:dynamic", "value", Map.of());
+ });
+ assertInstanceOf(IllegalArgumentException.class, ex.getCause());
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:put").to("state-store:myStore?operation=put");
+ from("direct:putIfAbsent").to("state-store:myStore?operation=putIfAbsent");
+ from("direct:get").to("state-store:myStore?operation=get");
+ from("direct:delete").to("state-store:myStore?operation=delete");
+ from("direct:contains").to("state-store:myStore?operation=contains");
+ from("direct:keys").to("state-store:myStore?operation=keys");
+ from("direct:size").to("state-store:myStore?operation=size");
+ from("direct:clear").to("state-store:myStore?operation=clear");
+ from("direct:dynamic").to("state-store:myStore");
+ }
+ };
+ }
+}
diff --git a/components/camel-state-store/camel-state-store/src/test/java/org/apache/camel/component/statestore/StateStoreTtlTest.java b/components/camel-state-store/camel-state-store/src/test/java/org/apache/camel/component/statestore/StateStoreTtlTest.java
new file mode 100644
index 0000000000000..f77979c740ee7
--- /dev/null
+++ b/components/camel-state-store/camel-state-store/src/test/java/org/apache/camel/component/statestore/StateStoreTtlTest.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.statestore;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+class StateStoreTtlTest extends CamelTestSupport {
+
+ @Test
+ void testEntryExpiresAfterTtl() throws Exception {
+ // put a value with 200ms TTL
+ template.requestBodyAndHeaders(
+ "direct:put", "expiring",
+ java.util.Map.of(StateStoreConstants.KEY, "ttlKey"));
+
+ // should be retrievable immediately
+ Object result = template.requestBodyAndHeaders(
+ "direct:get", null,
+ java.util.Map.of(StateStoreConstants.KEY, "ttlKey"));
+ assertEquals("expiring", result);
+
+ // wait for TTL to expire (5x margin over 200ms TTL)
+ Thread.sleep(1000);
+
+ // should be expired now
+ Object expired = template.requestBodyAndHeaders(
+ "direct:get", null,
+ java.util.Map.of(StateStoreConstants.KEY, "ttlKey"));
+ assertNull(expired);
+ }
+
+ @Test
+ void testContainsReturnsFalseAfterTtl() throws Exception {
+ template.requestBodyAndHeaders(
+ "direct:put", "expiring",
+ java.util.Map.of(StateStoreConstants.KEY, "ttlKey"));
+
+ Thread.sleep(1000);
+
+ Object exists = template.requestBodyAndHeaders(
+ "direct:contains", null,
+ java.util.Map.of(StateStoreConstants.KEY, "ttlKey"));
+ assertEquals(false, exists);
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:put").to("state-store:ttlStore?operation=put&ttl=200");
+ from("direct:get").to("state-store:ttlStore?operation=get");
+ from("direct:contains").to("state-store:ttlStore?operation=contains");
+ }
+ };
+ }
+}
diff --git a/components/camel-state-store/pom.xml b/components/camel-state-store/pom.xml
new file mode 100644
index 0000000000000..787b2d1851ccd
--- /dev/null
+++ b/components/camel-state-store/pom.xml
@@ -0,0 +1,42 @@
+
+
+
+ 4.0.0
+
+
+ org.apache.camel
+ components
+ 4.19.0-SNAPSHOT
+
+
+ camel-state-store-parent
+ pom
+
+ Camel :: State Store :: Parent
+ Camel State Store parent
+
+
+ camel-state-store
+ camel-state-store-caffeine
+ camel-state-store-redis
+ camel-state-store-infinispan
+
+
+
diff --git a/components/pom.xml b/components/pom.xml
index a5303ece65e61..efaab85409cb0 100644
--- a/components/pom.xml
+++ b/components/pom.xml
@@ -307,6 +307,7 @@
camel-splunk-hec
camel-sql
camel-ssh
+ camel-state-store
camel-stax
camel-stomp
camel-stream
diff --git a/core/camel-main/src/generated/resources/org/apache/camel/main/components.properties b/core/camel-main/src/generated/resources/org/apache/camel/main/components.properties
index 42086ed03ffa9..976216400d54b 100644
--- a/core/camel-main/src/generated/resources/org/apache/camel/main/components.properties
+++ b/core/camel-main/src/generated/resources/org/apache/camel/main/components.properties
@@ -351,6 +351,7 @@ spring-ws
sql
sql-stored
ssh
+state-store
stax
stitch
stomp
diff --git a/docs/components/modules/ROOT/examples/json/state-store.json b/docs/components/modules/ROOT/examples/json/state-store.json
new file mode 120000
index 0000000000000..41b1898528137
--- /dev/null
+++ b/docs/components/modules/ROOT/examples/json/state-store.json
@@ -0,0 +1 @@
+../../../../../../components/camel-state-store/camel-state-store/src/generated/resources/META-INF/org/apache/camel/component/statestore/state-store.json
\ No newline at end of file
diff --git a/docs/components/modules/ROOT/nav.adoc b/docs/components/modules/ROOT/nav.adoc
index baad9ec54566c..3a6a97db515c4 100644
--- a/docs/components/modules/ROOT/nav.adoc
+++ b/docs/components/modules/ROOT/nav.adoc
@@ -361,6 +361,7 @@
** xref:sql-component.adoc[SQL]
** xref:sql-stored-component.adoc[SQL Stored Procedure]
** xref:ssh-component.adoc[SSH]
+** xref:state-store-component.adoc[State Store]
** xref:stax-component.adoc[StAX]
** xref:stitch-component.adoc[Stitch]
** xref:stomp-component.adoc[Stomp]
diff --git a/docs/components/modules/ROOT/pages/state-store-component.adoc b/docs/components/modules/ROOT/pages/state-store-component.adoc
new file mode 120000
index 0000000000000..2ccd74acd0503
--- /dev/null
+++ b/docs/components/modules/ROOT/pages/state-store-component.adoc
@@ -0,0 +1 @@
+../../../../../components/camel-state-store/camel-state-store/src/main/docs/state-store-component.adoc
\ No newline at end of file
diff --git a/docs/components/modules/others/examples/json/state-store-caffeine.json b/docs/components/modules/others/examples/json/state-store-caffeine.json
new file mode 120000
index 0000000000000..15277f9fe6688
--- /dev/null
+++ b/docs/components/modules/others/examples/json/state-store-caffeine.json
@@ -0,0 +1 @@
+../../../../../../components/camel-state-store/camel-state-store-caffeine/src/generated/resources/state-store-caffeine.json
\ No newline at end of file
diff --git a/docs/components/modules/others/examples/json/state-store-infinispan.json b/docs/components/modules/others/examples/json/state-store-infinispan.json
new file mode 120000
index 0000000000000..9e22e40328f2d
--- /dev/null
+++ b/docs/components/modules/others/examples/json/state-store-infinispan.json
@@ -0,0 +1 @@
+../../../../../../components/camel-state-store/camel-state-store-infinispan/src/generated/resources/state-store-infinispan.json
\ No newline at end of file
diff --git a/docs/components/modules/others/examples/json/state-store-redis.json b/docs/components/modules/others/examples/json/state-store-redis.json
new file mode 120000
index 0000000000000..aac04d819206e
--- /dev/null
+++ b/docs/components/modules/others/examples/json/state-store-redis.json
@@ -0,0 +1 @@
+../../../../../../components/camel-state-store/camel-state-store-redis/src/generated/resources/state-store-redis.json
\ No newline at end of file
diff --git a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/ComponentsBuilderFactory.java b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/ComponentsBuilderFactory.java
index 92fe76e755006..b84b74476f210 100644
--- a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/ComponentsBuilderFactory.java
+++ b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/ComponentsBuilderFactory.java
@@ -4736,6 +4736,19 @@ static SqlStoredComponentBuilderFactory.SqlStoredComponentBuilder sqlStored() {
static SshComponentBuilderFactory.SshComponentBuilder ssh() {
return SshComponentBuilderFactory.ssh();
}
+ /**
+ * State Store (camel-state-store)
+ * Perform key-value operations against a pluggable state store backend.
+ *
+ * Category: cache
+ * Since: 4.19
+ * Maven coordinates: org.apache.camel:camel-state-store
+ *
+ * @return the dsl builder
+ */
+ static StateStoreComponentBuilderFactory.StateStoreComponentBuilder stateStore() {
+ return StateStoreComponentBuilderFactory.stateStore();
+ }
/**
* StAX (camel-stax)
* Process XML payloads by a SAX ContentHandler.
diff --git a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/StateStoreComponentBuilderFactory.java b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/StateStoreComponentBuilderFactory.java
new file mode 100644
index 0000000000000..c03dc31862381
--- /dev/null
+++ b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/StateStoreComponentBuilderFactory.java
@@ -0,0 +1,120 @@
+/* Generated by camel build tools - do NOT edit this file! */
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.builder.component.dsl;
+
+import javax.annotation.processing.Generated;
+import org.apache.camel.Component;
+import org.apache.camel.builder.component.AbstractComponentBuilder;
+import org.apache.camel.builder.component.ComponentBuilder;
+import org.apache.camel.component.statestore.StateStoreComponent;
+
+/**
+ * Perform key-value operations against a pluggable state store backend.
+ *
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.ComponentDslMojo")
+public interface StateStoreComponentBuilderFactory {
+
+ /**
+ * State Store (camel-state-store)
+ * Perform key-value operations against a pluggable state store backend.
+ *
+ * Category: cache
+ * Since: 4.19
+ * Maven coordinates: org.apache.camel:camel-state-store
+ *
+ * @return the dsl builder
+ */
+ static StateStoreComponentBuilder stateStore() {
+ return new StateStoreComponentBuilderImpl();
+ }
+
+ /**
+ * Builder for the State Store component.
+ */
+ interface StateStoreComponentBuilder extends ComponentBuilder {
+
+
+ /**
+ * Whether the producer should be started lazy (on the first message).
+ * By starting lazy you can use this to allow CamelContext and routes to
+ * startup in situations where a producer may otherwise fail during
+ * starting and cause the route to fail being started. By deferring this
+ * startup to be lazy then the startup failure can be handled during
+ * routing messages via Camel's routing error handlers. Beware that when
+ * the first message is processed then creating and starting the
+ * producer may take a little time and prolong the total processing time
+ * of the processing.
+ *
+ * The option is a: <code>boolean</code> type.
+ *
+ * Default: false
+ * Group: producer
+ *
+ * @param lazyStartProducer the value to set
+ * @return the dsl builder
+ */
+ default StateStoreComponentBuilder lazyStartProducer(boolean lazyStartProducer) {
+ doSetProperty("lazyStartProducer", lazyStartProducer);
+ return this;
+ }
+
+
+ /**
+ * Whether autowiring is enabled. This is used for automatic autowiring
+ * options (the option must be marked as autowired) by looking up in the
+ * registry to find if there is a single instance of matching type,
+ * which then gets configured on the component. This can be used for
+ * automatic configuring JDBC data sources, JMS connection factories,
+ * AWS Clients, etc.
+ *
+ * The option is a: <code>boolean</code> type.
+ *
+ * Default: true
+ * Group: advanced
+ *
+ * @param autowiredEnabled the value to set
+ * @return the dsl builder
+ */
+ default StateStoreComponentBuilder autowiredEnabled(boolean autowiredEnabled) {
+ doSetProperty("autowiredEnabled", autowiredEnabled);
+ return this;
+ }
+ }
+
+ class StateStoreComponentBuilderImpl
+ extends AbstractComponentBuilder
+ implements StateStoreComponentBuilder {
+ @Override
+ protected StateStoreComponent buildConcreteComponent() {
+ return new StateStoreComponent();
+ }
+ @Override
+ protected boolean setPropertyOnComponent(
+ Component component,
+ String name,
+ Object value) {
+ switch (name) {
+ case "lazyStartProducer": ((StateStoreComponent) component).setLazyStartProducer((boolean) value); return true;
+ case "autowiredEnabled": ((StateStoreComponent) component).setAutowiredEnabled((boolean) value); return true;
+ default: return false;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilderFactory.java
index 12c6a649732bc..d7e53decc9fbb 100644
--- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilderFactory.java
+++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilderFactory.java
@@ -359,6 +359,7 @@ public interface EndpointBuilderFactory
org.apache.camel.builder.endpoint.dsl.Sqs2EndpointBuilderFactory.Sqs2Builders,
org.apache.camel.builder.endpoint.dsl.SshEndpointBuilderFactory.SshBuilders,
org.apache.camel.builder.endpoint.dsl.StAXEndpointBuilderFactory.StAXBuilders,
+ org.apache.camel.builder.endpoint.dsl.StateStoreEndpointBuilderFactory.StateStoreBuilders,
org.apache.camel.builder.endpoint.dsl.StepFunctions2EndpointBuilderFactory.StepFunctions2Builders,
org.apache.camel.builder.endpoint.dsl.StitchEndpointBuilderFactory.StitchBuilders,
org.apache.camel.builder.endpoint.dsl.StompEndpointBuilderFactory.StompBuilders,
diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilders.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilders.java
index 49b40e06b59bb..21718cb9b05df 100644
--- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilders.java
+++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilders.java
@@ -356,6 +356,7 @@ public interface EndpointBuilders
org.apache.camel.builder.endpoint.dsl.Sqs2EndpointBuilderFactory,
org.apache.camel.builder.endpoint.dsl.SshEndpointBuilderFactory,
org.apache.camel.builder.endpoint.dsl.StAXEndpointBuilderFactory,
+ org.apache.camel.builder.endpoint.dsl.StateStoreEndpointBuilderFactory,
org.apache.camel.builder.endpoint.dsl.StepFunctions2EndpointBuilderFactory,
org.apache.camel.builder.endpoint.dsl.StitchEndpointBuilderFactory,
org.apache.camel.builder.endpoint.dsl.StompEndpointBuilderFactory,
diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/StaticEndpointBuilders.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/StaticEndpointBuilders.java
index 4a0f88874ac6d..c33f8e0e8beb6 100644
--- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/StaticEndpointBuilders.java
+++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/StaticEndpointBuilders.java
@@ -15845,6 +15845,46 @@ public static SshEndpointBuilderFactory.SshEndpointBuilder ssh(String path) {
public static SshEndpointBuilderFactory.SshEndpointBuilder ssh(String componentName, String path) {
return SshEndpointBuilderFactory.endpointBuilder(componentName, path);
}
+ /**
+ * State Store (camel-state-store)
+ * Perform key-value operations against a pluggable state store backend.
+ *
+ * Category: cache
+ * Since: 4.19
+ * Maven coordinates: org.apache.camel:camel-state-store
+ *
+ * Syntax: state-store:storeName
+ *
+ * Path parameter: storeName (required)
+ * The name of the state store
+ *
+ * @param path storeName
+ * @return the dsl builder
+ */
+ public static StateStoreEndpointBuilderFactory.StateStoreEndpointBuilder stateStore(String path) {
+ return stateStore("state-store", path);
+ }
+ /**
+ * State Store (camel-state-store)
+ * Perform key-value operations against a pluggable state store backend.
+ *
+ * Category: cache
+ * Since: 4.19
+ * Maven coordinates: org.apache.camel:camel-state-store
+ *
+ * Syntax: state-store:storeName
+ *
+ * Path parameter: storeName (required)
+ * The name of the state store
+ *
+ * @param componentName to use a custom component name for the endpoint
+ * instead of the default name
+ * @param path storeName
+ * @return the dsl builder
+ */
+ public static StateStoreEndpointBuilderFactory.StateStoreEndpointBuilder stateStore(String componentName, String path) {
+ return StateStoreEndpointBuilderFactory.endpointBuilder(componentName, path);
+ }
/**
* StAX (camel-stax)
* Process XML payloads by a SAX ContentHandler.
diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/StateStoreEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/StateStoreEndpointBuilderFactory.java
new file mode 100644
index 0000000000000..e68633ef7bd49
--- /dev/null
+++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/StateStoreEndpointBuilderFactory.java
@@ -0,0 +1,316 @@
+/* Generated by camel build tools - do NOT edit this file! */
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.builder.endpoint.dsl;
+
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.function.*;
+import java.util.stream.*;
+import javax.annotation.processing.Generated;
+import org.apache.camel.builder.EndpointConsumerBuilder;
+import org.apache.camel.builder.EndpointProducerBuilder;
+import org.apache.camel.builder.endpoint.AbstractEndpointBuilder;
+
+/**
+ * Perform key-value operations against a pluggable state store backend.
+ *
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.EndpointDslMojo")
+public interface StateStoreEndpointBuilderFactory {
+
+ /**
+ * Builder for endpoint for the State Store component.
+ */
+ public interface StateStoreEndpointBuilder
+ extends
+ EndpointProducerBuilder {
+ default AdvancedStateStoreEndpointBuilder advanced() {
+ return (AdvancedStateStoreEndpointBuilder) this;
+ }
+
+ /**
+ * The default operation to perform.
+ *
+ * The option is a:
+ * org.apache.camel.component.statestore.StateStoreOperations type.
+ *
+ * Group: producer
+ *
+ * @param operation the value to set
+ * @return the dsl builder
+ */
+ default StateStoreEndpointBuilder operation(org.apache.camel.component.statestore.StateStoreOperations operation) {
+ doSetProperty("operation", operation);
+ return this;
+ }
+ /**
+ * The default operation to perform.
+ *
+ * The option will be converted to a
+ * org.apache.camel.component.statestore.StateStoreOperations type.
+ *
+ * Group: producer
+ *
+ * @param operation the value to set
+ * @return the dsl builder
+ */
+ default StateStoreEndpointBuilder operation(String operation) {
+ doSetProperty("operation", operation);
+ return this;
+ }
+ /**
+ * Time-to-live in milliseconds for entries. 0 means no expiry.
+ *
+ * The option is a: long type.
+ *
+ * Default: 0
+ * Group: producer
+ *
+ * @param ttl the value to set
+ * @return the dsl builder
+ */
+ default StateStoreEndpointBuilder ttl(long ttl) {
+ doSetProperty("ttl", ttl);
+ return this;
+ }
+ /**
+ * Time-to-live in milliseconds for entries. 0 means no expiry.
+ *
+ * The option will be converted to a long type.
+ *
+ * Default: 0
+ * Group: producer
+ *
+ * @param ttl the value to set
+ * @return the dsl builder
+ */
+ default StateStoreEndpointBuilder ttl(String ttl) {
+ doSetProperty("ttl", ttl);
+ return this;
+ }
+ }
+
+ /**
+ * Advanced builder for endpoint for the State Store component.
+ */
+ public interface AdvancedStateStoreEndpointBuilder
+ extends
+ EndpointProducerBuilder {
+ default StateStoreEndpointBuilder basic() {
+ return (StateStoreEndpointBuilder) this;
+ }
+
+ /**
+ * Whether the producer should be started lazy (on the first message).
+ * By starting lazy you can use this to allow CamelContext and routes to
+ * startup in situations where a producer may otherwise fail during
+ * starting and cause the route to fail being started. By deferring this
+ * startup to be lazy then the startup failure can be handled during
+ * routing messages via Camel's routing error handlers. Beware that when
+ * the first message is processed then creating and starting the
+ * producer may take a little time and prolong the total processing time
+ * of the processing.
+ *
+ * The option is a: boolean type.
+ *
+ * Default: false
+ * Group: producer (advanced)
+ *
+ * @param lazyStartProducer the value to set
+ * @return the dsl builder
+ */
+ default AdvancedStateStoreEndpointBuilder lazyStartProducer(boolean lazyStartProducer) {
+ doSetProperty("lazyStartProducer", lazyStartProducer);
+ return this;
+ }
+ /**
+ * Whether the producer should be started lazy (on the first message).
+ * By starting lazy you can use this to allow CamelContext and routes to
+ * startup in situations where a producer may otherwise fail during
+ * starting and cause the route to fail being started. By deferring this
+ * startup to be lazy then the startup failure can be handled during
+ * routing messages via Camel's routing error handlers. Beware that when
+ * the first message is processed then creating and starting the
+ * producer may take a little time and prolong the total processing time
+ * of the processing.
+ *
+ * The option will be converted to a boolean type.
+ *
+ * Default: false
+ * Group: producer (advanced)
+ *
+ * @param lazyStartProducer the value to set
+ * @return the dsl builder
+ */
+ default AdvancedStateStoreEndpointBuilder lazyStartProducer(String lazyStartProducer) {
+ doSetProperty("lazyStartProducer", lazyStartProducer);
+ return this;
+ }
+ /**
+ * The backend to use. If not set, auto-discovers a single
+ * StateStoreBackend from the registry, or falls back to an in-memory
+ * store.
+ *
+ * The option is a:
+ * org.apache.camel.component.statestore.StateStoreBackend
+ * type.
+ *
+ * Group: advanced
+ *
+ * @param backend the value to set
+ * @return the dsl builder
+ */
+ default AdvancedStateStoreEndpointBuilder backend(org.apache.camel.component.statestore.StateStoreBackend backend) {
+ doSetProperty("backend", backend);
+ return this;
+ }
+ /**
+ * The backend to use. If not set, auto-discovers a single
+ * StateStoreBackend from the registry, or falls back to an in-memory
+ * store.
+ *
+ * The option will be converted to a
+ * org.apache.camel.component.statestore.StateStoreBackend
+ * type.
+ *
+ * Group: advanced
+ *
+ * @param backend the value to set
+ * @return the dsl builder
+ */
+ default AdvancedStateStoreEndpointBuilder backend(String backend) {
+ doSetProperty("backend", backend);
+ return this;
+ }
+ }
+
+ public interface StateStoreBuilders {
+ /**
+ * State Store (camel-state-store)
+ * Perform key-value operations against a pluggable state store backend.
+ *
+ * Category: cache
+ * Since: 4.19
+ * Maven coordinates: org.apache.camel:camel-state-store
+ *
+ * @return the dsl builder for the headers' name.
+ */
+ default StateStoreHeaderNameBuilder stateStore() {
+ return StateStoreHeaderNameBuilder.INSTANCE;
+ }
+ /**
+ * State Store (camel-state-store)
+ * Perform key-value operations against a pluggable state store backend.
+ *
+ * Category: cache
+ * Since: 4.19
+ * Maven coordinates: org.apache.camel:camel-state-store
+ *
+ * Syntax: state-store:storeName
+ *
+ * Path parameter: storeName (required)
+ * The name of the state store
+ *
+ * @param path storeName
+ * @return the dsl builder
+ */
+ default StateStoreEndpointBuilder stateStore(String path) {
+ return StateStoreEndpointBuilderFactory.endpointBuilder("state-store", path);
+ }
+ /**
+ * State Store (camel-state-store)
+ * Perform key-value operations against a pluggable state store backend.
+ *
+ * Category: cache
+ * Since: 4.19
+ * Maven coordinates: org.apache.camel:camel-state-store
+ *
+ * Syntax: state-store:storeName
+ *
+ * Path parameter: storeName (required)
+ * The name of the state store
+ *
+ * @param componentName to use a custom component name for the endpoint
+ * instead of the default name
+ * @param path storeName
+ * @return the dsl builder
+ */
+ default StateStoreEndpointBuilder stateStore(String componentName, String path) {
+ return StateStoreEndpointBuilderFactory.endpointBuilder(componentName, path);
+ }
+
+ }
+ /**
+ * The builder of headers' name for the State Store component.
+ */
+ public static class StateStoreHeaderNameBuilder {
+ /**
+ * The internal instance of the builder used to access to all the
+ * methods representing the name of headers.
+ */
+ private static final StateStoreHeaderNameBuilder INSTANCE = new StateStoreHeaderNameBuilder();
+
+ /**
+ * The operation to perform.
+ *
+ * The option is a: {@code
+ * org.apache.camel.component.statestore.StateStoreOperations} type.
+ *
+ * Group: producer
+ *
+ * @return the name of the header {@code StateStoreOperation}.
+ */
+ public String stateStoreOperation() {
+ return "CamelStateStoreOperation";
+ }
+ /**
+ * The key to use for the operation.
+ *
+ * The option is a: {@code String} type.
+ *
+ * Group: producer
+ *
+ * @return the name of the header {@code StateStoreKey}.
+ */
+ public String stateStoreKey() {
+ return "CamelStateStoreKey";
+ }
+ /**
+ * Per-message TTL override in milliseconds. Takes precedence over the
+ * endpoint ttl option.
+ *
+ * The option is a: {@code Long} type.
+ *
+ * Group: producer
+ *
+ * @return the name of the header {@code StateStoreTtl}.
+ */
+ public String stateStoreTtl() {
+ return "CamelStateStoreTtl";
+ }
+ }
+ static StateStoreEndpointBuilder endpointBuilder(String componentName, String path) {
+ class StateStoreEndpointBuilderImpl extends AbstractEndpointBuilder implements StateStoreEndpointBuilder, AdvancedStateStoreEndpointBuilder {
+ public StateStoreEndpointBuilderImpl(String path) {
+ super(componentName, path);
+ }
+ }
+ return new StateStoreEndpointBuilderImpl(path);
+ }
+}
\ No newline at end of file
diff --git a/dsl/camel-kamelet-main/src/generated/resources/camel-component-known-dependencies.properties b/dsl/camel-kamelet-main/src/generated/resources/camel-component-known-dependencies.properties
index e452e6d1ec4fb..608dadfc3b2c7 100644
--- a/dsl/camel-kamelet-main/src/generated/resources/camel-component-known-dependencies.properties
+++ b/dsl/camel-kamelet-main/src/generated/resources/camel-component-known-dependencies.properties
@@ -355,6 +355,7 @@ org.apache.camel.component.springrabbit.SpringRabbitMQComponent=camel:spring-rab
org.apache.camel.component.sql.SqlComponent=camel:sql
org.apache.camel.component.sql.stored.SqlStoredComponent=camel:sql
org.apache.camel.component.ssh.SshComponent=camel:ssh
+org.apache.camel.component.statestore.StateStoreComponent=camel:state-store
org.apache.camel.component.stax.StAXComponent=camel:stax
org.apache.camel.component.stitch.StitchComponent=camel:stitch
org.apache.camel.component.stomp.StompComponent=camel:stomp
diff --git a/parent/pom.xml b/parent/pom.xml
index 625fc58218fed..8d28a44a7ef4f 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -2724,6 +2724,26 @@
camel-ssh
${project.version}