001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.actuate.autoconfigure.jdbc; 018 019import java.util.Collection; 020import java.util.LinkedHashMap; 021import java.util.Map; 022import java.util.stream.Collectors; 023 024import javax.sql.DataSource; 025 026import org.springframework.beans.factory.InitializingBean; 027import org.springframework.beans.factory.ObjectProvider; 028import org.springframework.boot.actuate.autoconfigure.health.CompositeHealthIndicatorConfiguration; 029import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator; 030import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration; 031import org.springframework.boot.actuate.health.HealthIndicator; 032import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator; 033import org.springframework.boot.autoconfigure.AutoConfigureAfter; 034import org.springframework.boot.autoconfigure.AutoConfigureBefore; 035import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 036import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 037import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 038import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 039import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; 040import org.springframework.boot.jdbc.metadata.CompositeDataSourcePoolMetadataProvider; 041import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadata; 042import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider; 043import org.springframework.context.annotation.Bean; 044import org.springframework.context.annotation.Configuration; 045import org.springframework.jdbc.core.JdbcTemplate; 046import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; 047 048/** 049 * {@link EnableAutoConfiguration Auto-configuration} for 050 * {@link DataSourceHealthIndicator}. 051 * 052 * @author Dave Syer 053 * @author Christian Dupuis 054 * @author Andy Wilkinson 055 * @author Stephane Nicoll 056 * @author Arthur Kalimullin 057 * @since 2.0.0 058 */ 059@Configuration 060@ConditionalOnClass({ JdbcTemplate.class, AbstractRoutingDataSource.class }) 061@ConditionalOnBean(DataSource.class) 062@ConditionalOnEnabledHealthIndicator("db") 063@AutoConfigureBefore(HealthIndicatorAutoConfiguration.class) 064@AutoConfigureAfter(DataSourceAutoConfiguration.class) 065public class DataSourceHealthIndicatorAutoConfiguration extends 066 CompositeHealthIndicatorConfiguration<DataSourceHealthIndicator, DataSource> 067 implements InitializingBean { 068 069 private final Map<String, DataSource> dataSources; 070 071 private final Collection<DataSourcePoolMetadataProvider> metadataProviders; 072 073 private DataSourcePoolMetadataProvider poolMetadataProvider; 074 075 public DataSourceHealthIndicatorAutoConfiguration( 076 ObjectProvider<Map<String, DataSource>> dataSources, 077 ObjectProvider<DataSourcePoolMetadataProvider> metadataProviders) { 078 this.dataSources = filterDataSources(dataSources.getIfAvailable()); 079 this.metadataProviders = metadataProviders.orderedStream() 080 .collect(Collectors.toList()); 081 } 082 083 private Map<String, DataSource> filterDataSources( 084 Map<String, DataSource> candidates) { 085 if (candidates == null) { 086 return null; 087 } 088 Map<String, DataSource> dataSources = new LinkedHashMap<>(); 089 candidates.forEach((name, dataSource) -> { 090 if (!(dataSource instanceof AbstractRoutingDataSource)) { 091 dataSources.put(name, dataSource); 092 } 093 }); 094 return dataSources; 095 } 096 097 @Override 098 public void afterPropertiesSet() throws Exception { 099 this.poolMetadataProvider = new CompositeDataSourcePoolMetadataProvider( 100 this.metadataProviders); 101 } 102 103 @Bean 104 @ConditionalOnMissingBean(name = "dbHealthIndicator") 105 public HealthIndicator dbHealthIndicator() { 106 return createHealthIndicator(this.dataSources); 107 } 108 109 @Override 110 protected DataSourceHealthIndicator createHealthIndicator(DataSource source) { 111 return new DataSourceHealthIndicator(source, getValidationQuery(source)); 112 } 113 114 private String getValidationQuery(DataSource source) { 115 DataSourcePoolMetadata poolMetadata = this.poolMetadataProvider 116 .getDataSourcePoolMetadata(source); 117 return (poolMetadata != null) ? poolMetadata.getValidationQuery() : null; 118 } 119 120}