diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfiguration.java index be84c6efa790..26d2ba468166 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfiguration.java @@ -19,6 +19,7 @@ import java.util.Collection; import java.util.Iterator; import java.util.Map; +import java.util.Objects; import java.util.function.Function; import java.util.stream.Collectors; @@ -57,6 +58,7 @@ * @author Stephane Nicoll * @author Arthur Kalimullin * @author Julio Gomez + * @author Safeer Ansari * @since 2.0.0 */ @Configuration(proxyBeanMethods = false) @@ -124,10 +126,13 @@ static class RoutingDataSourceHealthContributor implements CompositeHealthContri private final CompositeHealthContributor delegate; + private static final String UNNAMED_DATASOURCE_KEY = "unnamed"; + RoutingDataSourceHealthContributor(AbstractRoutingDataSource routingDataSource, Function contributorFunction) { Map routedDataSources = routingDataSource.getResolvedDataSources().entrySet().stream() - .collect(Collectors.toMap((e) -> e.getKey().toString(), Map.Entry::getValue)); + .collect(Collectors.toMap((e) -> Objects.toString(e.getKey(), UNNAMED_DATASOURCE_KEY), + Map.Entry::getValue)); this.delegate = CompositeHealthContributor.fromMap(routedDataSources, contributorFunction); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfigurationTests.java index 91d7eac8e534..8316f0acd913 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfigurationTests.java @@ -49,6 +49,7 @@ * * @author Phillip Webb * @author Julio Gomez + * @author Safeer Ansari */ class DataSourceHealthContributorAutoConfigurationTests { @@ -136,6 +137,20 @@ void runWhenDisabledShouldNotCreateIndicator() { .doesNotHaveBean(CompositeHealthContributor.class)); } + @Test + void runWhenDataSourceHasNullRoutingKeyShouldProduceUnnamedComposedIndicator() { + this.contextRunner.withUserConfiguration(NullKeyRoutingDataSourceConfig.class).run((context) -> { + assertThat(context).hasSingleBean(RoutingDataSourceHealthContributor.class); + RoutingDataSourceHealthContributor routingHealthContributor = context + .getBean(RoutingDataSourceHealthContributor.class); + assertThat(routingHealthContributor.getContributor("unnamed")) + .isInstanceOf(DataSourceHealthIndicator.class); + assertThat(routingHealthContributor.getContributor("one")).isInstanceOf(DataSourceHealthIndicator.class); + assertThat(routingHealthContributor.iterator()).toIterable().extracting("name") + .containsExactlyInAnyOrder("unnamed", "one"); + }); + } + @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties static class DataSourceConfig { @@ -164,4 +179,19 @@ AbstractRoutingDataSource routingDataSource() { } + @Configuration(proxyBeanMethods = false) + static class NullKeyRoutingDataSourceConfig { + + @Bean + AbstractRoutingDataSource routingDataSource() { + Map dataSources = new HashMap<>(); + dataSources.put(null, mock(DataSource.class)); + dataSources.put("one", mock(DataSource.class)); + AbstractRoutingDataSource routingDataSource = mock(AbstractRoutingDataSource.class); + given(routingDataSource.getResolvedDataSources()).willReturn(dataSources); + return routingDataSource; + } + + } + }