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}