Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infinispan extension upgrades #18363

Merged
merged 1 commit into from
Jul 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@
<junit.jupiter.version>5.7.2</junit.jupiter.version>
<testng.version>6.14.2</testng.version>
<assertj.version>3.20.2</assertj.version>
<infinispan.version>12.1.4.Final</infinispan.version>
<infinispan.version>12.1.6.Final</infinispan.version>
<infinispan.protostream.version>4.4.1.Final</infinispan.protostream.version>
<caffeine.version>2.9.2</caffeine.version>
<netty.version>4.1.65.Final</netty.version>
Expand Down
190 changes: 123 additions & 67 deletions docs/src/main/asciidoc/infinispan-client.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,30 @@ https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc

include::./attributes.adoc[]

Infinispan is an in memory data grid that allows running in a server outside of application processes. This extension
provides functionality to allow the client that can connect to said server when running in Quarkus.
Infinispan is a distributed, in-memory key/value store that provides Quarkus applications with a highly configurable
and independently scalable data layer.
This extension gives you client functionality that connects applications running on Quarkus with remote Infinispan clusters.

Check out the https://infinispan.org/documentation[Infinispan documentation] to find out more about the Infinispan project,
in particular the https://infinispan.org/docs/stable/titles/hotrod_java/hotrod_java.html[Hot Rod Java client guide].
To find out more about Infinispan, visit the https://infinispan.org/documentation[Infinispan documentation].

== Solution

We recommend that you follow the instructions in the next sections and create the application step by step.
However, you can go right to the completed example.
We recommend that you complete each step in the following sections to create the application.
However, you can proceed directly to the completed solution as follows:

Clone the Git repository: `git clone {quickstarts-clone-url}`, or download an {quickstarts-archive-url}[archive].
Clone the Git repository: `git clone {quickstarts-clone-url}` or download an {quickstarts-archive-url}[archive].
Locate the solution in the `infinispan-client-quickstart` {quickstarts-tree-url}/infinispan-client-quickstart[directory].

The solution is located in the `infinispan-client-quickstart` {quickstarts-tree-url}/infinispan-client-quickstart[directory].
== Adding the Infinispan client extension

== Configuration

Once you have your Quarkus project configured you can add the `infinispan-client` extension
to your project by running the following from the command line in your project base directory.
Run the following command in the base directory of your Quarkus project to add the `infinispan-client` extension:

[source]
----
./mvnw quarkus:add-extension -Dextensions="infinispan-client"
----

This will add the following to your pom.xml
This command adds the following dependency to your `pom.xml`:

[source,xml]
----
Expand All @@ -42,15 +40,67 @@ This will add the following to your pom.xml
</dependency>
----

The Infinispan client is configurable in the `application.properties` file that can be
provided in the `src/main/resources` directory. These are the properties that
can be configured in this file:
== Configuring the Infinispan client

include::{generated-dir}/config/quarkus-infinispan-client.adoc[opts=optional, leveloffset=+1]
Open the `application.properties` file in the `src/main/resources` directory with any text editor.

Note that Infinispan documentation refers to a `hotrod-client.properties` file.
You can configure the Infinispan client with either properties file but `hotrod-client.properties` always takes
priority over `application.properties`.

Additionally, you cannot update configuration properties at runtime.
If you modify `application.properties` or `hotrod-client.properties`, you must rebuild the application before those changes take effect.

== Connecting to Infinispan clusters

Add the following properties to connect to Infinispan Server:

[source,properties]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @karesti , I'm checking the updates and trying to follow the steps however I got into some trouble :)

  • When I used a sample hello project with this configuration I was able to open the console at http://127.0.1.1:11222/console/ and follow the https://infinispan.org/get-started/. There was just one little confusion between 127.0.1.1:11222 and localhost:11222. localhost:11222 didn't work.

  • Then I tried the same with the quickstart and I wasn't able to compile the quickstart and failed to connect to localhost/127.0.0.1:11222. I'm probably missing some information.

  • I noticed that the configuration in the quickstart is different from the one in this guide. Which one should I use?

