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