diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 562a109..dd24251 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- java: [ '8' ]
+ java: [ '17', '23' ]
architecture: [ 'x64' ]
name: Build with JDK ${{ matrix.java }} on ${{ matrix.architecture }}
steps:
@@ -30,5 +30,5 @@ jobs:
restore-keys: ${{ runner.os }}-maven-
- name: Run Tests
- run: mvn verify -Ptests
+ run: mvn verify
diff --git a/.run/Template JUnit.run.xml b/.run/Template JUnit.run.xml
new file mode 100644
index 0000000..2fdad57
--- /dev/null
+++ b/.run/Template JUnit.run.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/modules/ROOT/pages/hazelcast-embedded-springboot.adoc b/docs/modules/ROOT/pages/hazelcast-embedded-springboot.adoc
index 31d8771..49b7085 100644
--- a/docs/modules/ROOT/pages/hazelcast-embedded-springboot.adoc
+++ b/docs/modules/ROOT/pages/hazelcast-embedded-springboot.adoc
@@ -30,58 +30,75 @@ If Hazelcast is on the classpath and a suitable configuration is found, Spring B
include::ROOT:example$hazelcast-embedded-springboot/pom.xml[tag=hazelcast-dep]
----
-Hazelcast configuration (`hazelcast.yaml`) is placed in the `src/main/resources/` directory. You only need to auto-wire the `HazelcastInstance` bean in the `CommandController` and use it to access to Hazelcast data structures:
+Hazelcast configuration (`hazelcast.yaml`) is placed in the `src/main/resources/` directory. You only need to define `map` bean, if you want to autowire the IMap instance:
+[source,java,indent=0]
+----
+include::ROOT:example$hazelcast-embedded-springboot/src/main/java/guides/hazelcast/springboot/HazelcastApplication.java[tag=imap-bean]
+----
+
+Bean will have name "map" and can be autowired by `IMap` type (if it's the only IMap added as bean).
+
+Now you can autowire the `IMap` bean in the `CommandController` and use it to access the Hazelcast structure:
[source,java,indent=0]
----
include::ROOT:example$hazelcast-embedded-springboot/src/main/java/guides/hazelcast/springboot/CommandController.java[]
----
+Please notice, that we've used `@Qualifier("map")` - it's strictly speaking optional, but once you add more IMaps, you will need to distinguish which map you want to autowire.
+
== Run the Sample Application
Run the application using Maven in a terminal:
+[source,bash]
----
-mvn spring-boot:run -Dspring-boot.run.jvmArguments="-Dserver.port=8080"
+mvn spring-boot:run -Dspring-boot.run.jvmArguments="-Dserver.port=8080 -Djava.net.preferIPv4Stack=true"
----
Then, rerun the application in another terminal.
NOTE: Notice the different value for the `server.port` argument.
+NOTE: We've used `-Djava.net.preferIPv4Stack=true`, because on some platforms there are problems with multicast on IPv6. Adding this option ensures smooth run when learning.
+
+[source,bash]
----
-mvn spring-boot:run -Dspring-boot.run.jvmArguments="-Dserver.port=8081"
+mvn spring-boot:run -Dspring-boot.run.jvmArguments="-Dserver.port=8081 -Djava.net.preferIPv4Stack=true"
----
After both application instances are initialized, you should see that the Hazelcast cluster is formed:
-```bash
+[source,bash]
+----
Members {size:2, ver:2} [
Member [192.168.1.64]:5701 - 520aec3f-58a6-4fcb-a3c7-498dcf37d8ff
Member [192.168.1.64]:5702 - 5c03e467-d457-4847-b49a-745a335db557 this
]
-```
+----
Now, you can issue HTTP requests to put and get data back. Run the following command to put the data into a Hazelcast distributed map:
-```bash
+[source,bash]
+----
curl --data "key=key1&value=hazelcast" "localhost:8080/put"
-```
+----
You will see the value in the output. Then run the command below to get the data back. Please note that the call is made to the other application instance:
-```bash
+[source,bash]
+----
curl "localhost:8081/get?key=key1"
-```
+----
-Again, you will see the value in the output since the data is distributed among Hazelcast cluster instances and can be accessed from any of them.
+Again, you will see the value in the output (`{"value":"hazelcast"}`), because the data is distributed among Hazelcast cluster instances and can be accessed from any of them.
== Test the Application
To run the integration tests, run the following command in terminal. But before, make sure to kill the running application instances.
----
-mvn verify -Ptests
+mvn verify
----
If the tests pass, you’ll see a similar output to the following:
diff --git a/pom.xml b/pom.xml
index bd64f34..9203662 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,13 +11,12 @@
org.springframework.boot
spring-boot-starter-parent
- 2.4.1
-
+ 3.4.1
- 1.8
- 5.3.2
+ 17
+ 5.5.0
@@ -26,35 +25,34 @@
org.springframework.boot
spring-boot-maven-plugin
+
+ maven-failsafe-plugin
+ 3.5.2
+
+
+
+ integration-test
+ verify
+
+
+
+
+ -Djava.net.preferIPv4Stack=true
+
+
-
-
- tests
-
-
-
- maven-failsafe-plugin
- 2.22.2
-
-
-
- integration-test
- verify
-
-
-
-
-
-
-
-
-
org.springframework.boot
spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
@@ -74,11 +72,24 @@
spring-boot-starter-webflux
test
+
+ org.springframework.boot
+ spring-boot-starter-log4j2
+
+
+ org.apache.logging.log4j
+ log4j-spring-boot
+
net.minidev
json-smart
- 2.3
+ 2.5.1
test
+
+ org.awaitility
+ awaitility
+ 4.2.2
+
diff --git a/src/main/java/guides/hazelcast/springboot/CommandController.java b/src/main/java/guides/hazelcast/springboot/CommandController.java
index 13f61d1..bb62a34 100644
--- a/src/main/java/guides/hazelcast/springboot/CommandController.java
+++ b/src/main/java/guides/hazelcast/springboot/CommandController.java
@@ -1,32 +1,27 @@
package guides.hazelcast.springboot;
-import com.hazelcast.core.HazelcastInstance;
+import com.hazelcast.map.IMap;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
-import java.util.concurrent.ConcurrentMap;
-
@RestController
public class CommandController {
- @Autowired
- private HazelcastInstance hazelcastInstance;
-
- private ConcurrentMap retrieveMap() {
- return hazelcastInstance.getMap("map");
- }
+ @Autowired @Qualifier("map")
+ private IMap map;
@PostMapping("/put")
public CommandResponse put(@RequestParam(value = "key") String key, @RequestParam(value = "value") String value) {
- retrieveMap().put(key, value);
+ map.put(key, value);
return new CommandResponse(value);
}
@GetMapping("/get")
public CommandResponse get(@RequestParam(value = "key") String key) {
- String value = retrieveMap().get(key);
+ String value = map.get(key);
return new CommandResponse(value);
}
}
diff --git a/src/main/java/guides/hazelcast/springboot/CommandResponse.java b/src/main/java/guides/hazelcast/springboot/CommandResponse.java
index a14999a..39174ad 100644
--- a/src/main/java/guides/hazelcast/springboot/CommandResponse.java
+++ b/src/main/java/guides/hazelcast/springboot/CommandResponse.java
@@ -1,18 +1,4 @@
package guides.hazelcast.springboot;
-public class CommandResponse {
-
- private String value;
-
- public CommandResponse(String value) {
- this.value = value;
- }
-
- public String getValue() {
- return value;
- }
-
- public void setValue(String value) {
- this.value = value;
- }
+public record CommandResponse (String value) {
}
diff --git a/src/main/java/guides/hazelcast/springboot/HazelcastApplication.java b/src/main/java/guides/hazelcast/springboot/HazelcastApplication.java
index 4880335..286e604 100644
--- a/src/main/java/guides/hazelcast/springboot/HazelcastApplication.java
+++ b/src/main/java/guides/hazelcast/springboot/HazelcastApplication.java
@@ -1,7 +1,10 @@
package guides.hazelcast.springboot;
+import com.hazelcast.core.HazelcastInstance;
+import com.hazelcast.map.IMap;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@@ -10,4 +13,11 @@ public class HazelcastApplication {
public static void main(String[] args) {
SpringApplication.run(HazelcastApplication.class, args);
}
+
+ //tag::imap-bean[]
+ @Bean
+ public IMap map(HazelcastInstance instance) {
+ return instance.getMap("map");
+ }
+ //end::imap-bean[]
}
diff --git a/src/main/resources/hazelcast.yaml b/src/main/resources/hazelcast.yaml
index 627d281..bb15dcd 100644
--- a/src/main/resources/hazelcast.yaml
+++ b/src/main/resources/hazelcast.yaml
@@ -1,2 +1,8 @@
hazelcast:
- cluster-name: hazelcast-cluster
\ No newline at end of file
+ cluster-name: hazelcast-cluster
+ properties:
+ hazelcast.logging.type: log4j2
+ network:
+ join:
+ multicast:
+ enabled: true
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
new file mode 100644
index 0000000..7e0d17a
--- /dev/null
+++ b/src/main/resources/log4j2.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/java/guides/hazelcast/springboot/CommandControllerIT.java b/src/test/java/guides/hazelcast/springboot/CommandControllerIT.java
index 9f5430c..8d5ca91 100644
--- a/src/test/java/guides/hazelcast/springboot/CommandControllerIT.java
+++ b/src/test/java/guides/hazelcast/springboot/CommandControllerIT.java
@@ -1,5 +1,6 @@
package guides.hazelcast.springboot;
+import com.hazelcast.config.Config;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import org.junit.jupiter.api.Test;
@@ -9,7 +10,9 @@
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.reactive.server.WebTestClient;
-import static org.junit.jupiter.api.Assertions.assertEquals;
+import java.time.Duration;
+
+import static org.awaitility.Awaitility.await;
import static org.springframework.http.HttpHeaders.ACCEPT;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@@ -26,7 +29,11 @@ public class CommandControllerIT {
@Test
public void testPutRequest(){
//when
- WebTestClient.ResponseSpec responseSpec = makePostRequest("/put?key={key}&value={value}", "key1", "value1");
+ WebTestClient.ResponseSpec responseSpec = webTestClient
+ .post()
+ .uri("/put?key={key}&value={value}", "key1", "value1")
+ .header(ACCEPT, APPLICATION_JSON_VALUE)
+ .exchange();
//then
responseSpec.expectStatus()
@@ -40,7 +47,11 @@ public void testPutRequest(){
@Test
public void testGetRequest(){
//given
- makePostRequest("/put?key={key}&value={value}", "key1", "value1");
+ webTestClient
+ .post()
+ .uri("/put?key={key}&value={value}", "key1", "value1")
+ .header(ACCEPT, APPLICATION_JSON_VALUE)
+ .exchange();
//when
WebTestClient.ResponseSpec responseSpec = webTestClient
@@ -58,20 +69,21 @@ public void testGetRequest(){
.jsonPath("$.value").isEqualTo("value1");
}
- private WebTestClient.ResponseSpec makePostRequest(String uri, Object... parameters) {
- return webTestClient
- .post()
- .uri(uri, parameters)
- .header(ACCEPT, APPLICATION_JSON_VALUE)
- .exchange();
- }
-
@Test
- public void testHazelcastCluster(){
+ public void testHazelcastCluster() {
+ Config config = Config.load();
+ config.setProperty("hazelcast.logging.type", "log4j2");
+
//given
- Hazelcast.newHazelcastInstance();
+ HazelcastInstance hz = Hazelcast.newHazelcastInstance();
//then
- assertEquals(2, hazelcastInstance.getCluster().getMembers().size());
+ try {
+ await()
+ .atMost(Duration.ofMinutes(2))
+ .until(() -> hazelcastInstance.getCluster().getMembers().size() == 2);
+ } finally {
+ hz.shutdown();
+ }
}
}
diff --git a/src/test/resources/hazelcast.yaml b/src/test/resources/hazelcast.yaml
index 34ee506..bb15dcd 100644
--- a/src/test/resources/hazelcast.yaml
+++ b/src/test/resources/hazelcast.yaml
@@ -1,5 +1,7 @@
hazelcast:
cluster-name: hazelcast-cluster
+ properties:
+ hazelcast.logging.type: log4j2
network:
join:
multicast:
diff --git a/src/test/resources/log4j2.xml b/src/test/resources/log4j2.xml
new file mode 100644
index 0000000..7e0d17a
--- /dev/null
+++ b/src/test/resources/log4j2.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+