How to connect your Quarkus application to Infinispan
Learn how to connect a Quarkus 3.x application to an Infinispan server using the Hot Rod client protocol, RESTEasy Reactive, and Jakarta EE standards. This guide covers ProtoStream schema generation for object marshalling, CRUD REST operations, and embedding Infinispan cache instances directly in Quarkus.
Infinispan is a distributed in-memory key/value data grid. An in-memory data grid is a form of middleware that stores sets of data for use in one or more applications, primarily in memory. There are different clients available to connect to a remote or embedded Infinispan server. In this tutorial, we will learn how to connect to Infinispan using the updated Quarkus 3.x extension and Jakarta EE specifications.
Starting Infinispan
For the purpose of this tutorial, we will be running a local Infinispan server with the following cache definition in infinispan.xml:
<cache-container default-cache="local">
<transport cluster="${infinispan.cluster.name}" stack="${infinispan.cluster.stack:tcp}" node-name="${infinispan.node.name:}"/>
<local-cache name="local"/>
<invalidation-cache name="invalidation" mode="SYNC"/>
<replicated-cache name="repl-sync" mode="SYNC"/>
<distributed-cache name="dist-sync" mode="SYNC"/>
</cache-container>
From the bin folder of Infinispan, run:
./server.sh
As an alternative, you can also run Infinispan using Docker as follows:
docker run -it -p 11222:11222 -e USER="admin" -e PASS="password" infinispan/server:latest
Creating the Quarkus Infinispan project
In order to create the Quarkus 3.x application, we will need the following set of dependencies in our pom.xml file, leveraging RESTEasy Reactive and the Hot Rod Infinispan client extension:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-infinispan-client</artifactId>
</dependency>
RESTEasy Reactive with Jackson handles high-performance HTTP endpoints, while the quarkus-infinispan-client dependency connects Quarkus to the remote Infinispan grid via Hot Rod protocol.
Here is our main Application listener class using Jakarta EE annotations:
package com.mastertheboss.infinispan;
import io.quarkus.infinispan.client.Remote;
import io.quarkus.runtime.StartupEvent;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;
import org.infinispan.client.hotrod.RemoteCache;
import org.infinispan.client.hotrod.annotation.*;
import org.infinispan.client.hotrod.event.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ApplicationScoped
public class InfinispanClientApp {
private static final Logger LOGGER = LoggerFactory.getLogger("InfinispanClientApp");
@Inject
@Remote("local")
RemoteCache<String, Customer> cache;
void onStart(@Observes StartupEvent ev) {
cache.addClientListener(new EventPrintListener());
Customer c = new Customer("1", "John", "Smith");
cache.put("1", c);
}
@ClientListener
static class EventPrintListener {
@ClientCacheEntryCreated
public void handleCreatedEvent(ClientCacheEntryCreatedEvent e) {
LOGGER.info("Someone has created an entry: " + e);
}
@ClientCacheEntryModified
public void handleModifiedEvent(ClientCacheEntryModifiedEvent e) {
LOGGER.info("Someone has modified an entry: " + e);
}
@ClientCacheEntryRemoved
public void handleRemovedEvent(ClientCacheEntryRemovedEvent e) {
LOGGER.info("Someone has removed an entry: " + e);
}
}
}
When the application bootstraps, we connect to the RemoteCache ("local"), attach a Hot Rod ClientListener to it, and insert an initial entry.
To marshal and unmarshal custom Java objects like Customer across Infinispan endpoints, we utilize ProtoStream serialization with @ProtoField and @ProtoFactory annotations:
package com.mastertheboss.infinispan;
import org.infinispan.protostream.annotations.ProtoFactory;
import org.infinispan.protostream.annotations.ProtoField;
import java.util.Objects;
public class Customer {
private String id;
private String name;
private String surname;
@ProtoField(number = 1)
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@ProtoFactory
public Customer(String id, String name, String surname) {
this.id = id;
this.name = name;
this.surname = surname;
}
@ProtoField(number = 2)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ProtoField(number = 3)
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
@Override
public String toString() {
return "Customer{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", surname='" + surname + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Customer)) return false;
Customer customer = (Customer) o;
return Objects.equals(id, customer.id) &&
Objects.equals(name, customer.name) &&
Objects.equals(surname, customer.surname);
}
@Override
public int hashCode() {
return Objects.hash(id, name, surname);
}
public Customer() {
}
}
Next, we declare a SerializationContextInitializer interface decorated with @AutoProtoSchemaBuilder to generate the Protocol Buffer schemas automatically during compile time:
package com.mastertheboss.infinispan;
import org.infinispan.protostream.SerializationContextInitializer;
import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder;
@AutoProtoSchemaBuilder(includeClasses = { Customer.class }, schemaPackageName = "customer_list")
public interface CustomerContextInitializer extends SerializationContextInitializer {
}
Now, we create a reactive REST Endpoint using Jakarta REST annotations to execute CRUD operations against our remote cache:
package com.mastertheboss.infinispan;
import io.quarkus.infinispan.client.Remote;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.infinispan.client.hotrod.RemoteCache;
@Path("/infinispan")
@ApplicationScoped
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class InfinispanEndpoint {
@Inject
@Remote("local")
RemoteCache<String, Customer> cache;
@GET
@Path("/{customerId}")
public Response get(@PathParam("customerId") String id) {
Customer customer = cache.get(id);
System.out.println("Got customer " + customer);
if (customer == null) {
return Response.status(Response.Status.NOT_FOUND).build();
}
return Response.ok(customer).build();
}
@POST
public Response create(Customer customer) {
cache.put(customer.getId(), customer);
System.out.println("Created customer " + customer);
return Response.status(Response.Status.CREATED).entity(customer).build();
}
@PUT
public Response update(Customer customer) {
cache.put(customer.getId(), customer);
System.out.println("Updated customer " + customer);
return Response.status(Response.Status.ACCEPTED).entity(customer).build();
}
@DELETE
@Path("/{customerId}")
public Response delete(@PathParam("customerId") String id) {
cache.remove(id);
System.out.println("Deleted customer " + id);
return Response.noContent().build();
}
}
Finally, configure the host connection details and credentials inside src/main/resources/application.properties:
quarkus.infinispan-client.hosts=localhost:11222
quarkus.infinispan-client.username=admin
quarkus.infinispan-client.password=password
Running the Quarkus application
Start your application using the Quarkus Dev mode:
mvn clean quarkus:dev
Test the endpoints via curl. To retrieve the customer initialized at startup with ID "1":
curl http://localhost:8080/infinispan/1
{"id":"1","name":"John","surname":"Smith"}
To create a new Customer:
curl -d '{"id":"2", "name":"Clark","surname":"Kent"}' -H "Content-Type: application/json" -X POST http://localhost:8080/infinispan
To update an existing Customer:
curl -d '{"id":"2", "name":"Peter","surname":"Parker"}' -H "Content-Type: application/json" -X PUT http://localhost:8080/infinispan
And to remove a Customer from the cluster:
curl -X DELETE http://localhost:8080/infinispan/2
Source code for this tutorial: https://github.com/fmarchioni/mastertheboss/tree/master/quarkus/infinispan-demo
Embedding Infinispan in Quarkus applications
It is also possible to bootstrap an Infinispan cache container inside a Quarkus application using the embedded API. To do so, add the following extension to your pom.xml:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-infinispan-embedded</artifactId>
</dependency>
With this dependency added, inject the org.infinispan.manager.EmbeddedCacheManager directly into your Jakarta EE beans:
@Inject
EmbeddedCacheManager emc;
This provides a local embedded cache container. If you wish to build a multi-node cluster programmatically, configure it via Java API or an external XML configuration file like dist.xml:
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.clustering().cacheMode(CacheMode.DIST_SYNC);
List<EmbeddedCacheManager> managers = new ArrayList<>(3);
try {
System.setProperty("jgroups.tcp.address", "127.0.0.1");
for (int i = 0; i < 3; i++) {
EmbeddedCacheManager ecm = new DefaultCacheManager(
Paths.get("src", "main", "resources", "dist.xml").toString());
ecm.start();
managers.add(ecm);
// Start the default cache
ecm.getCache();
}
} catch (Exception e) {
e.printStackTrace();
}
Here is an example dist.xml file using TCPPING discovery for cluster formation:
<infinispan
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:infinispan:config:14.0 http://www.infinispan.org/schemas/infinispan-config-14.0.xsd
urn:org:jgroups http://www.jgroups.org/schema/jgroups-5.2.xsd"
xmlns="urn:infinispan:config:14.0"
xmlns:ispn="urn:infinispan:config:14.0">
<jgroups>
<stack name="tcpping" extends="tcp">
<MPING ispn:stack.combine="REMOVE" xmlns="urn:org:jgroups"/>
<TCPPING async_discovery="true"
initial_hosts="${initial_hosts:127.0.0.1[7800],127.0.0.1[7801]}"
port_range="0" ispn:stack.combine="INSERT_AFTER" ispn:stack.position="TCP" xmlns="urn:org:jgroups"/>
</stack>
</jgroups>
<cache-container name="test" default-cache="dist">
<transport cluster="test" stack="tcpping"/>
<distributed-cache name="dist">
<memory>
<max-size object-size="21000"/>
</memory>
</distributed-cache>
</cache-container>
</infinispan>
Frequently Asked Questions (FAQs)
How does Quarkus 3.x handle ProtoStream schema registration for Infinispan?
In Quarkus 3.x, schema registration is automated during build time using the @AutoProtoSchemaBuilder annotation on an interface extending SerializationContextInitializer. Quarkus processes ProtoStream annotations, generates the Protobuf schema files, and registers them automatically with the Hot Rod client without requiring manually written .proto files.
What is the difference between Remote and Embedded Infinispan modes in Quarkus?
In Remote mode (quarkus-infinispan-client), Quarkus connects to an external Infinispan cluster using the lightweight Hot Rod client protocol, separating application lifecycle from cache storage. In Embedded mode (quarkus-infinispan-embedded), the Infinispan cache instance runs directly inside the JVM running Quarkus, sharing compute and memory resources with the application.
Can I use reactive non-blocking methods with RemoteCache in Quarkus?
Yes, Infinispan's Hot Rod client provides asynchronous methods (such as putAsync() and getAsync()) that return Java CompletableFuture instances. These can easily be transformed into Mutiny types like Uni or Multi in Quarkus RESTEasy Reactive endpoints to build completely non-blocking reactive pipelines.
Recommended Articles
Integrate PrimeFaces in Quarkus Applications with Jakarta EE 10
Learn how to integrate PrimeFaces library into Quarkus applications for Jakarta EE 10 environments.
A Comprehensive Comparison of WildFly Application Server and Quarkus Framework in Enterprise Java
Explore the features and use cases of WildFly and Quarkus for robust Java applications. #WildFly #Quarkus #EnterpriseJava
Mastering Quarkus Migrations: A Comprehensive Guide
Learn how to migrate your Quarkus applications with ease using our comprehensive migration guidelines and tools.
Create Standalone Quarkus Applications and Powerful Scripts Using JBang & Quarkus Command Mode
Learn how to develop standalone Quarkus applications with JBang and powerful scripts using Quarkus Command Mode. #Quarkus #Java #Microservices #CloudNative