From 9334dba9031ea09ef466cde1ae1d13ca5f544131 Mon Sep 17 00:00:00 2001 From: croway Date: Fri, 4 Sep 2026 09:08:40 +0200 Subject: [PATCH] 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 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 +- .../infinispan/ApplicationTest.java | 117 +++++++----------- infinispan/src/test/resources/infinispan.xml | 73 ----------- 6 files changed, 128 insertions(+), 155 deletions(-) create mode 100644 infinispan/src/main/java/org/apache/camel/example/springboot/infinispan/InfinispanConfiguration.java delete mode 100644 infinispan/src/test/resources/infinispan.xml diff --git a/infinispan/README.adoc b/infinispan/README.adoc index acd5b34ff..dc9aa813f 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 f0fcf7f85..0bb3d7914 100644 --- a/infinispan/pom.xml +++ b/infinispan/pom.xml @@ -66,12 +66,6 @@ camel-infinispan-starter - - org.testcontainers - testcontainers - ${testcontainers-version} - - org.springframework.boot @@ -83,6 +77,12 @@ camel-test-spring-junit6 test + + org.apache.camel + camel-test-infra-infinispan + ${project.version} + test + 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 000000000..4cfe275c2 --- /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 1978656c1..cc110d1ba 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 01a6a7531..76f899234 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 cmd = e -> { - e.getHostConfig().withPortBindings(new PortBinding(Ports.Binding.bindPort(port), - new ExposedPort(port))); - - }; - final Logger containerLog = LoggerFactory.getLogger("container." + name); - final Consumer 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 ee9522176..000000000 --- a/infinispan/src/test/resources/infinispan.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -