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}