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.autoconfigure.amqp; 018 019import java.time.Duration; 020import java.util.stream.Collectors; 021 022import com.rabbitmq.client.Channel; 023 024import org.springframework.amqp.core.AmqpAdmin; 025import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; 026import org.springframework.amqp.rabbit.connection.ConnectionFactory; 027import org.springframework.amqp.rabbit.connection.ConnectionNameStrategy; 028import org.springframework.amqp.rabbit.connection.RabbitConnectionFactoryBean; 029import org.springframework.amqp.rabbit.core.RabbitAdmin; 030import org.springframework.amqp.rabbit.core.RabbitMessagingTemplate; 031import org.springframework.amqp.rabbit.core.RabbitTemplate; 032import org.springframework.amqp.support.converter.MessageConverter; 033import org.springframework.beans.factory.ObjectProvider; 034import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 035import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 036import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 037import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 038import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; 039import org.springframework.boot.context.properties.EnableConfigurationProperties; 040import org.springframework.boot.context.properties.PropertyMapper; 041import org.springframework.context.annotation.Bean; 042import org.springframework.context.annotation.Configuration; 043import org.springframework.context.annotation.Import; 044 045/** 046 * {@link EnableAutoConfiguration Auto-configuration} for {@link RabbitTemplate}. 047 * <p> 048 * This configuration class is active only when the RabbitMQ and Spring AMQP client 049 * libraries are on the classpath. 050 * <P> 051 * Registers the following beans: 052 * <ul> 053 * <li>{@link org.springframework.amqp.rabbit.core.RabbitTemplate RabbitTemplate} if there 054 * is no other bean of the same type in the context.</li> 055 * <li>{@link org.springframework.amqp.rabbit.connection.CachingConnectionFactory 056 * CachingConnectionFactory} instance if there is no other bean of the same type in the 057 * context.</li> 058 * <li>{@link org.springframework.amqp.core.AmqpAdmin } instance as long as 059 * {@literal spring.rabbitmq.dynamic=true}.</li> 060 * </ul> 061 * <p> 062 * The {@link org.springframework.amqp.rabbit.connection.CachingConnectionFactory} honors 063 * the following properties: 064 * <ul> 065 * <li>{@literal spring.rabbitmq.port} is used to specify the port to which the client 066 * should connect, and defaults to 5672.</li> 067 * <li>{@literal spring.rabbitmq.username} is used to specify the (optional) username. 068 * </li> 069 * <li>{@literal spring.rabbitmq.password} is used to specify the (optional) password. 070 * </li> 071 * <li>{@literal spring.rabbitmq.host} is used to specify the host, and defaults to 072 * {@literal localhost}.</li> 073 * <li>{@literal spring.rabbitmq.virtualHost} is used to specify the (optional) virtual 074 * host to which the client should connect.</li> 075 * </ul> 076 * 077 * @author Greg Turnquist 078 * @author Josh Long 079 * @author Stephane Nicoll 080 * @author Gary Russell 081 * @author Phillip Webb 082 * @author Artsiom Yudovin 083 */ 084@Configuration 085@ConditionalOnClass({ RabbitTemplate.class, Channel.class }) 086@EnableConfigurationProperties(RabbitProperties.class) 087@Import(RabbitAnnotationDrivenConfiguration.class) 088public class RabbitAutoConfiguration { 089 090 @Configuration 091 @ConditionalOnMissingBean(ConnectionFactory.class) 092 protected static class RabbitConnectionFactoryCreator { 093 094 @Bean 095 public CachingConnectionFactory rabbitConnectionFactory( 096 RabbitProperties properties, 097 ObjectProvider<ConnectionNameStrategy> connectionNameStrategy) 098 throws Exception { 099 PropertyMapper map = PropertyMapper.get(); 100 CachingConnectionFactory factory = new CachingConnectionFactory( 101 getRabbitConnectionFactoryBean(properties).getObject()); 102 map.from(properties::determineAddresses).to(factory::setAddresses); 103 map.from(properties::isPublisherConfirms).to(factory::setPublisherConfirms); 104 map.from(properties::isPublisherReturns).to(factory::setPublisherReturns); 105 RabbitProperties.Cache.Channel channel = properties.getCache().getChannel(); 106 map.from(channel::getSize).whenNonNull().to(factory::setChannelCacheSize); 107 map.from(channel::getCheckoutTimeout).whenNonNull().as(Duration::toMillis) 108 .to(factory::setChannelCheckoutTimeout); 109 RabbitProperties.Cache.Connection connection = properties.getCache() 110 .getConnection(); 111 map.from(connection::getMode).whenNonNull().to(factory::setCacheMode); 112 map.from(connection::getSize).whenNonNull() 113 .to(factory::setConnectionCacheSize); 114 map.from(connectionNameStrategy::getIfUnique).whenNonNull() 115 .to(factory::setConnectionNameStrategy); 116 return factory; 117 } 118 119 private RabbitConnectionFactoryBean getRabbitConnectionFactoryBean( 120 RabbitProperties properties) throws Exception { 121 PropertyMapper map = PropertyMapper.get(); 122 RabbitConnectionFactoryBean factory = new RabbitConnectionFactoryBean(); 123 map.from(properties::determineHost).whenNonNull().to(factory::setHost); 124 map.from(properties::determinePort).to(factory::setPort); 125 map.from(properties::determineUsername).whenNonNull() 126 .to(factory::setUsername); 127 map.from(properties::determinePassword).whenNonNull() 128 .to(factory::setPassword); 129 map.from(properties::determineVirtualHost).whenNonNull() 130 .to(factory::setVirtualHost); 131 map.from(properties::getRequestedHeartbeat).whenNonNull() 132 .asInt(Duration::getSeconds).to(factory::setRequestedHeartbeat); 133 RabbitProperties.Ssl ssl = properties.getSsl(); 134 if (ssl.isEnabled()) { 135 factory.setUseSSL(true); 136 map.from(ssl::getAlgorithm).whenNonNull().to(factory::setSslAlgorithm); 137 map.from(ssl::getKeyStoreType).to(factory::setKeyStoreType); 138 map.from(ssl::getKeyStore).to(factory::setKeyStore); 139 map.from(ssl::getKeyStorePassword).to(factory::setKeyStorePassphrase); 140 map.from(ssl::getTrustStoreType).to(factory::setTrustStoreType); 141 map.from(ssl::getTrustStore).to(factory::setTrustStore); 142 map.from(ssl::getTrustStorePassword).to(factory::setTrustStorePassphrase); 143 map.from(ssl::isValidateServerCertificate).to((validate) -> factory 144 .setSkipServerCertificateValidation(!validate)); 145 map.from(ssl::getVerifyHostname) 146 .to(factory::setEnableHostnameVerification); 147 } 148 map.from(properties::getConnectionTimeout).whenNonNull() 149 .asInt(Duration::toMillis).to(factory::setConnectionTimeout); 150 factory.afterPropertiesSet(); 151 return factory; 152 } 153 154 } 155 156 @Configuration 157 @Import(RabbitConnectionFactoryCreator.class) 158 protected static class RabbitTemplateConfiguration { 159 160 private final RabbitProperties properties; 161 162 private final ObjectProvider<MessageConverter> messageConverter; 163 164 private final ObjectProvider<RabbitRetryTemplateCustomizer> retryTemplateCustomizers; 165 166 public RabbitTemplateConfiguration(RabbitProperties properties, 167 ObjectProvider<MessageConverter> messageConverter, 168 ObjectProvider<RabbitRetryTemplateCustomizer> retryTemplateCustomizers) { 169 this.properties = properties; 170 this.messageConverter = messageConverter; 171 this.retryTemplateCustomizers = retryTemplateCustomizers; 172 } 173 174 @Bean 175 @ConditionalOnSingleCandidate(ConnectionFactory.class) 176 @ConditionalOnMissingBean 177 public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) { 178 PropertyMapper map = PropertyMapper.get(); 179 RabbitTemplate template = new RabbitTemplate(connectionFactory); 180 MessageConverter messageConverter = this.messageConverter.getIfUnique(); 181 if (messageConverter != null) { 182 template.setMessageConverter(messageConverter); 183 } 184 template.setMandatory(determineMandatoryFlag()); 185 RabbitProperties.Template properties = this.properties.getTemplate(); 186 if (properties.getRetry().isEnabled()) { 187 template.setRetryTemplate(new RetryTemplateFactory( 188 this.retryTemplateCustomizers.orderedStream() 189 .collect(Collectors.toList())).createRetryTemplate( 190 properties.getRetry(), 191 RabbitRetryTemplateCustomizer.Target.SENDER)); 192 } 193 map.from(properties::getReceiveTimeout).whenNonNull().as(Duration::toMillis) 194 .to(template::setReceiveTimeout); 195 map.from(properties::getReplyTimeout).whenNonNull().as(Duration::toMillis) 196 .to(template::setReplyTimeout); 197 map.from(properties::getExchange).to(template::setExchange); 198 map.from(properties::getRoutingKey).to(template::setRoutingKey); 199 map.from(properties::getDefaultReceiveQueue).whenNonNull() 200 .to(template::setDefaultReceiveQueue); 201 return template; 202 } 203 204 private boolean determineMandatoryFlag() { 205 Boolean mandatory = this.properties.getTemplate().getMandatory(); 206 return (mandatory != null) ? mandatory : this.properties.isPublisherReturns(); 207 } 208 209 @Bean 210 @ConditionalOnSingleCandidate(ConnectionFactory.class) 211 @ConditionalOnProperty(prefix = "spring.rabbitmq", name = "dynamic", matchIfMissing = true) 212 @ConditionalOnMissingBean 213 public AmqpAdmin amqpAdmin(ConnectionFactory connectionFactory) { 214 return new RabbitAdmin(connectionFactory); 215 } 216 217 } 218 219 @Configuration 220 @ConditionalOnClass(RabbitMessagingTemplate.class) 221 @ConditionalOnMissingBean(RabbitMessagingTemplate.class) 222 @Import(RabbitTemplateConfiguration.class) 223 protected static class MessagingTemplateConfiguration { 224 225 @Bean 226 @ConditionalOnSingleCandidate(RabbitTemplate.class) 227 public RabbitMessagingTemplate rabbitMessagingTemplate( 228 RabbitTemplate rabbitTemplate) { 229 return new RabbitMessagingTemplate(rabbitTemplate); 230 } 231 232 } 233 234}