From 4012b4df5e7170f59c39e9574fdff34c664cd516 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 30 Jul 2015 14:23:36 +0200 Subject: [PATCH] Clarify acknowledge mode semantics and transaction recommendations Issue: SPR-13278 (cherry picked from commit e5a2b348298ee9fd890bd61cbcf9a70d8bf4a278) --- .../AbstractMessageListenerContainer.java | 76 +++++++++++-------- .../SimpleMessageListenerContainer.java | 3 +- 2 files changed, 45 insertions(+), 34 deletions(-) diff --git a/spring-jms/src/main/java/org/springframework/jms/listener/AbstractMessageListenerContainer.java b/spring-jms/src/main/java/org/springframework/jms/listener/AbstractMessageListenerContainer.java index 6642e4fcdfc..3605debf708 100644 --- a/spring-jms/src/main/java/org/springframework/jms/listener/AbstractMessageListenerContainer.java +++ b/spring-jms/src/main/java/org/springframework/jms/listener/AbstractMessageListenerContainer.java @@ -37,19 +37,19 @@ import org.springframework.util.ErrorHandler; import org.springframework.util.ReflectionUtils; /** - * Abstract base class for message listener containers. Can either host - * a standard JMS {@link javax.jms.MessageListener} or a Spring-specific - * {@link SessionAwareMessageListener}. + * Abstract base class for Spring message listener container implementations. + * Can either host a standard JMS {@link javax.jms.MessageListener} or Spring's + * {@link SessionAwareMessageListener} for actual message processing. * - *

Usually holds a single JMS {@link Connection} that all listeners are - * supposed to be registered on, which is the standard JMS way of managing - * listeners. Can alternatively also be used with a fresh Connection per - * listener, for J2EE-style XA-aware JMS messaging. The actual registration - * process is up to concrete subclasses. + *

Usually holds a single JMS {@link Connection} that all listeners are supposed + * to be registered on, which is the standard JMS way of managing listener sessions. + * Can alternatively also be used with a fresh Connection per listener, for Java EE + * style XA-aware JMS messaging. The actual registration process is up to concrete + * subclasses. * - *

NOTE: The default behavior of this message listener container - * is to never propagate an exception thrown by a message listener up to - * the JMS provider. Instead, it will log any such exception at the error level. + *

NOTE: The default behavior of this message listener container is to + * never propagate an exception thrown by a message listener up to the JMS + * provider. Instead, it will log any such exception at the error level. * This means that from the perspective of the attendant JMS provider no such * listener will ever fail. However, if error handling is necessary, then * any implementation of the {@link ErrorHandler} strategy may be provided to @@ -62,37 +62,48 @@ import org.springframework.util.ReflectionUtils; *

  • "sessionAcknowledgeMode" set to "AUTO_ACKNOWLEDGE" (default): * This mode is container-dependent: For {@link DefaultMessageListenerContainer}, * it means automatic message acknowledgment before listener execution, with - * no redelivery in case of an exception. For {@link SimpleMessageListenerContainer}, + * no redelivery in case of an exception and no redelivery in case of other listener + * execution interruptions either. For {@link SimpleMessageListenerContainer}, * it means automatic message acknowledgment after listener execution, with - * redelivery in case of an exception thrown, as defined by the JMS specification. - * In order to consistently achieve the latter behavior with any container variant, - * consider setting "sessionTransacted" to "true" instead. + * no redelivery in case of a user exception thrown but potential redelivery in case + * of the JVM dying during listener execution. In order to consistently arrange for + * redelivery with any container variant, consider "CLIENT_ACKNOWLEDGE" mode or - + * preferably - setting "sessionTransacted" to "true" instead. + *
  • "sessionAcknowledgeMode" set to "DUPS_OK_ACKNOWLEDGE": + * Lazy message acknowledgment during ({@link DefaultMessageListenerContainer}) + * or shortly after ({@link SimpleMessageListenerContainer}) listener execution; + * no redelivery in case of a user exception thrown but potential redelivery in case + * of the JVM dying during listener execution. In order to consistently arrange for + * redelivery with any container variant, consider "CLIENT_ACKNOWLEDGE" mode or - + * preferably - setting "sessionTransacted" to "true" instead. *
  • "sessionAcknowledgeMode" set to "CLIENT_ACKNOWLEDGE": * Automatic message acknowledgment after successful listener execution; - * best-effort redelivery in case of exception thrown. - *
  • "sessionAcknowledgeMode" set to "DUPS_OK_ACKNOWLEDGE": - * Lazy message acknowledgment during or after listener execution; - * potential redelivery in case of exception thrown. + * best-effort redelivery in case of a user exception thrown as well as in case + * of other listener execution interruptions (such as the JVM dying). *
  • "sessionTransacted" set to "true": * Transactional acknowledgment after successful listener execution; - * guaranteed redelivery in case of exception thrown. + * guaranteed redelivery in case of a user exception thrown as well as + * in case of other listener execution interruptions (such as the JVM dying). * - * The exact behavior might vary according to the concrete listener container - * and JMS provider used. * - *

    There are two solutions to the duplicate processing problem: + *

    There are two solutions to the duplicate message processing problem: *

    * Note that XA transaction coordination adds significant runtime overhead, * so it might be feasible to avoid it unless absolutely necessary. @@ -108,7 +119,7 @@ import org.springframework.util.ReflectionUtils; *
  • Alternatively, specify a * {@link org.springframework.transaction.jta.JtaTransactionManager} as * "transactionManager" for a fully XA-aware JMS provider - typically when - * running on a J2EE server, but also for other environments with a JTA + * running on a Java EE server, but also for other environments with a JTA * transaction manager present. This will give full "exactly-once" guarantees * without custom duplicate message checks, at the price of additional * runtime processing overhead. @@ -889,7 +900,7 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen if (errorHandler != null) { errorHandler.handleError(ex); } - else if (logger.isWarnEnabled()) { + else { logger.warn("Execution of JMS message listener failed, and no ErrorHandler has been set.", ex); } } @@ -901,7 +912,6 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen */ @SuppressWarnings("serial") private static class MessageRejectedWhileStoppingException extends RuntimeException { - } } diff --git a/spring-jms/src/main/java/org/springframework/jms/listener/SimpleMessageListenerContainer.java b/spring-jms/src/main/java/org/springframework/jms/listener/SimpleMessageListenerContainer.java index b5272f720bd..c784eb5a12f 100644 --- a/spring-jms/src/main/java/org/springframework/jms/listener/SimpleMessageListenerContainer.java +++ b/spring-jms/src/main/java/org/springframework/jms/listener/SimpleMessageListenerContainer.java @@ -47,7 +47,8 @@ import org.springframework.util.Assert; * on acknowledge modes and transaction options. Note that this container * exposes standard JMS behavior for the default "AUTO_ACKNOWLEDGE" mode: * that is, automatic message acknowledgment after listener execution, - * with redelivery in case of an exception thrown. + * with no redelivery in case of a user exception thrown but potential + * redelivery in case of the JVM dying during listener execution. * *

    For a different style of MessageListener handling, through looped * {@code MessageConsumer.receive()} calls that also allow for