From 8d0cdec0f5dac142b809823d5e37a546c9a843dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Gaw=C4=99da?= Date: Tue, 4 Feb 2025 18:36:37 +0100 Subject: [PATCH 1/7] Changes in code --- pom.xml | 11 +++++------ .../hazelcast/springboot/CommandController.java | 17 ++++++----------- .../hazelcast/springboot/CommandResponse.java | 16 +--------------- .../springboot/HazelcastApplication.java | 8 ++++++++ 4 files changed, 20 insertions(+), 32 deletions(-) diff --git a/pom.xml b/pom.xml index bd64f34..c46aaf4 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 @@ -36,7 +35,7 @@ maven-failsafe-plugin - 2.22.2 + 3.5.2 @@ -77,7 +76,7 @@ net.minidev json-smart - 2.3 + 2.5.1 test 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..5ee364a 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,9 @@ public class HazelcastApplication { public static void main(String[] args) { SpringApplication.run(HazelcastApplication.class, args); } + + @Bean + public IMap map(HazelcastInstance instance) { + return instance.getMap("map"); + } } From 7a87aa82aa7f195668ab2de7fe68e6f07db71b9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Gaw=C4=99da?= Date: Tue, 4 Feb 2025 19:19:00 +0100 Subject: [PATCH 2/7] Refactor tests --- .github/workflows/build.yml | 2 +- .run/Template JUnit.run.xml | 12 ++++++ .../pages/hazelcast-embedded-springboot.adoc | 8 +++- pom.xml | 22 ++++++++++ .../springboot/HazelcastApplication.java | 2 + src/main/resources/hazelcast.yaml | 9 ++++- .../springboot/CommandControllerIT.java | 40 ++++++++++++------- src/test/resources/hazelcast.yaml | 3 ++ src/test/resources/log4j2.xml | 17 ++++++++ 9 files changed, 98 insertions(+), 17 deletions(-) create mode 100644 .run/Template JUnit.run.xml create mode 100644 src/test/resources/log4j2.xml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 562a109..c7a9d5b 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: diff --git a/.run/Template JUnit.run.xml b/.run/Template JUnit.run.xml new file mode 100644 index 0000000..ef797ab --- /dev/null +++ b/.run/Template JUnit.run.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/docs/modules/ROOT/pages/hazelcast-embedded-springboot.adoc b/docs/modules/ROOT/pages/hazelcast-embedded-springboot.adoc index 31d8771..69f84b5 100644 --- a/docs/modules/ROOT/pages/hazelcast-embedded-springboot.adoc +++ b/docs/modules/ROOT/pages/hazelcast-embedded-springboot.adoc @@ -30,7 +30,13 @@ 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] +---- + +Now you can autowire the `IMap` bean in the `CommandController` and use it to access the Hazelcast structure: [source,java,indent=0] ---- diff --git a/pom.xml b/pom.xml index c46aaf4..47535b4 100644 --- a/pom.xml +++ b/pom.xml @@ -44,6 +44,9 @@ + + -Djava.net.preferIPv4Stack=true + @@ -54,6 +57,12 @@ org.springframework.boot spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + @@ -73,11 +82,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.5.1 test + + org.awaitility + awaitility + 4.2.2 + diff --git a/src/main/java/guides/hazelcast/springboot/HazelcastApplication.java b/src/main/java/guides/hazelcast/springboot/HazelcastApplication.java index 5ee364a..286e604 100644 --- a/src/main/java/guides/hazelcast/springboot/HazelcastApplication.java +++ b/src/main/java/guides/hazelcast/springboot/HazelcastApplication.java @@ -14,8 +14,10 @@ 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..2c0bad2 100644 --- a/src/main/resources/hazelcast.yaml +++ b/src/main/resources/hazelcast.yaml @@ -1,2 +1,9 @@ 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 + loopbackModeEnabled: true 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..2c0bad2 100644 --- a/src/test/resources/hazelcast.yaml +++ b/src/test/resources/hazelcast.yaml @@ -1,6 +1,9 @@ hazelcast: cluster-name: hazelcast-cluster + properties: + hazelcast.logging.type: log4j2 network: join: multicast: enabled: true + loopbackModeEnabled: true diff --git a/src/test/resources/log4j2.xml b/src/test/resources/log4j2.xml new file mode 100644 index 0000000..eb50f80 --- /dev/null +++ b/src/test/resources/log4j2.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + From f1f9b2ded27c7e6978bc18dccbfe43ec14f836a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Gaw=C4=99da?= Date: Tue, 4 Feb 2025 19:37:12 +0100 Subject: [PATCH 3/7] Final changes --- .../pages/hazelcast-embedded-springboot.adoc | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/docs/modules/ROOT/pages/hazelcast-embedded-springboot.adoc b/docs/modules/ROOT/pages/hazelcast-embedded-springboot.adoc index 69f84b5..0fac512 100644 --- a/docs/modules/ROOT/pages/hazelcast-embedded-springboot.adoc +++ b/docs/modules/ROOT/pages/hazelcast-embedded-springboot.adoc @@ -47,40 +47,45 @@ include::ROOT:example$hazelcast-embedded-springboot/src/main/java/guides/hazelca 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. +[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 From d1d148bef006d48e06ea63036241ad4ba3e5e84e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Gaw=C4=99da?= Date: Tue, 4 Feb 2025 19:39:41 +0100 Subject: [PATCH 4/7] Add log4j2.xml to prod res., cleanup --- src/main/resources/hazelcast.yaml | 1 - src/main/resources/log4j2.xml | 13 +++++++++++++ src/test/resources/hazelcast.yaml | 1 - src/test/resources/log4j2.xml | 4 ---- 4 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 src/main/resources/log4j2.xml diff --git a/src/main/resources/hazelcast.yaml b/src/main/resources/hazelcast.yaml index 2c0bad2..bb15dcd 100644 --- a/src/main/resources/hazelcast.yaml +++ b/src/main/resources/hazelcast.yaml @@ -6,4 +6,3 @@ hazelcast: join: multicast: enabled: true - loopbackModeEnabled: 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/resources/hazelcast.yaml b/src/test/resources/hazelcast.yaml index 2c0bad2..bb15dcd 100644 --- a/src/test/resources/hazelcast.yaml +++ b/src/test/resources/hazelcast.yaml @@ -6,4 +6,3 @@ hazelcast: join: multicast: enabled: true - loopbackModeEnabled: true diff --git a/src/test/resources/log4j2.xml b/src/test/resources/log4j2.xml index eb50f80..7e0d17a 100644 --- a/src/test/resources/log4j2.xml +++ b/src/test/resources/log4j2.xml @@ -9,9 +9,5 @@ - - - - From 175bfff7becbfb9e0d79846eef9f8fbcb599e6d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Gaw=C4=99da?= Date: Tue, 4 Feb 2025 19:40:12 +0100 Subject: [PATCH 5/7] Just to make GH happy --- .run/Template JUnit.run.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.run/Template JUnit.run.xml b/.run/Template JUnit.run.xml index ef797ab..2fdad57 100644 --- a/.run/Template JUnit.run.xml +++ b/.run/Template JUnit.run.xml @@ -9,4 +9,4 @@