diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/AbstractConnectionFactoryConfigurer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/AbstractConnectionFactoryConfigurer.java new file mode 100644 index 0000000000..3ece213986 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/AbstractConnectionFactoryConfigurer.java @@ -0,0 +1,61 @@ +package org.springframework.boot.autoconfigure.amqp; + +import org.springframework.amqp.rabbit.connection.AbstractConnectionFactory; +import org.springframework.amqp.rabbit.connection.ConnectionNameStrategy; +import org.springframework.boot.context.properties.PropertyMapper; +import org.springframework.util.Assert; + +/** + * Configures {@link AbstractConnectionFactory Rabbit ConnectionFactory} with sensible + * defaults. + * + * @param the connection factory type. + * @author Chris Bono + * @since 2.6.0 + */ +public abstract class AbstractConnectionFactoryConfigurer { + + private RabbitProperties rabbitProperties; + + private ConnectionNameStrategy connectionNameStrategy; + + public RabbitProperties getRabbitProperties() { + return rabbitProperties; + } + + public void setRabbitProperties(RabbitProperties rabbitProperties) { + this.rabbitProperties = rabbitProperties; + } + + public ConnectionNameStrategy getConnectionNameStrategy() { + return connectionNameStrategy; + } + + public void setConnectionNameStrategy(ConnectionNameStrategy connectionNameStrategy) { + this.connectionNameStrategy = connectionNameStrategy; + } + + /** + * Configure the specified Rabbit connection factory - delegating to + * {@link #configureSpecific} for the connection factory implementation specific + * settings. The factory can be further tuned and default settings can be overridden. + * @param connectionFactory the connection factory instance to configure + */ + public void configure(T connectionFactory) { + Assert.notNull(connectionFactory, "ConnectionFactory must not be null"); + PropertyMapper map = PropertyMapper.get(); + map.from(this.rabbitProperties::determineAddresses).to(connectionFactory::setAddresses); + map.from(this.rabbitProperties::getAddressShuffleMode).whenNonNull() + .to(connectionFactory::setAddressShuffleMode); + map.from(connectionNameStrategy).whenNonNull().to(connectionFactory::setConnectionNameStrategy); + configureSpecific(connectionFactory); + } + + /** + * Configure the specified Rabbit connection factory with implementation specific + * settings. + * @param connectionFactory the connection factory instance to configure + */ + protected abstract void configureSpecific(T connectionFactory); + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/CachingConnectionFactoryConfigurer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/CachingConnectionFactoryConfigurer.java new file mode 100644 index 0000000000..455508210d --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/CachingConnectionFactoryConfigurer.java @@ -0,0 +1,38 @@ +package org.springframework.boot.autoconfigure.amqp; + +import java.time.Duration; + +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; +import org.springframework.boot.context.properties.PropertyMapper; + +/** + * Configures {@link CachingConnectionFactory Rabbit CachingConnectionFactory} with + * sensible defaults. + * + * @author Chris Bono + * @since 2.6.0 + */ +public class CachingConnectionFactoryConfigurer extends AbstractConnectionFactoryConfigurer { + + /** + * Configure the specified Rabbit caching connection factory with implementation + * specific settings. + * @param connectionFactory the {@link CachingConnectionFactory} instance to configure + */ + @Override + public void configureSpecific(CachingConnectionFactory connectionFactory) { + PropertyMapper map = PropertyMapper.get(); + RabbitProperties rabbitProperties = getRabbitProperties(); + map.from(rabbitProperties::isPublisherReturns).to(connectionFactory::setPublisherReturns); + map.from(rabbitProperties::getPublisherConfirmType).whenNonNull() + .to(connectionFactory::setPublisherConfirmType); + RabbitProperties.Cache.Channel channel = rabbitProperties.getCache().getChannel(); + map.from(channel::getSize).whenNonNull().to(connectionFactory::setChannelCacheSize); + map.from(channel::getCheckoutTimeout).whenNonNull().as(Duration::toMillis) + .to(connectionFactory::setChannelCheckoutTimeout); + RabbitProperties.Cache.Connection connection = rabbitProperties.getCache().getConnection(); + map.from(connection::getMode).whenNonNull().to(connectionFactory::setCacheMode); + map.from(connection::getSize).whenNonNull().to(connectionFactory::setConnectionCacheSize); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration.java index 3c09a5b2fd..bb76ce7014 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration.java @@ -16,7 +16,6 @@ package org.springframework.boot.autoconfigure.amqp; -import java.time.Duration; import java.util.stream.Collectors; import com.rabbitmq.client.Channel; @@ -40,7 +39,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -84,6 +82,7 @@ import org.springframework.core.io.ResourceLoader; * @author Gary Russell * @author Phillip Webb * @author Artsiom Yudovin + * @author Chris Bono * @since 1.0.0 */ @Configuration(proxyBeanMethods = false) @@ -93,73 +92,48 @@ import org.springframework.core.io.ResourceLoader; public class RabbitAutoConfiguration { @Configuration(proxyBeanMethods = false) - @ConditionalOnMissingBean(ConnectionFactory.class) protected static class RabbitConnectionFactoryCreator { @Bean - public CachingConnectionFactory rabbitConnectionFactory(RabbitProperties properties, - ResourceLoader resourceLoader, ObjectProvider credentialsProvider, - ObjectProvider credentialsRefreshService, - ObjectProvider connectionNameStrategy, - ObjectProvider connectionFactoryCustomizers) throws Exception { - com.rabbitmq.client.ConnectionFactory connectionFactory = getRabbitConnectionFactoryBean(properties, - resourceLoader, credentialsProvider, credentialsRefreshService).getObject(); - connectionFactoryCustomizers.orderedStream() - .forEach((customizer) -> customizer.customize(connectionFactory)); - CachingConnectionFactory factory = new CachingConnectionFactory(connectionFactory); - PropertyMapper map = PropertyMapper.get(); - map.from(properties::determineAddresses).to(factory::setAddresses); - map.from(properties::getAddressShuffleMode).whenNonNull().to(factory::setAddressShuffleMode); - map.from(properties::isPublisherReturns).to(factory::setPublisherReturns); - map.from(properties::getPublisherConfirmType).whenNonNull().to(factory::setPublisherConfirmType); - RabbitProperties.Cache.Channel channel = properties.getCache().getChannel(); - map.from(channel::getSize).whenNonNull().to(factory::setChannelCacheSize); - map.from(channel::getCheckoutTimeout).whenNonNull().as(Duration::toMillis) - .to(factory::setChannelCheckoutTimeout); - RabbitProperties.Cache.Connection connection = properties.getCache().getConnection(); - map.from(connection::getMode).whenNonNull().to(factory::setCacheMode); - map.from(connection::getSize).whenNonNull().to(factory::setConnectionCacheSize); - map.from(connectionNameStrategy::getIfUnique).whenNonNull().to(factory::setConnectionNameStrategy); - return factory; - } - - private RabbitConnectionFactoryBean getRabbitConnectionFactoryBean(RabbitProperties properties, + @ConditionalOnMissingBean + RabbitConnectionFactoryBeanConfigurer rabbitConnectionFactoryBeanConfigurer(RabbitProperties properties, ResourceLoader resourceLoader, ObjectProvider credentialsProvider, ObjectProvider credentialsRefreshService) { - RabbitConnectionFactoryBean factory = new RabbitConnectionFactoryBean(); - factory.setResourceLoader(resourceLoader); - PropertyMapper map = PropertyMapper.get(); - map.from(properties::determineHost).whenNonNull().to(factory::setHost); - map.from(properties::determinePort).to(factory::setPort); - map.from(properties::determineUsername).whenNonNull().to(factory::setUsername); - map.from(properties::determinePassword).whenNonNull().to(factory::setPassword); - map.from(properties::determineVirtualHost).whenNonNull().to(factory::setVirtualHost); - map.from(properties::getRequestedHeartbeat).whenNonNull().asInt(Duration::getSeconds) - .to(factory::setRequestedHeartbeat); - map.from(properties::getRequestedChannelMax).to(factory::setRequestedChannelMax); - RabbitProperties.Ssl ssl = properties.getSsl(); - if (ssl.determineEnabled()) { - factory.setUseSSL(true); - map.from(ssl::getAlgorithm).whenNonNull().to(factory::setSslAlgorithm); - map.from(ssl::getKeyStoreType).to(factory::setKeyStoreType); - map.from(ssl::getKeyStore).to(factory::setKeyStore); - map.from(ssl::getKeyStorePassword).to(factory::setKeyStorePassphrase); - map.from(ssl::getKeyStoreAlgorithm).whenNonNull().to(factory::setKeyStoreAlgorithm); - map.from(ssl::getTrustStoreType).to(factory::setTrustStoreType); - map.from(ssl::getTrustStore).to(factory::setTrustStore); - map.from(ssl::getTrustStorePassword).to(factory::setTrustStorePassphrase); - map.from(ssl::getTrustStoreAlgorithm).whenNonNull().to(factory::setTrustStoreAlgorithm); - map.from(ssl::isValidateServerCertificate) - .to((validate) -> factory.setSkipServerCertificateValidation(!validate)); - map.from(ssl::getVerifyHostname).to(factory::setEnableHostnameVerification); - } - map.from(properties::getConnectionTimeout).whenNonNull().asInt(Duration::toMillis) - .to(factory::setConnectionTimeout); - map.from(properties::getChannelRpcTimeout).whenNonNull().asInt(Duration::toMillis) - .to(factory::setChannelRpcTimeout); - map.from(credentialsProvider::getIfUnique).whenNonNull().to(factory::setCredentialsProvider); - map.from(credentialsRefreshService::getIfUnique).whenNonNull().to(factory::setCredentialsRefreshService); - factory.afterPropertiesSet(); + RabbitConnectionFactoryBeanConfigurer configurer = new RabbitConnectionFactoryBeanConfigurer(); + configurer.setRabbitProperties(properties); + configurer.setResourceLoader(resourceLoader); + configurer.setCredentialsProvider(credentialsProvider.getIfUnique()); + configurer.setCredentialsRefreshService(credentialsRefreshService.getIfUnique()); + return configurer; + } + + @Bean + @ConditionalOnMissingBean + CachingConnectionFactoryConfigurer rabbitConnectionFactoryConfigurer(RabbitProperties rabbitProperties, + ObjectProvider connectionNameStrategy) { + CachingConnectionFactoryConfigurer configurer = new CachingConnectionFactoryConfigurer(); + configurer.setRabbitProperties(rabbitProperties); + configurer.setConnectionNameStrategy(connectionNameStrategy.getIfUnique()); + return configurer; + } + + @Bean + @ConditionalOnMissingBean(ConnectionFactory.class) + CachingConnectionFactory rabbitConnectionFactory( + RabbitConnectionFactoryBeanConfigurer rabbitConnectionFactoryBeanConfigurer, + CachingConnectionFactoryConfigurer rabbitCachingConnectionFactoryConfigurer, + ObjectProvider connectionFactoryCustomizers) throws Exception { + + RabbitConnectionFactoryBean connectionFactoryBean = new RabbitConnectionFactoryBean(); + rabbitConnectionFactoryBeanConfigurer.configure(connectionFactoryBean); + connectionFactoryBean.afterPropertiesSet(); + com.rabbitmq.client.ConnectionFactory connectionFactory = connectionFactoryBean.getObject(); + connectionFactoryCustomizers.orderedStream() + .forEach((customizer) -> customizer.customize(connectionFactory)); + + CachingConnectionFactory factory = new CachingConnectionFactory(connectionFactory); + rabbitCachingConnectionFactoryConfigurer.configure(factory); + return factory; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitConnectionFactoryBeanConfigurer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitConnectionFactoryBeanConfigurer.java new file mode 100644 index 0000000000..568a5094b6 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitConnectionFactoryBeanConfigurer.java @@ -0,0 +1,104 @@ +package org.springframework.boot.autoconfigure.amqp; + +import java.time.Duration; + +import com.rabbitmq.client.impl.CredentialsProvider; +import com.rabbitmq.client.impl.CredentialsRefreshService; + +import org.springframework.amqp.rabbit.connection.RabbitConnectionFactoryBean; +import org.springframework.boot.context.properties.PropertyMapper; +import org.springframework.core.io.ResourceLoader; +import org.springframework.util.Assert; + +/** + * Configures {@link RabbitConnectionFactoryBean} with sensible defaults. + * + * @author Chris Bono + * @since 2.6.0 + */ +public class RabbitConnectionFactoryBeanConfigurer { + + private RabbitProperties rabbitProperties; + + private ResourceLoader resourceLoader; + + private CredentialsProvider credentialsProvider; + + private CredentialsRefreshService credentialsRefreshService; + + public RabbitProperties getRabbitProperties() { + return rabbitProperties; + } + + public void setRabbitProperties(RabbitProperties rabbitProperties) { + this.rabbitProperties = rabbitProperties; + } + + public ResourceLoader getResourceLoader() { + return resourceLoader; + } + + public void setResourceLoader(ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + } + + public CredentialsProvider getCredentialsProvider() { + return credentialsProvider; + } + + public void setCredentialsProvider(CredentialsProvider credentialsProvider) { + this.credentialsProvider = credentialsProvider; + } + + public CredentialsRefreshService getCredentialsRefreshService() { + return credentialsRefreshService; + } + + public void setCredentialsRefreshService(CredentialsRefreshService credentialsRefreshService) { + this.credentialsRefreshService = credentialsRefreshService; + } + + /** + * Configure the specified rabbit connection factory bean. The factory bean can be + * further tuned and default settings can be overridden. It is the repsonsiblity of + * the caller to invoke {@link RabbitConnectionFactoryBean#afterPropertiesSet()} + * though. + * @param factory the {@link RabbitConnectionFactoryBean} instance to configure + */ + public void configure(RabbitConnectionFactoryBean factory) { + Assert.notNull(factory, "RabbitConnectionFactoryBean must not be null"); + factory.setResourceLoader(resourceLoader); + PropertyMapper map = PropertyMapper.get(); + map.from(this.rabbitProperties::determineHost).whenNonNull().to(factory::setHost); + map.from(this.rabbitProperties::determinePort).to(factory::setPort); + map.from(this.rabbitProperties::determineUsername).whenNonNull().to(factory::setUsername); + map.from(this.rabbitProperties::determinePassword).whenNonNull().to(factory::setPassword); + map.from(this.rabbitProperties::determineVirtualHost).whenNonNull().to(factory::setVirtualHost); + map.from(this.rabbitProperties::getRequestedHeartbeat).whenNonNull().asInt(Duration::getSeconds) + .to(factory::setRequestedHeartbeat); + map.from(this.rabbitProperties::getRequestedChannelMax).to(factory::setRequestedChannelMax); + RabbitProperties.Ssl ssl = this.rabbitProperties.getSsl(); + if (ssl.determineEnabled()) { + factory.setUseSSL(true); + map.from(ssl::getAlgorithm).whenNonNull().to(factory::setSslAlgorithm); + map.from(ssl::getKeyStoreType).to(factory::setKeyStoreType); + map.from(ssl::getKeyStore).to(factory::setKeyStore); + map.from(ssl::getKeyStorePassword).to(factory::setKeyStorePassphrase); + map.from(ssl::getKeyStoreAlgorithm).whenNonNull().to(factory::setKeyStoreAlgorithm); + map.from(ssl::getTrustStoreType).to(factory::setTrustStoreType); + map.from(ssl::getTrustStore).to(factory::setTrustStore); + map.from(ssl::getTrustStorePassword).to(factory::setTrustStorePassphrase); + map.from(ssl::getTrustStoreAlgorithm).whenNonNull().to(factory::setTrustStoreAlgorithm); + map.from(ssl::isValidateServerCertificate) + .to((validate) -> factory.setSkipServerCertificateValidation(!validate)); + map.from(ssl::getVerifyHostname).to(factory::setEnableHostnameVerification); + } + map.from(this.rabbitProperties::getConnectionTimeout).whenNonNull().asInt(Duration::toMillis) + .to(factory::setConnectionTimeout); + map.from(this.rabbitProperties::getChannelRpcTimeout).whenNonNull().asInt(Duration::toMillis) + .to(factory::setChannelRpcTimeout); + map.from(credentialsProvider).whenNonNull().to(factory::setCredentialsProvider); + map.from(credentialsRefreshService).whenNonNull().to(factory::setCredentialsRefreshService); + } + +}