diff --git a/spring-5/pom.xml b/spring-5/pom.xml index a76730f628..4c2df68f1b 100644 --- a/spring-5/pom.xml +++ b/spring-5/pom.xml @@ -39,10 +39,14 @@ org.springframework.boot spring-boot-starter-webflux - + org.springframework.boot spring-boot-starter-hateoas + + org.springframework.boot + spring-boot-starter-actuator + org.projectreactor reactor-spring diff --git a/spring-5/src/main/java/com/baeldung/actuator/DownstreamServiceReactiveHealthIndicator.java b/spring-5/src/main/java/com/baeldung/actuator/DownstreamServiceReactiveHealthIndicator.java new file mode 100644 index 0000000000..5f36330ff6 --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/actuator/DownstreamServiceReactiveHealthIndicator.java @@ -0,0 +1,23 @@ +package com.baeldung.actuator; + +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.ReactiveHealthIndicator; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; + +@Component +public class DownstreamServiceReactiveHealthIndicator implements ReactiveHealthIndicator { + + @Override + public Mono health() { + return checkDownstreamServiceHealth().onErrorResume( + ex -> Mono.just(new Health.Builder().down(ex).build()) + ); + } + + private Mono checkDownstreamServiceHealth() { + // we could use WebClient to check health reactively + return Mono.just(new Health.Builder().up().build()); + } + +} \ No newline at end of file diff --git a/spring-5/src/main/java/com/baeldung/actuator/FeaturesEndpoint.java b/spring-5/src/main/java/com/baeldung/actuator/FeaturesEndpoint.java new file mode 100644 index 0000000000..2ed32501ae --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/actuator/FeaturesEndpoint.java @@ -0,0 +1,47 @@ +package com.baeldung.actuator; + +import org.springframework.boot.actuate.endpoint.annotation.*; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Component +@Endpoint(id = "features", enableByDefault = true) +public class FeaturesEndpoint { + + private Map features = new ConcurrentHashMap<>(); + + @ReadOperation + public Map features() { + return features; + } + + @ReadOperation + public Feature feature(@Selector String name) { + return features.get(name); + } + + @WriteOperation + public void configureFeature(@Selector String name, Feature feature) { + features.put(name, feature); + } + + @DeleteOperation + public void deleteFeature(@Selector String name) { + features.remove(name); + } + + public static class Feature { + private Boolean enabled; + + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + } + +} diff --git a/spring-5/src/main/java/com/baeldung/actuator/InfoWebEndpointExtension.java b/spring-5/src/main/java/com/baeldung/actuator/InfoWebEndpointExtension.java new file mode 100644 index 0000000000..acd92d1846 --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/actuator/InfoWebEndpointExtension.java @@ -0,0 +1,32 @@ +package com.baeldung.actuator; + +import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; +import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse; +import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension; +import org.springframework.boot.actuate.info.InfoEndpoint; +import org.springframework.stereotype.Component; + +import java.util.Map; + +@Component +@EndpointWebExtension(endpoint = InfoEndpoint.class) +public class InfoWebEndpointExtension { + + private final InfoEndpoint delegate; + + public InfoWebEndpointExtension(InfoEndpoint infoEndpoint) { + this.delegate = infoEndpoint; + } + + @ReadOperation + public WebEndpointResponse info() { + Map info = this.delegate.info(); + Integer status = getStatus(info); + return new WebEndpointResponse<>(info, status); + } + + private Integer getStatus(Map info) { + // return 5xx if this is a snapshot + return 200; + } +} \ No newline at end of file diff --git a/spring-5/src/main/java/com/baeldung/security/SecurityConfig.java b/spring-5/src/main/java/com/baeldung/security/SecurityConfig.java index a9e44a2eee..d31f1552fc 100644 --- a/spring-5/src/main/java/com/baeldung/security/SecurityConfig.java +++ b/spring-5/src/main/java/com/baeldung/security/SecurityConfig.java @@ -17,6 +17,7 @@ public class SecurityConfig { public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) { return http.authorizeExchange() .pathMatchers("/admin").hasAuthority("ROLE_ADMIN") + .pathMatchers("/actuator/**").permitAll() .anyExchange().authenticated() .and().formLogin() .and().build(); diff --git a/spring-5/src/main/resources/application.properties b/spring-5/src/main/resources/application.properties index ccec014c2b..a7e3ec0d5a 100644 --- a/spring-5/src/main/resources/application.properties +++ b/spring-5/src/main/resources/application.properties @@ -1,3 +1,5 @@ server.port=8081 +management.endpoints.web.expose=* +info.app.name=Spring Boot 2 actuator Application logging.level.root=INFO \ No newline at end of file diff --git a/spring-5/src/test/java/com/baeldung/actuator/ActuatorInfoIntegrationTest.java b/spring-5/src/test/java/com/baeldung/actuator/ActuatorInfoIntegrationTest.java new file mode 100644 index 0000000000..964cf1a1ea --- /dev/null +++ b/spring-5/src/test/java/com/baeldung/actuator/ActuatorInfoIntegrationTest.java @@ -0,0 +1,35 @@ +package com.baeldung.actuator; + +import com.baeldung.jsonb.Spring5Application; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Spring5Application.class) +public class ActuatorInfoIntegrationTest { + + @Autowired + private TestRestTemplate restTemplate; + + @Test + public void whenGetInfo_thenReturns200() throws IOException { + final ResponseEntity responseEntity = this.restTemplate.getForEntity("/actuator/info", String.class); + assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); + } + + @Test + public void whenFeatures_thenReturns200() throws IOException { + final ResponseEntity responseEntity = this.restTemplate.getForEntity("/actuator/features", String.class); + assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); + } +} \ No newline at end of file