This is an automated email from the ASF dual-hosted git repository. jamesnetherton pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
The following commit(s) were added to refs/heads/main by this push: new 62c14ec Improve Infinispan extension test coverage 62c14ec is described below commit 62c14ec4760a327b606b876f5780dc518692c5d1 Author: James Netherton <jamesnether...@gmail.com> AuthorDate: Thu Mar 24 15:20:44 2022 +0000 Improve Infinispan extension test coverage Fixes #3658 --- .../pages/reference/extensions/infinispan.adoc | 13 + .../runtime/src/main/doc/configuration.adoc | 2 + .../runtime/src/main/doc/limitations.adoc | 3 + integration-tests/infinispan/pom.xml | 33 ++ .../component/infinispan/InfinispanResources.java | 303 ++++++++++++- .../component/infinispan/InfinispanRoutes.java | 275 +++++++++++- .../quarkus/component/infinispan/model/Person.java | 44 ++ .../component/infinispan/model/PersonSchema.java | 24 ++ .../infinispan/InfinispanServerTestResource.java | 4 +- .../component/infinispan/InfinispanTest.java | 479 ++++++++++++++++++++- .../infinispan/src/test/resources/infinispan.xml | 82 ++++ pom.xml | 1 + poms/bom/pom.xml | 9 + 13 files changed, 1241 insertions(+), 31 deletions(-) diff --git a/docs/modules/ROOT/pages/reference/extensions/infinispan.adoc b/docs/modules/ROOT/pages/reference/extensions/infinispan.adoc index fec768d..951cf43 100644 --- a/docs/modules/ROOT/pages/reference/extensions/infinispan.adoc +++ b/docs/modules/ROOT/pages/reference/extensions/infinispan.adoc @@ -38,3 +38,16 @@ Or add the coordinates to your existing project: ---- Check the xref:user-guide/index.adoc[User guide] for more information about writing Camel Quarkus applications. + +== Camel Quarkus limitations + +=== InfinispanRemoteAggregationRepository in native mode + +At present the `InfinispanRemoteAggregationRepository` is not supported in native mode. + + +== Additional Camel Quarkus configuration + +You can either configure the Infinispan client via the relevant Camel Infinispan component & endpoint options, or you +may use the https://quarkus.io/guides/infinispan-client#configuration-reference[Quarkus Infinispan extension configuration properties]. + diff --git a/extensions/infinispan/runtime/src/main/doc/configuration.adoc b/extensions/infinispan/runtime/src/main/doc/configuration.adoc new file mode 100644 index 0000000..000b2d0 --- /dev/null +++ b/extensions/infinispan/runtime/src/main/doc/configuration.adoc @@ -0,0 +1,2 @@ +You can either configure the Infinispan client via the relevant Camel Infinispan component & endpoint options, or you +may use the https://quarkus.io/guides/infinispan-client#configuration-reference[Quarkus Infinispan extension configuration properties]. diff --git a/extensions/infinispan/runtime/src/main/doc/limitations.adoc b/extensions/infinispan/runtime/src/main/doc/limitations.adoc new file mode 100644 index 0000000..007d7b3 --- /dev/null +++ b/extensions/infinispan/runtime/src/main/doc/limitations.adoc @@ -0,0 +1,3 @@ +=== InfinispanRemoteAggregationRepository in native mode + +At present the `InfinispanRemoteAggregationRepository` is not supported in native mode. diff --git a/integration-tests/infinispan/pom.xml b/integration-tests/infinispan/pom.xml index e5df665..eaaa533 100644 --- a/integration-tests/infinispan/pom.xml +++ b/integration-tests/infinispan/pom.xml @@ -44,6 +44,10 @@ <artifactId>camel-quarkus-direct</artifactId> </dependency> <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-mock</artifactId> + </dependency> + <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy</artifactId> </dependency> @@ -147,6 +151,19 @@ </exclusion> </exclusions> </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-mock-deployment</artifactId> + <version>${project.version}</version> + <type>pom</type> + <scope>test</scope> + <exclusions> + <exclusion> + <groupId>*</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> </dependencies> </profile> <profile> @@ -160,6 +177,22 @@ <skipTests>true</skipTests> </properties> </profile> + <profile> + <id>jdk17-build</id> + <activation> + <jdk>[17,)</jdk> + </activation> + <build> + <plugins> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine>--add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.util.concurrent=ALL-UNNAMED</argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> </profiles> </project> diff --git a/integration-tests/infinispan/src/main/java/org/apache/camel/quarkus/component/infinispan/InfinispanResources.java b/integration-tests/infinispan/src/main/java/org/apache/camel/quarkus/component/infinispan/InfinispanResources.java index 9bdd07f..d852091 100644 --- a/integration-tests/infinispan/src/main/java/org/apache/camel/quarkus/component/infinispan/InfinispanResources.java +++ b/integration-tests/infinispan/src/main/java/org/apache/camel/quarkus/component/infinispan/InfinispanResources.java @@ -17,30 +17,48 @@ package org.apache.camel.quarkus.component.infinispan; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; -import javax.annotation.PostConstruct; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import javax.json.Json; import javax.json.JsonObject; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; import javax.ws.rs.GET; +import javax.ws.rs.PATCH; import javax.ws.rs.POST; import javax.ws.rs.Path; +import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; import org.apache.camel.CamelContext; import org.apache.camel.ProducerTemplate; +import org.apache.camel.component.infinispan.InfinispanConstants; +import org.apache.camel.component.infinispan.InfinispanQueryBuilder; import org.apache.camel.component.infinispan.remote.InfinispanRemoteComponent; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.quarkus.component.infinispan.model.Person; +import org.apache.camel.util.CollectionHelper; import org.infinispan.client.hotrod.RemoteCacheManager; +import org.infinispan.client.hotrod.ServerStatistics; -@Path("/test") +import static org.apache.camel.quarkus.component.infinispan.InfinispanRoutes.CORRELATOR_HEADER; + +@Path("/infinispan") @ApplicationScoped public class InfinispanResources { - public static final String CACHE_NAME = "camel"; + public static final String CACHE_NAME_CAMEL = "camel"; + public static final String CACHE_NAME_QUARKUS = "quarkus"; @Inject RemoteCacheManager cacheManager; @@ -51,11 +69,6 @@ public class InfinispanResources { @Inject CamelContext camelContext; - @PostConstruct - public void setUp() { - cacheManager.administration().getOrCreateCache(CACHE_NAME, (String) null); - } - @Path("/inspect") @GET @Produces(MediaType.APPLICATION_JSON) @@ -68,26 +81,294 @@ public class InfinispanResources { .build(); } + @Path("/aggregate") + @POST + @Consumes(MediaType.TEXT_PLAIN) + public void aggregate(@QueryParam("component") String component, String content) { + String uri = component.equals("infinispan") ? "direct:camelAggregation" : "direct:quarkusAggregation"; + Map<String, Object> headers = getCommonHeaders(component); + headers.put(CORRELATOR_HEADER, CORRELATOR_HEADER); + template.sendBodyAndHeaders(uri, content, headers); + } + + @Path("/clear") + @DELETE + public void clear(@QueryParam("component") String component) { + Map<String, Object> headers = getCommonHeaders(component); + template.sendBodyAndHeaders("direct:clear", null, headers); + } + + @Path("/clearAsync") + @DELETE + public void clearAsync(@QueryParam("component") String component) + throws ExecutionException, InterruptedException, TimeoutException { + Map<String, Object> headers = getCommonHeaders(component); + CompletableFuture<?> future = template.requestBodyAndHeaders("direct:clearAsync", null, headers, + CompletableFuture.class); + future.get(5, TimeUnit.SECONDS); + } + + @Path("/compute") + @POST + public void compute(@QueryParam("component") String component) { + Map<String, Object> headers = getCommonHeaders(component); + template.sendBodyAndHeaders("direct:compute", null, headers); + } + + @Path("/computeAsync") + @POST + public void computeAsync(@QueryParam("component") String component) + throws ExecutionException, InterruptedException, TimeoutException { + Map<String, Object> headers = getCommonHeaders(component); + CompletableFuture<?> future = template.requestBodyAndHeaders("direct:computeAsync", null, headers, + CompletableFuture.class); + future.get(5, TimeUnit.SECONDS); + } + + @Path("/containsKey") + @GET + @Produces(MediaType.TEXT_PLAIN) + public Boolean containsKey(@QueryParam("component") String component) { + Map<String, Object> headers = getCommonHeaders(component); + return template.requestBodyAndHeaders("direct:containsKey", null, headers, Boolean.class); + } + + @Path("/containsValue") + @GET + @Produces(MediaType.TEXT_PLAIN) + public Boolean containsValue(@QueryParam("component") String component, @QueryParam("value") String value) { + Map<String, Object> headers = getCommonHeaders(component); + return template.requestBodyAndHeaders("direct:containsValue", value, headers, Boolean.class); + } + @Path("/get") @GET @Produces(MediaType.TEXT_PLAIN) - public String get(@QueryParam("component") String component) { + public String get(@QueryParam("component") String component, @QueryParam("key") String key) { + Map<String, Object> headers = getCommonHeaders(component); + headers.put(InfinispanConstants.KEY, Objects.requireNonNullElse(key, "the-key")); + return template.requestBodyAndHeaders("direct:get", null, headers, String.class); + } + + @Path("/getOrDefault") + @GET + @Produces(MediaType.TEXT_PLAIN) + public String getOrDefault(@QueryParam("component") String component) { Map<String, Object> headers = getCommonHeaders(component); - return template.requestBodyAndHeaders("direct:get", "", headers, String.class); + return template.requestBodyAndHeaders("direct:getOrDefault", null, headers, String.class); } @Path("/put") @POST + @Consumes(MediaType.TEXT_PLAIN) @Produces(MediaType.TEXT_PLAIN) public String put(@QueryParam("component") String component, String content) { Map<String, Object> headers = getCommonHeaders(component); return template.requestBodyAndHeaders("direct:put", content, headers, String.class); } + @Path("/putAsync") + @POST + @Consumes(MediaType.TEXT_PLAIN) + public void putAsync(@QueryParam("component") String component, String content) + throws ExecutionException, InterruptedException, TimeoutException { + Map<String, Object> headers = getCommonHeaders(component); + CompletableFuture<?> future = template.requestBodyAndHeaders("direct:putAsync", content, headers, + CompletableFuture.class); + future.get(5, TimeUnit.SECONDS); + } + + @Path("/putAll") + @POST + public void putAll(@QueryParam("component") String component) { + Map<String, String> body = CollectionHelper.mapOf("key-1", "value-1", "key-2", "value-2"); + Map<String, Object> headers = getCommonHeaders(component); + template.sendBodyAndHeaders("direct:putAll", body, headers); + } + + @Path("/putAllAsync") + @POST + public void putAllAsync(@QueryParam("component") String component) + throws ExecutionException, InterruptedException, TimeoutException { + Map<String, String> body = CollectionHelper.mapOf("key-1", "value-1", "key-2", "value-2"); + Map<String, Object> headers = getCommonHeaders(component); + CompletableFuture<?> future = template.requestBodyAndHeaders("direct:putAllAsync", body, headers, + CompletableFuture.class); + future.get(5, TimeUnit.SECONDS); + } + + @Path("/putIdempotent") + @POST + @Consumes(MediaType.TEXT_PLAIN) + public void putIdempotent( + @QueryParam("component") String component, + @QueryParam("messageId") String messageId, + String content) { + String uri = component.equals("infinispan") ? "direct:camelIdempotent" : "direct:quarkusIdempotent"; + Map<String, Object> headers = getCommonHeaders(component); + headers.put("MessageId", messageId); + template.sendBodyAndHeaders(uri, content, headers); + } + + @Path("/putIfAbsent") + @POST + @Consumes(MediaType.TEXT_PLAIN) + @Produces(MediaType.TEXT_PLAIN) + public String putIfAbsent(@QueryParam("component") String component, String content) { + Map<String, Object> headers = getCommonHeaders(component); + return template.requestBodyAndHeaders("direct:putIfAbsent", content, headers, String.class); + } + + @Path("/putIfAbsentAsync") + @POST + @Consumes(MediaType.TEXT_PLAIN) + public void putIfAbsentAsync(@QueryParam("component") String component, String content) + throws ExecutionException, InterruptedException, TimeoutException { + Map<String, Object> headers = getCommonHeaders(component); + CompletableFuture<?> future = template.requestBodyAndHeaders("direct:putIfAbsentAsync", content, headers, + CompletableFuture.class); + future.get(5, TimeUnit.SECONDS); + } + + @Path("/query") + @GET + @Produces(MediaType.TEXT_PLAIN) + @SuppressWarnings("unchecked") + public Response query(@QueryParam("component") String component) { + Map<String, Object> headers = getCommonHeaders(component); + String cacheName = (String) headers.get("cacheName"); + + cacheManager.getCache(cacheName).put("person", new Person("Test", "Person")); + + String query = "FROM person.Person WHERE firstName = 'Test'"; + InfinispanQueryBuilder builder = InfinispanQueryBuilder.create(query); + + headers.put(InfinispanConstants.QUERY_BUILDER, builder); + + List<String> result = template.requestBodyAndHeaders("direct:query", null, headers, List.class); + if (result.isEmpty()) { + return Response.status(404).build(); + } + return Response.ok().entity(result.get(0)).build(); + } + + @Path("/remove") + @DELETE + public void remove(@QueryParam("component") String component) { + Map<String, Object> headers = getCommonHeaders(component); + template.requestBodyAndHeaders("direct:remove", null, headers, String.class); + } + + @Path("/removeAsync") + @DELETE + public void removeAsync(@QueryParam("component") String component) + throws ExecutionException, InterruptedException, TimeoutException { + Map<String, Object> headers = getCommonHeaders(component); + CompletableFuture<?> future = template.requestBodyAndHeaders("direct:removeAsync", null, headers, + CompletableFuture.class); + future.get(5, TimeUnit.SECONDS); + } + + @Path("/replace") + @PATCH + @Consumes(MediaType.TEXT_PLAIN) + public void replace(@QueryParam("component") String component, String content) { + Map<String, Object> headers = getCommonHeaders(component); + template.sendBodyAndHeaders("direct:replace", content, headers); + } + + @Path("/replaceAsync") + @PATCH + @Consumes(MediaType.TEXT_PLAIN) + public void replaceAsync(@QueryParam("component") String component, String content) + throws ExecutionException, InterruptedException, TimeoutException { + Map<String, Object> headers = getCommonHeaders(component); + CompletableFuture<?> future = template.requestBodyAndHeaders("direct:replaceAsync", content, headers, + CompletableFuture.class); + future.get(5, TimeUnit.SECONDS); + } + + @Path("/size") + @GET + @Produces(MediaType.TEXT_PLAIN) + public Integer size(@QueryParam("component") String component) { + Map<String, Object> headers = getCommonHeaders(component); + return template.requestBodyAndHeaders("direct:size", null, headers, Integer.class); + } + + @Path("/stats") + @GET + @Produces(MediaType.TEXT_PLAIN) + public Integer stats(@QueryParam("component") String component) { + Map<String, Object> headers = getCommonHeaders(component); + ServerStatistics statistics = template.requestBodyAndHeaders("direct:stats", null, headers, ServerStatistics.class); + return statistics.getIntStatistic(ServerStatistics.CURRENT_NR_OF_ENTRIES); + } + + @Path("/mock/aggregation/results") + @GET + public void assertMockEndpointAggregationResults(@QueryParam("uri") String uri) throws InterruptedException { + MockEndpoint mockEndpoint = camelContext.getEndpoint(uri, MockEndpoint.class); + mockEndpoint.expectedMessageCount(2); + mockEndpoint.expectedBodiesReceived(1 + 3 + 4 + 5, 6 + 7 + 20 + 21); + + try { + mockEndpoint.assertIsSatisfied(5000); + } finally { + mockEndpoint.reset(); + } + } + + @Path("/mock/event/results") + @GET + public void assertMockEndpointEventResults(@QueryParam("uri") String uri) throws InterruptedException { + MockEndpoint mockEndpoint = camelContext.getEndpoint(uri, MockEndpoint.class); + mockEndpoint.expectedMessageCount(1); + mockEndpoint.message(0).header(InfinispanConstants.EVENT_TYPE).isEqualTo("CLIENT_CACHE_ENTRY_CREATED"); + mockEndpoint.message(0).header(InfinispanConstants.CACHE_NAME).isNotNull(); + mockEndpoint.message(0).header(InfinispanConstants.KEY).isEqualTo("test-key"); + + try { + mockEndpoint.assertIsSatisfied(5000); + } finally { + mockEndpoint.reset(); + } + } + + @Path("/mock/idempotent/results") + @GET + public void assertMockEndpointIdempotentResults(@QueryParam("uri") String uri) throws InterruptedException { + MockEndpoint mockEndpoint = camelContext.getEndpoint(uri, MockEndpoint.class); + mockEndpoint.expectedMessageCount(1); + try { + mockEndpoint.assertIsSatisfied(5000); + } finally { + mockEndpoint.reset(); + } + } + + @POST + @Path("consumer/{routeId}/{enabled}") + public void manageRoute( + @PathParam("routeId") String routeId, + @PathParam("enabled") boolean enabled) throws Exception { + if (enabled) { + camelContext.getRouteController().startRoute(routeId); + } else { + camelContext.getRouteController().stopRoute(routeId); + } + } + private Map<String, Object> getCommonHeaders(String componentName) { Map<String, Object> headers = new HashMap<>(); headers.put("component", componentName); - headers.put("cacheName", CACHE_NAME); + + if (componentName.equals("infinispan")) { + headers.put("cacheName", CACHE_NAME_CAMEL); + } else { + headers.put("cacheName", CACHE_NAME_QUARKUS); + } + return headers; } } diff --git a/integration-tests/infinispan/src/main/java/org/apache/camel/quarkus/component/infinispan/InfinispanRoutes.java b/integration-tests/infinispan/src/main/java/org/apache/camel/quarkus/component/infinispan/InfinispanRoutes.java index c194cee..e9476d7 100644 --- a/integration-tests/infinispan/src/main/java/org/apache/camel/quarkus/component/infinispan/InfinispanRoutes.java +++ b/integration-tests/infinispan/src/main/java/org/apache/camel/quarkus/component/infinispan/InfinispanRoutes.java @@ -17,36 +17,194 @@ package org.apache.camel.quarkus.component.infinispan; import java.nio.charset.StandardCharsets; +import java.util.Set; +import java.util.function.BiFunction; import javax.inject.Named; +import org.apache.camel.AggregationStrategy; +import org.apache.camel.CamelContext; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.infinispan.InfinispanConstants; import org.apache.camel.component.infinispan.InfinispanOperation; +import org.apache.camel.component.infinispan.remote.InfinispanRemoteAggregationRepository; import org.apache.camel.component.infinispan.remote.InfinispanRemoteComponent; import org.apache.camel.component.infinispan.remote.InfinispanRemoteComponentConfigurer; +import org.apache.camel.component.infinispan.remote.InfinispanRemoteConfiguration; +import org.apache.camel.component.infinispan.remote.InfinispanRemoteCustomListener; +import org.apache.camel.component.infinispan.remote.InfinispanRemoteIdempotentRepository; +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigProvider; +import org.infinispan.client.hotrod.RemoteCacheManager; +import org.infinispan.client.hotrod.annotation.ClientCacheEntryCreated; +import org.infinispan.client.hotrod.annotation.ClientListener; +import org.infinispan.client.hotrod.configuration.Configuration; +import org.infinispan.client.hotrod.configuration.ConfigurationBuilder; +import org.infinispan.client.hotrod.event.ClientCacheEntryCreatedEvent; +import org.infinispan.commons.marshall.StringMarshaller; + +import static org.apache.camel.quarkus.component.infinispan.InfinispanResources.CACHE_NAME_CAMEL; +import static org.apache.camel.quarkus.component.infinispan.InfinispanResources.CACHE_NAME_QUARKUS; public class InfinispanRoutes extends RouteBuilder { + public static final int COMPLETION_SIZE = 4; + public static final String CORRELATOR_HEADER = "CORRELATOR_HEADER"; + @Override public void configure() { - // we do not need to set any information about the target infinispan server - // as the RemoteConnectionManager is produced by the infinispan extension - // and camel-main automatically bind it to the component + from("direct:clear") + .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.CLEAR) + .setHeader(InfinispanConstants.KEY).constant("the-key") + .toD("${header.component}:${header.cacheName}"); - from("direct:put") - .convertBodyTo(byte[].class) - .to("log:cache?showAll=true") - .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.PUT) - .setHeader(InfinispanConstants.KEY).constant("the-key".getBytes(StandardCharsets.UTF_8)) + from("direct:clearAsync") + .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.CLEARASYNC) + .setHeader(InfinispanConstants.KEY).constant("the-key") + .toD("${header.component}:${header.cacheName}"); + + from("direct:compute") + .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.COMPUTE) + .setHeader(InfinispanConstants.KEY).constant("the-key") + .toD("${header.component}:${header.cacheName}?remappingFunction=#customMappingFunction"); + + from("direct:computeAsync") + .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.COMPUTEASYNC) + .setHeader(InfinispanConstants.KEY).constant("the-key") + .toD("${header.component}:${header.cacheName}?remappingFunction=#customMappingFunction"); + + from("direct:containsKey") + .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.CONTAINSKEY) + .setHeader(InfinispanConstants.KEY).constant("the-key") + .toD("${header.component}:${header.cacheName}"); + + from("direct:containsValue") + .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.CONTAINSVALUE) .setHeader(InfinispanConstants.VALUE).body() - .toD("${header.component}:${header.cacheName}") - .to("log:put?showAll=true"); + .toD("${header.component}:${header.cacheName}"); from("direct:get") .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.GET) - .setHeader(InfinispanConstants.KEY).constant("the-key".getBytes(StandardCharsets.UTF_8)) - .toD("${header.component}:${header.cacheName}") - .to("log:get?showAll=true"); + .toD("${header.component}:${header.cacheName}"); + + from("direct:getOrDefault") + .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.GETORDEFAULT) + .setHeader(InfinispanConstants.KEY).constant("the-key") + .setHeader(InfinispanConstants.DEFAULT_VALUE).constant("default-value") + .toD("${header.component}:${header.cacheName}"); + + from("direct:put") + .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.PUT) + .setHeader(InfinispanConstants.KEY).constant("the-key") + .setHeader(InfinispanConstants.VALUE).body() + .toD("${header.component}:${header.cacheName}"); + + from("direct:putAsync") + .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.PUTASYNC) + .setHeader(InfinispanConstants.KEY).constant("the-key") + .setHeader(InfinispanConstants.VALUE).body() + .toD("${header.component}:${header.cacheName}"); + + from("direct:putAll") + .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.PUTALL) + .setHeader(InfinispanConstants.MAP).body() + .toD("${header.component}:${header.cacheName}"); + + from("direct:putAllAsync") + .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.PUTALLASYNC) + .setHeader(InfinispanConstants.MAP).body() + .toD("${header.component}:${header.cacheName}"); + + from("direct:putIfAbsent") + .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.PUTIFABSENT) + .setHeader(InfinispanConstants.KEY).constant("the-key") + .setHeader(InfinispanConstants.VALUE).body() + .toD("${header.component}:${header.cacheName}"); + + from("direct:putIfAbsentAsync") + .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.PUTIFABSENTASYNC) + .setHeader(InfinispanConstants.KEY).constant("the-key") + .setHeader(InfinispanConstants.VALUE).body() + .toD("${header.component}:${header.cacheName}"); + + from("direct:query") + .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.QUERY) + .toD("${header.component}:${header.cacheName}"); + + from("direct:remove") + .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.REMOVE) + .setHeader(InfinispanConstants.KEY).constant("the-key") + .toD("${header.component}:${header.cacheName}"); + + from("direct:removeAsync") + .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.REMOVEASYNC) + .setHeader(InfinispanConstants.KEY).constant("the-key") + .toD("${header.component}:${header.cacheName}"); + + from("direct:replace") + .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.REPLACE) + .setHeader(InfinispanConstants.KEY).constant("the-key") + .setHeader(InfinispanConstants.VALUE).body() + .toD("${header.component}:${header.cacheName}"); + + from("direct:replaceAsync") + .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.REPLACEASYNC) + .setHeader(InfinispanConstants.KEY).constant("the-key") + .setHeader(InfinispanConstants.VALUE).body() + .toD("${header.component}:${header.cacheName}"); + + from("direct:size") + .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.SIZE) + .setHeader(InfinispanConstants.KEY).constant("the-key") + .toD("${header.component}:${header.cacheName}"); + + from("direct:stats") + .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.STATS) + .toD("${header.component}:${header.cacheName}"); + + from("infinispan:camel?eventTypes=CLIENT_CACHE_ENTRY_CREATED") + .id("infinispan-events") + .autoStartup(false) + .to("mock:resultCreated"); + + // Only start aggregation repository routes in JVM mode + if (!"executable".equals(System.getProperty("org.graalvm.nativeimage.kind"))) { + from("direct:camelAggregation") + .aggregate(header(CORRELATOR_HEADER)) + .aggregationRepository(createAggregationRepository("infinispan")) + .aggregationStrategy(createAggregationStrategy()) + .completionSize(COMPLETION_SIZE) + .to("mock:aggregationResult"); + + from("direct:quarkusAggregation") + .aggregate(header(CORRELATOR_HEADER)) + .aggregationRepository(createAggregationRepository("infinispan-quarkus")) + .aggregationStrategy(createAggregationStrategy()) + .completionSize(COMPLETION_SIZE) + .to("mock:aggregationResult"); + } + + from("direct:camelIdempotent") + .idempotentConsumer(header("MessageID"), createIdempotentRepository("infinispan")) + .to("mock:resultIdempotent"); + + from("direct:quarkusIdempotent") + .idempotentConsumer(header("MessageID"), createIdempotentRepository("infinispan-quarkus")) + .to("mock:resultIdempotent"); + + from("infinispan-quarkus:quarkus?eventTypes=CLIENT_CACHE_ENTRY_CREATED") + .id("infinispan-quarkus-events") + .autoStartup(false) + .to("mock:resultCreated"); + + from("infinispan:camel?customListener=#customListener") + .id("infinispan-custom-listener") + .autoStartup(false) + .to("mock:resultCustomListener"); + + from("infinispan-quarkus:quarkus?customListener=#customListener") + .id("infinispan-quarkus-custom-listener") + .autoStartup(false) + .to("mock:resultCustomListener"); } @Named("infinispan-quarkus") @@ -59,4 +217,95 @@ public class InfinispanRoutes extends RouteBuilder { public InfinispanRemoteComponentConfigurer quarkusInfinispanConfigurer() { return new InfinispanRemoteComponentConfigurer(); } + + @Named("customMappingFunction") + public BiFunction<String, String, String> mappingFunction() { + return (k, v) -> v + "-remapped"; + } + + @Named("customListener") + public InfinispanRemoteCustomListener customListener() { + return new CustomListener(); + } + + private InfinispanRemoteIdempotentRepository createIdempotentRepository(String componentName) { + String cacheName = componentName.equals("infinispan") ? CACHE_NAME_CAMEL : CACHE_NAME_QUARKUS; + InfinispanRemoteConfiguration configuration = getConfiguration(componentName); + InfinispanRemoteIdempotentRepository repository = new InfinispanRemoteIdempotentRepository(cacheName); + repository.setConfiguration(configuration); + return repository; + } + + private InfinispanRemoteAggregationRepository createAggregationRepository(String componentName) { + String cacheName = componentName.equals("infinispan") ? CACHE_NAME_CAMEL : CACHE_NAME_QUARKUS; + InfinispanRemoteAggregationRepository aggregationRepository = new InfinispanRemoteAggregationRepository(cacheName); + InfinispanRemoteConfiguration configuration = getConfiguration(componentName); + aggregationRepository.setConfiguration(configuration); + return aggregationRepository; + } + + private InfinispanRemoteConfiguration getConfiguration(String componentName) { + CamelContext camelContext = getCamelContext(); + InfinispanRemoteComponent component = camelContext.getComponent(componentName, InfinispanRemoteComponent.class); + InfinispanRemoteConfiguration configuration = component.getConfiguration().clone(); + configuration.setCacheContainerConfiguration(getConfigurationBuilder()); + if (componentName.equals("infinispan-quarkus")) { + Set<RemoteCacheManager> beans = camelContext.getRegistry().findByType(RemoteCacheManager.class); + RemoteCacheManager cacheManager = beans.iterator().next(); + configuration.setCacheContainer(cacheManager); + } + return configuration; + } + + private Configuration getConfigurationBuilder() { + Config config = ConfigProvider.getConfig(); + ConfigurationBuilder clientBuilder = new ConfigurationBuilder(); + String[] hostParts = config.getValue("quarkus.infinispan-client.server-list", String.class).split(":"); + + clientBuilder.addServer() + .host(hostParts[0]) + .port(Integer.parseInt(hostParts[1])); + + clientBuilder + .security() + .authentication() + .username(config.getValue("quarkus.infinispan-client.auth-username", String.class)) + .password(config.getValue("quarkus.infinispan-client.auth-password", String.class)) + .serverName(config.getValue("quarkus.infinispan-client.auth-server-name", String.class)) + .saslMechanism(config.getValue("quarkus.infinispan-client.sasl-mechanism", String.class)) + .realm(config.getValue("quarkus.infinispan-client.auth-realm", String.class)) + .marshaller(new StringMarshaller(StandardCharsets.UTF_8)); + + return clientBuilder.build(); + } + + private AggregationStrategy createAggregationStrategy() { + return (oldExchange, newExchange) -> { + if (oldExchange == null) { + return newExchange; + } else { + Integer n = newExchange.getIn().getBody(Integer.class); + Integer o = oldExchange.getIn().getBody(Integer.class); + Integer v = (o == null ? 0 : o) + (n == null ? 0 : n); + oldExchange.getIn().setBody(v, Integer.class); + return oldExchange; + } + }; + } + + @ClientListener + static final class CustomListener extends InfinispanRemoteCustomListener { + + @ClientCacheEntryCreated + public void entryCreated(ClientCacheEntryCreatedEvent<?> event) { + if (isAccepted(event.getType())) { + getEventProcessor().processEvent( + event.getType().toString(), + getCacheName(), + event.getKey(), + null, + null); + } + } + } } diff --git a/integration-tests/infinispan/src/main/java/org/apache/camel/quarkus/component/infinispan/model/Person.java b/integration-tests/infinispan/src/main/java/org/apache/camel/quarkus/component/infinispan/model/Person.java new file mode 100644 index 0000000..9902d83 --- /dev/null +++ b/integration-tests/infinispan/src/main/java/org/apache/camel/quarkus/component/infinispan/model/Person.java @@ -0,0 +1,44 @@ +/* + * 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.quarkus.component.infinispan.model; + +import java.util.Objects; + +import org.infinispan.protostream.annotations.ProtoFactory; +import org.infinispan.protostream.annotations.ProtoField; + +public class Person { + + private final String firstName; + private final String lastName; + + @ProtoFactory + public Person(String firstName, String lastName) { + this.firstName = Objects.requireNonNull(firstName); + this.lastName = Objects.requireNonNull(lastName); + } + + @ProtoField(number = 1) + public String getFirstName() { + return firstName; + } + + @ProtoField(number = 2) + public String getLastName() { + return lastName; + } +} diff --git a/integration-tests/infinispan/src/main/java/org/apache/camel/quarkus/component/infinispan/model/PersonSchema.java b/integration-tests/infinispan/src/main/java/org/apache/camel/quarkus/component/infinispan/model/PersonSchema.java new file mode 100644 index 0000000..4569d61 --- /dev/null +++ b/integration-tests/infinispan/src/main/java/org/apache/camel/quarkus/component/infinispan/model/PersonSchema.java @@ -0,0 +1,24 @@ +/* + * 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.quarkus.component.infinispan.model; + +import org.infinispan.protostream.GeneratedSchema; +import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder; + +@AutoProtoSchemaBuilder(includeClasses = { Person.class }, schemaPackageName = "person") +interface PersonSchema extends GeneratedSchema { +} diff --git a/integration-tests/infinispan/src/test/java/org/apache/camel/quarkus/component/infinispan/InfinispanServerTestResource.java b/integration-tests/infinispan/src/test/java/org/apache/camel/quarkus/component/infinispan/InfinispanServerTestResource.java index 812828d..54ebeac 100644 --- a/integration-tests/infinispan/src/test/java/org/apache/camel/quarkus/component/infinispan/InfinispanServerTestResource.java +++ b/integration-tests/infinispan/src/test/java/org/apache/camel/quarkus/component/infinispan/InfinispanServerTestResource.java @@ -23,6 +23,7 @@ import org.apache.camel.util.CollectionHelper; import org.apache.commons.lang3.SystemUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.testcontainers.containers.BindMode; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.utility.TestcontainersConfiguration; @@ -45,6 +46,8 @@ public class InfinispanServerTestResource implements QuarkusTestResourceLifecycl .withExposedPorts(HOTROD_PORT) .withEnv("USER", USER) .withEnv("PASS", PASS) + .withClasspathResourceMapping("infinispan.xml", "/user-config/infinispan.xml", BindMode.READ_ONLY) + .withCommand("-c", "/user-config/infinispan.xml") .waitingFor(Wait.forListeningPort()); container.start(); @@ -58,7 +61,6 @@ public class InfinispanServerTestResource implements QuarkusTestResourceLifecycl Map<String, String> result = CollectionHelper.mapOf( // quarkus "quarkus.infinispan-client.server-list", serverList, - "quarkus.infinispan-client.near-cache-max-entries", "3", "quarkus.infinispan-client.auth-username", USER, "quarkus.infinispan-client.auth-password", PASS, "quarkus.infinispan-client.auth-realm", "default", diff --git a/integration-tests/infinispan/src/test/java/org/apache/camel/quarkus/component/infinispan/InfinispanTest.java b/integration-tests/infinispan/src/test/java/org/apache/camel/quarkus/component/infinispan/InfinispanTest.java index abe2592..c39731c 100644 --- a/integration-tests/infinispan/src/test/java/org/apache/camel/quarkus/component/infinispan/InfinispanTest.java +++ b/integration-tests/infinispan/src/test/java/org/apache/camel/quarkus/component/infinispan/InfinispanTest.java @@ -16,12 +16,20 @@ */ package org.apache.camel.quarkus.component.infinispan; +import java.util.UUID; +import java.util.stream.IntStream; +import java.util.stream.Stream; + import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.DisabledOnNativeImage; import io.quarkus.test.junit.QuarkusTest; import io.restassured.RestAssured; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; @@ -30,30 +38,489 @@ import static org.hamcrest.Matchers.notNullValue; @QuarkusTestResource(InfinispanServerTestResource.class) public class InfinispanTest { + @AfterEach + public void afterEach() { + for (String componentName : componentNames()) { + RestAssured.with() + .queryParam("component", componentName) + .delete("/infinispan/clear") + .then() + .statusCode(204); + + RestAssured.with() + .queryParam("component", componentName) + .get("/infinispan/get") + .then() + .statusCode(204); + } + } + + @DisabledOnNativeImage("https://github.com/apache/camel-quarkus/issues/3657") + @ParameterizedTest + @MethodSource("componentNames") + public void aggregate(String componentName) { + // TODO: https://github.com/apache/camel-quarkus/issues/3657 + // + // Enable testing InfinispanRemoteAggregationRepository with the Quarkus configured client. + // Technically it is possible with a custom META-INF/hotrod-client.properties and setting + // infinispan.client.hotrod.marshaller=org.infinispan.jboss.marshalling.core.JBossUserMarshaller + // However, it potentially impacts some of the Quarkus Infinispan extension functionality that relies on + // the default configured ProtoStreamMarshaller, thus we avoid doing it in this test suite + Assumptions.assumeTrue(componentName.equals("infinispan")); + + Stream.of(1, 3, 4, 5, 6, 7, 20, 21) + .forEach(value -> { + RestAssured.with() + .queryParam("component", componentName) + .body(value) + .post("/infinispan/aggregate") + .then() + .statusCode(204); + }); + + RestAssured.with() + .queryParam("uri", "mock:aggregationResult") + .get("/infinispan/mock/aggregation/results") + .then() + .statusCode(204); + } + + @ParameterizedTest + @MethodSource("componentNamesWithSynchronicity") + public void clear(String componentName, boolean isAsync) { + RestAssured.with() + .queryParam("component", componentName) + .body("Hello " + componentName) + .post("/infinispan/put") + .then() + .statusCode(204); + + RestAssured.with() + .queryParam("component", componentName) + .get("/infinispan/get") + .then() + .statusCode(200) + .body(is("Hello " + componentName)); + + RestAssured.with() + .queryParam("component", componentName) + .delete(computePath("/infinispan/clear", isAsync)) + .then() + .statusCode(204); + + RestAssured.with() + .queryParam("component", componentName) + .get("/infinispan/get") + .then() + .statusCode(204); + } + + @ParameterizedTest + @MethodSource("componentNamesWithSynchronicity") + public void compute(String componentName, boolean isAsync) { + RestAssured.with() + .queryParam("component", componentName) + .body("Initial value") + .post("/infinispan/put") + .then() + .statusCode(204); + + RestAssured.with() + .queryParam("component", componentName) + .post(computePath("/infinispan/compute", isAsync)) + .then() + .statusCode(204); + + RestAssured.with() + .queryParam("component", componentName) + .get("/infinispan/get") + .then() + .statusCode(200) + .body(is("Initial value-remapped")); + } + + @ParameterizedTest + @MethodSource("componentNames") + public void containsKey(String componentName) { + RestAssured.with() + .queryParam("component", componentName) + .get("/infinispan/containsKey") + .then() + .statusCode(200) + .body(is("false")); + + RestAssured.with() + .queryParam("component", componentName) + .body("Hello " + componentName) + .post("/infinispan/put") + .then() + .statusCode(204); + + RestAssured.with() + .queryParam("component", componentName) + .get("/infinispan/containsKey") + .then() + .statusCode(200) + .body(is("true")); + } + + @ParameterizedTest + @MethodSource("componentNames") + public void containsValue(String componentName) { + String value = "test-value"; + + RestAssured.with() + .queryParam("component", componentName) + .queryParam("value", value) + .get("/infinispan/containsValue") + .then() + .statusCode(200) + .body(is("false")); + + RestAssured.with() + .queryParam("component", componentName) + .body(value) + .post("/infinispan/put") + .then() + .statusCode(204); + + RestAssured.with() + .queryParam("component", componentName) + .queryParam("value", value) + .get("/infinispan/containsValue") + .then() + .statusCode(200) + .body(is("true")); + } + + @ParameterizedTest + @MethodSource("componentNames") + public void customListener(String componentName) { + RestAssured.with() + .queryParam("component", componentName) + .post("/infinispan/consumer/" + componentName + "-custom-listener/true") + .then() + .statusCode(204); + + try { + RestAssured.with() + .queryParam("component", componentName) + .body("Hello " + componentName) + .post("/infinispan/put") + .then() + .statusCode(204); + + RestAssured.with() + .queryParam("uri", "mock:resultCustomListener") + .get("/infinispan/mock/event/results") + .then() + .statusCode(204); + } finally { + RestAssured.with() + .queryParam("component", componentName) + .post("/infinispan/consumer/" + componentName + "-custom-listener/false") + .then() + .statusCode(204); + } + } + + @ParameterizedTest + @MethodSource("componentNames") + public void events(String componentName) { + RestAssured.with() + .queryParam("component", componentName) + .post("/infinispan/consumer/" + componentName + "-events/true") + .then() + .statusCode(204); + + try { + RestAssured.with() + .queryParam("component", componentName) + .body("Hello " + componentName) + .post("/infinispan/put") + .then() + .statusCode(204); + + RestAssured.with() + .queryParam("uri", "mock:resultCreated") + .get("/infinispan/mock/event/results") + .then() + .statusCode(204); + } finally { + RestAssured.with() + .queryParam("component", componentName) + .post("/infinispan/consumer/" + componentName + "-events/false") + .then() + .statusCode(204); + } + } + + @ParameterizedTest + @MethodSource("componentNames") + public void getOrDefault(String componentName) { + RestAssured.with() + .queryParam("component", componentName) + .get("/infinispan/getOrDefault") + .then() + .statusCode(200) + .body(is("default-value")); + + RestAssured.with() + .queryParam("component", componentName) + .body("Hello " + componentName) + .post("/infinispan/put") + .then() + .statusCode(204); + + RestAssured.with() + .queryParam("component", componentName) + .get("/infinispan/getOrDefault") + .then() + .statusCode(200) + .body(is("Hello " + componentName)); + } + + @ParameterizedTest + @MethodSource("componentNames") + public void idempotent(String componentName) { + String messageId = UUID.randomUUID().toString(); + + IntStream.of(1, 10).forEach(value -> { + RestAssured.with() + .queryParam("component", componentName) + .queryParam("messageId", messageId) + .body("Message " + value) + .post("/infinispan/putIdempotent") + .then() + .statusCode(204); + }); + + RestAssured.with() + .queryParam("uri", "mock:resultIdempotent") + .get("/infinispan/mock/idempotent/results") + .then() + .statusCode(204); + } + @Test public void inspect() { RestAssured.when() - .get("/test/inspect") + .get("/infinispan/inspect") .then().body( "hosts", is(notNullValue()), "cache-manager", is("none")); } @ParameterizedTest - @ValueSource(strings = { "infinispan", "infinispan-quarkus" }) - public void testInfinispan(String componentName) { + @MethodSource("componentNamesWithSynchronicity") + public void put(String componentName, boolean isAsync) { + RestAssured.with() + .queryParam("component", componentName) + .body("Hello " + componentName) + .post(computePath("/infinispan/put", isAsync)) + .then() + .statusCode(204); + + RestAssured.with() + .queryParam("component", componentName) + .get("/infinispan/get") + .then() + .statusCode(200) + .body(is("Hello " + componentName)); + } + + @ParameterizedTest + @MethodSource("componentNamesWithSynchronicity") + public void putAll(String componentName, boolean isAsync) { + RestAssured.with() + .queryParam("component", componentName) + .post(computePath("/infinispan/putAll", isAsync)) + .then() + .statusCode(204); + + RestAssured.with() + .queryParam("component", componentName) + .queryParam("key", "key-1") + .get("/infinispan/get") + .then() + .statusCode(200) + .body(is("value-1")); + + RestAssured.with() + .queryParam("component", componentName) + .queryParam("key", "key-2") + .get("/infinispan/get") + .then() + .statusCode(200) + .body(is("value-2")); + } + + @ParameterizedTest + @MethodSource("componentNamesWithSynchronicity") + public void putIfAbsent(String componentName, boolean isAsync) { RestAssured.with() .queryParam("component", componentName) .body("Hello " + componentName) - .post("/test/put") + .post(computePath("/infinispan/putIfAbsent", isAsync)) .then() .statusCode(204); RestAssured.with() .queryParam("component", componentName) - .get("/test/get") + .get("/infinispan/get") .then() .statusCode(200) .body(is("Hello " + componentName)); + + RestAssured.with() + .queryParam("component", componentName) + .body("An alternative value") + .post(computePath("/infinispan/putIfAbsent", isAsync)) + .then() + .statusCode(204); + + RestAssured.with() + .queryParam("component", componentName) + .get("/infinispan/get") + .then() + .statusCode(200) + .body(is("Hello " + componentName)); + } + + @ParameterizedTest + @MethodSource("componentNames") + public void query(String componentName) { + RestAssured.with() + .queryParam("component", componentName) + .get("/infinispan/query") + .then() + .statusCode(200); + } + + @ParameterizedTest + @MethodSource("componentNamesWithSynchronicity") + public void remove(String componentName, boolean isAsync) { + RestAssured.with() + .queryParam("component", componentName) + .body("Hello " + componentName) + .post(computePath("/infinispan/put", isAsync)) + .then() + .statusCode(204); + + RestAssured.with() + .queryParam("component", componentName) + .delete(computePath("/infinispan/remove", isAsync)) + .then() + .statusCode(204); + + RestAssured.with() + .queryParam("component", componentName) + .get("/infinispan/get") + .then() + .statusCode(204); + } + + @ParameterizedTest + @MethodSource("componentNamesWithSynchronicity") + public void replace(String componentName, boolean isAsync) { + RestAssured.with() + .queryParam("component", componentName) + .body("Hello " + componentName) + .post(computePath("/infinispan/put", isAsync)) + .then() + .statusCode(204); + + RestAssured.with() + .queryParam("component", componentName) + .get("/infinispan/get") + .then() + .statusCode(200) + .body(is("Hello " + componentName)); + + RestAssured.with() + .queryParam("component", componentName) + .body("replaced cache value") + .patch(computePath("/infinispan/replace", isAsync)) + .then() + .statusCode(204); + + RestAssured.with() + .queryParam("component", componentName) + .get("/infinispan/get") + .then() + .statusCode(200) + .body(is("replaced cache value")); + } + + @ParameterizedTest + @MethodSource("componentNames") + public void size(String componentName) { + RestAssured.with() + .queryParam("component", componentName) + .get("/infinispan/size") + .then() + .statusCode(200) + .body(is("0")); + + RestAssured.with() + .queryParam("component", componentName) + .body("Hello " + componentName) + .post("/infinispan/put") + .then() + .statusCode(204); + + RestAssured.with() + .queryParam("component", componentName) + .get("/infinispan/size") + .then() + .statusCode(200) + .body(is("1")); + } + + @ParameterizedTest + @MethodSource("componentNames") + public void stats(String componentName) { + RestAssured.with() + .queryParam("component", componentName) + .get("/infinispan/stats") + .then() + .statusCode(200) + .body(is("0")); + + RestAssured.with() + .queryParam("component", componentName) + .body("Hello " + componentName) + .post("/infinispan/put") + .then() + .statusCode(204); + + RestAssured.with() + .queryParam("component", componentName) + .get("/infinispan/stats") + .then() + .statusCode(200) + .body(is("1")); + } + + private String computePath(String path, boolean isAsync) { + if (isAsync) { + path += "Async"; + } + return path; + } + + public static String[] componentNames() { + return new String[] { + "infinispan", + "infinispan-quarkus" + }; + } + + public static Stream<Arguments> componentNamesWithSynchronicity() { + return Stream.of( + Arguments.of("infinispan", false), + Arguments.of("infinispan-quarkus", false), + Arguments.of("infinispan", true), + Arguments.of("infinispan-quarkus", true)); } } diff --git a/integration-tests/infinispan/src/test/resources/infinispan.xml b/integration-tests/infinispan/src/test/resources/infinispan.xml new file mode 100644 index 0000000..60acf47 --- /dev/null +++ b/integration-tests/infinispan/src/test/resources/infinispan.xml @@ -0,0 +1,82 @@ +<?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> + + <!-- Used by tests where camel-infinispan manages its own cache container --> + <local-cache name="camel"> + </local-cache> + + <!-- Used by tests where camel-infinispan uses the Quarkus managed cache container --> + <local-cache name="quarkus"> + </local-cache> + </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> diff --git a/pom.xml b/pom.xml index 4f06a59..b10bb94 100644 --- a/pom.xml +++ b/pom.xml @@ -98,6 +98,7 @@ <hapi-fhir.version>${hapi-fhir-version}</hapi-fhir.version> <hbase.version>${hbase-version}</hbase.version> <htrace.version>4.2.0-incubating</htrace.version><!-- Mess in hbase transitive deps --> + <infinispan.version>13.0.6.Final</infinispan.version><!-- @sync io.quarkus:quarkus-bom:${quarkus.version} dep:org.infinispan:infinispan-core --> <influxdb.version>${influx-java-driver-version}</influxdb.version> <jackson1.version>1.9.13</jackson1.version><!-- Mess in the transitive dependencies of spark and hbase-testing-util --> <jackson-asl.version>${jackson1.version}</jackson-asl.version><!-- Can be different from jackson1.version on some occasions --> diff --git a/poms/bom/pom.xml b/poms/bom/pom.xml index 982311d..e0e9d02 100644 --- a/poms/bom/pom.xml +++ b/poms/bom/pom.xml @@ -1472,6 +1472,10 @@ <groupId>org.infinispan</groupId> <artifactId>infinispan-core</artifactId> </exclusion> + <exclusion> + <groupId>org.infinispan</groupId> + <artifactId>infinispan-marshaller-protostuff</artifactId> + </exclusion> </exclusions> </dependency> <dependency> @@ -6577,6 +6581,11 @@ <version>${graalvm.version}</version> </dependency> <dependency> + <groupId>org.infinispan</groupId> + <artifactId>infinispan-jboss-marshalling</artifactId> + <version>${infinispan.version}</version> + </dependency> + <dependency> <groupId>org.influxdb</groupId> <artifactId>influxdb-java</artifactId> <version>${influxdb.version}</version>