diff --git a/hazelcast/README.md b/hazelcast/README.md new file mode 100644 index 0000000..ac8b7a6 --- /dev/null +++ b/hazelcast/README.md @@ -0,0 +1,24 @@ +# Sample for running using Hazelcast. + +- http://localhost:8080 -> Spring Boot Main Application +- http://localhost:8090 -> Spring Boot Replica Application + +![Architecture](architecture.png) + +## Prerequisites + +- Java +- Maven +- Docker and Docker Compose + +## Run Everything +You can run the whole build and start all apps in docker containers with the following script +```bash +chmod u+x buildAndRunAll.sh +./buildAndRunAll.sh +``` + +## Stop Everything +```bash +docker compose down -v +``` \ No newline at end of file diff --git a/hazelcast/apps/hello-world/.gitignore b/hazelcast/apps/hello-world/.gitignore new file mode 100644 index 0000000..b18abdf --- /dev/null +++ b/hazelcast/apps/hello-world/.gitignore @@ -0,0 +1,5 @@ +target +.idea +*.iml +*.log +*.gz diff --git a/hazelcast/apps/hello-world/Dockerfile b/hazelcast/apps/hello-world/Dockerfile new file mode 100644 index 0000000..7940f0a --- /dev/null +++ b/hazelcast/apps/hello-world/Dockerfile @@ -0,0 +1,8 @@ +# https://hub.docker.com/_/eclipse-temurin/ +FROM eclipse-temurin:21 + +VOLUME /tmp + +COPY target/app.jar /opt/app/app.jar + +CMD ["bash", "-c", "java $JAVA_OPTS -jar /opt/app/app.jar"] diff --git a/hazelcast/apps/hello-world/build.sh b/hazelcast/apps/hello-world/build.sh new file mode 100755 index 0000000..9b23c00 --- /dev/null +++ b/hazelcast/apps/hello-world/build.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +# Build App +mvn package +# Build Docker Image +docker build --tag hello-world-hazelcast . diff --git a/hazelcast/apps/hello-world/pom.xml b/hazelcast/apps/hello-world/pom.xml new file mode 100644 index 0000000..80ea01a --- /dev/null +++ b/hazelcast/apps/hello-world/pom.xml @@ -0,0 +1,73 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.5.6 + + + de.codecentric + hello-world-hazelcast + 1.0.0-SNAPSHOT + Hello World + + + 17 + 3.5.5 + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-web + + + de.codecentric + spring-boot-admin-starter-client + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + de.codecentric + spring-boot-admin-dependencies + ${spring-boot-admin.version} + pom + import + + + + + + app + + + org.springframework.boot + spring-boot-maven-plugin + + + repackage + + repackage + + build-info + + + + + + + + diff --git a/hazelcast/apps/hello-world/src/main/java/de/codecentric/helloworld/hazelcast/HelloWorld.java b/hazelcast/apps/hello-world/src/main/java/de/codecentric/helloworld/hazelcast/HelloWorld.java new file mode 100644 index 0000000..af0b7df --- /dev/null +++ b/hazelcast/apps/hello-world/src/main/java/de/codecentric/helloworld/hazelcast/HelloWorld.java @@ -0,0 +1,14 @@ +package de.codecentric.helloworld.hazelcast; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class HelloWorld { + + @GetMapping("/") + public String hello() { + return "Hello World!"; + } + +} diff --git a/hazelcast/apps/hello-world/src/main/java/de/codecentric/helloworld/hazelcast/HelloWorldHazelcastApplication.java b/hazelcast/apps/hello-world/src/main/java/de/codecentric/helloworld/hazelcast/HelloWorldHazelcastApplication.java new file mode 100644 index 0000000..c492cff --- /dev/null +++ b/hazelcast/apps/hello-world/src/main/java/de/codecentric/helloworld/hazelcast/HelloWorldHazelcastApplication.java @@ -0,0 +1,13 @@ +package de.codecentric.helloworld.hazelcast; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class HelloWorldHazelcastApplication { + + public static void main(String[] args) { + SpringApplication.run(HelloWorldHazelcastApplication.class, args); + } + +} diff --git a/hazelcast/apps/hello-world/src/main/resources/application.yml b/hazelcast/apps/hello-world/src/main/resources/application.yml new file mode 100644 index 0000000..fd7af28 --- /dev/null +++ b/hazelcast/apps/hello-world/src/main/resources/application.yml @@ -0,0 +1,47 @@ +server: + port: 8081 +spring: + application: # Application-Info for the Info-Actuator + name: "@pom.artifactId@" + boot: + admin: + client: + url: "http://spring-boot-admin-member-1:8080" # use docker compose internal service name to access sba server + instance: + # if admin server should talk to hello through proxy, the following properties could be used, + # see https://docs.spring-boot-admin.com/current/client.html#spring-boot-admin-client + #health-url: "http://host.docker.internal:8888/hello/actuator/health" + #management-url: "http://host.docker.internal:8888/hello/actuator" + #management-base-url: "http://host.docker.internal:8888/hello" + #service-url: "http://host.docker.internal:8888/hello" +management: # Actuator Configuration + endpoints: + web: + exposure: + include: "*" + endpoint: # Health-Actuator + health: + show-details: always + info: # Info-Actuator + java: + enabled: true + os: + enabled: true + build: + enabled: true + env: + enabled: true +info: # Application-Info for the Info-Actuator + group: "@pom.groupId@" + artifact: "@pom.artifactId@" + description: "@pom.description@" + version: "@pom.version@" + spring-boot: "@pom.parent.version@" + # Tags for the Spring Boot Admin UI + tags: + spring-boot: "@pom.parent.version@" +logging: # Logging-File for the Logfile-Actuator + file: + name: "hello-world.log" + level: + ROOT: debug diff --git a/hazelcast/apps/hello-world/src/test/java/de/codecentric/helloworld/hazelcast/HelloWorldHazelcastApplicationTests.java b/hazelcast/apps/hello-world/src/test/java/de/codecentric/helloworld/hazelcast/HelloWorldHazelcastApplicationTests.java new file mode 100644 index 0000000..dc0a420 --- /dev/null +++ b/hazelcast/apps/hello-world/src/test/java/de/codecentric/helloworld/hazelcast/HelloWorldHazelcastApplicationTests.java @@ -0,0 +1,13 @@ +package de.codecentric.helloworld.hazelcast; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class HelloWorldHazelcastApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/hazelcast/apps/spring-boot-admin/.gitignore b/hazelcast/apps/spring-boot-admin/.gitignore new file mode 100644 index 0000000..b18abdf --- /dev/null +++ b/hazelcast/apps/spring-boot-admin/.gitignore @@ -0,0 +1,5 @@ +target +.idea +*.iml +*.log +*.gz diff --git a/hazelcast/apps/spring-boot-admin/Dockerfile b/hazelcast/apps/spring-boot-admin/Dockerfile new file mode 100644 index 0000000..7940f0a --- /dev/null +++ b/hazelcast/apps/spring-boot-admin/Dockerfile @@ -0,0 +1,8 @@ +# https://hub.docker.com/_/eclipse-temurin/ +FROM eclipse-temurin:21 + +VOLUME /tmp + +COPY target/app.jar /opt/app/app.jar + +CMD ["bash", "-c", "java $JAVA_OPTS -jar /opt/app/app.jar"] diff --git a/hazelcast/apps/spring-boot-admin/build.sh b/hazelcast/apps/spring-boot-admin/build.sh new file mode 100755 index 0000000..4d4d3ab --- /dev/null +++ b/hazelcast/apps/spring-boot-admin/build.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +# Build App +mvn package +# Build Docker Image +docker build --tag spring-boot-admin-hazelcast . diff --git a/hazelcast/apps/spring-boot-admin/pom.xml b/hazelcast/apps/spring-boot-admin/pom.xml new file mode 100644 index 0000000..b2b158f --- /dev/null +++ b/hazelcast/apps/spring-boot-admin/pom.xml @@ -0,0 +1,81 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.5.6 + + + de.codecentric + spring-boot-admin-hazelcast + 1.0.0-SNAPSHOT + spring-boot-admin-hazelcast + spring-boot-admin using hazelcast as cluster + + + 17 + 3.5.5 + 2025.0.0 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.cloud + spring-cloud-starter + + + de.codecentric + spring-boot-admin-starter-server + + + + com.hazelcast + hazelcast + + + + + + de.codecentric + spring-boot-admin-dependencies + ${spring-boot-admin.version} + pom + import + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + app + + + org.springframework.boot + spring-boot-maven-plugin + + + repackage + + repackage + + build-info + + + + + + + + diff --git a/hazelcast/apps/spring-boot-admin/src/main/java/de/codecentric/springbootadmin/hazelcast/SpringBootAdminHazelcastApplication.java b/hazelcast/apps/spring-boot-admin/src/main/java/de/codecentric/springbootadmin/hazelcast/SpringBootAdminHazelcastApplication.java new file mode 100644 index 0000000..d5c47fb --- /dev/null +++ b/hazelcast/apps/spring-boot-admin/src/main/java/de/codecentric/springbootadmin/hazelcast/SpringBootAdminHazelcastApplication.java @@ -0,0 +1,56 @@ +package de.codecentric.springbootadmin.hazelcast; + +import com.hazelcast.config.*; +import com.hazelcast.spi.merge.PutIfAbsentMergePolicy; +import de.codecentric.boot.admin.server.config.EnableAdminServer; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +import static de.codecentric.boot.admin.server.config.AdminServerHazelcastAutoConfiguration.DEFAULT_NAME_EVENT_STORE_MAP; +import static de.codecentric.boot.admin.server.config.AdminServerHazelcastAutoConfiguration.DEFAULT_NAME_SENT_NOTIFICATIONS_MAP; +import static java.util.Collections.singletonList; + +@SpringBootApplication +@EnableAdminServer +public class SpringBootAdminHazelcastApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootAdminHazelcastApplication.class, args); + } + + @Bean + public Config hazelcastConfig() { + // This map is used to store the events. + // It should be configured to reliably hold all the data, + // Spring Boot Admin will compact the events, if there are too many + MapConfig eventStoreMap = new MapConfig(DEFAULT_NAME_EVENT_STORE_MAP).setInMemoryFormat(InMemoryFormat.OBJECT) + .setBackupCount(1) + .setMergePolicyConfig(new MergePolicyConfig(PutIfAbsentMergePolicy.class.getName(), 100)); + // This map is used to deduplicate the notifications. + // If data in this map gets lost it should not be a big issue as it will atmost + // lead to + // the same notification to be sent by multiple instances + MapConfig sentNotificationsMap = new MapConfig(DEFAULT_NAME_SENT_NOTIFICATIONS_MAP) + .setInMemoryFormat(InMemoryFormat.OBJECT) + .setBackupCount(1) + .setEvictionConfig( + new EvictionConfig().setEvictionPolicy(EvictionPolicy.LRU).setMaxSizePolicy(MaxSizePolicy.PER_NODE)) + .setMergePolicyConfig(new MergePolicyConfig(PutIfAbsentMergePolicy.class.getName(), 100)); + + Config config = new Config(); + config.addMapConfig(eventStoreMap); + config.addMapConfig(sentNotificationsMap); + config.setProperty("hazelcast.jmx", "true"); + + // network and join configuration (simple defaults good for local/dev) + NetworkConfig network = config.getNetworkConfig(); + network.setPort(5701).setPortAutoIncrement(true); + + JoinConfig join = network.getJoin(); + join.getMulticastConfig().setEnabled(true); + + return config; + } + +} diff --git a/hazelcast/apps/spring-boot-admin/src/main/resources/application.yml b/hazelcast/apps/spring-boot-admin/src/main/resources/application.yml new file mode 100644 index 0000000..7667dfa --- /dev/null +++ b/hazelcast/apps/spring-boot-admin/src/main/resources/application.yml @@ -0,0 +1,40 @@ +spring: + application: # Application-Info for the Info-Actuator + name: "@pom.artifactId@" + cloud: + discovery: + client: + simple: + instances: + hello-world: + - uri: http://hello-world-hazelcast:8081 + +management: # Actuator Configuration + endpoints: + web: + exposure: + include: "*" + endpoint: # Health-Actuator + health: + show-details: always + info: # Info-Actuator + java: + enabled: true + os: + enabled: true + build: + enabled: true + env: + enabled: true +info: # Application-Info for the Info-Actuator + group: "@pom.groupId@" + artifact: "@pom.artifactId@" + description: "@pom.description@" + version: "@pom.version@" + spring-boot: "@pom.parent.version@" + # Tags for the Spring Boot Admin UI + tags: + spring-boot: "@pom.parent.version@" +logging: # Logging-File for the Logfile-Actuator + file: + name: "hello-world.log" diff --git a/hazelcast/architecture.png b/hazelcast/architecture.png new file mode 100644 index 0000000..25f390e Binary files /dev/null and b/hazelcast/architecture.png differ diff --git a/hazelcast/buildAndRunAll.sh b/hazelcast/buildAndRunAll.sh new file mode 100755 index 0000000..df59a3e --- /dev/null +++ b/hazelcast/buildAndRunAll.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +cd apps/spring-boot-admin +./build.sh +cd ../.. + +cd apps/hello-world +./build.sh +cd ../.. + +docker compose up -d diff --git a/hazelcast/docker-compose.yml b/hazelcast/docker-compose.yml new file mode 100644 index 0000000..c733a02 --- /dev/null +++ b/hazelcast/docker-compose.yml @@ -0,0 +1,18 @@ +services: + hello-world: + container_name: hello-world-hazelcast + image: hello-world-hazelcast + + spring-boot-admin-member-1: + container_name: spring-boot-admin-hazelcast-member-1 + image: spring-boot-admin-hazelcast + ports: + - "8080:8080" + environment: + SPRING_BOOT_ADMIN_UI_PUBLICURL: http://localhost:8080 + + spring-boot-admin-member-2: + container_name: spring-boot-admin-hazelcast-member-2 + image: spring-boot-admin-hazelcast + ports: + - "8090:8080"