0
votes

I'm using Spring @JmsListener to consume message from IBM MQ queue (running in Docker container). I test a scenario as below:

  1. Send message to MQ via web browser
  2. My Spring service will consume and send extracted data from message to a stub (wiremock)
  3. service receives success response and exit normally

In a happy case, message will be committed and removed from the queue, now I add response delay in stub (30s for example), when service waiting for response, I quit Docker to simulate a network issue or MQ down (docker stop cause MQ quiescing which is not my expectation).

So I have 2 questions here?

  1. How can I catch the commit exception thrown by DefaultMessageListenerContainer?
  2. I use DefaultJmsListenerContainerFactory::setExceptionListener() method to attach a listener, I can log the exception here but logback MDC is not logged (MDC contains messageId and payload for audit purpose). How can I pass MDC values to this listener?

Source code:

@Bean
public DefaultJmsListenerContainerFactory containerFactory() {
  DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
  factory.setConnectionFactory(createConnectionFactory());
  factory.setExceptionListener(exceptionListener());
  factory.setSessionAcknowledgeMode(javax.jms.Session.CLIENT_ACKNOWLEDGE);
  return factory;
}

@Bean
public ExceptionListener exceptionListener() {
  return exception -> {
    if (exception.getCause() instanceof InterruptedException) {
      // MDC value is not logged in below log.error()
      log.error("commit error");
    } else {
      log.error("jms connection error");
    }
  }
}

---

@JmsListener(id="0", destination="DEV.QUEUE.1", containerFactory="containerFactory")
public void listener(Message msg) {
  try {
    // extract data
    MDC.put("key", value); // for audit
    // call stub
  } catch (JMSException e) {
    throw new Exception(e); // throw exception, don't commit
  }
}

Any help would be most appreciated.

1
I don't see any code that sets up a transaction, or a delay between message fetch and commit, or a a commit.chughts
@daury if you run docker stop on the MQ queue manager docker instance then your queue manager will stop. If you terminate the process then it will terminate. Can you use docker network disconnect to simulate a network outage between your various containers? Do you see the JMSXDeliveryCount incremented for the message you are processing?richc
@chughts I don't set up transaction here, if listener return normally without exception, message is committed and removed from queue, otherwise it will be moved to a DLQ - this behavior is configured in IBM MQ.dauruy
@richc JMSXDeliveryCount is 1 when I receive msg and after I disconnect, it's still 1, but there is no exception thrown from my service when I run docker network disconnectdauruy
When I run dis qs(DEV.QUEUE.1) it returns UNCOM(1), but after a while, it returns UNCOM(NO)dauruy

1 Answers

0
votes
  1. I can catch the JMSException in the error handler
@Bean
public DefaultJmsListenerContainerFactory containerFactory() {
  ...
  factory.setErrorHandler(errorHandler());
}

@Bean
public ErrorHandler errorHandler() {
  return exception -> {
    if (exception.getMessage().contains("session has already been closed")) {
      // log something here
    }
  }
}
  1. There is a MDC.clear() in my listener method so that's why I lost MDC fields. Remove it solves the issue.