mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-10-12 16:15:23 +00:00
feat: Microservices pattern : Self-Registration (#3245)
* Services and information * README.md updated * unit tests added for controllers * pom.xml, clean up, java version update and slf4j logging added * pom files updated * Removed children maven wrappers * Microservice-self-registration added to pom file * POM file updated for self-registration * pom file updated * Update pom.xml correction to mudule name * SonarQube comments addressed * sonarqube comments on coverage addressed * Code coverage for custom health checks * changes for unit tests * changes for unit tests * changes to for code coverage improvement
This commit is contained in:
1 parent
3ba2560e09
commit
812e4cd6a2
39 files changed
+1049
No files matched your search
@@ -0,0 +1,2 @@
|
||||
/mvnw text eol=lf
|
||||
*.cmd text eol=crlf
|
||||
@@ -0,0 +1,33 @@
|
||||
HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.4.4</version>
|
||||
<relativePath/> </parent>
|
||||
<groupId>com.learning</groupId>
|
||||
<artifactId>contextservice</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>contextservice</name>
|
||||
<description>contextservice</description>
|
||||
|
||||
<properties>
|
||||
<spring-cloud.version>2024.0.1</spring-cloud.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.38</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>${spring-cloud.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.learning.contextservice;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
@EnableFeignClients
|
||||
public class ContextserviceApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ContextserviceApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package com.learning.contextservice;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
||||
@Component("myCustomHealthCheck")
|
||||
public class MyCustomHealthCheck implements HealthIndicator {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MyCustomHealthCheck.class);
|
||||
|
||||
private volatile boolean isHealthy = true;
|
||||
|
||||
@Scheduled(fixedRate = 5000) // Run every 5 seconds
|
||||
public void updateHealthStatus() {
|
||||
// Perform checks here to determine the current health
|
||||
// For example, check database connectivity, external service availability, etc.
|
||||
isHealthy = performHealthCheck();
|
||||
log.info("Update health status : {}", isHealthy);
|
||||
}
|
||||
|
||||
boolean performHealthCheck() {
|
||||
boolean current = System.currentTimeMillis() % 10000 < 5000; // Simulate fluctuating health
|
||||
log.debug("Performing health check, current status: {}", current);
|
||||
return current; // Simulate fluctuating health
|
||||
}
|
||||
|
||||
@Override
|
||||
public Health health() {
|
||||
if (isHealthy) {
|
||||
log.info("Health check successful, service is UP");
|
||||
return Health.up().withDetail("message", "Service is running and scheduled checks are OK").build();
|
||||
} else {
|
||||
log.warn("Health check failed, service is DOWN");
|
||||
return Health.down().withDetail("error", "Scheduled health checks failed").build();
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package com.learning.contextservice.client;
|
||||
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
@FeignClient(name = "greetingservice")
|
||||
public interface GreetingServiceClient {
|
||||
|
||||
@GetMapping("/greeting")
|
||||
String getGreeting();
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.learning.contextservice.controller;
|
||||
|
||||
import com.learning.contextservice.client.GreetingServiceClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class ContextController {
|
||||
|
||||
private final GreetingServiceClient greetingServiceClient;
|
||||
private final String userRegion;
|
||||
|
||||
@Autowired
|
||||
public ContextController(GreetingServiceClient greetingServiceClient, @Value("${user.region}") String userRegion) {
|
||||
this.greetingServiceClient = greetingServiceClient;
|
||||
this.userRegion = userRegion;
|
||||
}
|
||||
|
||||
@GetMapping("/context")
|
||||
public String getContext() {
|
||||
String greeting = greetingServiceClient.getGreeting();
|
||||
return "The Greeting Service says: "+greeting+" from "+userRegion;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
server:
|
||||
port: 8082
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: contextservice
|
||||
|
||||
eureka:
|
||||
client:
|
||||
service-url.defaultZone: http://localhost:8761/eureka
|
||||
|
||||
user:
|
||||
region: Chennai, Tamil Nadu, India
|
||||
|
||||
management:
|
||||
endpoint:
|
||||
health:
|
||||
show-details: always
|
||||
web:
|
||||
exposure:
|
||||
include: health
|
||||
|
||||
logging:
|
||||
file:
|
||||
name: application.log
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.learning.contextservice;
|
||||
|
||||
import com.learning.contextservice.client.GreetingServiceClient;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
|
||||
|
||||
@SpringBootTest(classes = ContextserviceApplication.class)
|
||||
@AutoConfigureMockMvc
|
||||
@Import(TestConfig.class)
|
||||
class ContextControllerTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@MockitoBean
|
||||
private GreetingServiceClient greetingServiceClient;
|
||||
|
||||
@Value("${user.region}")
|
||||
private String userRegion;
|
||||
|
||||
@Test
|
||||
void shouldReturnContextGreeting() throws Exception{
|
||||
Mockito.when(greetingServiceClient.getGreeting()).thenReturn("Mocked Hello");
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.get("/context")
|
||||
.accept(MediaType.TEXT_PLAIN))
|
||||
.andExpect(MockMvcResultMatchers.status().isOk())
|
||||
.andExpect(MockMvcResultMatchers.content().string("The Greeting Service says: Mocked Hello from Chennai, Tamil Nadu, India"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnContextServiceHealthStatusUp() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.get("/actuator/health"))
|
||||
.andExpect(MockMvcResultMatchers.status().isOk())
|
||||
.andExpect(MockMvcResultMatchers.content().string(Matchers.containsString("\"status\":\"UP\"")));
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.learning.contextservice;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class ContextserviceApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
// This is a basic integration test that checks if the Spring Application Context loads successfully.
|
||||
// If the context loads without any exceptions, the test is considered passing.
|
||||
// It is often left empty as the act of loading the context is the primary verification.
|
||||
// You can add specific assertions here if you want to verify the presence or state of certain beans.
|
||||
}
|
||||
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.learning.contextservice;
|
||||
|
||||
import com.learning.contextservice.client.GreetingServiceClient;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class TestConfig {
|
||||
|
||||
@Bean
|
||||
public GreetingServiceClient greetingServiceClient() {
|
||||
GreetingServiceClient mockClient = Mockito.mock(GreetingServiceClient.class);
|
||||
Mockito.when(mockClient.getGreeting()).thenReturn("Mocked Hello");
|
||||
return mockClient;
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.learning.contextservice;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.boot.actuate.health.Status;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class MyCustomHealthCheckTest {
|
||||
|
||||
@Test
|
||||
void testHealthUp() {
|
||||
MyCustomHealthCheck healthCheck = new MyCustomHealthCheck();
|
||||
// Simulate a healthy state
|
||||
ReflectionTestUtils.setField(healthCheck, "isHealthy", true);
|
||||
Health health = healthCheck.health();
|
||||
assertEquals(Status.UP, health.getStatus());
|
||||
assertTrue(health.getDetails().containsKey("message"));
|
||||
assertEquals("Service is running and scheduled checks are OK", health.getDetails().get("message"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHealthDown() {
|
||||
MyCustomHealthCheck healthCheck = new MyCustomHealthCheck();
|
||||
// Simulate an unhealthy state
|
||||
ReflectionTestUtils.setField(healthCheck, "isHealthy", false);
|
||||
Health health = healthCheck.health();
|
||||
assertEquals(Status.DOWN, health.getStatus());
|
||||
assertTrue(health.getDetails().containsKey("error"));
|
||||
assertEquals("Scheduled health checks failed", health.getDetails().get("error"));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user