From 4983d3c7f7d3256eb1e49ddb3059d666176a0bcf Mon Sep 17 00:00:00 2001 From: Andrey Litvitski Date: Wed, 24 Dec 2025 19:02:57 +0300 Subject: [PATCH 1/2] Refine default enablement of gRPC health service Enable gRPC health by default only when a BindableService is present. Respect explicit spring.grpc.server.health.enabled overrides and add tests. Closes: gh-334 Signed-off-by: Andrey Litvitski --- .../GrpcServerHealthAutoConfiguration.java | 25 +++++++++++++++++-- ...rpcServerHealthAutoConfigurationTests.java | 13 ++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/spring-grpc-server-spring-boot-autoconfigure/src/main/java/org/springframework/boot/grpc/server/autoconfigure/health/GrpcServerHealthAutoConfiguration.java b/spring-grpc-server-spring-boot-autoconfigure/src/main/java/org/springframework/boot/grpc/server/autoconfigure/health/GrpcServerHealthAutoConfiguration.java index 16de28c2..2ef51ede 100644 --- a/spring-grpc-server-spring-boot-autoconfigure/src/main/java/org/springframework/boot/grpc/server/autoconfigure/health/GrpcServerHealthAutoConfiguration.java +++ b/spring-grpc-server-spring-boot-autoconfigure/src/main/java/org/springframework/boot/grpc/server/autoconfigure/health/GrpcServerHealthAutoConfiguration.java @@ -24,7 +24,6 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionMessage; import org.springframework.boot.autoconfigure.condition.ConditionOutcome; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.SpringBootCondition; @@ -55,13 +54,14 @@ * * @author Daniel Theuke (daniel.theuke@heuboe.de) * @author Chris Bono + * @author Andrey Litvitski * @since 1.0.0 */ @AutoConfiguration(before = GrpcServerFactoryAutoConfiguration.class) @ConditionalOnSpringGrpc @ConditionalOnClass(HealthStatusManager.class) @ConditionalOnGrpcServerEnabled("health") -@ConditionalOnBean(BindableService.class) +@Conditional(GrpcServerHealthAutoConfiguration.OnHealthDefaultEnablementCondition.class) public final class GrpcServerHealthAutoConfiguration { @Bean(destroyMethod = "enterTerminalState") @@ -129,4 +129,25 @@ public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeM } + static class OnHealthDefaultEnablementCondition extends SpringBootCondition { + + @Override + public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { + String propertyName = "spring.grpc.server.health.enabled"; + BindResult enabled = Binder.get(context.getEnvironment()).bind(propertyName, Boolean.class); + if (enabled.isBound()) { + return enabled.get() ? ConditionOutcome.match("Health explicitly enabled") + : ConditionOutcome.noMatch("Health explicitly disabled"); + } + if (context.getBeanFactory() == null) { + return ConditionOutcome.noMatch("Context bean factory is null"); + } + String[] bindableServices = context.getBeanFactory() + .getBeanNamesForType(BindableService.class, false, false); + return (bindableServices.length > 0) ? ConditionOutcome.match("BindableService found") + : ConditionOutcome.noMatch("No BindableService found and health not explicitly enabled"); + } + + } + } diff --git a/spring-grpc-server-spring-boot-autoconfigure/src/test/java/org/springframework/boot/grpc/server/autoconfigure/health/GrpcServerHealthAutoConfigurationTests.java b/spring-grpc-server-spring-boot-autoconfigure/src/test/java/org/springframework/boot/grpc/server/autoconfigure/health/GrpcServerHealthAutoConfigurationTests.java index fe99400d..c42b4a18 100644 --- a/spring-grpc-server-spring-boot-autoconfigure/src/test/java/org/springframework/boot/grpc/server/autoconfigure/health/GrpcServerHealthAutoConfigurationTests.java +++ b/spring-grpc-server-spring-boot-autoconfigure/src/test/java/org/springframework/boot/grpc/server/autoconfigure/health/GrpcServerHealthAutoConfigurationTests.java @@ -130,6 +130,19 @@ void whenServerEnabledPropertySetTrueThenAutoConfigurationIsNotSkipped() { .run((context) -> assertThat(context).hasSingleBean(GrpcServerHealthAutoConfiguration.class)); } + @Test + void whenNoBindableServiceAndHealthPropertyNotSetDoesNotAutoConfigureBeans() { + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(GrpcServerHealthAutoConfiguration.class)) + .run((context) -> assertThat(context).doesNotHaveBean(GrpcServerHealthAutoConfiguration.class)); + } + + @Test + void whenNoBindableServiceAndHealthPropertyTrueAutoConfiguresBeans() { + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(GrpcServerHealthAutoConfiguration.class)) + .withPropertyValues("spring.grpc.server.health.enabled=true") + .run((context) -> assertThat(context).hasSingleBean(GrpcServerHealthAutoConfiguration.class)); + } + @Disabled("Will be tested in an integration test once the Actuator adapter is implemented") @Test void enterTerminalStateIsCalledWhenStatusManagerIsStopped() { From cbfbdbf1cc5458c0cd34206e946ff838fe6eab71 Mon Sep 17 00:00:00 2001 From: Andrey Litvitski Date: Wed, 24 Dec 2025 19:09:18 +0300 Subject: [PATCH 2/2] Clarify default server-side gRPC health behavior Document that server-side gRPC health is enabled by default only when a BindableService is present. Signed-off-by: Andrey Litvitski --- spring-grpc-docs/src/main/antora/modules/ROOT/pages/health.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spring-grpc-docs/src/main/antora/modules/ROOT/pages/health.adoc b/spring-grpc-docs/src/main/antora/modules/ROOT/pages/health.adoc index bdb9be53..b53a0ba6 100644 --- a/spring-grpc-docs/src/main/antora/modules/ROOT/pages/health.adoc +++ b/spring-grpc-docs/src/main/antora/modules/ROOT/pages/health.adoc @@ -7,6 +7,8 @@ The health service is registered with the gRPC server and a `HealthStatusManager IMPORTANT: The health service resides in the `io.grpc:grpc-services` library which is marked as `optional` by Spring gRPC. You must add this dependency to your application in order for it to be autoconfigured. +NOTE: Server-side gRPC health is enabled by default when the application defines at least one `BindableService`. If no server-side gRPC services are present, health is disabled by default and must be explicitly enabled using `spring.grpc.server.health.enabled=true`. + == Actuator Health When Spring Boot Actuator is added to your project and the {spring-boot-docs}/actuator/endpoints.html#actuator.endpoints.health[Health endpoint] is available, the framework will automatically periodically update the health status of a configured list of Spring Boot {spring-boot-docs}/actuator/endpoints.html#actuator.endpoints.health.auto-configured-health-indicators[health indicators], including any ({spring-boot-docs}/actuator/endpoints.html#actuator.endpoints.health.writing-custom-health-indicators[custom indicators]). By default, the aggregate status of the individual indicators is also used to update the overall server status (`""`).