Skip to content

Commit 1971e40

Browse files
Katia Arestitristantarrant
authored andcommitted
Adds tutorial for Spring Boot auto registration
1 parent 5158503 commit 1971e40

18 files changed

Lines changed: 569 additions & 2 deletions

File tree

integrations/spring-boot/cache-remote-reactor/src/main/java/org/infinispan/tutorial/simple/spring/remote/Reader.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ public Reader(BasqueNamesRepository repository, RemoteCacheManager remoteCacheMa
2222
this.repository = repository;
2323
random = new Random();
2424
this.remoteCacheManager = remoteCacheManager;
25-
// Upload the generated schema in the server
25+
// Upload the generated schema in the server.
26+
// Since 16.2, schemas are auto-discovered and uploaded by the Spring Boot starter.
27+
// To disable auto-upload, set infinispan.remote.use-schema-registration=false
2628
remoteCacheManager.administration().schemas().createOrUpdate(new BasquesNamesSchemaBuilderImpl());
2729
}
2830

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
# tag::config[]
22
infinispan.remote.server-list=127.0.0.1:11222
33
infinispan.remote.reactive=true
4+
5+
# Protobuf schemas are automatically discovered and uploaded to the server at startup.
6+
# To disable this behavior, uncomment the following property:
7+
# infinispan.remote.use-schema-registration=false
48
# end::config[]
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
= Spring Boot Automatic Protobuf Schema Registration
2+
:summary: Build a Spring Boot REST app with automatic Protobuf schema discovery and registration on a remote Infinispan Server
3+
:mode: integration
4+
:topics: spring-boot,protostream,schema
5+
:keywords: spring-boot,protobuf,protostream,schema,auto-registration,rest,full-text,indexed
6+
:duration: 10
7+
:source-dir: integrations/spring-boot/cache-remote-schema-auto-registration
8+
9+
== What You Will Learn
10+
11+
How to use the Infinispan Spring Boot starter with automatic Protobuf schema registration.
12+
Starting with Infinispan 16.2, the starter automatically discovers `GeneratedSchema` implementations on the classpath, registers them as serialization context initializers, and uploads the schemas to the Infinispan Server at startup.
13+
No manual `schemas().createOrUpdate()` call is needed.
14+
15+
This tutorial builds a REST application that stores indexed characters in a remote cache and performs full-text search queries.
16+
17+
== Prerequisites
18+
19+
* Java 17+
20+
* Spring Boot
21+
* Docker or Podman (optional)
22+
23+
You can start an Infinispan Server manually with Docker or Podman:
24+
25+
[source,shell]
26+
----
27+
docker run -it --rm -p 11222:11222 -e USER=admin -e PASS=password quay.io/infinispan/server:latest
28+
----
29+
30+
TIP: You can replace `docker` with `podman` in the command above if you use Podman.
31+
32+
TIP: If no server is running on `localhost:11222`, the application automatically starts an Infinispan container using Testcontainers.
33+
34+
== Step 1: Add Dependencies
35+
36+
Add the Infinispan Spring Boot remote starter and the ProtoStream annotation processor:
37+
38+
[source,xml]
39+
----
40+
<dependency>
41+
<groupId>org.infinispan</groupId>
42+
<artifactId>infinispan-spring-boot4-starter-remote</artifactId>
43+
</dependency>
44+
<dependency>
45+
<groupId>org.infinispan.protostream</groupId>
46+
<artifactId>protostream-processor</artifactId>
47+
</dependency>
48+
----
49+
50+
== Step 2: Configure the Connection and Cache
51+
52+
In `application.properties`, configure the connection, the marshaller, and the cache definition:
53+
54+
[source,properties]
55+
----
56+
include::src/main/resources/application.properties[tag=config]
57+
----
58+
59+
Key points:
60+
61+
* `infinispan.remote.connection-uri` provides the connection string including credentials.
62+
* `infinispan.remote.cache.characters.configuration-uri` points to a classpath XML file that defines the cache.
63+
* `infinispan.remote.cache.characters.marshaller` is set to `ProtoStreamMarshaller` because the default is `JavaSerializationMarshaller`.
64+
* There is no manual schema registration code. The starter automatically discovers and uploads Protobuf schemas at startup.
65+
* To disable auto schema upload, set `infinispan.remote.use-schema-registration=false`.
66+
67+
== Step 3: Define a Protobuf Entity
68+
69+
Create an indexed Protobuf record with `@Text` fields for full-text search:
70+
71+
[source,java]
72+
----
73+
include::src/main/java/tutorial/spring/infinispan/model/Character.java[tag=entity]
74+
----
75+
76+
== Step 4: Define the Protobuf Schema
77+
78+
Create a `@ProtoSchema` interface that extends `GeneratedSchema`.
79+
The starter will discover this interface automatically on the classpath, register it with the serialization context, and upload the generated `.proto` schema to the server:
80+
81+
[source,java]
82+
----
83+
include::src/main/java/tutorial/spring/infinispan/model/AppSchema.java[tag=schema]
84+
----
85+
86+
== Step 5: Query the Cache
87+
88+
Inject the `RemoteCacheManager` and perform full-text queries using Ickle:
89+
90+
[source,java]
91+
----
92+
include::src/main/java/tutorial/spring/infinispan/service/CharacterSearch.java[tag=service]
93+
----
94+
95+
== Step 6: Expose REST Endpoints
96+
97+
Create a Spring `@RestController` that exposes the search service:
98+
99+
[source,java]
100+
----
101+
include::src/main/java/tutorial/spring/infinispan/CharactersResource.java[tag=resource]
102+
----
103+
104+
== Step 7: Run the Tutorial
105+
106+
[source,bash]
107+
----
108+
mvn spring-boot:run
109+
----
110+
111+
Then query the REST endpoints:
112+
113+
[source,bash]
114+
----
115+
curl http://localhost:8080/characters/0
116+
curl "http://localhost:8080/characters/query?term=Felix"
117+
----
118+
119+
== How Auto Schema Registration Works
120+
121+
The Infinispan Spring Boot starter (16.2+) automatically:
122+
123+
1. Scans your application packages for `GeneratedSchema` implementations using Spring's classpath scanning.
124+
2. Registers them as `SerializationContextInitializer` instances with the `RemoteCacheManager`.
125+
3. Uploads the `.proto` schemas to the Infinispan Server via `RemoteSchemasAdmin.createOrUpdate()`.
126+
127+
This is controlled by the `infinispan.remote.use-schema-registration` property, which defaults to `true`.
128+
129+
NOTE: The auto-discovery scans packages registered with Spring Boot's `AutoConfigurationPackages` and skips internal Infinispan packages (`org.infinispan.*`).
130+
Make sure your application uses a different base package so the scanner picks up your schema.
131+
132+
== What's Next
133+
134+
* link:/tutorials/integrations-spring-boot-cache-remote/[Spring Boot remote caching] for caching with `@Cacheable` and `@CacheEvict`
135+
* link:/tutorials/integrations-quarkus-infinispan-client-example/[Quarkus Infinispan Client] for the same pattern with Quarkus
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<parent>
6+
<relativePath>../../../pom.xml</relativePath>
7+
<version>16.3.0-SNAPSHOT</version>
8+
<groupId>org.infinispan.tutorial.simple</groupId>
9+
<artifactId>infinispan-simple-tutorials</artifactId>
10+
</parent>
11+
12+
<artifactId>infinispan-spring-boot-cache-remote-schema-auto-registration</artifactId>
13+
<name>Infinispan Simple Tutorials: Spring Boot Cache Remote Schema Auto Registration</name>
14+
15+
<dependencyManagement>
16+
<dependencies>
17+
<!-- override spring boot dependency of infinispan -->
18+
<dependency>
19+
<groupId>org.infinispan</groupId>
20+
<artifactId>infinispan-bom</artifactId>
21+
<version>${version.infinispan}</version>
22+
<type>pom</type>
23+
<scope>import</scope>
24+
</dependency>
25+
<dependency>
26+
<groupId>org.springframework.boot</groupId>
27+
<artifactId>spring-boot-starter-parent</artifactId>
28+
<version>${version.spring.boot4}</version>
29+
<type>pom</type>
30+
<scope>import</scope>
31+
</dependency>
32+
<dependency>
33+
<groupId>org.junit</groupId>
34+
<artifactId>junit-bom</artifactId>
35+
<version>${version.junit5}</version>
36+
<type>pom</type>
37+
<scope>import</scope>
38+
</dependency>
39+
</dependencies>
40+
</dependencyManagement>
41+
42+
<dependencies>
43+
<dependency>
44+
<groupId>org.infinispan</groupId>
45+
<artifactId>infinispan-spring-boot4-starter-remote</artifactId>
46+
</dependency>
47+
48+
<dependency>
49+
<groupId>org.infinispan.protostream</groupId>
50+
<artifactId>protostream-processor</artifactId>
51+
<version>${version.protostream}</version>
52+
</dependency>
53+
54+
<dependency>
55+
<groupId>org.springframework.boot</groupId>
56+
<artifactId>spring-boot-starter-web</artifactId>
57+
</dependency>
58+
59+
<dependency>
60+
<groupId>org.springframework.boot</groupId>
61+
<artifactId>spring-boot-starter-actuator</artifactId>
62+
</dependency>
63+
64+
<dependency>
65+
<groupId>org.springframework.boot</groupId>
66+
<artifactId>spring-boot-starter-test</artifactId>
67+
<scope>test</scope>
68+
</dependency>
69+
<dependency>
70+
<groupId>org.testcontainers</groupId>
71+
<artifactId>testcontainers-junit-jupiter</artifactId>
72+
<scope>test</scope>
73+
</dependency>
74+
<dependency>
75+
<groupId>org.infinispan</groupId>
76+
<artifactId>testcontainers-infinispan</artifactId>
77+
</dependency>
78+
<dependency>
79+
<groupId>org.infinispan.tutorial.simple</groupId>
80+
<artifactId>connect-to-infinispan-server</artifactId>
81+
<version>${project.version}</version>
82+
</dependency>
83+
</dependencies>
84+
85+
<build>
86+
<plugins>
87+
<plugin>
88+
<groupId>org.springframework.boot</groupId>
89+
<artifactId>spring-boot-maven-plugin</artifactId>
90+
<version>${version.spring.boot4}</version>
91+
</plugin>
92+
</plugins>
93+
</build>
94+
95+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package tutorial.spring.infinispan;
2+
3+
import org.infinispan.tutorial.simple.connect.TutorialsConnectorHelper;
4+
import org.springframework.boot.SpringApplication;
5+
import org.springframework.boot.autoconfigure.SpringBootApplication;
6+
7+
@SpringBootApplication
8+
public class CharactersApp {
9+
10+
public static void main(String[] args) {
11+
TutorialsConnectorHelper.startInfinispanContainer();
12+
if (TutorialsConnectorHelper.isContainerStarted()) {
13+
String connectionUri = String.format("hotrod://%s:%s@127.0.0.1:%d",
14+
TutorialsConnectorHelper.USER,
15+
TutorialsConnectorHelper.PASSWORD,
16+
TutorialsConnectorHelper.INFINISPAN_CONTAINER.getFirstMappedPort());
17+
SpringApplication.run(CharactersApp.class,
18+
"--infinispan.remote.connection-uri=" + connectionUri);
19+
} else {
20+
SpringApplication.run(CharactersApp.class, args);
21+
}
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package tutorial.spring.infinispan;
2+
3+
import java.util.Collections;
4+
import java.util.Set;
5+
6+
import org.springframework.http.HttpStatus;
7+
import org.springframework.web.bind.annotation.GetMapping;
8+
import org.springframework.web.bind.annotation.PathVariable;
9+
import org.springframework.web.bind.annotation.RequestMapping;
10+
import org.springframework.web.bind.annotation.RequestParam;
11+
import org.springframework.web.bind.annotation.RestController;
12+
import org.springframework.web.server.ResponseStatusException;
13+
14+
import tutorial.spring.infinispan.model.Character;
15+
import tutorial.spring.infinispan.service.CharacterSearch;
16+
17+
// tag::resource[]
18+
@RestController
19+
@RequestMapping("/characters")
20+
public class CharactersResource {
21+
22+
private final CharacterSearch searchService;
23+
24+
public CharactersResource(CharacterSearch searchService) {
25+
this.searchService = searchService;
26+
}
27+
28+
@GetMapping("/{id}")
29+
public Character byId(@PathVariable("id") String id) {
30+
Character character = searchService.getById(id);
31+
if (character == null) {
32+
throw new ResponseStatusException(HttpStatus.NOT_FOUND,
33+
"Character with id of " + id + " does not exist.");
34+
}
35+
return character;
36+
}
37+
38+
@GetMapping("/query")
39+
public Set<String> searchCharacter(@RequestParam(value = "term", required = false) String term) {
40+
if (term == null) {
41+
return Collections.emptySet();
42+
}
43+
return searchService.search(term);
44+
}
45+
}
46+
// end::resource[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package tutorial.spring.infinispan.model;
2+
3+
import org.infinispan.protostream.GeneratedSchema;
4+
import org.infinispan.protostream.annotations.ProtoSchema;
5+
6+
// tag::schema[]
7+
@ProtoSchema(schemaPackageName = "tutorial",
8+
includeClasses = {Character.class, Archetype.class},
9+
dependsOn = {
10+
org.infinispan.protostream.types.java.CommonTypes.class
11+
})
12+
public interface AppSchema extends GeneratedSchema {
13+
}
14+
// end::schema[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package tutorial.spring.infinispan.model;
2+
3+
import org.infinispan.protostream.annotations.Proto;
4+
5+
@Proto
6+
public enum Archetype {
7+
HERO,
8+
MENTOR,
9+
HERALD,
10+
SHADOW,
11+
ALLY,
12+
TRICKSTER,
13+
SHAPESHIFTER
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package tutorial.spring.infinispan.model;
2+
3+
import java.util.UUID;
4+
5+
import org.infinispan.api.annotations.indexing.Indexed;
6+
import org.infinispan.api.annotations.indexing.Text;
7+
import org.infinispan.protostream.annotations.Proto;
8+
9+
// tag::entity[]
10+
@Proto
11+
@Indexed
12+
public record Character(UUID id, @Text String name, @Text String bio, Archetype archetype) {
13+
14+
}
15+
// end::entity[]

0 commit comments

Comments
 (0)