diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/AcknowledgeMode.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/AcknowledgeMode.java new file mode 100644 index 00000000000..f3c3240d7e2 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/AcknowledgeMode.java @@ -0,0 +1,105 @@ +/* + * 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; + +import org.springframework.jms.support.JmsAccessor; + +/** + * Acknowledge modes for a JMS Session. Supports the acknowledge modes defined by + * {@link jakarta.jms.Session} as well as other, non-standard modes. + * + *
+ * Note that {@link jakarta.jms.Session#SESSION_TRANSACTED} is not defined. It should be
+ * handled through a call to {@link JmsAccessor#setSessionTransacted(boolean)}.
+ *
+ * @author Andy Wilkinson
+ * @since 3.2.0
+ */
+public final class AcknowledgeMode {
+
+ private static final Map
- * {@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 {
/**
diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json
index bfd976e9b92..5f8c301fd27 100644
--- a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json
+++ b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -3011,6 +3011,40 @@
}
]
},
+ {
+ "name": "spring.jms.listener.session.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.session.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.server",
"providers": [
diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/AcknowledgeModeTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/AcknowledgeModeTests.java
new file mode 100644
index 00000000000..77957f5a967
--- /dev/null
+++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/AcknowledgeModeTests.java
@@ -0,0 +1,88 @@
+/*
+ * 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 jakarta.jms.Session;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+
+/**
+ * Tests for {@link AcknowledgeMode}.
+ *
+ * @author Andy Wilkinson
+ */
+class AcknowledgeModeTests {
+
+ @ParameterizedTest
+ @EnumSource(Mapping.class)
+ void stringIsMappedToInt(Mapping mapping) {
+ assertThat(AcknowledgeMode.of(mapping.actual)).extracting(AcknowledgeMode::getMode).isEqualTo(mapping.expected);
+ }
+
+ @Test
+ void mapShouldThrowWhenMapIsCalledWithUnknownNonIntegerString() {
+ assertThatIllegalArgumentException().isThrownBy(() -> AcknowledgeMode.of("some-string"))
+ .withMessage(
+ "'some-string' is neither a known acknowledge mode (auto, client, or dups_ok) nor an integer value");
+ }
+
+ private enum Mapping {
+
+ AUTO_LOWER_CASE("auto", Session.AUTO_ACKNOWLEDGE),
+
+ CLIENT_LOWER_CASE("client", Session.CLIENT_ACKNOWLEDGE),
+
+ DUPS_OK_LOWER_CASE("dups_ok", Session.DUPS_OK_ACKNOWLEDGE),
+
+ AUTO_UPPER_CASE("AUTO", Session.AUTO_ACKNOWLEDGE),
+
+ CLIENT_UPPER_CASE("CLIENT", Session.CLIENT_ACKNOWLEDGE),
+
+ DUPS_OK_UPPER_CASE("DUPS_OK", Session.DUPS_OK_ACKNOWLEDGE),
+
+ AUTO_MIXED_CASE("AuTo", Session.AUTO_ACKNOWLEDGE),
+
+ CLIENT_MIXED_CASE("CliEnT", Session.CLIENT_ACKNOWLEDGE),
+
+ DUPS_OK_MIXED_CASE("dUPs_Ok", Session.DUPS_OK_ACKNOWLEDGE),
+
+ DUPS_OK_KEBAB_CASE("DUPS-OK", Session.DUPS_OK_ACKNOWLEDGE),
+
+ DUPS_OK_NO_SEPARATOR_UPPER_CASE("DUPSOK", Session.DUPS_OK_ACKNOWLEDGE),
+
+ DUPS_OK_NO_SEPARATOR_LOWER_CASE("dupsok", Session.DUPS_OK_ACKNOWLEDGE),
+
+ DUPS_OK_NO_SEPARATOR_MIXED_CASE("duPSok", Session.DUPS_OK_ACKNOWLEDGE),
+
+ INTEGER("36", 36);
+
+ private final String actual;
+
+ private final int expected;
+
+ Mapping(String actual, int expected) {
+ this.actual = actual;
+ this.expected = expected;
+ }
+
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java
index 49f5ddb3b27..b9b6025bae4 100644
--- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java
+++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java
@@ -24,14 +24,18 @@ import jakarta.jms.Session;
import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
import org.junit.jupiter.api.Test;
+import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
+import org.springframework.aot.test.generate.TestGenerationContext;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
+import org.springframework.context.aot.ApplicationContextAotGenerator;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
@@ -160,6 +164,16 @@ class JmsAutoConfigurationTests {
assertThat(container).hasFieldOrPropertyWithValue("receiveTimeout", 2000L);
}
+ @Test
+ void testJmsListenerContainerFactoryWithNonStandardAcknowledgeMode() {
+ this.contextRunner.withUserConfiguration(EnableJmsConfiguration.class)
+ .withPropertyValues("spring.jms.listener.session.acknowledge-mode=9")
+ .run((context) -> {
+ DefaultMessageListenerContainer container = getContainer(context, "jmsListenerContainerFactory");
+ assertThat(container.getSessionAcknowledgeMode()).isEqualTo(9);
+ });
+ }
+
@Test
void testJmsListenerContainerFactoryWithDefaultSettings() {
this.contextRunner.withUserConfiguration(EnableJmsConfiguration.class)
@@ -300,6 +314,16 @@ class JmsAutoConfigurationTests {
});
}
+ @Test
+ void testJmsTemplateWithNonStandardAcknowledgeMode() {
+ this.contextRunner.withUserConfiguration(EnableJmsConfiguration.class)
+ .withPropertyValues("spring.jms.template.session.acknowledge-mode=7")
+ .run((context) -> {
+ JmsTemplate jmsTemplate = context.getBean(JmsTemplate.class);
+ assertThat(jmsTemplate.getSessionAcknowledgeMode()).isEqualTo(7);
+ });
+ }
+
@Test
void testJmsMessagingTemplateUseConfiguredDefaultDestination() {
this.contextRunner.withPropertyValues("spring.jms.template.default-destination=testQueue").run((context) -> {
@@ -367,6 +391,17 @@ class JmsAutoConfigurationTests {
.hasBean(JmsListenerConfigUtils.JMS_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME));
}
+ @Test
+ void runtimeHintsAreRegisteredForBindingOfAcknowledgeMode() {
+ try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
+ context.register(ArtemisAutoConfiguration.class, JmsAutoConfiguration.class);
+ TestGenerationContext generationContext = new TestGenerationContext();
+ new ApplicationContextAotGenerator().processAheadOfTime(context, generationContext);
+ assertThat(RuntimeHintsPredicates.reflection().onMethod(AcknowledgeMode.class, "of").invoke())
+ .accepts(generationContext.getRuntimeHints());
+ }
+ }
+
@Configuration(proxyBeanMethods = false)
static class TestConfiguration {