Restore AbstractRoutingDataSource health support
Update `DataSourceHealthContributorAutoConfiguration` so that any
`AbstractRoutingDataSource` beans are still included in the overall
health. Prior to this commit, a regression in Spring Boot 2.2 meant
that if a single routing bean was found an `IllegalArgumentException`
would be thrown.
In Spring Boot 2.1 all `AbstractRoutingDataSource` would be filtered
from the results, but if no results existed the following was returned:
  "details": {
    "db": {
      "status": "UNKNOWN"
    },
In Spring Boot 2.2 we now always include routing datasource beans, even
if other non-routing database beans are found. The health details
includes `"routing" : true` to help users disambiguate any results.
Fixes gh-18661
			
			
This commit is contained in:
		
							parent
							
								
									ba30ee03df
								
							
						
					
					
						commit
						c5138c56ff
					
				| 
						 | 
					@ -17,7 +17,6 @@
 | 
				
			||||||
package org.springframework.boot.actuate.autoconfigure.jdbc;
 | 
					package org.springframework.boot.actuate.autoconfigure.jdbc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.Collection;
 | 
					import java.util.Collection;
 | 
				
			||||||
import java.util.LinkedHashMap;
 | 
					 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
import java.util.stream.Collectors;
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,7 +26,10 @@ import org.springframework.beans.factory.InitializingBean;
 | 
				
			||||||
import org.springframework.beans.factory.ObjectProvider;
 | 
					import org.springframework.beans.factory.ObjectProvider;
 | 
				
			||||||
import org.springframework.boot.actuate.autoconfigure.health.CompositeHealthContributorConfiguration;
 | 
					import org.springframework.boot.actuate.autoconfigure.health.CompositeHealthContributorConfiguration;
 | 
				
			||||||
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
 | 
					import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
 | 
				
			||||||
 | 
					import org.springframework.boot.actuate.health.AbstractHealthIndicator;
 | 
				
			||||||
 | 
					import org.springframework.boot.actuate.health.Health.Builder;
 | 
				
			||||||
import org.springframework.boot.actuate.health.HealthContributor;
 | 
					import org.springframework.boot.actuate.health.HealthContributor;
 | 
				
			||||||
 | 
					import org.springframework.boot.actuate.health.HealthIndicator;
 | 
				
			||||||
import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator;
 | 
					import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator;
 | 
				
			||||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
 | 
					import org.springframework.boot.autoconfigure.AutoConfigureAfter;
 | 
				
			||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 | 
					import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 | 
				
			||||||
| 
						 | 
					@ -60,7 +62,7 @@ import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
 | 
				
			||||||
@ConditionalOnEnabledHealthIndicator("db")
 | 
					@ConditionalOnEnabledHealthIndicator("db")
 | 
				
			||||||
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
 | 
					@AutoConfigureAfter(DataSourceAutoConfiguration.class)
 | 
				
			||||||
public class DataSourceHealthContributorAutoConfiguration extends
 | 
					public class DataSourceHealthContributorAutoConfiguration extends
 | 
				
			||||||
		CompositeHealthContributorConfiguration<DataSourceHealthIndicator, DataSource> implements InitializingBean {
 | 
							CompositeHealthContributorConfiguration<AbstractHealthIndicator, DataSource> implements InitializingBean {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private final Collection<DataSourcePoolMetadataProvider> metadataProviders;
 | 
						private final Collection<DataSourcePoolMetadataProvider> metadataProviders;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -79,24 +81,14 @@ public class DataSourceHealthContributorAutoConfiguration extends
 | 
				
			||||||
	@Bean
 | 
						@Bean
 | 
				
			||||||
	@ConditionalOnMissingBean(name = { "dbHealthIndicator", "dbHealthContributor" })
 | 
						@ConditionalOnMissingBean(name = { "dbHealthIndicator", "dbHealthContributor" })
 | 
				
			||||||
	public HealthContributor dbHealthContributor(Map<String, DataSource> dataSources) {
 | 
						public HealthContributor dbHealthContributor(Map<String, DataSource> dataSources) {
 | 
				
			||||||
		return createContributor(filterDataSources(dataSources));
 | 
							return createContributor(dataSources);
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	private Map<String, DataSource> filterDataSources(Map<String, DataSource> candidates) {
 | 
					 | 
				
			||||||
		if (candidates == null) {
 | 
					 | 
				
			||||||
			return null;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		Map<String, DataSource> dataSources = new LinkedHashMap<>();
 | 
					 | 
				
			||||||
		candidates.forEach((name, dataSource) -> {
 | 
					 | 
				
			||||||
			if (!(dataSource instanceof AbstractRoutingDataSource)) {
 | 
					 | 
				
			||||||
				dataSources.put(name, dataSource);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
		return dataSources;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	protected DataSourceHealthIndicator createIndicator(DataSource source) {
 | 
						protected AbstractHealthIndicator createIndicator(DataSource source) {
 | 
				
			||||||
 | 
							if (source instanceof AbstractRoutingDataSource) {
 | 
				
			||||||
 | 
								return new RoutingDataSourceHealthIndicator();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		return new DataSourceHealthIndicator(source, getValidationQuery(source));
 | 
							return new DataSourceHealthIndicator(source, getValidationQuery(source));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -105,4 +97,17 @@ public class DataSourceHealthContributorAutoConfiguration extends
 | 
				
			||||||
		return (poolMetadata != null) ? poolMetadata.getValidationQuery() : null;
 | 
							return (poolMetadata != null) ? poolMetadata.getValidationQuery() : null;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * {@link HealthIndicator} used for {@link AbstractRoutingDataSource} beans where we
 | 
				
			||||||
 | 
						 * can't actually query for the status.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						static class RoutingDataSourceHealthIndicator extends AbstractHealthIndicator {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							@Override
 | 
				
			||||||
 | 
							protected void doHealthCheck(Builder builder) throws Exception {
 | 
				
			||||||
 | 
								builder.unknown().withDetail("routing", true);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,7 @@ import javax.sql.DataSource;
 | 
				
			||||||
import org.junit.jupiter.api.Test;
 | 
					import org.junit.jupiter.api.Test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
 | 
					import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
 | 
				
			||||||
 | 
					import org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthContributorAutoConfiguration.RoutingDataSourceHealthIndicator;
 | 
				
			||||||
import org.springframework.boot.actuate.health.CompositeHealthContributor;
 | 
					import org.springframework.boot.actuate.health.CompositeHealthContributor;
 | 
				
			||||||
import org.springframework.boot.actuate.health.NamedContributor;
 | 
					import org.springframework.boot.actuate.health.NamedContributor;
 | 
				
			||||||
import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator;
 | 
					import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator;
 | 
				
			||||||
| 
						 | 
					@ -71,10 +72,20 @@ class DataSourceHealthContributorAutoConfigurationTests {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	void runShouldFilterRoutingDataSource() {
 | 
						void runWithRoutingAndEmbeddedDataSourceShouldFilterRoutingDataSource() {
 | 
				
			||||||
		this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, RoutingDatasourceConfig.class)
 | 
							this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, RoutingDatasourceConfig.class)
 | 
				
			||||||
				.run((context) -> assertThat(context).hasSingleBean(DataSourceHealthIndicator.class)
 | 
									.run((context) -> {
 | 
				
			||||||
						.doesNotHaveBean(CompositeHealthContributor.class));
 | 
										CompositeHealthContributor composite = context.getBean(CompositeHealthContributor.class);
 | 
				
			||||||
 | 
										assertThat(composite.getContributor("dataSource")).isInstanceOf(DataSourceHealthIndicator.class);
 | 
				
			||||||
 | 
										assertThat(composite.getContributor("routingDataSource"))
 | 
				
			||||||
 | 
												.isInstanceOf(RoutingDataSourceHealthIndicator.class);
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						void runWithOnlyRoutingDataSourceShouldFilterRoutingDataSource() {
 | 
				
			||||||
 | 
							this.contextRunner.withUserConfiguration(RoutingDatasourceConfig.class)
 | 
				
			||||||
 | 
									.run((context) -> assertThat(context).hasSingleBean(RoutingDataSourceHealthIndicator.class));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue