You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by fm...@apache.org on 2026/09/09 11:48:21 UTC

(camel-spring-boot-examples) branch main updated: Fix infinispan example: create the missing default cache on startup

This is an automated email from the ASF dual-hosted git repository.

Croway pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-spring-boot-examples.git


The following commit(s) were added to refs/heads/main by this push:
     new d71abdfe Fix infinispan example: create the missing default cache on startup
d71abdfe is described below

commit d71abdfeddcfa98aaa40062662ee15478ae63c6a
Author: croway <fe...@gmail.com>
AuthorDate: Fri Sep 4 09:08:40 2026 +0200

    Fix infinispan example: create the missing default cache on startup
    
    camel infra run infinispan (as of the current camel-test-infra-infinispan
    config, unchanged since at least 4.22.0) only provisions the "default"
    cache-container, not an actual cache inside it. The route's
    to("infinispan://default") therefore hit CacheNotFoundException /
    NullPointerException, contradicting the README's "runs OOTB" claim. The
    test suite masked this by spinning up its own container with a custom,
    outdated infinispan.xml (schema 13.0) that predeclared the cache, so it
    never exercised the documented workflow.
    
    Fix, following the same pattern camel-infinispan's own IT tests use
    (InfinispanRemoteTestSupport#getOrCreateCache):
    - Add InfinispanConfiguration, a RemoteCacheManager @Bean that calls
      administration().getOrCreateCache("default", ...) on startup and is
      autowired into the infinispan component by type.
    - Move connection settings out of camel.component.infinispan.* (now
      unused, since the component uses the bean directly) into plain
      infinispan.* properties backed by env-var placeholders with sane
      defaults, which also resolves the new CamelSecurityPolicyAutoConfiguration
      plaintext-secret warnings (that check only scans camel.*-prefixed keys).
    - Rewrite ApplicationTest to use camel-test-infra-infinispan's
      InfinispanServiceFactory + @DynamicPropertySource instead of a hand-rolled
      Testcontainers setup, so the test exercises the same server config as
      camel infra run and would have caught this regression.
    - Update the README to explain the cache-creation behavior and the env
      vars for pointing at a different server.
    
    Co-Authored-By: Claude Sonnet 5 <no...@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01Wgr7MBBqUbNGG6xrH6ybWq
---
 infinispan/README.adoc                             |   9 ++
 infinispan/pom.xml                                 |  12 +--
 .../infinispan/InfinispanConfiguration.java        |  59 +++++++++++
 .../src/main/resources/application.properties      |  13 ++-
 .../springboot/infinispan/ApplicationTest.java     | 117 ++++++++-------------
 infinispan/src/test/resources/infinispan.xml       |  73 -------------
 6 files changed, 128 insertions(+), 155 deletions(-)

diff --git a/infinispan/README.adoc b/infinispan/README.adoc
index acd5b34f..dc9aa813 100644
--- a/infinispan/README.adoc
+++ b/infinispan/README.adoc
@@ -5,6 +5,11 @@
 This example demonstrates how you can use Camel-Infinispan Starter component. The example is really simple: put a key/value pair in a remote cache and get the same key.
 This example starts an Infinispan server with Infinispan Docker Image, so it can run OOTB.
 
+`camel infra run infinispan` only provisions the "default" cache-container on the server, it does not
+create a cache inside it. So the application itself creates the "default" cache on startup, via the
+`RemoteCacheManager` bean in `InfinispanConfiguration`, the same approach Camel's own Infinispan test
+suite uses (`RemoteCacheManager#administration().getOrCreateCache(...)`).
+
 === Build and test
 
 You can build this example using:
@@ -23,6 +28,10 @@ You can run this example using:
 
 And you should see output in the console.
 
+By default, the example connects to `localhost:11222` with the `admin`/`password` credentials that
+`camel infra run infinispan` uses. To point at a different server, set the `INFINISPAN_HOST`,
+`INFINISPAN_PORT`, `INFINISPAN_USERNAME` and `INFINISPAN_PASSWORD` environment variables.
+
 === Help and contributions
 
 If you hit any problem using Camel or have some feedback, then please
diff --git a/infinispan/pom.xml b/infinispan/pom.xml
index f0fcf7f8..0bb3d791 100644
--- a/infinispan/pom.xml
+++ b/infinispan/pom.xml
@@ -66,12 +66,6 @@
             <artifactId>camel-infinispan-starter</artifactId>
         </dependency>
 
-        <dependency>
-            <groupId>org.testcontainers</groupId>
-            <artifactId>testcontainers</artifactId>
-            <version>${testcontainers-version}</version>
-        </dependency>
-
         <!-- Test -->
         <dependency>
             <groupId>org.springframework.boot</groupId>
@@ -83,6 +77,12 @@
             <artifactId>camel-test-spring-junit6</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-test-infra-infinispan</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/infinispan/src/main/java/org/apache/camel/example/springboot/infinispan/InfinispanConfiguration.java b/infinispan/src/main/java/org/apache/camel/example/springboot/infinispan/InfinispanConfiguration.java
new file mode 100644
index 00000000..4cfe275c
--- /dev/null
+++ b/infinispan/src/main/java/org/apache/camel/example/springboot/infinispan/InfinispanConfiguration.java
@@ -0,0 +1,59 @@
+/*
+ * 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.example.springboot.infinispan;
+
+import org.infinispan.client.hotrod.RemoteCacheManager;
+import org.infinispan.configuration.cache.CacheMode;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class InfinispanConfiguration {
+
+    public static final String CACHE_NAME = "default";
+
+    /**
+     * camel infra run infinispan only provisions the "default" cache-container, not a cache inside it, so the
+     * cache must be created on first connect. Camel autowires this bean into infinispan:// endpoints by type.
+     */
+    @Bean
+    public RemoteCacheManager cacheContainer(
+            @Value("${infinispan.host}") String host,
+            @Value("${infinispan.port}") int port,
+            @Value("${infinispan.username}") String username,
+            @Value("${infinispan.password}") String password) {
+
+        org.infinispan.client.hotrod.configuration.ConfigurationBuilder clientBuilder
+                = new org.infinispan.client.hotrod.configuration.ConfigurationBuilder();
+        clientBuilder.addServer().host(host).port(port);
+        clientBuilder.security().authentication()
+                .username(username)
+                .password(password)
+                .serverName("infinispan")
+                .saslMechanism("SCRAM-SHA-512")
+                .realm("default");
+
+        RemoteCacheManager cacheManager = new RemoteCacheManager(clientBuilder.build());
+        cacheManager.administration().getOrCreateCache(
+                CACHE_NAME,
+                new org.infinispan.configuration.cache.ConfigurationBuilder()
+                        .clustering().cacheMode(CacheMode.DIST_SYNC).build());
+        return cacheManager;
+    }
+}
diff --git a/infinispan/src/main/resources/application.properties b/infinispan/src/main/resources/application.properties
index 1978656c..cc110d1b 100644
--- a/infinispan/src/main/resources/application.properties
+++ b/infinispan/src/main/resources/application.properties
@@ -17,9 +17,12 @@
 
 camel.main.name=Infinispan
 camel.main.run-controller=true
