Spring MDP - how to shut it down on bad message
-
12-12-2019 - |
Question
I have got a Spring MDP implemented using Spring DefaultMessageListenderContainer
listening to an input queue on WebSphere MQ v7.1. If there is a bad message coming in (that causes RuntimeException
), what currently happens is that, the transaction is rolled back, and the message is put back into the queue. However the MDP goes into an infinite loop.
Question 1: For my requirements I would like to be able to shut down the processing the moment it sees a bad message. No retries needed. Is it possible to shutdown the message listener gracefully in case it sees a bad message (as opposed to crude System.exit()
or methods of that sort)? I definitely don't like it to go into an infinite loop.
Edit:
Question 2: Is there a way to stop or suspend the listener container to stop further processing of messages?
Solution 3
I solved the problem in the following manner, not sure if this is the best way, however it works.
- MDP Implements ApplicationContextAware; I also maintain a listener state (enum with OPEN, CLOSE, ERROR values) MDP Code fragment below:
//context private ConfigurableApplicationContext applicationContext; //listener state private ListenerState listenerState = ListenerState.OPEN; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = (ConfigurableApplicationContext) applicationContext; } //onMessage method public void processMessages(....) { try { process(...); } catch (Throwable t) { listenerState = ListenerState.ERROR; throw new RuntimeException(...); } } @Override public void stopContext() { applicationContext.stop(); }
- In the java main that loads the spring context i do this:
//check for errors for exit Listener listener = (Listener)context.getBean("listener"); listenerContainer listenerContainer = (ListenerContainer)context.getBean("listenerContainer"); try { while(true) { Thread.sleep(1000); //sleep for 1 sec if(!listener.getListenerState().equals(ListenerState.OPEN)) { listener.stopContext(); listenerContainer.stop(); System.exit(1); }> } } catch (InterruptedException e) { throw new RuntimeException(e); }
OTHER TIPS
The usual way to process this is to have an error queue and when you see a bad message to put it into the error queue.
Some systems handle this for you such as IBM MQ Series. You just need to configure the error queue and how many retries you want ant it will put it there.
An administrator will then look through these queues and take proper action on the messages that are in the queue (i.e. fix and resubmit them)
Actually, System.exit()
is too brutal and... won't work. Retrying of failed messages is handled on the broker (WMQ) side so the message will be redelivered once you restart your application.
The problem you are describing is called poison-message and should be handled on the broker side. It seems to be described in Handling poison messages in WMQ manual and in How WebSphere Application Server handles poison messages.