Some of our Spring-JMS developers need to consume from a queue using transactions with a backout queue pattern. The goal is that after a message is rolled back a certain number of times, then it will be automatically moved to the backout queue. We have the target and backout queues defined as follows on the server:
DEFINE QLOCAL('MYQUEUE') CLWLUSEQ(ANY) CLUSTER('MYCLUS') DEFBIND(NOTFIXED) BOQNAME('MYQUEUE.BO') BOTHRESH(3) REPLACE
DEFINE QLOCAL('MYQUEUE.BO') CLWLUSEQ(ANY) CLUSTER('MYCLUS') DEFBIND(NOTFIXED) REPLACE
We've found several JMS examples using MQ transactions with commit and rollback. There seem to be multiple ways to code this in JMS. What we're finding is that some will automatically move the message to the backout queue once the threshold is exceeded and others do not. For example, this example does automatically move the message:
@SpringBootApplication
@RestController
@EnableJms
@EnableTransactionManagement
public class MqTransactApplication {
...
private static JmsTemplate jmsTemplate;
private static JmsTransactionManager jmsTransaction;
private TransactionStatus status = null;
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(MqTransactApplication.class, args);
jmsTemplate = context.getBean(JmsTemplate.class);
jmsTransaction = new JmsTransactionManager();
jmsTransaction.setConnectionFactory(jmsTemplate.getConnectionFactory());
}
@PostMapping(value = "recv", consumes = { MediaType.APPLICATION_JSON_VALUE }, produces = {
MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<RecvResponse> recv(@RequestBody RecvRequest request) {
try {
TransactionStatus status = jmsTransaction.getTransaction(null);
String msg = jmsTemplate.receiveAndConvert(QUEUE_NAME).toString();
boolean commit = this.handleMessage(msg);
if (commit) {
jmsTransaction.commit(status);
} else {
jmsTransaction.rollback(status);
}
return ResponseEntity.created(null).body(new RecvResponse("Success", msg));
} catch (JmsException ex) {
ex.printStackTrace();
return ResponseEntity.created(null).body(new RecvResponse("Fail"));
}
}
}
However, this approach does not. The code continues to receive the same message after rollback even though the backout count is greater than than the threshold.
@Component
public class Responder implements SessionAwareMessageListener {
@JmsListener(destination = Requester.qName)
@Transactional(rollbackFor = Exception.class)
public void onMessage(Message msg, Session session) throws JMSException {
String text;
if (msg instanceof TextMessage) {
text = ((TextMessage) msg).getText();
}
else {
text = msg.toString();
}
final String msgID = msg.getJMSMessageID();
MessageProducer replyDest = session.createProducer(msg.getJMSReplyTo());
TextMessage replyMsg = session.createTextMessage("Replying to " + text);
replyMsg.setJMSCorrelationID(msgID);
replyDest.send(replyMsg);
if (!msg.getJMSRedelivered()) {
System.out.println("Doing a rollback");
session.rollback();
/*throw new JMSException("Instead of rollback?");*/
}
else {
System.out.println("Doing a commit");
session.commit();
}
}
}
Is there something missing from the above example?
More importantly, is there a best practice or preferred way to consume messages with JMS when using a backout queue? I've seen other references to MessageConsumer and ConnectionConsumer classes in this context (neither of which we have tried, yet).
Thanks
------------------------------
Jim Creasman
------------------------------