-camel.component.infinispan.hosts=localhost:11222
-camel.component.infinispan.username=admin
-camel.component.infinispan.password=password
-camel.component.infinispan.security-server-name=infinispan
-camel.component.infinispan.secure=true
+
+# Connection details for the RemoteCacheManager bean built in InfinispanConfiguration.
+# Not under the camel.* prefix: the manager is supplied to the infinispan component as a
+# bean (autowired by type), so these are plain Spring properties, not Camel component config.
+infinispan.host=${INFINISPAN_HOST:localhost}
+infinispan.port=${INFINISPAN_PORT:11222}
+infinispan.username=${INFINISPAN_USERNAME:admin}
+infinispan.password=${INFINISPAN_PASSWORD:password}
 
diff --git a/infinispan/src/test/java/org/apache/camel/example/springboot/infinispan/ApplicationTest.java b/infinispan/src/test/java/org/apache/camel/example/springboot/infinispan/ApplicationTest.java
index 01a6a753..76f89923 100644
--- a/infinispan/src/test/java/org/apache/camel/example/springboot/infinispan/ApplicationTest.java
+++ b/infinispan/src/test/java/org/apache/camel/example/springboot/infinispan/ApplicationTest.java
@@ -1,88 +1,63 @@
+/*
+ * 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.example.springboot.infinispan;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.ProducerTemplate;
 import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.infra.infinispan.services.InfinispanService;
+import org.apache.camel.test.infra.infinispan.services.InfinispanServiceFactory;
 import org.apache.camel.test.spring.junit6.CamelSpringBootTest;
 
 import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.junit.jupiter.api.extension.RegisterExtension;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
-import org.testcontainers.containers.BindMode;
-import org.testcontainers.containers.GenericContainer;
-import org.testcontainers.containers.output.OutputFrame;
-import org.testcontainers.containers.output.Slf4jLogConsumer;
-import org.testcontainers.containers.wait.strategy.Wait;
-
-import com.github.dockerjava.api.command.CreateContainerCmd;
-import com.github.dockerjava.api.model.ExposedPort;
-import com.github.dockerjava.api.model.PortBinding;
-import com.github.dockerjava.api.model.Ports;
-
-import java.util.function.Consumer;
+import org.springframework.test.context.DynamicPropertyRegistry;
+import org.springframework.test.context.DynamicPropertySource;
 
 @CamelSpringBootTest
 @SpringBootTest(classes = Application.class)
 final class ApplicationTest {
 
-	private static final Logger LOG = LoggerFactory.getLogger(ApplicationTest.class);
-
-	private static final String CONTAINER_IMAGE = "quay.io/infinispan/server:15.2.1.Final";
-
-	private static GenericContainer<?> container;
-
-	@Autowired
-	private ProducerTemplate producerTemplate;
-
-	@Autowired
-	private CamelContext camelContext;
-
-	private static String host = "localhost";
-
-	private static Integer port = 11222;
-
-	private static String name = "infinispan";
-
-	private static String username = "admin";
-
-	private static String password = "password";
-
-	private ApplicationTest() {
-	}
-
-	@BeforeAll
-	public static void initContainer() {
-		LOG.info("start infinispan docker container");
-		final Consumer<CreateContainerCmd> cmd = e -> {
-			e.getHostConfig().withPortBindings(new PortBinding(Ports.Binding.bindPort(port),
-					new ExposedPort(port)));
-
-		};
-		final Logger containerLog = LoggerFactory.getLogger("container." + name);
-		final Consumer<OutputFrame> logConsumer = new Slf4jLogConsumer(containerLog);
-
-		container = new GenericContainer<>(CONTAINER_IMAGE).withNetworkAliases(name)
-				.withEnv("USER", username).withEnv("PASS", password)
-				.withLogConsumer(logConsumer)
-				.withClasspathResourceMapping("infinispan.xml", "/user-config/infinispan.xml",
-						BindMode.READ_ONLY)
-				.withCommand("-c", "/user-config/infinispan.xml").withExposedPorts(port)
-				.withCreateContainerCmdModifier(cmd).waitingFor(Wait.forListeningPort())
-				.waitingFor(Wait.forLogMessage(".*Infinispan.*Server.*started.*", 1));
-		container.start();
-	}
-
-	@Test
-	public void shouldPopulateCache() throws Exception {
-		final MockEndpoint mock = camelContext.getEndpoint("mock:result", MockEndpoint.class);
-		mock.expectedMessageCount(1);
-		producerTemplate.sendBody("direct:test", null);
-		mock.assertIsSatisfied();
-		Assertions.assertEquals("test", mock.getExchanges().get(0).getIn().getBody(String.class));
-	}
+    @RegisterExtension
+    static InfinispanService service = InfinispanServiceFactory.createSingletonInfinispanService();
+
+    @DynamicPropertySource
+    static void infinispanProperties(DynamicPropertyRegistry registry) {
+        registry.add("infinispan.host", service::host);
+        registry.add("infinispan.port", service::port);
+        registry.add("infinispan.username", service::username);
+        registry.add("infinispan.password", service::password);
+    }
+
+    @Autowired
+    private ProducerTemplate producerTemplate;
+
+    @Autowired
+    private CamelContext camelContext;
+
+    @Test
+    void shouldPopulateCache() throws Exception {
+        final MockEndpoint mock = camelContext.getEndpoint("mock:result", MockEndpoint.class);
+        mock.expectedMessageCount(1);
+        producerTemplate.sendBody("direct:test", null);
+        mock.assertIsSatisfied();
+        Assertions.assertEquals("test", mock.getExchanges().get(0).getIn().getBody(String.class));
+    }
 }
diff --git a/infinispan/src/test/resources/infinispan.xml b/infinispan/src/test/resources/infinispan.xml
deleted file mode 100644
index ee952217..00000000
--- a/infinispan/src/test/resources/infinispan.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-    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.
-
--->
-<infinispan xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-		xsi:schemaLocation="urn:infinispan:config:13.0 https://infinispan.org/schemas/infinispan-config-13.0.xsd
-                            urn:infinispan:server:13.0 https://infinispan.org/schemas/infinispan-server-13.0.xsd"
-		xmlns="urn:infinispan:config:13.0"
-		xmlns:server="urn:infinispan:server:13.0">
-
-	<cache-container name="default" statistics="true">
-		<metrics accurate-size="true"/>
-		<transport cluster="${infinispan.cluster.name:cluster}" stack="${infinispan.cluster.stack:tcp}" node-name="${infinispan.node.name:}"/>
-		<security>
-			<authorization/>
-		</security>
-		<distributed-cache name="default"/>
-	</cache-container>
-
-	<server xmlns="urn:infinispan:server:13.0">
-		<interfaces>
-			<interface name="public">
-				<inet-address value="${infinispan.bind.address:127.0.0.1}"/>
-			</interface>
-		</interfaces>
-
-		<socket-bindings default-interface="public" port-offset="${infinispan.socket.binding.port-offset:0}">
-			<socket-binding name="default" port="${infinispan.bind.port:11222}"/>
-			<socket-binding name="memcached" port="11221"/>
-		</socket-bindings>
-
-		<security>
-			<credential-stores>
-				<credential-store name="credentials" path="credentials.pfx">
-					<clear-text-credential clear-text="secret"/>
-				</credential-store>
-			</credential-stores>
-			<security-realms>
-				<security-realm name="default">
-					<!-- Uncomment to enable TLS on the realm -->
-					<!-- server-identities>
-					   <ssl>
-						  <keystore path="application.keystore"
-									password="password" alias="server"
-									generate-self-signed-certificate-host="localhost"/>
-					   </ssl>
-					</server-identities-->
-					<properties-realm groups-attribute="Roles">
-						<user-properties path="users.properties"/>
-						<group-properties path="groups.properties"/>
-					</properties-realm>
-				</security-realm>
-			</security-realms>
-		</security>
-
-		<endpoints socket-binding="default" security-realm="default"/>
-	</server>
-</infinispan>