From 1f00c27c7eb4e8bf67a31c953e05f655cf634aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Sun, 14 Feb 2021 03:37:57 -0600 Subject: [PATCH 1/2] Add support to auto-configure javax.jms.ExceptionListener See gh-25278 --- ...JmsListenerContainerFactoryConfigurer.java | 17 +++++++++++++- .../jms/JmsAnnotationDrivenConfiguration.java | 10 ++++++-- .../jms/JmsAutoConfigurationTests.java | 23 ++++++++++++++++++- 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/DefaultJmsListenerContainerFactoryConfigurer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/DefaultJmsListenerContainerFactoryConfigurer.java index ccb1f730b02..86e32462c32 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/DefaultJmsListenerContainerFactoryConfigurer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/DefaultJmsListenerContainerFactoryConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2021 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. @@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.jms; import java.time.Duration; import javax.jms.ConnectionFactory; +import javax.jms.ExceptionListener; import org.springframework.jms.config.DefaultJmsListenerContainerFactory; import org.springframework.jms.support.converter.MessageConverter; @@ -30,6 +31,7 @@ import org.springframework.util.Assert; * Configure {@link DefaultJmsListenerContainerFactory} with sensible defaults. * * @author Stephane Nicoll + * @author Eddú Meléndez * @since 1.3.3 */ public final class DefaultJmsListenerContainerFactoryConfigurer { @@ -42,6 +44,8 @@ public final class DefaultJmsListenerContainerFactoryConfigurer { private JmsProperties jmsProperties; + private ExceptionListener exceptionListener; + /** * Set the {@link DestinationResolver} to use or {@code null} if no destination * resolver should be associated with the factory by default. @@ -77,6 +81,14 @@ public final class DefaultJmsListenerContainerFactoryConfigurer { this.jmsProperties = jmsProperties; } + /** + * Set the {@link ExceptionListener}. + * @param exceptionListener the {@link ExceptionListener} + */ + void setExceptionListener(ExceptionListener exceptionListener) { + this.exceptionListener = exceptionListener; + } + /** * Configure the specified jms listener container factory. The factory can be further * tuned and default settings can be overridden. @@ -113,6 +125,9 @@ public final class DefaultJmsListenerContainerFactoryConfigurer { if (receiveTimeout != null) { factory.setReceiveTimeout(receiveTimeout.toMillis()); } + if (this.exceptionListener != null) { + factory.setExceptionListener(this.exceptionListener); + } } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAnnotationDrivenConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAnnotationDrivenConfiguration.java index 271a3a9da4e..432cba4bff5 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAnnotationDrivenConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAnnotationDrivenConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2021 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.ExceptionListener; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -38,6 +39,7 @@ import org.springframework.transaction.jta.JtaTransactionManager; * * @author Phillip Webb * @author Stephane Nicoll + * @author Eddú Meléndez */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass(EnableJms.class) @@ -49,14 +51,17 @@ class JmsAnnotationDrivenConfiguration { private final ObjectProvider messageConverter; + private final ObjectProvider exceptionListener; + private final JmsProperties properties; JmsAnnotationDrivenConfiguration(ObjectProvider destinationResolver, ObjectProvider transactionManager, ObjectProvider messageConverter, - JmsProperties properties) { + ObjectProvider exceptionListener, JmsProperties properties) { this.destinationResolver = destinationResolver; this.transactionManager = transactionManager; this.messageConverter = messageConverter; + this.exceptionListener = exceptionListener; this.properties = properties; } @@ -67,6 +72,7 @@ class JmsAnnotationDrivenConfiguration { configurer.setDestinationResolver(this.destinationResolver.getIfUnique()); configurer.setTransactionManager(this.transactionManager.getIfUnique()); configurer.setMessageConverter(this.messageConverter.getIfUnique()); + configurer.setExceptionListener(this.exceptionListener.getIfUnique()); configurer.setJmsProperties(this.properties); return configurer; } 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 4989e3ce0fa..6ab88476298 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2021 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.ExceptionListener; import javax.jms.Session; import org.apache.activemq.ActiveMQConnectionFactory; @@ -56,6 +57,7 @@ import static org.mockito.Mockito.mock; * @author Greg Turnquist * @author Stephane Nicoll * @author Aurélien Leboulanger + * @author Eddú Meléndez */ class JmsAutoConfigurationTests { @@ -393,6 +395,15 @@ class JmsAutoConfigurationTests { .hasBean(JmsListenerConfigUtils.JMS_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME)); } + @Test + void testDefaultContainerFactoryWithExceptionListener() { + this.contextRunner.withUserConfiguration(TestConfiguration11.class, EnableJmsConfiguration.class) + .run((context) -> { + DefaultMessageListenerContainer container = getContainer(context, "jmsListenerContainerFactory"); + assertThat(container.getExceptionListener()).isSameAs(context.getBean("exceptionListener")); + }); + } + @Configuration(proxyBeanMethods = false) static class TestConfiguration { @@ -548,6 +559,16 @@ class JmsAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class TestConfiguration11 { + + @Bean + ExceptionListener exceptionListener() { + return mock(ExceptionListener.class); + } + + } + @Configuration(proxyBeanMethods = false) @EnableJms static class EnableJmsConfiguration { From 8c0c30e7a87c257af0536f391221aded6d19be8f Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 15 Feb 2021 13:58:25 +0100 Subject: [PATCH 2/2] Polish "Add support to auto-configure javax.jms.ExceptionListener" See gh-25278 --- ...JmsListenerContainerFactoryConfigurer.java | 27 ++++++++--------- .../jms/JmsAutoConfigurationTests.java | 29 +++++++------------ .../docs/asciidoc/spring-boot-features.adoc | 2 +- 3 files changed, 25 insertions(+), 33 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/DefaultJmsListenerContainerFactoryConfigurer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/DefaultJmsListenerContainerFactoryConfigurer.java index 86e32462c32..d8ed7372333 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/DefaultJmsListenerContainerFactoryConfigurer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/DefaultJmsListenerContainerFactoryConfigurer.java @@ -40,12 +40,12 @@ public final class DefaultJmsListenerContainerFactoryConfigurer { private MessageConverter messageConverter; + private ExceptionListener exceptionListener; + private JtaTransactionManager transactionManager; private JmsProperties jmsProperties; - private ExceptionListener exceptionListener; - /** * Set the {@link DestinationResolver} to use or {@code null} if no destination * resolver should be associated with the factory by default. @@ -64,6 +64,15 @@ public final class DefaultJmsListenerContainerFactoryConfigurer { this.messageConverter = messageConverter; } + /** + * Set the {@link ExceptionListener} to use or {@code null} if no exception listener + * should be associated by default. + * @param exceptionListener the {@link ExceptionListener} + */ + void setExceptionListener(ExceptionListener exceptionListener) { + this.exceptionListener = exceptionListener; + } + /** * Set the {@link JtaTransactionManager} to use or {@code null} if the JTA support * should not be used. @@ -81,14 +90,6 @@ public final class DefaultJmsListenerContainerFactoryConfigurer { this.jmsProperties = jmsProperties; } - /** - * Set the {@link ExceptionListener}. - * @param exceptionListener the {@link ExceptionListener} - */ - void setExceptionListener(ExceptionListener exceptionListener) { - this.exceptionListener = exceptionListener; - } - /** * Configure the specified jms listener container factory. The factory can be further * tuned and default settings can be overridden. @@ -112,6 +113,9 @@ public final class DefaultJmsListenerContainerFactoryConfigurer { if (this.messageConverter != null) { factory.setMessageConverter(this.messageConverter); } + if (this.exceptionListener != null) { + factory.setExceptionListener(this.exceptionListener); + } JmsProperties.Listener listener = this.jmsProperties.getListener(); factory.setAutoStartup(listener.isAutoStartup()); if (listener.getAcknowledgeMode() != null) { @@ -125,9 +129,6 @@ public final class DefaultJmsListenerContainerFactoryConfigurer { if (receiveTimeout != null) { factory.setReceiveTimeout(receiveTimeout.toMillis()); } - if (this.exceptionListener != null) { - factory.setExceptionListener(this.exceptionListener); - } } } 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 6ab88476298..b198a4f8c91 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 @@ -212,6 +212,16 @@ class JmsAutoConfigurationTests { }); } + @Test + void testDefaultContainerFactoryWithExceptionListener() { + ExceptionListener exceptionListener = mock(ExceptionListener.class); + this.contextRunner.withUserConfiguration(EnableJmsConfiguration.class) + .withBean(ExceptionListener.class, () -> exceptionListener).run((context) -> { + DefaultMessageListenerContainer container = getContainer(context, "jmsListenerContainerFactory"); + assertThat(container.getExceptionListener()).isSameAs(exceptionListener); + }); + } + @Test void testCustomContainerFactoryWithConfigurer() { this.contextRunner.withUserConfiguration(TestConfiguration9.class, EnableJmsConfiguration.class) @@ -395,15 +405,6 @@ class JmsAutoConfigurationTests { .hasBean(JmsListenerConfigUtils.JMS_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME)); } - @Test - void testDefaultContainerFactoryWithExceptionListener() { - this.contextRunner.withUserConfiguration(TestConfiguration11.class, EnableJmsConfiguration.class) - .run((context) -> { - DefaultMessageListenerContainer container = getContainer(context, "jmsListenerContainerFactory"); - assertThat(container.getExceptionListener()).isSameAs(context.getBean("exceptionListener")); - }); - } - @Configuration(proxyBeanMethods = false) static class TestConfiguration { @@ -559,16 +560,6 @@ class JmsAutoConfigurationTests { } - @Configuration(proxyBeanMethods = false) - static class TestConfiguration11 { - - @Bean - ExceptionListener exceptionListener() { - return mock(ExceptionListener.class); - } - - } - @Configuration(proxyBeanMethods = false) @EnableJms static class EnableJmsConfiguration { diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc index 4c3a1f73bbf..33ec9511872 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc @@ -5416,7 +5416,7 @@ If a `DestinationResolver` or a `MessageConverter` bean is defined, it is associ ==== Receiving a Message When the JMS infrastructure is present, any bean can be annotated with `@JmsListener` to create a listener endpoint. If no `JmsListenerContainerFactory` has been defined, a default one is configured automatically. -If a `DestinationResolver` or a `MessageConverter` beans is defined, it is associated automatically to the default factory. +If a `DestinationResolver`, a `MessageConverter`, or a `javax.jms.ExceptionListener` beans are defined, they are associated automatically with the default factory. By default, the default factory is transactional. If you run in an infrastructure where a `JtaTransactionManager` is present, it is associated to the listener container by default.