From 95f7180298337ec9f140d4f6bb81ffd0e0e1dff2 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 14 Feb 2018 15:32:23 +0100 Subject: [PATCH] Explicit documentation notes on transacted sessions vs AUTO_ACKNOWLEDGE Issue: SPR-16487 --- .../jms/annotation/EnableJms.java | 3 ++- .../jms/annotation/JmsListener.java | 8 +++++- src/docs/asciidoc/integration.adoc | 27 ++++++++++++------- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/spring-jms/src/main/java/org/springframework/jms/annotation/EnableJms.java b/spring-jms/src/main/java/org/springframework/jms/annotation/EnableJms.java index 0d908450d2a..9f187870208 100644 --- a/spring-jms/src/main/java/org/springframework/jms/annotation/EnableJms.java +++ b/spring-jms/src/main/java/org/springframework/jms/annotation/EnableJms.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 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. @@ -41,6 +41,7 @@ import org.springframework.context.annotation.Import; * DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); * factory.setConnectionFactory(connectionFactory()); * factory.setDestinationResolver(destinationResolver()); + * factory.setSessionTransacted(true); * factory.setConcurrency("5"); * return factory; * } diff --git a/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListener.java b/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListener.java index 78329881883..11b21b18715 100644 --- a/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListener.java +++ b/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 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. @@ -33,6 +33,12 @@ import org.springframework.messaging.handler.annotation.MessageMapping; * assumed to be available with a bean name of {@code jmsListenerContainerFactory} * unless an explicit default has been provided through configuration. * + *

Consider setting up a custom + * {@link org.springframework.jms.config.DefaultJmsListenerContainerFactory} bean. + * For production purposes, you'll typically fine-tune timeouts and recovery settings. + * Most importantly, the default 'AUTO_ACKNOWLEDGE' mode does not provide reliability + * guarantees, so make sure to use transacted sessions in case of reliability needs. + * *

Processing of {@code @JmsListener} annotations is performed by registering a * {@link JmsListenerAnnotationBeanPostProcessor}. This can be done manually or, * more conveniently, through the {@code } element or diff --git a/src/docs/asciidoc/integration.adoc b/src/docs/asciidoc/integration.adoc index fb9ad9779fb..1951a56be3e 100644 --- a/src/docs/asciidoc/integration.adoc +++ b/src/docs/asciidoc/integration.adoc @@ -1797,6 +1797,13 @@ the message getting redelivered. Alternatively, consider using 'CLIENT_ACKNOWLED which provides redelivery in case of an exception as well but does not use transacted Sessions and therefore does not include any other Session operations (such as sending response messages) in the transaction protocol. + +**The default 'AUTO_ACKNOWLEDGE' mode does not provide proper reliability guarantees.** +Messages may get lost when listener execution fails (since the provider will automatically +acknowledge each message after listener invocation, with no exceptions to be propagated to +the provider) or when the listener container shuts down (this may be configured through +the 'acceptMessagesWhileStopping' flag). Make sure to use transacted sessions in case of +reliability needs, e.g. for reliable queue handling and durable topic subscriptions. ==== [[jms-mdp-default]] @@ -1834,6 +1841,13 @@ alternative: wrapping your entire processing with an XA transaction (through con your `DefaultMessageListenerContainer` with an `JtaTransactionManager`), covering the reception of the JMS message as well as the execution of the business logic in your message listener (including database operations etc). + +**The default 'AUTO_ACKNOWLEDGE' mode does not provide proper reliability guarantees.** +Messages may get lost when listener execution fails (since the provider will automatically +acknowledge each message before listener invocation) or when the listener container shuts +down (this may be configured through the 'acceptMessagesWhileStopping' flag). Make sure +to use transacted sessions in case of reliability needs, e.g. for reliable queue handling +and durable topic subscriptions. ==== @@ -2065,7 +2079,6 @@ Below is a simple implementation of an MDP: throw new IllegalArgumentException("Message must be of type TextMessage"); } } - } ---- @@ -2108,7 +2121,6 @@ handling method with access to the JMS `Session` from which the `Message` was re public interface SessionAwareMessageListener { void onMessage(Message message, Session session) throws JMSException; - } ---- @@ -2153,7 +2165,6 @@ various `Message` types that they can receive and handle. void handleMessage(byte[] message); void handleMessage(Serializable message); - } ---- @@ -2200,7 +2211,6 @@ also how the `'receive(..)'` method is strongly typed to receive and respond onl public interface TextMessageDelegate { void receive(TextMessage message); - } ---- @@ -2242,7 +2252,6 @@ non-void value. Consider the interface and class: // notice the return type... String receive(TextMessage message); - } ---- @@ -2469,10 +2478,10 @@ your `@Configuration` classes. @Bean public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() { - DefaultJmsListenerContainerFactory factory = - new DefaultJmsListenerContainerFactory(); + DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); factory.setConnectionFactory(connectionFactory()); factory.setDestinationResolver(destinationResolver()); + factory.setSessionTransacted(true); factory.setConcurrency("3-10"); return factory; } @@ -2501,6 +2510,7 @@ element. class="org.springframework.jms.config.DefaultJmsListenerContainerFactory"> + ---- @@ -2696,8 +2706,7 @@ the time to live, you can configure the `JmsListenerContainerFactory` accordingl @Bean public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() { - DefaultJmsListenerContainerFactory factory = - new DefaultJmsListenerContainerFactory(); + DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); factory.setConnectionFactory(connectionFactory()); QosSettings replyQosSettings = new ReplyQosSettings(); replyQosSettings.setPriority(2);