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 5abdbba22d7..69cb1ac86c8 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 @@ -67,6 +67,14 @@ class JmsAnnotationDrivenConfiguration { if (this.destinationResolver != null) { factory.setDestinationResolver(this.destinationResolver); } + JmsProperties.Listener listener = this.properties.getListener(); + if (listener.getAcknowledgmentMode() != null) { + factory.setSessionAcknowledgeMode(listener.getAcknowledgmentMode().getMode()); + } + String concurrency = listener.formatConcurrency(); + if (concurrency != null) { + factory.setConcurrency(concurrency); + } return factory; } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsProperties.java index d9fceac0fa6..e8702a1b4b2 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -38,6 +38,8 @@ public class JmsProperties { */ private String jndiName; + private final Listener listener = new Listener(); + public boolean isPubSubDomain() { return this.pubSubDomain; } @@ -54,4 +56,98 @@ public class JmsProperties { this.jndiName = jndiName; } + public Listener getListener() { + return listener; + } + + public static class Listener { + + /** + * Acknowledgment mode of the container. By default, the listener is + * transacted with automatic acknowledgment. + */ + private AcknowledgmentMode acknowledgmentMode; + + /** + * Minimum number of concurrent consumers. + */ + private Integer concurrency; + + /** + * Maximum number of concurrent consumers. + */ + private Integer maxConcurrency; + + public AcknowledgmentMode getAcknowledgmentMode() { + return acknowledgmentMode; + } + + public void setAcknowledgmentMode(AcknowledgmentMode acknowledgmentMode) { + this.acknowledgmentMode = acknowledgmentMode; + } + + public Integer getConcurrency() { + return this.concurrency; + } + + public void setConcurrency(Integer concurrency) { + this.concurrency = concurrency; + } + + public Integer getMaxConcurrency() { + return this.maxConcurrency; + } + + public void setMaxConcurrency(Integer maxConcurrency) { + this.maxConcurrency = maxConcurrency; + } + + public String formatConcurrency() { + if (this.concurrency == null) { + return (this.maxConcurrency != null ? "1-" + this.maxConcurrency : null); + } + return (this.maxConcurrency != null ? this.concurrency + "-" + + this.maxConcurrency : String.valueOf(this.concurrency)); + } + } + + /** + * Translate the acknowledgment modes defined on the {@link javax.jms.Session}. + * + *

{@link javax.jms.Session#SESSION_TRANSACTED} is not defined as we take + * care of this already via a call to {@code setSessionTransacted}. + */ + public enum AcknowledgmentMode { + + /** + * Messages sent or received from the session are automatically acknowledged. This + * is the simplest mode and enables once-only message delivery guarantee. + */ + AUTO(1), + + /** + * Messages are acknowledged once the message listener implementation has + * called {@link javax.jms.Message#acknowledge()}. This mode gives the application + * (rather than the JMS provider) complete control over message acknowledgement. + */ + CLIENT(2), + + /** + * Similar to auto acknowledgment except that said acknowledgment is lazy. As a + * consequence, the messages might be delivered more than once. This mode enables + * at-least-once message delivery guarantee. + */ + DUPS_OK(3); + + private final int mode; + + AcknowledgmentMode(int mode) { + this.mode = mode; + } + + public int getMode() { + return mode; + } + } + } 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 c4fd6ce6655..4d2f98cc883 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2015 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. @@ -17,6 +17,7 @@ package org.springframework.boot.autoconfigure.jms; import javax.jms.ConnectionFactory; +import javax.jms.Session; import org.apache.activemq.ActiveMQConnectionFactory; import org.apache.activemq.pool.PooledConnectionFactory; @@ -143,6 +144,23 @@ public class JmsAutoConfigurationTests { jmsListenerContainerFactory.getClass()); } + @Test + public void testJmsListenerContainerFactoryWithCustomSettings() { + load(EnableJmsConfiguration.class, + "spring.jms.listener.acknowledgmentMode=client", + "spring.jms.listener.concurrency=2", + "spring.jms.listener.maxConcurrency=10"); + JmsListenerContainerFactory jmsListenerContainerFactory = this.context + .getBean("jmsListenerContainerFactory", JmsListenerContainerFactory.class); + assertEquals(DefaultJmsListenerContainerFactory.class, + jmsListenerContainerFactory.getClass()); + DefaultMessageListenerContainer listenerContainer = ((DefaultJmsListenerContainerFactory) + jmsListenerContainerFactory).createListenerContainer(mock(JmsListenerEndpoint.class)); + assertEquals(2, listenerContainer.getConcurrentConsumers()); + assertEquals(10, listenerContainer.getMaxConcurrentConsumers()); + assertEquals(Session.CLIENT_ACKNOWLEDGE, listenerContainer.getSessionAcknowledgeMode()); + } + @Test public void testDefaultContainerFactoryWithJtaTransactionManager() { this.context = createContext(TestConfiguration7.class, diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsPropertiesTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsPropertiesTests.java new file mode 100644 index 00000000000..888110d346c --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsPropertiesTests.java @@ -0,0 +1,59 @@ +/* + * Copyright 2012-2015 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 org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +/** + * Tests for {@link JmsProperties}. + * + * @author Stephane Nicoll + */ +public class JmsPropertiesTests { + + @Test + public void formatConcurrencyNull() { + JmsProperties properties = new JmsProperties(); + assertNull(properties.getListener().formatConcurrency()); + } + + @Test + public void formatConcurrencyOnlyLowerBound() { + JmsProperties properties = new JmsProperties(); + properties.getListener().setConcurrency(2); + assertEquals("2", properties.getListener().formatConcurrency()); + } + + @Test + public void formatConcurrencyOnlyHigherBound() { + JmsProperties properties = new JmsProperties(); + properties.getListener().setMaxConcurrency(5); + assertEquals("1-5", properties.getListener().formatConcurrency()); + } + + @Test + public void formatConcurrencyBothBounds() { + JmsProperties properties = new JmsProperties(); + properties.getListener().setConcurrency(2); + properties.getListener().setMaxConcurrency(10); + assertEquals("2-10", properties.getListener().formatConcurrency()); + } + +} diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 37c62aa7eec..4a94c77bed6 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -523,6 +523,9 @@ content into your application; rather pick only the properties that you need. # JMS ({sc-spring-boot-autoconfigure}/jms/JmsProperties.{sc-ext}[JmsProperties]) spring.jms.jndi-name= # JNDI location of a JMS ConnectionFactory + spring.jms.listener.acknowledgment-mode= # session acknowledgment mode + spring.jms.listener.concurrency= # minimum number of concurrent consumers + spring.jms.listener.maxConcurrency= # maximum number of concurrent consumers spring.jms.pub-sub-domain= # false for queue (default), true for topic # Email ({sc-spring-boot-autoconfigure}/mail/MailProperties.{sc-ext}[MailProperties])