/* * Copyright 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 com.github.gavlyukovskiy.boot.jdbc.decorator.flexypool; import com.github.gavlyukovskiy.boot.jdbc.decorator.DataSourceDecoratorProperties; import com.github.gavlyukovskiy.boot.jdbc.decorator.flexypool.FlexyPoolProperties.AcquiringStrategy.IncrementPool; import com.github.gavlyukovskiy.boot.jdbc.decorator.flexypool.FlexyPoolProperties.AcquiringStrategy.Retry; import com.vladmihalcea.flexypool.FlexyPoolDataSource; import com.vladmihalcea.flexypool.adaptor.DBCP2PoolAdapter; import com.vladmihalcea.flexypool.adaptor.HikariCPPoolAdapter; import com.vladmihalcea.flexypool.adaptor.TomcatCPPoolAdapter; import com.vladmihalcea.flexypool.config.PropertyLoader; import com.vladmihalcea.flexypool.connection.ConnectionProxyFactory; import com.vladmihalcea.flexypool.event.Event; import com.vladmihalcea.flexypool.event.EventListener; import com.vladmihalcea.flexypool.metric.MetricsFactory; import com.vladmihalcea.flexypool.metric.micrometer.MicrometerMetrics; import com.vladmihalcea.flexypool.strategy.ConnectionAcquiringStrategyFactory; import com.vladmihalcea.flexypool.strategy.IncrementPoolOnTimeoutConnectionAcquiringStrategy; import com.vladmihalcea.flexypool.strategy.RetryConnectionAcquiringStrategy; import com.vladmihalcea.flexypool.util.ClassLoaderUtils; import com.zaxxer.hikari.HikariDataSource; import org.apache.commons.dbcp2.BasicDataSource; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionMessage; import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.core.type.AnnotatedTypeMetadata; import javax.annotation.PostConstruct; import javax.sql.DataSource; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import static org.slf4j.LoggerFactory.getLogger; /** * Configuration for integration with flexy-pool, allows to use define custom {@link ConnectionAcquiringStrategyFactory}, * {@link MetricsFactory}, {@link ConnectionProxyFactory} and {@link EventListener}. * * @author Arthur Gavlyukovskiy * @since 1.2 */ public class FlexyPoolConfiguration { static <T extends DataSource> List<ConnectionAcquiringStrategyFactory<?, T>> mergeFactories( List<ConnectionAcquiringStrategyFactory<?, T>> factories, FlexyPoolProperties flexyPool) { List<ConnectionAcquiringStrategyFactory<?, T>> newFactories = new ArrayList<>(); List<? extends Class<?>> factoryClasses; if (factories != null) { factoryClasses = factories.stream().map(Object::getClass).collect(Collectors.toList()); newFactories.addAll(factories); } else { factoryClasses = Collections.emptyList(); } if (!factoryClasses.contains(IncrementPoolOnTimeoutConnectionAcquiringStrategy.Factory.class)) { IncrementPool incrementPool = flexyPool.getAcquiringStrategy().getIncrementPool(); if (incrementPool.getMaxOverflowPoolSize() > 0) { newFactories.add(new IncrementPoolOnTimeoutConnectionAcquiringStrategy.Factory<>( incrementPool.getMaxOverflowPoolSize(), incrementPool.getTimeoutMillis())); } } if (!factoryClasses.contains(RetryConnectionAcquiringStrategy.Factory.class)) { Retry retry = flexyPool.getAcquiringStrategy().getRetry(); if (retry.getAttempts() > 0) { newFactories.add(new RetryConnectionAcquiringStrategy.Factory<>(retry.getAttempts())); } } return newFactories; } @ConditionalOnClass(FlexyPoolDataSource.class) @Import({ MicrometerConfiguration.class, PropertyFlexyConfiguration.class, HikariFlexyConfiguration.class, TomcatFlexyConfiguration.class, Dbcp2FlexyConfiguration.class, FlexyPoolCustomizerConfiguration.class }) public static class Ordered { } @ConditionalOnMissingBean(PropertyFlexyConfiguration.class) static class FlexyPoolCustomizerConfiguration { @Autowired private DataSourceDecoratorProperties dataSourceDecoratorProperties; @Autowired(required = false) private MetricsFactory metricsFactory; @Autowired(required = false) private ConnectionProxyFactory connectionProxyFactory; @Autowired(required = false) private List<EventListener<? extends Event>> eventListeners; @Bean public FlexyPoolConfigurationBuilderCustomizer flexyPoolConfigurationBuilderCustomizer() { return (beanName, builder, dataSourceClass) -> { FlexyPoolProperties flexyPool = dataSourceDecoratorProperties.getFlexyPool(); builder.setMetricLogReporterMillis(flexyPool.getMetrics().getReporter().getLog().getMillis()); builder.setJmxEnabled(flexyPool.getMetrics().getReporter().getJmx().isEnabled()); builder.setJmxAutoStart(flexyPool.getMetrics().getReporter().getJmx().isAutoStart()); builder.setConnectionAcquireTimeThresholdMillis(flexyPool.getThreshold().getConnection().getAcquire()); builder.setConnectionLeaseTimeThresholdMillis(flexyPool.getThreshold().getConnection().getLease()); if (metricsFactory != null) { builder.setMetricsFactory(metricsFactory); } if (connectionProxyFactory != null) { builder.setConnectionProxyFactory(connectionProxyFactory); } if (eventListeners != null) { builder.setEventListenerResolver(() -> eventListeners); } }; } } @Configuration @ConditionalOnMissingBean(FlexyPoolDataSourceDecorator.class) @ConditionalOnClass(HikariCPPoolAdapter.class) @ConditionalOnBean(HikariDataSource.class) static class HikariFlexyConfiguration { @Autowired(required = false) private List<ConnectionAcquiringStrategyFactory<?, HikariDataSource>> connectionAcquiringStrategyFactories; @Autowired private DataSourceDecoratorProperties dataSourceDecoratorProperties; @Bean public FlexyPoolDataSourceDecorator flexyPoolDataSourceDecorator() { return new FlexyPoolDataSourceDecorator( mergeFactories(connectionAcquiringStrategyFactories, dataSourceDecoratorProperties.getFlexyPool()), HikariCPPoolAdapter.FACTORY, HikariDataSource.class); } } @Configuration @ConditionalOnMissingBean(FlexyPoolDataSourceDecorator.class) @ConditionalOnClass(TomcatCPPoolAdapter.class) @ConditionalOnBean(org.apache.tomcat.jdbc.pool.DataSource.class) static class TomcatFlexyConfiguration { @Autowired(required = false) private List<ConnectionAcquiringStrategyFactory<?, org.apache.tomcat.jdbc.pool.DataSource>> connectionAcquiringStrategyFactories; @Autowired private DataSourceDecoratorProperties dataSourceDecoratorProperties; @Bean public FlexyPoolDataSourceDecorator flexyPoolDataSourceDecorator() { return new FlexyPoolDataSourceDecorator( mergeFactories(connectionAcquiringStrategyFactories, dataSourceDecoratorProperties.getFlexyPool()), TomcatCPPoolAdapter.FACTORY, org.apache.tomcat.jdbc.pool.DataSource.class); } } @Configuration @ConditionalOnMissingBean(FlexyPoolDataSourceDecorator.class) @ConditionalOnClass(DBCP2PoolAdapter.class) @ConditionalOnBean(BasicDataSource.class) static class Dbcp2FlexyConfiguration { @Autowired(required = false) private List<ConnectionAcquiringStrategyFactory<?, BasicDataSource>> connectionAcquiringStrategyFactories; @Autowired private DataSourceDecoratorProperties dataSourceDecoratorProperties; @Bean public FlexyPoolDataSourceDecorator flexyPoolDataSourceDecorator() { return new FlexyPoolDataSourceDecorator( mergeFactories(connectionAcquiringStrategyFactories, dataSourceDecoratorProperties.getFlexyPool()), DBCP2PoolAdapter.FACTORY, BasicDataSource.class); } } @Configuration @ConditionalOnMissingBean(FlexyPoolDataSourceDecorator.class) @Conditional(FlexyPoolConfiguration.FlexyPoolConfigurationAvailableCondition.class) static class PropertyFlexyConfiguration { private static final Logger log = getLogger(PropertyFlexyConfiguration.class); @Autowired(required = false) private List<ConnectionAcquiringStrategyFactory<?, javax.sql.DataSource>> connectionAcquiringStrategyFactories; @PostConstruct public void warnIfAnyStrategyFound() { if (connectionAcquiringStrategyFactories != null) { log.warn("ConnectionAcquiringStrategyFactory beans found in the context will not be applied to " + "FlexyDataSource due to property based configuration of FlexyPool"); } } @Bean public FlexyPoolDataSourceDecorator flexyPoolDataSourceDecorator() { return new FlexyPoolDataSourceDecorator(); } } @Configuration @ConditionalOnClass(MicrometerMetrics.class) static class MicrometerConfiguration { @Bean @ConditionalOnMissingBean public MetricsFactory metricsFactory() { return MicrometerMetrics.FACTORY; } } private static class FlexyPoolConfigurationAvailableCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { ConditionMessage.Builder message = ConditionMessage.forCondition("FlexyPoolConfigurationAvailable"); String propertiesFilePath = System.getProperty(PropertyLoader.PROPERTIES_FILE_PATH); if (propertiesFilePath != null && ClassLoaderUtils.getResource(propertiesFilePath) != null) { return ConditionOutcome.match(message.found("FlexyPool configuration file").items(propertiesFilePath)); } if (ClassLoaderUtils.getResource(PropertyLoader.PROPERTIES_FILE_NAME) != null) { return ConditionOutcome.match(message.found("FlexyPool configuration file").items(PropertyLoader.PROPERTIES_FILE_NAME)); } return ConditionOutcome.noMatch(message.didNotFind("FlexyPool configuration file").atAll()); } } }