I'm building a service around Azure Service Bus with Java Language and Spring framework using JMS listeners. I could figure out a way to send a message directly to the DeadLetterQueue, without waiting for the retry. This is useful if you know that the error is kind of fatal and makes no sense to retry. Here is my code to send to DLQ directly:
@JmsListener(
destination = "topic-name",
containerFactory = "topicJmsListenerContainerFactory",
subscription = "subscription-name"
)
public void receive(JmsMessage message, Session session) throws JMSException, JsonProcessingException, IllegalAccessException, NoSuchFieldException {
log.info("Message received from {} : {}", subscriptionName, message.getJMSMessageID());
try {
StatusDto status = service.process(parseMessageToDto(message), message.getJMSCorrelationID());
log.info("Message: {} sent with success. Status: {}", message.getJMSCorrelationID(), mapper.writeValueAsString(status));
} catch (FatalException | JsonProcessingException | ConstraintViolationException exception) {
serviceBusHelper.moveToDeadLetterQueue(message, session, exception);
}
}
Here is my serviceBusHelper that is sending the message directly to DLQ.
private final int deadLetterQueue = ProviderConstants.ACK_TYPE.REJECTED.ordinal();
public void moveToDeadLetterQueue(JmsMessage message, Session session, Exception reason)
throws NoSuchFieldException, IllegalAccessException, JMSException, JsonProcessingException {
message.setReadOnlyProperties(false);
String deadLetterReason = reason.getClass().getSimpleName();
message.setStringProperty("DeadLetterReason", deadLetterReason);
message.setStringProperty("DeadLetterErrorDescription", reason.getMessage());
message.setAcknowledgeCallback(buildDlqAcknowledgeCallback(session));
message.acknowledge();
log.info("Moved message: {}, to dead letter queue due to: {}", message.getJMSCorrelationID(), deadLetterReason);
}
JmsAcknowledgeCallback buildDlqAcknowledgeCallback(Session session) throws NoSuchFieldException, IllegalAccessException {
JmsAcknowledgeCallback callback = new JmsAcknowledgeCallback(getInnerSessionFromAzureServiceBus(session));
callback.setAckType(deadLetterQueue);
return callback;
}
JmsSession getInnerSessionFromAzureServiceBus(Session session) throws NoSuchFieldException, IllegalAccessException {
log.debug("Fetch inner session from session with class: {}", session.getClass().getSimpleName());
Session serviceBusJmsSession = (Session) unProxy(session); // Session is usually a proxy.
Field field = serviceBusJmsSession.getClass().getDeclaredField("innerSession");
field.setAccessible(true);
return (JmsSession) field.get(serviceBusJmsSession);
}
Object unProxy(Object proxy) throws NoSuchFieldException, IllegalAccessException {
if (!Proxy.isProxyClass(proxy.getClass())) {
return proxy;
} else {
InvocationHandler handler = Proxy.getInvocationHandler(proxy);
Field targetField = handler.getClass().getDeclaredField("target");
targetField.setAccessible(true);
return unProxy(targetField.get(handler));
}
}
I had to use reflection to be able to fetch the JmsSession and create an acknowledgeCallback with the ACK_TYPE.REJECTED that is sending directly to DLQ the message.
Finally here is my configuration for the topicJmsListenerContainerFactory:
spring.jms.servicebus.namespace=namespace
spring.jms.servicebus.pricing-tier=standard
spring.jms.servicebus.passwordless-enabled=true
spring.jms.servicebus.enabled=true
spring.jms.cache.producers=false
spring.jms.template.session.transacted=false
spring.jms.template.session.acknowledge-mode=client
spring.jms.listener.session.transacted=false
spring.jms.listener.session.acknowledge-mode=client
spring.jms.servicebus.listener.subscription-durable=true
spring.jms.listener.receive-timeout=60000
The acknowledge-mode=client allows me to acknowledge my message and to use the callback that sends the message to DLQ. However that doesn't allow mes to use a transacted session that seems to be able to update and add a message property.
To summarize: With this configuration and code, I'm able to send a message directly to the DLQ but the DeadLetterReason is not added. Do you know or have an idea how I could add this string property?
Thanks a lot.



