Skip to content

Commit 4fbc1a0

Browse files
committed
Migrate to Spring Boot 3.x and Java 17
Removed Graal Native Agent since it Spring 6 now has improved support for native hints
1 parent f2b5d88 commit 4fbc1a0

File tree

13 files changed

+117
-135
lines changed

13 files changed

+117
-135
lines changed

README.md

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
## Multi-instance Reactive Chat App using Spring Boot WebFlux and Redis Pub/Sub
66

7-
Scalable Java 11 Spring Boot WebFlux Chat Application to demonstrate use of Reactive Redis [Pub/Sub] using
7+
Scalable Java 17 Spring Boot 3.x WebFlux Chat Application to demonstrate use of Reactive Redis [Pub/Sub] using
88
Reactive [WebSocket Handler], without using any external Message Broker like RabbitMQ to sync messages between different
99
instances.
1010

@@ -16,7 +16,7 @@ Both JVM based application and [Graal Native Image] is supported.
1616
>1. [Spring-Boot 2.3: Java-11 version](https://github.com/RawSanj/spring-redis-websocket/tree/spring-boot-web-2.3)
1717
>2. [Spring-Boot 1.5: Java-8 version](https://github.com/RawSanj/spring-redis-websocket/tree/spring-boot-1.5.x)
1818
19-
> The reactive spring-boot 2.x based spring-redis-websocket application can be found in below:
19+
> The older reactive spring-boot 2.x (java 11) based spring-redis-websocket application can be found in below:
2020
>1. [Spring-Boot 2.4.6: Java-11 Reactive JVM & Graal Native version](https://github.com/RawSanj/spring-redis-websocket/tree/spring-boot-webflux-graal-native-2.4.6)
2121
>2. [Spring-Boot 2.5.2: Java-11 Reactive JVM & Graal Native version](https://github.com/RawSanj/spring-redis-websocket/tree/spring-boot-webflux-graal-native-2.5.2)
2222
@@ -42,11 +42,10 @@ This application uses Spring Data Redis APIs which doesn't have default Graal hi
4242
Hence, this application is configured to use GraalVM native image tracing agent allows intercepting reflection, resources or proxy usage on the JVM by running simple Integration Tests which requires Redis.
4343

4444
1. To run integration test which uses [Redis TestContainers](https://www.testcontainers.org/supported_docker_environment) so [Docker] should be configured properly to run [Testcontainers]
45-
2. You also need to install [GraalVM JDK](https://github.com/graalvm/graalvm-ce-builds/releases/tag/vm-21.0.0.2) and [native-image](https://www.graalvm.org/reference-manual/native-image) component:
45+
2. You also need to install [GraalVM JDK](https://github.com/graalvm/graalvm-ce-builds/releases/tag/vm-22.3.0) and [native-image](https://www.graalvm.org/reference-manual/native-image) component:
4646
```sh
47-
$ sdk install java 21.1.0.r11-grl # Using [SDKMAN](https://sdkman.io/jdks) install GraalVM distribution of JDK
48-
49-
$ gu install native-image # Then install [native-image](https://www.graalvm.org/reference-manual/native-image) component
47+
$ sdk install java 22.3.r17-nik # Using [SDKMAN](https://sdkman.io/jdks) install GraalVM distribution of JDK
48+
$ sdk use java 22.3.r17-nik
5049
```
5150

5251
##### Clone repo:
@@ -71,7 +70,7 @@ Build and run the **spring-redis-websocket** native image:
7170
```sh
7271
$ cd spring-redis-websocket
7372

74-
$ mvn -Pnative clean package -DskipNativeImage=false
73+
$ mvn -Pnative clean package
7574

7675
$ target/spring-redis-websocket # run the executable binary
7776
```
@@ -103,9 +102,9 @@ $ mvn -Pnative clean spring-boot:build-image
103102
Run docker image:
104103

105104
```sh
106-
$ docker run -d -p 8080:8080 rawsanj/spring-redis-websocket:2.5.2-webflux # JVM based Docker Image
105+
$ docker run -d -p 8080:8080 rawsanj/spring-redis-websocket:3.0.1-webflux # JVM based Docker Image
107106

108-
$ docker run -d -p 8080:8080 rawsanj/spring-redis-websocket:2.5.2-native # Graal Native Image based Docker Image
107+
$ docker run -d -p 8080:8080 rawsanj/spring-redis-websocket:3.0.1-native # Graal Native Image based Docker Image
109108
```
110109

111110
#### Run multiple instances using docker-compose locally

build.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
#!/usr/bin/env bash
22

3-
# Runs Tests and Creates Docker Image based on Spring Graal Native Image - Uses TestContainers to start the Redis Container required for Test (Docker is required)
3+
# Runs Tests and Creates Docker Image based on Spring Graal Native Image
44
echo '**************************************************************'
55
echo 'Creating Docker Image based on Spring Graal Native Image'
66
mvn -Pnative clean spring-boot:build-image
77
echo 'Docker Image based on Spring Graal Native Image Creation Completed!'
88
echo '**************************************************************'
99

10-
# Creates Docker Image - Skips Integration Tests and hence Docker is not required
10+
# Creates Docker Image
1111
echo '**************************************************************'
1212
echo 'Creating Docker Image based on JVM'
1313
mvn clean spring-boot:build-image
1414
echo 'Docker Image based on JVM Creation Completed!'
1515
echo '**************************************************************'
1616

17-
# Runs Tests and Creates Graal Native Image - Uses TestContainers to start the Redis Container required for Test (Docker is required)
17+
# Runs Tests and Creates Graal Native Image
1818
echo '**************************************************************'
1919
echo 'Creating Graal Native Image'
2020
mvn -Pnative clean package -DskipNativeImage=false

pom.xml

Lines changed: 25 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,24 @@
55

66
<groupId>com.github.rawsanj</groupId>
77
<artifactId>spring-redis-websocket</artifactId>
8-
<version>2.5.2</version>
8+
<version>3.0.1</version>
99
<packaging>jar</packaging>
1010

1111
<name>spring-redis-websocket</name>
12-
<description>Spring Boot Project for WebSocket Notification/Chat using Redis Pub/Sub - Java 11</description>
12+
<description>Spring Boot 3.x - Project for WebSocket Notification/Chat using Redis Pub/Sub - Java 17</description>
1313

1414
<parent>
1515
<groupId>org.springframework.boot</groupId>
1616
<artifactId>spring-boot-starter-parent</artifactId>
17-
<version>2.5.2</version>
17+
<version>3.0.1</version>
1818
<relativePath/> <!-- lookup parent from repository -->
1919
</parent>
2020

2121
<properties>
22-
<java.version>11</java.version>
22+
<java.version>17</java.version>
2323
<registry.image.name>rawsanj/spring-redis-websocket</registry.image.name>
2424
<skipNativeImage>true</skipNativeImage>
25-
<testcontainers.version>1.15.3</testcontainers.version>
25+
<testcontainers.version>1.17.6</testcontainers.version>
2626
</properties>
2727

2828
<dependencies>
@@ -49,11 +49,6 @@
4949
<optional>true</optional>
5050
</dependency>
5151

52-
<dependency>
53-
<groupId>org.springframework</groupId>
54-
<artifactId>spring-context-indexer</artifactId>
55-
</dependency>
56-
5752
<dependency>
5853
<groupId>org.springframework.boot</groupId>
5954
<artifactId>spring-boot-starter-test</artifactId>
@@ -86,27 +81,6 @@
8681
</dependencies>
8782
</dependencyManagement>
8883

89-
<repositories>
90-
<repository>
91-
<id>spring-releases</id>
92-
<name>Spring Releases</name>
93-
<url>https://repo.spring.io/release</url>
94-
<snapshots>
95-
<enabled>false</enabled>
96-
</snapshots>
97-
</repository>
98-
</repositories>
99-
<pluginRepositories>
100-
<pluginRepository>
101-
<id>spring-releases</id>
102-
<name>Spring Releases</name>
103-
<url>https://repo.spring.io/release</url>
104-
<snapshots>
105-
<enabled>false</enabled>
106-
</snapshots>
107-
</pluginRepository>
108-
</pluginRepositories>
109-
11084
<build>
11185
<plugins>
11286
<plugin>
@@ -117,6 +91,14 @@
11791
<name>${registry.image.name}:${project.version}-webflux</name>
11892
</image>
11993
</configuration>
94+
<executions>
95+
<execution>
96+
<id>process-aot</id>
97+
<goals>
98+
<goal>process-aot</goal>
99+
</goals>
100+
</execution>
101+
</executions>
120102
</plugin>
121103
</plugins>
122104
</build>
@@ -127,91 +109,38 @@
127109

128110
<properties>
129111
<skipNativeImage>true</skipNativeImage>
130-
<spring-native.version>0.10.1</spring-native.version>
131112
</properties>
132113

133-
<dependencies>
134-
<!--Graal Native Image Dependencies-->
135-
<dependency>
136-
<groupId>org.springframework.experimental</groupId>
137-
<artifactId>spring-native</artifactId>
138-
<version>${spring-native.version}</version>
139-
</dependency>
140-
</dependencies>
141-
142114
<build>
143115
<plugins>
144116
<plugin>
145-
<groupId>org.apache.maven.plugins</groupId>
146-
<artifactId>maven-surefire-plugin</artifactId>
147-
<configuration>
148-
<argLine>
149-
-DspringAot=true -agentlib:native-image-agent=access-filter-file=target/classes/access-filter.json,config-merge-dir=target/classes/META-INF/native-image
150-
</argLine>
151-
<includes>
152-
<!-- <include>**/SpringRedisWebSocketApplicationIT.java</include>-->
153-
</includes>
154-
</configuration>
155-
</plugin>
156-
<plugin>
157-
<groupId>org.graalvm.buildtools</groupId>
158-
<artifactId>native-maven-plugin</artifactId>
159-
<version>0.9.0</version>
117+
<groupId>org.springframework.boot</groupId>
118+
<artifactId>spring-boot-maven-plugin</artifactId>
160119
<configuration>
161-
<mainClass>com.github.rawsanj.SpringRedisWebSocketApplication</mainClass>
162-
<imageName>${project.artifactId}</imageName>
163-
<buildArgs>
164-
-DspringAot=true
165-
-Dspring.xml.ignore=true
166-
-Dspring.spel.ignore=true
167-
-Dspring.native.remove-yaml-support=true
168-
</buildArgs>
169-
<skip>${skipNativeImage}</skip>
120+
<image>
121+
<name>${registry.image.name}:${project.version}-native</name>
122+
</image>
170123
</configuration>
171124
<executions>
172125
<execution>
126+
<id>process-aot</id>
173127
<goals>
174-
<goal>build</goal>
128+
<goal>process-aot</goal>
175129
</goals>
176-
<phase>package</phase>
177130
</execution>
178131
</executions>
179132
</plugin>
180133
<plugin>
181-
<groupId>org.springframework.boot</groupId>
182-
<artifactId>spring-boot-maven-plugin</artifactId>
183-
<version>${project.parent.version}</version>
134+
<groupId>org.graalvm.buildtools</groupId>
135+
<artifactId>native-maven-plugin</artifactId>
184136
<configuration>
185-
<image>
186-
<name>${registry.image.name}:${project.version}-native</name>
187-
<builder>paketobuildpacks/builder:tiny</builder>
188-
<env>
189-
<BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
190-
<BP_BOOT_NATIVE_IMAGE_BUILD_ARGUMENTS>
191-
-DspringAot=true
192-
-Dspring.xml.ignore=true
193-
-Dspring.spel.ignore=true
194-
-Dspring.native.remove-yaml-support=true
195-
</BP_BOOT_NATIVE_IMAGE_BUILD_ARGUMENTS>
196-
</env>
197-
</image>
137+
<skip>${skipNativeImage}</skip>
198138
</configuration>
199-
</plugin>
200-
<plugin>
201-
<groupId>org.springframework.experimental</groupId>
202-
<artifactId>spring-aot-maven-plugin</artifactId>
203-
<version>${spring-native.version}</version>
204139
<executions>
205140
<execution>
206-
<id>test-generate</id>
207-
<goals>
208-
<goal>test-generate</goal>
209-
</goals>
210-
</execution>
211-
<execution>
212-
<id>generate</id>
141+
<id>build-native-binary</id>
213142
<goals>
214-
<goal>generate</goal>
143+
<goal>compile-no-fork</goal>
215144
</goals>
216145
</execution>
217146
</executions>

src/main/java/com/github/rawsanj/config/ChatConstants.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ public class ChatConstants {
55
public static final String MESSAGE_TOPIC = "MESSAGE";
66
public static final String MESSAGE_COUNTER_KEY = "TOTAL_MESSAGE_COUNT";
77
public static final String ACTIVE_USER_KEY = "ACTIVE_USER_COUNT";
8-
public static final String WEBSOCKET_MESSAGE_MAPPING = "/redis-chat";
8+
public static final String WEBSOCKET_MESSAGE_MAPPING = "/redis-chat/**";
99

1010
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.github.rawsanj.config;
2+
3+
import com.github.rawsanj.model.ChatMessage;
4+
import com.github.rawsanj.model.Message;
5+
import org.springframework.aot.hint.RuntimeHints;
6+
import org.springframework.aot.hint.RuntimeHintsRegistrar;
7+
import org.springframework.context.annotation.ImportRuntimeHints;
8+
import org.springframework.stereotype.Component;
9+
10+
@Component
11+
@ImportRuntimeHints(RuntimeHintsConfig.SerdeRuntimeHints.class)
12+
public class RuntimeHintsConfig {
13+
14+
static class SerdeRuntimeHints implements RuntimeHintsRegistrar {
15+
16+
@Override
17+
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
18+
hints.serialization()
19+
.registerType(ChatMessage.class)
20+
.registerType(Message.class);
21+
}
22+
}
23+
}
Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
package com.github.rawsanj.handler;
22

3-
import com.fasterxml.jackson.annotation.JsonAutoDetect;
43
import com.github.rawsanj.messaging.RedisChatMessagePublisher;
5-
import lombok.AllArgsConstructor;
6-
import lombok.Data;
7-
import lombok.NoArgsConstructor;
4+
import com.github.rawsanj.model.Message;
85
import org.springframework.beans.factory.annotation.Value;
96
import org.springframework.context.annotation.Bean;
107
import org.springframework.context.annotation.Configuration;
@@ -18,7 +15,7 @@
1815
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
1916
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
2017

21-
@Configuration(proxyBeanMethods=false)
18+
@Configuration(proxyBeanMethods = false)
2219
public class WebHttpHandler {
2320

2421
@Bean
@@ -29,12 +26,4 @@ public RouterFunction<ServerResponse> htmlRouter(@Value("classpath:/static/index
2926
.flatMap(aLong -> ServerResponse.ok().bodyValue(new Message("Message Sent Successfully!."))));
3027
}
3128

32-
@Data
33-
@AllArgsConstructor
34-
@NoArgsConstructor
35-
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
36-
public static class Message {
37-
private String message;
38-
}
39-
4029
}

src/main/java/com/github/rawsanj/model/ChatMessage.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
import lombok.Data;
66
import lombok.NoArgsConstructor;
77

8+
import java.io.Serializable;
9+
810
@Data
911
@AllArgsConstructor
1012
@NoArgsConstructor
1113
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
12-
public class ChatMessage {
14+
public class ChatMessage implements Serializable {
1315

1416
private Integer id;
1517
private String message;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.github.rawsanj.model;
2+
3+
import com.fasterxml.jackson.annotation.JsonAutoDetect;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
8+
import java.io.Serializable;
9+
10+
@Data
11+
@AllArgsConstructor
12+
@NoArgsConstructor
13+
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
14+
public class Message implements Serializable {
15+
private String message;
16+
}

src/main/resources/application.properties

Lines changed: 0 additions & 3 deletions
This file was deleted.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
spring:
2+
data.redis:
3+
host: localhost
4+
password: SuperSecretRedisPassword
5+
port: 6379
6+
application:
7+
name: spring-redis-reactive-websocket

0 commit comments

Comments
 (0)