Abstract base class for message listener containers. Can either host a standard JMS {@link javax.jms.MessageListener} or a Spring-specific{@link SessionAwareMessageListener}.
Usually holds a single JMS {@link Connection} that all listeners aresupposed 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.
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.
The listener container offers the following message acknowledgment options:
- "sessionAcknowledgeMode" set to "AUTO_ACKNOWLEDGE" (default): Automatic message acknowledgment before listener execution; no redelivery in case of exception thrown.
- "sessionAcknowledgeMode" set to "CLIENT_ACKNOWLEDGE": Automatic message acknowledgment after successful listener execution; no 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.
- "sessionTransacted" set to "true": Transactional acknowledgment after successful listener execution; guaranteed redelivery in case of exception thrown.
The exact behavior might vary according to the concrete listener container and JMS provider used.
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 and rollback the active transaction if there is one. This means that from the perspective of the attendant JMS provider no listener will ever fail.
There are two solutions to the duplicate processing problem:
- Either add duplicate message detection to your listener, in the form of a business entity existence check or a protocol table check. This usually just needs to be done in case of the JMSRedelivered flag being set on the incoming message (else just process straightforwardly).
- Or wrap the entire processing with an XA transaction, covering the reception of the message as well as the execution of the message listener. This is only supported by {@link DefaultMessageListenerContainer}, through specifying a "transactionManager" (typically a {@link org.springframework.transaction.jta.JtaTransactionManager}, with a corresponding XA-aware JMS {@link javax.jms.ConnectionFactory} passed in as"connectionFactory").
Note that XA transaction coordination adds significant runtime overhead, so it might be feasible to avoid it unless absolutely necessary.
Recommendations:
- The general recommendation is to set "sessionTransacted" to "true", typically in combination with local database transactions triggered by the listener implementation, through Spring's standard transaction facilities. This will work nicely in Tomcat or in a standalone environment, often combined with custom duplicate message detection (if it is unacceptable to ever process the same message twice).
- 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 transaction manager present. This will give full "exactly-once" guarantees without custom duplicate message checks, at the price of additional runtime processing overhead.
Note that it is also possible to specify a {@link org.springframework.jms.connection.JmsTransactionManager} as external"transactionManager", providing fully synchronized Spring transactions based on local JMS transactions. The effect is similar to "sessionTransacted" set to "true", the difference being that this external transaction management will also affect independent JMS access code within the service layer (e.g. based on {@link org.springframework.jms.core.JmsTemplate} or{@link org.springframework.jms.connection.TransactionAwareConnectionFactoryProxy}), not just direct JMS Session usage in a {@link SessionAwareMessageListener}.
@author Juergen Hoeller
@since 2.0
@see #setMessageListener
@see javax.jms.MessageListener
@see SessionAwareMessageListener
@see #handleListenerException
@see DefaultMessageListenerContainer
@see SimpleMessageListenerContainer
@see org.springframework.jms.listener.endpoint.JmsMessageEndpointManager