diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAnnotationDrivenConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAnnotationDrivenConfiguration.java index 0bf5f33f4f..8c94af415b 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAnnotationDrivenConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAnnotationDrivenConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -20,7 +20,6 @@ import org.springframework.amqp.rabbit.annotation.EnableRabbit; import org.springframework.amqp.rabbit.config.RabbitListenerConfigUtils; import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; import org.springframework.amqp.rabbit.connection.ConnectionFactory; -import org.springframework.boot.autoconfigure.amqp.RabbitProperties.Listener; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; @@ -37,30 +36,18 @@ import org.springframework.context.annotation.Configuration; @ConditionalOnClass(EnableRabbit.class) class RabbitAnnotationDrivenConfiguration { + @Bean + @ConditionalOnMissingBean + public RabbitListenerContainerFactoryConfigurer rabbitListenerContainerFactoryConfigurer() { + return new RabbitListenerContainerFactoryConfigurer(); + } + @Bean @ConditionalOnMissingBean(name = "rabbitListenerContainerFactory") public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory( - ConnectionFactory connectionFactory, RabbitProperties config) { - SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory(); - factory.setConnectionFactory(connectionFactory); - Listener listenerConfig = config.getListener(); - factory.setAutoStartup(listenerConfig.isAutoStartup()); - if (listenerConfig.getAcknowledgeMode() != null) { - factory.setAcknowledgeMode(listenerConfig.getAcknowledgeMode()); - } - if (listenerConfig.getConcurrency() != null) { - factory.setConcurrentConsumers(listenerConfig.getConcurrency()); - } - if (listenerConfig.getMaxConcurrency() != null) { - factory.setMaxConcurrentConsumers(listenerConfig.getMaxConcurrency()); - } - if (listenerConfig.getPrefetch() != null) { - factory.setPrefetchCount(listenerConfig.getPrefetch()); - } - if (listenerConfig.getTransactionSize() != null) { - factory.setTxSize(listenerConfig.getTransactionSize()); - } - return factory; + RabbitListenerContainerFactoryConfigurer configurer, + ConnectionFactory connectionFactory) { + return configurer.createRabbitListenerContainerFactory(connectionFactory); } @EnableRabbit diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitListenerContainerFactoryConfigurer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitListenerContainerFactoryConfigurer.java new file mode 100644 index 0000000000..0be7111086 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitListenerContainerFactoryConfigurer.java @@ -0,0 +1,87 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.config.SimpleRabbitListenerContainerFactory; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.Assert; + +/** + * Configure {@link RabbitListenerContainerFactory} with sensible defaults. + * + * @author Stephane Nicoll + * @since 1.3.3 + */ +public final class RabbitListenerContainerFactoryConfigurer { + + private RabbitProperties rabbitProperties; + + /** + * Set the {@link RabbitProperties} to use. + * @param rabbitProperties the {@link RabbitProperties} + */ + @Autowired + public void setRabbitProperties(RabbitProperties rabbitProperties) { + this.rabbitProperties = rabbitProperties; + } + + /** + * Create a new and pre-configured {@link SimpleRabbitListenerContainerFactory} instance + * for the specified {@link ConnectionFactory}. + * @param connectionFactory the {@link ConnectionFactory} to use. + * @return a pre-configured {@link SimpleRabbitListenerContainerFactory} + */ + public SimpleRabbitListenerContainerFactory createRabbitListenerContainerFactory( + ConnectionFactory connectionFactory) { + SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory(); + configure(factory, connectionFactory); + return factory; + } + + /** + * Apply the default settings for the specified jms listener container factory. The + * factory can be further tuned and default settings can be overridden. + * @param factory the {@link SimpleRabbitListenerContainerFactory} instance to configure + * @param connectionFactory the {@link ConnectionFactory} to use + */ + public void configure(SimpleRabbitListenerContainerFactory factory, + ConnectionFactory connectionFactory) { + Assert.notNull(factory, "Factory must not be null"); + Assert.notNull(connectionFactory, "ConnectionFactory must not be null"); + factory.setConnectionFactory(connectionFactory); + RabbitProperties.Listener listenerConfig = this.rabbitProperties.getListener(); + factory.setAutoStartup(listenerConfig.isAutoStartup()); + if (listenerConfig.getAcknowledgeMode() != null) { + factory.setAcknowledgeMode(listenerConfig.getAcknowledgeMode()); + } + if (listenerConfig.getConcurrency() != null) { + factory.setConcurrentConsumers(listenerConfig.getConcurrency()); + } + if (listenerConfig.getMaxConcurrency() != null) { + factory.setMaxConcurrentConsumers(listenerConfig.getMaxConcurrency()); + } + if (listenerConfig.getPrefetch() != null) { + factory.setPrefetchCount(listenerConfig.getPrefetch()); + } + if (listenerConfig.getTransactionSize() != null) { + factory.setTxSize(listenerConfig.getTransactionSize()); + } + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAnnotationDrivenConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAnnotationDrivenConfiguration.java index c3ccd61f6e..82801c929a 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAnnotationDrivenConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAnnotationDrivenConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -18,7 +18,6 @@ package org.springframework.boot.autoconfigure.jms; import javax.jms.ConnectionFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnJndi; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -29,7 +28,6 @@ import org.springframework.jms.config.DefaultJmsListenerContainerFactory; import org.springframework.jms.config.JmsListenerConfigUtils; import org.springframework.jms.support.destination.DestinationResolver; import org.springframework.jms.support.destination.JndiDestinationResolver; -import org.springframework.transaction.jta.JtaTransactionManager; /** * Configuration for Spring 4.1 annotation driven JMS. @@ -42,41 +40,17 @@ import org.springframework.transaction.jta.JtaTransactionManager; @ConditionalOnClass(EnableJms.class) class JmsAnnotationDrivenConfiguration { - @Autowired(required = false) - private DestinationResolver destinationResolver; - - @Autowired(required = false) - private JtaTransactionManager transactionManager; - - @Autowired - private JmsProperties properties; + @Bean + @ConditionalOnMissingBean + public JmsListenerContainerFactoryConfigurer jmsListenerContainerFactoryConfigurer() { + return new JmsListenerContainerFactoryConfigurer(); + } @Bean @ConditionalOnMissingBean(name = "jmsListenerContainerFactory") public DefaultJmsListenerContainerFactory jmsListenerContainerFactory( - ConnectionFactory connectionFactory) { - DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); - factory.setConnectionFactory(connectionFactory); - factory.setPubSubDomain(this.properties.isPubSubDomain()); - if (this.transactionManager != null) { - factory.setTransactionManager(this.transactionManager); - } - else { - factory.setSessionTransacted(true); - } - if (this.destinationResolver != null) { - factory.setDestinationResolver(this.destinationResolver); - } - JmsProperties.Listener listener = this.properties.getListener(); - factory.setAutoStartup(listener.isAutoStartup()); - if (listener.getAcknowledgeMode() != null) { - factory.setSessionAcknowledgeMode(listener.getAcknowledgeMode().getMode()); - } - String concurrency = listener.formatConcurrency(); - if (concurrency != null) { - factory.setConcurrency(concurrency); - } - return factory; + JmsListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) { + return configurer.createJmsListenerContainerFactory(connectionFactory); } @EnableJms diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsListenerContainerFactoryConfigurer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsListenerContainerFactoryConfigurer.java new file mode 100644 index 0000000000..46facd8207 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsListenerContainerFactoryConfigurer.java @@ -0,0 +1,116 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.jms; + +import javax.jms.ConnectionFactory; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jms.config.DefaultJmsListenerContainerFactory; +import org.springframework.jms.config.JmsListenerContainerFactory; +import org.springframework.jms.support.destination.DestinationResolver; +import org.springframework.transaction.jta.JtaTransactionManager; +import org.springframework.util.Assert; + +/** + * Configure {@link JmsListenerContainerFactory} with sensible defaults. + * + * @author Stephane Nicoll + * @since 1.3.3 + */ +public final class JmsListenerContainerFactoryConfigurer { + + private DestinationResolver destinationResolver; + + private JtaTransactionManager transactionManager; + + private JmsProperties jmsProperties; + + /** + * Set the {@link DestinationResolver} to use or {@code null} if no destination + * resolver should be associated with the factory by default. + * @param destinationResolver the {@link DestinationResolver} + */ + @Autowired(required = false) + public void setDestinationResolver(DestinationResolver destinationResolver) { + this.destinationResolver = destinationResolver; + } + + /** + * Set the {@link JtaTransactionManager} to use or {@code null} if the JTA + * support should not be used. + * @param transactionManager the {@link JtaTransactionManager} + */ + @Autowired(required = false) + public void setTransactionManager(JtaTransactionManager transactionManager) { + this.transactionManager = transactionManager; + } + + /** + * Set the {@link JmsProperties to use}. + * @param jmsProperties the {@link JmsProperties} + */ + @Autowired + public void setJmsProperties(JmsProperties jmsProperties) { + this.jmsProperties = jmsProperties; + } + + /** + * Create a new and pre-configured {@link DefaultJmsListenerContainerFactory} instance + * for the specified {@link ConnectionFactory}. + * @param connectionFactory the {@link ConnectionFactory} to use. + * @return a pre-configured {@link DefaultJmsListenerContainerFactory} + */ + public DefaultJmsListenerContainerFactory createJmsListenerContainerFactory( + ConnectionFactory connectionFactory) { + DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); + configure(factory, connectionFactory); + return factory; + } + + /** + * Apply the default settings for the specified jms listener container factory. The + * factory can be further tuned and default settings can be overridden. + * @param factory the {@link DefaultJmsListenerContainerFactory} instance to configure + * @param connectionFactory the {@link ConnectionFactory} to use + */ + public void configure(DefaultJmsListenerContainerFactory factory, + ConnectionFactory connectionFactory) { + Assert.notNull(factory, "Factory must not be null"); + Assert.notNull(connectionFactory, "ConnectionFactory must not be null"); + factory.setConnectionFactory(connectionFactory); + factory.setPubSubDomain(this.jmsProperties.isPubSubDomain()); + if (this.transactionManager != null) { + factory.setTransactionManager(this.transactionManager); + } + else { + factory.setSessionTransacted(true); + } + if (this.destinationResolver != null) { + factory.setDestinationResolver(this.destinationResolver); + } + JmsProperties.Listener listener = this.jmsProperties.getListener(); + factory.setAutoStartup(listener.isAutoStartup()); + if (listener.getAcknowledgeMode() != null) { + factory.setSessionAcknowledgeMode(listener.getAcknowledgeMode().getMode()); + } + String concurrency = listener.formatConcurrency(); + if (concurrency != null) { + factory.setConcurrency(concurrency); + } + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java index 425efab1cf..d8f1a18ebc 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java @@ -202,6 +202,20 @@ public class JmsAutoConfigurationTests { .getPropertyValue("transactionManager")).isNull(); } + @Test + public void testCustomContainerFactoryWithConfigurer() { + this.context = doLoad(new Class[]{TestConfiguration9.class, + EnableJmsConfiguration.class}, "spring.jms.listener.autoStartup=false"); + assertThat(this.context.containsBean("jmsListenerContainerFactory")).isTrue(); + JmsListenerContainerFactory jmsListenerContainerFactory = this.context.getBean( + "customListenerContainerFactory", JmsListenerContainerFactory.class); + assertThat(jmsListenerContainerFactory).isInstanceOf(DefaultJmsListenerContainerFactory.class); + DefaultMessageListenerContainer listenerContainer = ((DefaultJmsListenerContainerFactory) jmsListenerContainerFactory) + .createListenerContainer(mock(JmsListenerEndpoint.class)); + assertThat(listenerContainer.getCacheLevel()).isEqualTo(DefaultMessageListenerContainer.CACHE_CONSUMER); + assertThat(listenerContainer.isAutoStartup()).isFalse(); + } + @Test public void testPubSubDisabledByDefault() { load(TestConfiguration.class); @@ -435,6 +449,21 @@ public class JmsAutoConfigurationTests { } + @Configuration + protected static class TestConfiguration9 { + + @Bean + JmsListenerContainerFactory customListenerContainerFactory( + JmsListenerContainerFactoryConfigurer configurer, + ConnectionFactory connectionFactory) { + DefaultJmsListenerContainerFactory factory = configurer + .createJmsListenerContainerFactory(connectionFactory); + factory.setCacheLevel(DefaultMessageListenerContainer.CACHE_CONSUMER); + return factory; + + } + } + @Configuration @EnableJms protected static class EnableJmsConfiguration { diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 33fb12926b..39c6ee8371 100644 --- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -3504,6 +3504,44 @@ The following component creates a listener endpoint on the `someQueue` destinati TIP: Check {spring-javadoc}/jms/annotation/EnableJms.{dc-ext}[the Javadoc of `@EnableJms`] for more details. +If you need to create more `JmsListenerContainerFactory` instances or if you want to override +the default, Spring Boot provides a `JmsListenerContainerFactoryConfigurer` that you can use +to initialize a `DefaultJmsListenerContainerFactory` with the same settings as the one that +is auto-configured. + +For instance, the following exposes another factory that uses a specific `MessageConverter`: + +[source,java,indent=0] +---- + @Configuration + static class JmsConfiguration { + + @Bean + public DefaultJmsListenerContainerFactory myFactory( + JmsListenerContainerFactoryConfigurer configurer) { + DefaultJmsListenerContainerFactory factory = configurer + .createJmsListenerContainerFactory(connectionFactory()); + factory.setMessageConverter(myMessageConverter()); + return factory; + } + + } +---- + +That you can use in any `@JmsListener`-annotated method as follows: + +[source,java,indent=0] +---- + @Component + public class MyBean { + + @JmsListener(destination = "someQueue", **containerFactory="myFactory"**) + public void processMessage(String content) { + // ... + } + + } +---- [[boot-features-amqp]] @@ -3600,6 +3638,44 @@ The following component creates a listener endpoint on the `someQueue` queue: TIP: Check {spring-amqp-javadoc}/rabbit/annotation/EnableRabbit.{dc-ext}[the Javadoc of `@EnableRabbit`] for more details. +If you need to create more `RabbitListenerContainerFactory` instances or if you want to override +the default, Spring Boot provides a `RabbitListenerContainerFactoryConfigurer` that you can use +to initialize a `SimpleRabbitListenerContainerFactory` with the same settings as the one that +is auto-configured. + +For instance, the following exposes another factory that uses a specific `MessageConverter`: + +[source,java,indent=0] +---- + @Configuration + static class RabbitConfiguration { + + @Bean + public SimpleRabbitListenerContainerFactory myFactory( + RabbitListenerContainerFactoryConfigurer configurer) { + SimpleRabbitListenerContainerFactory factory = configurer + .createRabbitListenerContainerFactory(connectionFactory()); + factory.setMessageConverter(myMessageConverter()); + return factory; + } + + } +---- + +That you can use in any `@RabbitListener`-annotated method as follows: + +[source,java,indent=0] +---- + @Component + public class MyBean { + + @RabbitListener(queues = "someQueue", **containerFactory="myFactory"**) + public void processMessage(String content) { + // ... + } + + } +---- [[boot-features-email]]