diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 512f855a2247c..e544ab24d8c3e 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -131,8 +131,8 @@ 5.9.1 1.5.0 6.14.2 - 13.0.11.Final - 4.4.3.Final + 14.0.9.Final + 4.6.2.Final 2.9.3 4.1.86.Final 1.8.0 @@ -5214,6 +5214,11 @@ ${smallrye-reactive-messaging.version} + + org.infinispan + infinispan-api + ${infinispan.version} + org.infinispan infinispan-core diff --git a/docs/src/main/asciidoc/infinispan-client.adoc b/docs/src/main/asciidoc/infinispan-client.adoc index 1e80e747e91dc..ea113be059537 100644 --- a/docs/src/main/asciidoc/infinispan-client.adoc +++ b/docs/src/main/asciidoc/infinispan-client.adoc @@ -44,9 +44,9 @@ This command adds the following dependency to your build file: .build.gradle ---- implementation 'io.quarkus:quarkus-infinispan-client' -annotationProcessor 'org.infinispan.protostream:protostream-processor:4.5.0.Final' <1> +annotationProcessor 'org.infinispan.protostream:protostream-processor:4.5.1.Final' <1> ---- -<1> Mandatory in the gradle build to enable the generation of the files in the annotation based serialization +<1> Mandatory in the Gradle build to enable the generation of the files in the annotation based serialization == Configuring the Infinispan client @@ -100,7 +100,7 @@ to create it on first access, use one of the following properties: [source,properties] ---- -quarkus.infinispan-client.cache.books.configuration-uri=cacheConfig.xml <1> +quarkus.infinispan-client.cache.books.configuration-uri=cacheConfig.json <1> quarkus.infinispan-client.cache.magazine.configuration= <2> ---- <1> The file name located under the `resources` folder that contains the configuration of the 'books' cache @@ -109,15 +109,42 @@ quarkus.infinispan-client.cache.magazine.configuration= ---- +.JSON +[source,json,options="nowrap",subs=attributes+,role="secondary"] +---- +{ + "distributed-cache": { + "encoding": { + "media-type": "application/x-protostream" + } + } +} +---- + +.YAML +[source,yaml,options="nowrap",subs=attributes+,role="secondary"] +---- +distributedCache: + encoding: + mediaType: "application/x-protostream" +---- + === Authentication mechanisms You can use the following authentication mechanisms with the Infinispan client: @@ -130,10 +157,7 @@ Other authentication mechanisms, such as SCRAM and GSSAPI, are not yet verified You can find more information on configuring authentication in https://infinispan.org/docs/stable/titles/hotrod_java/hotrod_java.html#hotrod_endpoint_auth-hotrod-client-configuration[Hot Rod Endpoint Authentication Mechanisms]. -[NOTE] -==== -You must configure authentication in the `hotrod-client.properties` file if you use Dependency Injection. -==== +NOTE: You must configure authentication in the `hotrod-client.properties` file if you use Dependency Injection. == Serialization (Key Value types support) @@ -267,10 +291,12 @@ interface BookStoreSchema extends GeneratedSchema { ---- [TIP] +==== Protostream provides default Protobuf mappers for commonly used types as `BigDecimal`, included in the `org.infinispan.protostream.types` package. +==== So in this case we will automatically generate the marshaller and schemas for the included classes and -place them in the schema package automatically. The package does not have to be provided, but if you use Infinispan query capabilities, you must know the generated package. +place them in the schema package automatically. The package does not have to be provided, but if you use Infinispan search capabilities, you must know the generated package. NOTE: In Quarkus the `schemaFileName` and `schemaFilePath` attributes should NOT be set on the `AutoProtoSchemaBuilder` annotation. Setting either attributes causes native runtime errors. @@ -439,7 +465,8 @@ the field, constructor or method. In the below code we utilize field and constru this.remoteCacheManager = remoteCacheManager; } - @Inject @Remote("myCache") + @Inject + @Remote("myCache") RemoteCache cache; RemoteCacheManager remoteCacheManager; @@ -470,23 +497,97 @@ for Kubernetes deployments, Infinispan Console, https://infinispan.org/docs/stable/titles/rest/rest.html#rest_v2_protobuf_schemas[REST API] or the https://infinispan.org/docs/stable/titles/encoding/encoding.html#registering-sci-remote-caches_marshalling[Hot Rod Java Client]. - == Querying -The Infinispan client supports both indexed and non-indexed querying as long as the +The Infinispan client supports both indexed and non-indexed search as long as the `ProtoStreamMarshaller` is configured above. This allows the user to query based on the -properties of the proto schema. +properties of the proto schema. *Indexed queries are preferred for performance reasons*. + +.XML +[source,xml,options="nowrap",subs=attributes+,role="primary"] +---- + + + + + book_sample.Book + + + +---- + +.JSON +[source,json,options="nowrap",subs=attributes+,role="secondary"] +---- +{ + "books": { + "distributed-cache": { + ... + "indexing": { + "enabled": true, + "storage": "filesystem", + "startupMode": "PURGE", + "indexed-entities": [ + "book_sample.Book" + ] + } + } + } +} +---- + +.YAML +[source,yaml,options="nowrap",subs=attributes+,role="secondary"] +---- +distributedCache: + # other configuration + indexing: + enabled: "true" + storage: "filesystem" + startupMode: "PURGE" + indexedEntities: + - "book_sample.Book" +---- Query builds upon the proto definitions you can configure when setting up the `ProtoStreamMarshaller`. Either method of Serialization above will automatically register the schema with the server at startup, meaning that you will automatically gain the ability to query objects stored in the remote Infinispan Server. -You can read more about https://infinispan.org/docs/stable/titles/developing/developing.html#creating_ickle_queries-querying[querying] in the Infinispan documentation. +.Book.java +[source,java] +---- +@Indexed <1> +public class Book { + + @ProtoFactory + public Book(String title, String description, int publicationYear, Set authors) { + ... + } + + @ProtoField(number = 1) + @Text <2> + public String getTitle() { + return title; + } + + @ProtoField(number = 2) + @Keyword(projectable = true, sortable = true, normalizer = "lowercase", indexNullAs = "unnamed", norms = false) <3> + public String getDescription() { + return description; + } + ... +---- +<1> `@Indexed` annotation makes the POJO indexable +<2> `@Basic` annotation is used for indexed fields without any special transformation +<3> `@Keyword` annotation is used to apply a normalizer to a text field You can use either the Query DSL or the Ickle Query language with the Quarkus Infinispan client extension. +NOTE: You can read more about https://infinispan.org/docs/stable/titles/query/query.html[querying] in the Infinispan documentation. + + == Counters Infinispan also has a notion of counters and the Quarkus Infinispan client supports them out of diff --git a/extensions/infinispan-client/deployment/pom.xml b/extensions/infinispan-client/deployment/pom.xml index ba865f639aaaf..003671c759694 100644 --- a/extensions/infinispan-client/deployment/pom.xml +++ b/extensions/infinispan-client/deployment/pom.xml @@ -84,6 +84,10 @@ org.apache.logging.log4j log4j-core + + org.slf4j + jcl-over-slf4j + org.glassfish javax.json diff --git a/extensions/infinispan-client/runtime/pom.xml b/extensions/infinispan-client/runtime/pom.xml index ce7a828f9cc7e..31b0e1818f749 100644 --- a/extensions/infinispan-client/runtime/pom.xml +++ b/extensions/infinispan-client/runtime/pom.xml @@ -86,6 +86,10 @@ opentelemetry-api + + org.infinispan + infinispan-api + org.infinispan infinispan-remote-query-client diff --git a/independent-projects/enforcer-rules/src/main/resources/enforcer-rules/quarkus-banned-dependencies.xml b/independent-projects/enforcer-rules/src/main/resources/enforcer-rules/quarkus-banned-dependencies.xml index 805b934d866db..9c37d9cf628d4 100644 --- a/independent-projects/enforcer-rules/src/main/resources/enforcer-rules/quarkus-banned-dependencies.xml +++ b/independent-projects/enforcer-rules/src/main/resources/enforcer-rules/quarkus-banned-dependencies.xml @@ -98,8 +98,9 @@ org.apache.tomcat.embed:tomcat-embed-core org.jboss.modules:jboss-modules - - org.javassist:javassist + + @@ -107,4 +108,4 @@ - \ No newline at end of file + diff --git a/integration-tests/infinispan-client/src/main/java/io/quarkus/it/infinispan/client/Book.java b/integration-tests/infinispan-client/src/main/java/io/quarkus/it/infinispan/client/Book.java index ca404a8f7985c..45dfc48c3a002 100644 --- a/integration-tests/infinispan-client/src/main/java/io/quarkus/it/infinispan/client/Book.java +++ b/integration-tests/infinispan-client/src/main/java/io/quarkus/it/infinispan/client/Book.java @@ -4,12 +4,16 @@ import java.util.Objects; import java.util.Set; +import org.infinispan.api.annotations.indexing.Indexed; +import org.infinispan.api.annotations.indexing.Keyword; +import org.infinispan.api.annotations.indexing.Text; import org.infinispan.protostream.annotations.ProtoFactory; import org.infinispan.protostream.annotations.ProtoField; /** * @author William Burns */ +@Indexed public class Book { private final String title; private final String description; @@ -29,11 +33,13 @@ public Book(String title, String description, int publicationYear, Set a } @ProtoField(number = 1) + @Text public String getTitle() { return title; } @ProtoField(number = 2) + @Keyword(projectable = true, sortable = true, normalizer = "lowercase", indexNullAs = "unnamed", norms = false) public String getDescription() { return description; } diff --git a/integration-tests/infinispan-client/src/main/java/io/quarkus/it/infinispan/client/TestServlet.java b/integration-tests/infinispan-client/src/main/java/io/quarkus/it/infinispan/client/TestServlet.java index 7352e9f637d16..c75e6cf594091 100644 --- a/integration-tests/infinispan-client/src/main/java/io/quarkus/it/infinispan/client/TestServlet.java +++ b/integration-tests/infinispan-client/src/main/java/io/quarkus/it/infinispan/client/TestServlet.java @@ -4,16 +4,19 @@ import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.stream.Collectors; import javax.inject.Inject; import javax.ws.rs.Consumes; import javax.ws.rs.GET; +import javax.ws.rs.POST; import javax.ws.rs.PUT; 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; @@ -25,11 +28,14 @@ import org.infinispan.counter.api.CounterConfiguration; import org.infinispan.counter.api.CounterManager; import org.infinispan.counter.api.CounterType; +import org.infinispan.counter.api.Storage; import org.infinispan.counter.api.StrongCounter; +import org.infinispan.counter.api.WeakCounter; import org.infinispan.query.dsl.Query; import org.infinispan.query.dsl.QueryFactory; import io.quarkus.infinispan.client.Remote; +import io.smallrye.common.annotation.Blocking; @Path("/test") public class TestServlet { @@ -111,16 +117,38 @@ public String ickleQueryAuthorSurname(@PathParam("id") String name) { .collect(Collectors.joining(",", "[", "]")); } + @Path("counter/{id}") + @POST + @Produces(MediaType.TEXT_PLAIN) + @Blocking + public boolean defineCounter(@PathParam("id") String id, @QueryParam("type") String type, + @QueryParam("storage") String storage) { + cacheSetup.ensureStarted(); + CounterConfiguration configuration = counterManager.getConfiguration(id); + if (configuration == null) { + configuration = CounterConfiguration.builder(CounterType.valueOf(type)).storage(Storage.valueOf(storage)).build(); + return counterManager.defineCounter(id, configuration); + } + return true; + } + @Path("incr/{id}") @GET @Produces(MediaType.TEXT_PLAIN) + @Blocking public CompletionStage incrementCounter(@PathParam("id") String id) { cacheSetup.ensureStarted(); CounterConfiguration configuration = counterManager.getConfiguration(id); if (configuration == null) { - configuration = CounterConfiguration.builder(CounterType.BOUNDED_STRONG).build(); - counterManager.defineCounter(id, configuration); + return CompletableFuture.completedFuture(0L); } + + if (configuration.type() == CounterType.WEAK) { + WeakCounter weakCounter = counterManager.getWeakCounter(id); + weakCounter.sync().increment(); + return CompletableFuture.completedFuture(weakCounter.getValue()); + } + StrongCounter strongCounter = counterManager.getStrongCounter(id); return strongCounter.incrementAndGet(); } diff --git a/integration-tests/infinispan-client/src/main/resources/application.properties b/integration-tests/infinispan-client/src/main/resources/application.properties index 3754896fb54dd..5a02e6e32f971 100644 --- a/integration-tests/infinispan-client/src/main/resources/application.properties +++ b/integration-tests/infinispan-client/src/main/resources/application.properties @@ -1,4 +1,5 @@ quarkus.infinispan-client.cache.default.configuration-uri=cacheConfig.xml +quarkus.infinispan-client.cache.books.configuration-uri=booksIndexedConfig.json quarkus.infinispan-client.cache.magazine.configuration= quarkus.infinispan-client.cache.default.near-cache-mode=INVALIDATED quarkus.infinispan-client.cache.default.near-cache-max-entries=2 @@ -6,4 +7,4 @@ quarkus.infinispan-client.cache.default.near-cache-use-bloom-filter=false quarkus.infinispan-client.cache.magazine.near-cache-mode=INVALIDATED quarkus.infinispan-client.cache.magazine.near-cache-max-entries=2 quarkus.infinispan-client.cache.magazine.near-cache-use-bloom-filter=false -quarkus.native.resources.includes=cacheConfig.xml \ No newline at end of file +quarkus.native.resources.includes=cacheConfig.xml,booksIndexedConfig.json diff --git a/integration-tests/infinispan-client/src/main/resources/booksIndexedConfig.json b/integration-tests/infinispan-client/src/main/resources/booksIndexedConfig.json new file mode 100644 index 0000000000000..6f515612766d4 --- /dev/null +++ b/integration-tests/infinispan-client/src/main/resources/booksIndexedConfig.json @@ -0,0 +1,16 @@ +{ + "distributed-cache": { + "statistics": true, + "encoding": { + "media-type": "application/x-protostream" + }, + "indexing": { + "enabled": true, + "storage": "filesystem", + "startup-mode": "Purge", + "indexed-entities": [ + "book_sample.Book" + ] + } + } +} \ No newline at end of file diff --git a/integration-tests/infinispan-client/src/test/java/io/quarkus/it/infinispan/client/InfinispanClientFunctionalityTest.java b/integration-tests/infinispan-client/src/test/java/io/quarkus/it/infinispan/client/InfinispanClientFunctionalityTest.java index 9de116a8d3964..57dd0a9291bf2 100644 --- a/integration-tests/infinispan-client/src/test/java/io/quarkus/it/infinispan/client/InfinispanClientFunctionalityTest.java +++ b/integration-tests/infinispan-client/src/test/java/io/quarkus/it/infinispan/client/InfinispanClientFunctionalityTest.java @@ -3,6 +3,8 @@ import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; +import org.infinispan.counter.api.CounterType; +import org.infinispan.counter.api.Storage; import org.junit.jupiter.api.Test; import io.quarkus.test.junit.QuarkusTest; @@ -32,8 +34,30 @@ public void testIckleQuery() { @Test public void testCounterIncrement() { - String initialValue = RestAssured.when().get("test/incr/somevalue").body().print(); - String nextValue = RestAssured.when().get("test/incr/somevalue").body().print(); + RestAssured.given() + .queryParam("type", CounterType.BOUNDED_STRONG) + .queryParam("storage", Storage.VOLATILE) + .post("test/counter/strong-1").body().print(); + + RestAssured.given() + .queryParam("type", CounterType.WEAK) + .queryParam("storage", Storage.VOLATILE) + .post("test/counter/weak-1").body().print(); + + RestAssured.given() + .queryParam("type", CounterType.UNBOUNDED_STRONG) + .queryParam("storage", Storage.PERSISTENT) + .post("test/counter/strong-2").body().print(); + + assertCounterIncrement("strong-1"); + assertCounterIncrement("weak-1"); + assertCounterIncrement("strong-2"); + } + + private void assertCounterIncrement(String counterName) { + String initialValue = RestAssured.given() + .get("test/incr/" + counterName).body().print(); + String nextValue = RestAssured.when().get("test/incr/" + counterName).body().print(); assertEquals(Integer.parseInt(initialValue) + 1, Integer.parseInt(nextValue)); } diff --git a/jakarta/rewrite.yml b/jakarta/rewrite.yml index f1c0c9c319d3a..a0dcbc485b0b6 100644 --- a/jakarta/rewrite.yml +++ b/jakarta/rewrite.yml @@ -849,12 +849,6 @@ type: specs.openrewrite.org/v1beta/recipe name: io.quarkus.infinispan displayName: Adjust Infinispan version and dependencies recipeList: - - org.openrewrite.maven.ChangePropertyValue: - key: infinispan.version - newValue: 14.0.0.Final - - org.openrewrite.maven.ChangePropertyValue: - key: infinispan.protostream.version - newValue: 4.5.0.Final - org.openrewrite.maven.ChangeManagedDependencyGroupIdAndArtifactId: oldGroupId: org.infinispan oldArtifactId: infinispan-core