Skip to content

Add support for configuring non-standard JMS acknowledge modes #37576

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2021 the original author or authors.
* Copyright 2012-2023 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.
Expand Down Expand Up @@ -32,6 +32,7 @@
*
* @author Stephane Nicoll
* @author Eddú Meléndez
* @author Vedran Pavic
* @since 1.3.3
*/
public final class DefaultJmsListenerContainerFactoryConfigurer {
Expand Down Expand Up @@ -119,7 +120,7 @@ public void configure(DefaultJmsListenerContainerFactory factory, ConnectionFact
JmsProperties.Listener listener = this.jmsProperties.getListener();
factory.setAutoStartup(listener.isAutoStartup());
if (listener.getAcknowledgeMode() != null) {
factory.setSessionAcknowledgeMode(listener.getAcknowledgeMode().getMode());
factory.setSessionAcknowledgeMode(JmsAcknowledgeModeMapper.map(listener.getAcknowledgeMode()));
}
String concurrency = listener.formatConcurrency();
if (concurrency != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2012-2023 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
*
* https://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 java.util.HashMap;
import java.util.Map;

import jakarta.jms.Session;

/**
* Helper class used to map JMS acknowledge modes.
*
* @author Vedran Pavic
*/
final class JmsAcknowledgeModeMapper {

private static final Map<String, Integer> acknowledgeModes = new HashMap<>(3);

static {
acknowledgeModes.put("auto", Session.AUTO_ACKNOWLEDGE);
acknowledgeModes.put("client", Session.CLIENT_ACKNOWLEDGE);
acknowledgeModes.put("dups_ok", Session.DUPS_OK_ACKNOWLEDGE);
}

private JmsAcknowledgeModeMapper() {
}

static int map(String acknowledgeMode) {
return acknowledgeModes.computeIfAbsent(acknowledgeMode.toLowerCase(), Integer::parseInt);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
*
* @author Greg Turnquist
* @author Stephane Nicoll
* @author Vedran Pavic
* @since 1.0.0
*/
@AutoConfiguration
Expand Down Expand Up @@ -88,6 +89,11 @@ public JmsTemplate jmsTemplate(ConnectionFactory connectionFactory) {

private void mapTemplateProperties(Template properties, JmsTemplate template) {
PropertyMapper map = PropertyMapper.get();
map.from(properties::getAcknowledgeMode)
.whenNonNull()
.to((acknowledgeMode) -> template
.setSessionAcknowledgeMode(JmsAcknowledgeModeMapper.map(acknowledgeMode)));
map.from(properties::getSessionTransacted).whenNonNull().to(template::setSessionTransacted);
map.from(properties::getDefaultDestination).whenNonNull().to(template::setDefaultDestinationName);
map.from(properties::getDeliveryDelay).whenNonNull().as(Duration::toMillis).to(template::setDeliveryDelay);
map.from(properties::determineQosEnabled).to(template::setExplicitQosEnabled);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
* @author Greg Turnquist
* @author Phillip Webb
* @author Stephane Nicoll
* @author Vedran Pavic
* @since 1.0.0
*/
@ConfigurationProperties(prefix = "spring.jms")
Expand Down Expand Up @@ -144,7 +145,7 @@ public static class Listener {
* Acknowledge mode of the container. By default, the listener is transacted with
* automatic acknowledgment.
*/
private AcknowledgeMode acknowledgeMode;
private String acknowledgeMode;

/**
* Minimum number of concurrent consumers. When max-concurrency is not specified
Expand Down Expand Up @@ -172,11 +173,11 @@ public void setAutoStartup(boolean autoStartup) {
this.autoStartup = autoStartup;
}

public AcknowledgeMode getAcknowledgeMode() {
public String getAcknowledgeMode() {
return this.acknowledgeMode;
}

public void setAcknowledgeMode(AcknowledgeMode acknowledgeMode) {
public void setAcknowledgeMode(String acknowledgeMode) {
this.acknowledgeMode = acknowledgeMode;
}

Expand Down Expand Up @@ -227,6 +228,16 @@ public void setReceiveTimeout(Duration receiveTimeout) {

public static class Template {

/**
* Acknowledgement mode used when creating JMS sessions to send a message.
*/
private String acknowledgeMode;

/**
* Whether to use transacted JMS sessions.
*/
private Boolean sessionTransacted;

/**
* Default destination to use on send and receive operations that do not have a
* destination parameter.
Expand Down Expand Up @@ -267,6 +278,22 @@ public static class Template {
*/
private Duration receiveTimeout;

public String getAcknowledgeMode() {
return this.acknowledgeMode;
}

public void setAcknowledgeMode(String acknowledgeMode) {
this.acknowledgeMode = acknowledgeMode;
}

public Boolean getSessionTransacted() {
return this.sessionTransacted;
}

public void setSessionTransacted(Boolean sessionTransacted) {
this.sessionTransacted = sessionTransacted;
}

public String getDefaultDestination() {
return this.defaultDestination;
}
Expand Down Expand Up @@ -332,47 +359,6 @@ public void setReceiveTimeout(Duration receiveTimeout) {

}

/**
* Translate the acknowledge modes defined on the {@link jakarta.jms.Session}.
*
* <p>
* {@link jakarta.jms.Session#SESSION_TRANSACTED} is not defined as we take care of
* this already through a call to {@code setSessionTransacted}.
*/
public enum AcknowledgeMode {

/**
* 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 jakarta.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;

AcknowledgeMode(int mode) {
this.mode = mode;
}

public int getMode() {
return this.mode;
}

}

public enum DeliveryMode {

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2987,6 +2987,40 @@
}
]
},
{
"name": "spring.jms.listener.acknowledge-mode",
"values": [
{
"value": "auto",
"description": "Messages sent or received from the session are automatically acknowledged. This is the simplest mode and enables once-only message delivery guarantee."
},
{
"value": "client",
"description": "Messages are acknowledged once the message listener implementation has called \"jakarta.jms.Message#acknowledge()\". This mode gives the application (rather than the JMS provider) complete control over message acknowledgement."
},
{
"value": "dups_ok",
"description": "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."
}
]
},
{
"name": "spring.jms.template.acknowledge-mode",
"values": [
{
"value": "auto",
"description": "Messages sent or received from the session are automatically acknowledged. This is the simplest mode and enables once-only message delivery guarantee."
},
{
"value": "client",
"description": "Messages are acknowledged once the message listener implementation has called \"jakarta.jms.Message#acknowledge()\". This mode gives the application (rather than the JMS provider) complete control over message acknowledgement."
},
{
"value": "dups_ok",
"description": "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."
}
]
},
{
"name": "spring.jmx.registration-policy",
"defaultValue": "fail-on-existing"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,14 +253,17 @@ void testJmsTemplateWithDestinationResolver() {
@Test
void testJmsTemplateFullCustomization() {
this.contextRunner.withUserConfiguration(MessageConvertersConfiguration.class)
.withPropertyValues("spring.jms.template.default-destination=testQueue",
.withPropertyValues("spring.jms.template.acknowledge-mode=client",
"spring.jms.template.session-transacted=true", "spring.jms.template.default-destination=testQueue",
"spring.jms.template.delivery-delay=500", "spring.jms.template.delivery-mode=non-persistent",
"spring.jms.template.priority=6", "spring.jms.template.time-to-live=6000",
"spring.jms.template.receive-timeout=2000")
.run((context) -> {
JmsTemplate jmsTemplate = context.getBean(JmsTemplate.class);
assertThat(jmsTemplate.getMessageConverter()).isSameAs(context.getBean("myMessageConverter"));
assertThat(jmsTemplate.isPubSubDomain()).isFalse();
assertThat(jmsTemplate.getSessionAcknowledgeMode()).isEqualTo(Session.CLIENT_ACKNOWLEDGE);
assertThat(jmsTemplate.isSessionTransacted()).isTrue();
assertThat(jmsTemplate.getDefaultDestinationName()).isEqualTo("testQueue");
assertThat(jmsTemplate.getDeliveryDelay()).isEqualTo(500);
assertThat(jmsTemplate.getDeliveryMode()).isOne();
Expand Down