Description
When you configure a JMS listener with SessionAcknowledgeMode set to CLIENT_ACKNOWLEDGE or a custom mode like the UNORDERED_ACKNOWLEDGE mode of AWS SQS, messages are deleted from the SQS after receiving and processing them without a specific message.acknowledge() call.
After searching through debugging and the code, it seems that the problem lies in the combination of the commitIfNecessary function (located in spring-framework/spring-jms/src/main/java/org/springframework/jms/listener/AbstractMessageListenerContainer.java) and the isClientAcknowledge function (located in spring-framework/spring-jms/src/main/java/org/springframework/jms/support/JmsAccessor.java).
The isClientAcknowledge function was committed on 2023/06/12 with the following code:
protected boolean isClientAcknowledge(Session session) throws JMSException {
int mode = session.getAcknowledgeMode();
return (mode != Session.SESSION_TRANSACTED &&
mode != Session.AUTO_ACKNOWLEDGE &&
mode != Session.DUPS_OK_ACKNOWLEDGE);
}
And a specific message:
"This commit updates JmsAccessor to handle custom JMS acknowledgment modes as client acknowledge, which is useful when working with JMS providers that provide non-standard variations of CLIENT_ACKNOWLEDGE, such as AWS SQS and its UNORDERED_ACKNOWLEDGE mode."
However, commitIfNecessary, called always in the last line of doExecuteListener in the same class, commits the message with the condition "isClientAcknowledge(session)":
protected void commitIfNecessary(Session session, @Nullable Message message) throws JMSException {
// Commit session or acknowledge message.
if (session.getTransacted()) {
// Commit necessary - but avoid commit call within a JTA transaction.
if (isSessionLocallyTransacted(session)) {
// Transacted session created by this container -> commit.
JmsUtils.commitIfNecessary(session);
}
}
else if (message != null && isClientAcknowledge(session)) {
message.acknowledge();
}
}
Is this correct? Commit should only be necessary in SESSION_TRANSACTED, AUTO_ACKNOWLEDGE, and DUPS_OK_ACKNOWLEDGE modes. Probably the correct condition is:
else if (message != null && !isClientAcknowledge(session))