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 00000000000..55ce5e0d2c9 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/AbstractConnectionFactoryConfigurer.java @@ -0,0 +1,73 @@ +/* + * Copyright 2012-2021 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 + * + * https://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.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 final RabbitProperties rabbitProperties; + + private ConnectionNameStrategy connectionNameStrategy; + + protected AbstractConnectionFactoryConfigurer(RabbitProperties properties) { + Assert.notNull(properties, "RabbitProperties must not be null"); + this.rabbitProperties = properties; + } + + protected final ConnectionNameStrategy getConnectionNameStrategy() { + return this.connectionNameStrategy; + } + + protected final void setConnectionNameStrategy(ConnectionNameStrategy connectionNameStrategy) { + this.connectionNameStrategy = connectionNameStrategy; + } + + /** + * Configures the given {@code connectionFactory} with sensible defaults. + * @param connectionFactory connection factory to configure + */ + public final 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(this.connectionNameStrategy).whenNonNull().to(connectionFactory::setConnectionNameStrategy); + configure(connectionFactory, this.rabbitProperties); + } + + /** + * Configures the given {@code connectionFactory} using the given + * {@code rabbitProperties}. + * @param connectionFactory connection factory to configure + * @param rabbitProperties properties to use for the configuration + */ + protected abstract void configure(T connectionFactory, RabbitProperties rabbitProperties); + +} 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 00000000000..1c8ecdcca7c --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/CachingConnectionFactoryConfigurer.java @@ -0,0 +1,51 @@ +/* + * Copyright 2012-2021 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 + * + * https://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.autoconfigure.amqp; + +import java.time.Duration; + +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; +import org.springframework.boot.context.properties.PropertyMapper; + +/** + * Configures Rabbit {@link CachingConnectionFactory} with sensible defaults. + * + * @author Chris Bono + * @since 2.6.0 + */ +public class CachingConnectionFactoryConfigurer extends AbstractConnectionFactoryConfigurer { + + public CachingConnectionFactoryConfigurer(RabbitProperties properties) { + super(properties); + } + + @Override + public void configure(CachingConnectionFactory connectionFactory, RabbitProperties rabbitProperties) { + PropertyMapper map = PropertyMapper.get(); + 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 3c09a5b2fdf..6a8740b24d9 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,46 @@ 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, + @ConditionalOnMissingBean + RabbitConnectionFactoryBeanConfigurer rabbitConnectionFactoryBeanConfigurer(RabbitProperties properties, ResourceLoader resourceLoader, ObjectProvider credentialsProvider, - ObjectProvider credentialsRefreshService, - ObjectProvider connectionNameStrategy, + ObjectProvider credentialsRefreshService) { + RabbitConnectionFactoryBeanConfigurer configurer = new RabbitConnectionFactoryBeanConfigurer(resourceLoader, + properties); + configurer.setCredentialsProvider(credentialsProvider.getIfUnique()); + configurer.setCredentialsRefreshService(credentialsRefreshService.getIfUnique()); + return configurer; + } + + @Bean + @ConditionalOnMissingBean + CachingConnectionFactoryConfigurer rabbitConnectionFactoryConfigurer(RabbitProperties rabbitProperties, + ObjectProvider connectionNameStrategy) { + CachingConnectionFactoryConfigurer configurer = new CachingConnectionFactoryConfigurer(rabbitProperties); + configurer.setConnectionNameStrategy(connectionNameStrategy.getIfUnique()); + return configurer; + } + + @Bean + @ConditionalOnMissingBean(ConnectionFactory.class) + CachingConnectionFactory rabbitConnectionFactory( + RabbitConnectionFactoryBeanConfigurer rabbitConnectionFactoryBeanConfigurer, + CachingConnectionFactoryConfigurer rabbitCachingConnectionFactoryConfigurer, ObjectProvider connectionFactoryCustomizers) throws Exception { - com.rabbitmq.client.ConnectionFactory connectionFactory = getRabbitConnectionFactoryBean(properties, - resourceLoader, credentialsProvider, credentialsRefreshService).getObject(); + + 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); - 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; - } + rabbitCachingConnectionFactoryConfigurer.configure(factory); - private RabbitConnectionFactoryBean getRabbitConnectionFactoryBean(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(); 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 00000000000..8ec5ee0a2f5 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitConnectionFactoryBeanConfigurer.java @@ -0,0 +1,101 @@ +/* + * Copyright 2012-2020 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 + * + * https://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.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 final RabbitProperties rabbitProperties; + + private final ResourceLoader resourceLoader; + + private CredentialsProvider credentialsProvider; + + private CredentialsRefreshService credentialsRefreshService; + + public RabbitConnectionFactoryBeanConfigurer(ResourceLoader resourceLoader, RabbitProperties properties) { + this.resourceLoader = resourceLoader; + this.rabbitProperties = properties; + } + + public void setCredentialsProvider(CredentialsProvider credentialsProvider) { + this.credentialsProvider = credentialsProvider; + } + + 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(this.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(this.credentialsProvider).whenNonNull().to(factory::setCredentialsProvider); + map.from(this.credentialsRefreshService).whenNonNull().to(factory::setCredentialsRefreshService); + } + +}