/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.endpoint;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadata;
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvider;
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProviders;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Primary;
/**
* A {@link PublicMetrics} implementation that provides data source usage statistics.
*
* @author Stephane Nicoll
* @since 1.2.0
*/
public class DataSourcePublicMetrics implements PublicMetrics {
private static final String DATASOURCE_SUFFIX = "dataSource";
@Autowired
private ApplicationContext applicationContext;
@Autowired
private Collection<DataSourcePoolMetadataProvider> providers;
private final Map<String, DataSourcePoolMetadata> metadataByPrefix = new HashMap<>();
@PostConstruct
public void initialize() {
DataSource primaryDataSource = getPrimaryDataSource();
DataSourcePoolMetadataProvider provider = new DataSourcePoolMetadataProviders(
this.providers);
for (Map.Entry<String, DataSource> entry : this.applicationContext
.getBeansOfType(DataSource.class).entrySet()) {
String beanName = entry.getKey();
DataSource bean = entry.getValue();
String prefix = createPrefix(beanName, bean, bean.equals(primaryDataSource));
DataSourcePoolMetadata poolMetadata = provider
.getDataSourcePoolMetadata(bean);
if (poolMetadata != null) {
this.metadataByPrefix.put(prefix, poolMetadata);
}
}
}
@Override
public Collection<Metric<?>> metrics() {
Set<Metric<?>> metrics = new LinkedHashSet<>();
for (Map.Entry<String, DataSourcePoolMetadata> entry : this.metadataByPrefix
.entrySet()) {
String prefix = entry.getKey();
prefix = (prefix.endsWith(".") ? prefix : prefix + ".");
DataSourcePoolMetadata metadata = entry.getValue();
addMetric(metrics, prefix + "active", metadata.getActive());
addMetric(metrics, prefix + "usage", metadata.getUsage());
}
return metrics;
}
private <T extends Number> void addMetric(Set<Metric<?>> metrics, String name,
T value) {
if (value != null) {
metrics.add(new Metric<>(name, value));
}
}
/**
* Create the prefix to use for the metrics to associate with the given
* {@link DataSource}.
* @param name the name of the data source bean
* @param dataSource the data source to configure
* @param primary if this data source is the primary data source
* @return a prefix for the given data source
*/
protected String createPrefix(String name, DataSource dataSource, boolean primary) {
if (primary) {
return "datasource.primary";
}
if (name.length() > DATASOURCE_SUFFIX.length()
&& name.toLowerCase().endsWith(DATASOURCE_SUFFIX.toLowerCase())) {
name = name.substring(0, name.length() - DATASOURCE_SUFFIX.length());
}
return "datasource." + name;
}
/**
* Attempt to locate the primary {@link DataSource} (i.e. either the only data source
* available or the one amongst the candidates marked as {@link Primary}). Return
* {@code null} if there no primary data source could be found.
* @return the primary datasource
*/
private DataSource getPrimaryDataSource() {
try {
return this.applicationContext.getBean(DataSource.class);
}
catch (NoSuchBeanDefinitionException ex) {
return null;
}
}
}