I have limited knowledge about Quarkus and this is my first experience with Infinispan however I wanted to share this with you in case that some of it is relevant. Please let me know if you want me to share the errors I got with the infinispan-client-quickstart. I'm on zulip or gchat.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The configuration in the quickstart and the guide are the same now, I did a PR to remove the unnecessary default values and simplify the connection.
http://localhost:11222/console/welcome and http://127.0.0.1:11222/console/welcome are the same url that point to the local running infinispan server (with a container or downloading the server.
maybe you are missing a config in your hosts file?
thanks for trying the quickstart!

----
# Infinispan Server address
quarkus.infinispan-client.server-list=localhost:11222

# Authentication
quarkus.infinispan-client.auth-username=admin
quarkus.infinispan-client.auth-password=password

# Infinispan client intelligence
# Use BASIC as a Docker for Mac workaround
quarkus.infinispan-client.client-intelligence=BASIC
----

.Running Infinispan Server

To use the Infinispan client extension, you need at least one running instance of Infinispan Server.

Check out our 5 minute https://infinispan.org/get-started/[Getting stated with Infinispan] tutorial to run Infinispan Server locally.

Infinispan Server also enables authentication and security authorization by default so you need to create a user with permissions.

* If you run the Infinispan Server image, pass the `USER="admin"` and `PASS="password"` parameters.
* If you run the bare metal distribution, use the Command Line Interface (CLI) as follows:
+
[source,bash]
----
$ ./bin/cli.sh user create admin -p password
----

=== Authentication mechanisms

You can use the following authentication mechanisms with the Infinispan client:

* DIGEST-MD5
* PLAIN (recommended only in combination with TLS encryption)
* EXTERNAL

Other authentication mechanisms, such as SCRAM and GSSAPI, are not yet verified with the Infinispan client.

It is also possible to configure a `hotrod-client.properties` as described in the Infinispan user guide. Note that
the `hotrod-client.properties` values overwrite any matching property from the other configuration values (eg. near cache).
This properties file is build time only and if it is changed, requires a full rebuild.
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.
====

== Serialization (Key Value types support)

Expand Down Expand Up @@ -94,12 +144,21 @@ public class Book {
}
----

Serialization of user types uses a library based on protobuf, called Protostream.
Serialization of user types uses a library based on protobuf,
called https://github.com/infinispan/protostream[Protostream].

[TIP]
====
Infinispan caches can store keys and values in different encodings, but recommend using https://developers.google.com/protocol-buffers[Protocol Buffers (Protobuf)].

For more information see our https://infinispan.org/docs/stable/titles/encoding/encoding.html[Cache Encoding and Marshalling] guide.
====


=== Annotation based Serialization

This can be done automatically by adding protostream annotations to your user classes.
In addition a single Initializer annotated interface is required which controls how
In addition, a single Initializer annotated interface is required which controls how
the supporting classes are generated.

Here is an example of how the preceding classes should be changed:
Expand Down Expand Up @@ -159,38 +218,40 @@ Here is an example of how the preceding classes should be changed:
If your classes have only mutable fields, then the `ProtoFactory` annotation
is not required, assuming your class has a no arg constructor.

Then all that is required is a very simple `SerializationContextInitializer` interface with an annotation
Then all that is required is a very simple `GeneratedSchema` interface with an annotation
on it to specify configuration settings

.BookContextInitializer.java
.BooksSchema.java
[source,java]
----
import org.infinispan.protostream.SerializationContextInitializer;
import org.infinispan.protostream.GeneratedSchema;
import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder;
import org.infinispan.protostream.types.java.math.BigDecimalAdapter;

@AutoProtoSchemaBuilder(includeClasses = { Book.class, Author.class, BigDecimalAdapter.class }, schemaPackageName = "book_sample")
interface BookContextInitializer extends SerializationContextInitializer {
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
utilize querying, 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 query 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 will cause native runtime to error.
NOTE: In Quarkus the `schemaFileName` and `schemaFilePath` attributes should NOT be set on the `AutoProtoSchemaBuilder` annotation. Setting either attributes causes native runtime errors.

=== User written serialization
=== Custom serialization

The previous method is suggested for any case when the user can annotate their classes.
Unfortunately the user may not be able to annotate all classes they will put in the
cache. In this case you must define your schema and create your own Marshaller(s)
yourself.

Protobuf schema:: You can supply a protobuf schema through either one of two ways.
. Proto File
+
You can put the `.proto` file in the `META-INF` directory of the project. These files will
. Proto File
+
You can put the `.proto` file in the `META-INF` directory of the project. These files will
automatically be picked up at initialization time.
+
.library.proto
Expand All @@ -210,9 +271,9 @@ message Author {
required string surname = 2;
}
----
. In Code
+
Or you can define the proto schema directly in user code by defining a produced bean of type
. In Code
+
Or you can define the proto schema directly in user code by defining a produced bean of type
`org.infinispan.protostream.FileDescriptorSource`.
+
[source,java]
Expand Down Expand Up @@ -357,9 +418,29 @@ annotation is not required and if it is not supplied, the default cache will be

NOTE: Other types may be supported for injection, please see other sections for more information

=== Registering Protobuf Schemas with Infinispan Server
You need to register the generated Protobuf schemas with Infinispan Server to perform queries or convert from
`Protobuf` to other media types such as `JSON`.

[TIP]
You can check the schemas that exist under the `Schemas` tab by logging into
Infinispan Console at `http://localhost:11222`

By default Protobuf schemas generated this way will be registered by this extension when the client first connects.
However, it might be required to handle the registration manually as a schema may evolve over time when used in
production, so you can disable this from occurring by configuring the
`quarkus.infinispan-client.use-schema-registration` to `false`.

To configure the schema manually
please use https://infinispan.org/docs/infinispan-operator/master/operator.html[Infinispan Operator]
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 querying as long as the
`ProtoStreamMarshaller` is configured above. This allows the user to query based on the
properties of the proto schema.

Expand Down Expand Up @@ -404,40 +485,15 @@ Encryption at this point requires additional steps to get working.
The first step is to configure the `hotrod-client.properties` file to point to your truststore
and/or keystore. This is further detailed https://infinispan.org/docs/stable/titles/hotrod_java/hotrod_java.html#hotrod_encryption[here].

The Infinispan Client extension enables SSL by default. You can read more about this
The Infinispan Client extension enables SSL/TLS by default. You can read more about this
at link:native-and-ssl[Using SSL With Native Executables].

== Authentication

This chart illustrates what mechanisms have been verified to be working properly with
the Quarkus Infinispan Client extension.

.Mechanisms
|===
| Name | Verified

| DIGEST-MD5
| [green]*Y*

| PLAIN
| [green]*Y*

| EXTERNAL
| [green]*Y*

| GSSAPI
| [red]*N*

| Custom
| [red]*N*

|===

The guide for configuring these can be found https://infinispan.org/docs/stable/titles/hotrod_java/hotrod_java.html#hotrod_authentication[here].
However you need to configure these through the `hotrod-client.properties` file if using Dependency Injection.

== Additional Features

The Infinispan Client has additional features that were not mentioned here. This means this
feature was not tested in a Quarkus environment and they may or may not work. Please let us
know if you need these added!

== Configuration Reference

include::{generated-dir}/config/quarkus-infinispan-client.adoc[opts=optional, leveloffset=+1]
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.infinispan.protostream.BaseMarshaller;
import org.infinispan.protostream.EnumMarshaller;
import org.infinispan.protostream.FileDescriptorSource;
import org.infinispan.protostream.GeneratedSchema;
import org.infinispan.protostream.MessageMarshaller;
import org.infinispan.protostream.RawProtobufMarshaller;
import org.infinispan.protostream.SerializationContextInitializer;
Expand Down Expand Up @@ -149,6 +150,9 @@ InfinispanPropertiesBuildItem setup(ApplicationArchivesBuildItem applicationArch
InfinispanClientProducer.handleProtoStreamRequirements(properties);
Collection<ClassInfo> initializerClasses = index.getAllKnownImplementors(DotName.createSimple(
SerializationContextInitializer.class.getName()));
initializerClasses
karesti marked this conversation as resolved.
Show resolved Hide resolved
.addAll(index.getAllKnownImplementors(DotName.createSimple(GeneratedSchema.class.getName())));

Set<SerializationContextInitializer> initializers = new HashSet<>(initializerClasses.size());
for (ClassInfo ci : initializerClasses) {
Class<?> initializerClass = Thread.currentThread().getContextClassLoader().loadClass(ci.toString());
Expand Down
4 changes: 4 additions & 0 deletions extensions/infinispan-client/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.infinispan.protostream</groupId>
<artifactId>protostream-types</artifactId>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,41 +56,40 @@ public class InfinispanClientProducer {

private void initialize() {
log.debug("Initializing CacheManager");
Configuration conf;
if (properties == null) {
// We already loaded and it wasn't present - so use an empty config
conf = new ConfigurationBuilder().build();
} else {
conf = builderFromProperties(properties).build();
// We already loaded and it wasn't present - so don't initialize the cache manager
return;
}
cacheManager = new RemoteCacheManager(conf);

// TODO: do we want to automatically register all the proto file definitions?
RemoteCache<String, String> protobufMetadataCache = null;

Set<SerializationContextInitializer> initializers = (Set) properties.remove(PROTOBUF_INITIALIZERS);
if (initializers != null) {
for (SerializationContextInitializer initializer : initializers) {
if (protobufMetadataCache == null) {
protobufMetadataCache = cacheManager.getCache(
ProtobufMetadataManagerConstants.PROTOBUF_METADATA_CACHE_NAME);
}
protobufMetadataCache.put(initializer.getProtoFileName(), initializer.getProtoFile());
}
}
Configuration conf = builderFromProperties(properties).build();
cacheManager = new RemoteCacheManager(conf);

for (Map.Entry<Object, Object> property : properties.entrySet()) {
Object key = property.getKey();
if (key instanceof String) {
String keyString = (String) key;
if (keyString.startsWith(InfinispanClientProducer.PROTOBUF_FILE_PREFIX)) {
String fileName = keyString.substring(InfinispanClientProducer.PROTOBUF_FILE_PREFIX.length());
String fileContents = (String) property.getValue();
InfinispanClientRuntimeConfig infinispanClientRuntimeConfig = this.infinispanClientRuntimeConfig.get();
if (infinispanClientRuntimeConfig.useSchemaRegistration.orElse(Boolean.TRUE)) {
RemoteCache<String, String> protobufMetadataCache = null;
Set<SerializationContextInitializer> initializers = (Set) properties.remove(PROTOBUF_INITIALIZERS);
if (initializers != null) {
for (SerializationContextInitializer initializer : initializers) {
if (protobufMetadataCache == null) {
protobufMetadataCache = cacheManager.getCache(
ProtobufMetadataManagerConstants.PROTOBUF_METADATA_CACHE_NAME);
}
protobufMetadataCache.put(fileName, fileContents);
protobufMetadataCache.put(initializer.getProtoFileName(), initializer.getProtoFile());
}
}
for (Map.Entry<Object, Object> property : properties.entrySet()) {
Object key = property.getKey();
if (key instanceof String) {
String keyString = (String) key;
if (keyString.startsWith(InfinispanClientProducer.PROTOBUF_FILE_PREFIX)) {
String fileName = keyString.substring(InfinispanClientProducer.PROTOBUF_FILE_PREFIX.length());
String fileContents = (String) property.getValue();
if (protobufMetadataCache == null) {
protobufMetadataCache = cacheManager.getCache(
ProtobufMetadataManagerConstants.PROTOBUF_METADATA_CACHE_NAME);
}
protobufMetadataCache.put(fileName, fileContents);
}
}
}
}
Expand Down