I am using ActiveMQ to send the message.
So when I sent a message, the message comes to receive message. On successful insertion, it is acknowledged.
But I have code after acknowledgement, which can throw NullPointerException.
So to produce that exception intentionally, I have thrown NullPointerException.
So when it does that:
Message is not dequeued and the same message comes again to the onMessage function.
My code is:
public void onMessage(Message message) {
String msg = null;
try
{
msg = receiveMessage(message);
// Other code to insert message in db
message.acknowledge();
if(true)
{
throw new NullPointerException("npe"));
}
** // Other code which might produce a null pointer exception **
}
catch(Exception ex)
{
}
}
Why is the message again coming to onMessage() function as I have acknowledge() it also.
Since I have already inserted the message in db.
Doesn't the message inside queue will be removed on acknowledge()?
How I can achieve this?
You use AUTO acknowledge mode with message listners, then by specification, a message is redelivered if the message listeners fails to return successfully (for instance if there is an exception thrown).
In your case, you are trying to manually acknowledge the message, but that is not possible using a session created with createSession(false, Session.AUTO_ACKNOWLEDGE).
Your code would have worked with Session.CLIENT_ACKNOWLEDGE.
Otherwise, you want to catch the exceptions inside the onMessage method, while using AUTO_ACKNOWLEDGE.
To get a more fine grained controll over your messages, please consider using transacted sessions and use session.commit(); to confirm a message has been read.
Have you checked that you are not using transacted sessions?. When using transacted sessions,the acknowledge mode is ignored, so:
Your message.acknowledge() would effectively be a no-op
Your uncaught exception would be triggering a "session rollback" when escaping your message listener, forcing redelivery of the message.
NOTE: Your published code has a catch (Exception ex) { }, so I don't know exactly how your exception escapes outside.
You can create a separate method for processing the message, by which I mean that in the onMessage() function write code for only insertion of that message into the database.
And create a separate function for the processing of that message.
So that if you get any error during processing, the message will not come to onMessage() again.
When you use a transacted JMS acknowledge mode, your message will be received by JMS-listener several times (in AMQ by default it is approximately eight) till be processed without exception or will be moved by JMS-container to DQL-queue. See Message Redelivery and DLQ Handling for details.
Managing transactions depends on the framework used by you. I prefer to use Spring Framework, so my Spring XML configuration is looks like:
<jms:listener-container container-type="default"
connection-factory="calendarConnectionFactory"
acknowledge="transacted"
destination-type="queue"
cache="consumer"
concurrency="1-5">
<jms:listener destination="${jms.calendar.destination}" ref="calendarListener"/>
</jms:listener-container>
And the Java code of my message listener is
#Override
#Transactional(propagation = Propagation.REQUIRED,
noRollbackFor =
{ClassCastException.class, IllegalArgumentException.class})
public void onMessage(Message message) {
....
}
So I can manage what exceptions will rollback the transaction or not.
Related
Currently, my program is processing the messages being received from a queue but we encountered a xml file that has an error and what happens is it keeps looping on the same message and retrying to process it.
I would like to move the message to dead letter queue when a message like this occurs again.
What I did right now is that I created a class that will "producer.send(destination, msg)" to the dead queue and call this function on the try-catch but it seems that it is not working.
As #JoshMc hinted you should be treating the error messages as poison messages. For that you will need to enable transactions, and invoke a rollback for the error message.
ie. logic that looks like
// Create a connection factory
JmsFactoryFactory ff = JmsFactoryFactory.getInstance(WMQConstants.WMQ_PROVIDER);
JmsConnectionFactory cf = ff.createConnectionFactory();
//Set connection properties
...
context = cf.createContext(JMSContext.SESSION_TRANSACTED);
try {
...
// Message Processing
...
// All is OK
context.commit();
} catch (Exception e) {
// Message processing failed
context.rollback();
}
If a backout queue and backout threshold is set then the poison message is put on to the backout queue (BOQNAME) after BOTHRESH attempts at handling the message.
All this is done for you, by the underlying MQ Client code.
There is an explanation in this article - https://developer.ibm.com/articles/an-introduction-to-local-transactions-using-mq-and-jms/
which also links to sample code here - https://github.com/ibm-messaging/mq-dev-patterns/tree/master/transactions/JMS/SE
I am using Apache Camel with Spring to send messages from my Java service. I need to handle/trigger certain events in case of any error occurred at exchange. I am using below code to achieve my objective.
try
{
producerTemplate.sendBody(endPoint, bytes);
}
catch (final RuntimeCamelException exception)
{
LOGGER.error("Exception occured in sendBody", exception.getMessage(), exception);
handleError(); // handle error here.
}
In order to test I set the value of endPoint to incorrect route name broadcast.SIMULATOR.ROUTE1. When I am running above code, I can see following error in console but it never comes inside catch block.
[33m16:15:51,714 WARN [org.springframework.jms.connection.CachingConnectionFactory] (QpidJMS Connection Executor: ID:7dacac8c-93ce-48c0-92fe-8dc0e8:1) Encountered a JMSException - resetting the underlying JMS Connection: javax.jms.JMSSecurityException: Admin#QPID9019 cannot publish to broadcast with routing-key broadcast.SIMULATOR.ROUTE1 (/builddir/build/BUILD/qpid-cpp-1.36.0/src/qpid/broker/amqp/Authorise.cpp:126) [condition = amqp:unauthorized-access]
at org.apache.qpid.jms.provider.amqp.AmqpSupport.convertToException(AmqpSupport.java:143) [qpid-jms-client-0.23.0.jar:]
at org.apache.qpid.jms.provider.amqp.AmqpSupport.convertToException(AmqpSupport.java:117) [qpid-jms-client-0.23.0.jar:]
I am sending multiple messages to the route. For first message, JMSSecurityException is logged in console and execution continues. From second message onwards, execution goes inside catch with IllegalStateException (Session is closed) .
How do I bring execution inside catch block with first message only (for JMSSecurityException)?
It depends on the JMS client you are using. As some of them send the message in an asynchronous fashion. They may have a configuration option you can use to turn this off.
For example Apache ActiveMQ has this with asyncSend option
http://activemq.apache.org/async-sends.html which you can then turn off.
Could you please clarify the following problem. Is it possible to return message to queue in case Message Driven Bean could not process a message. My code likes :
public void onMessage(Message message) {
try {
doSomethingWithMessage(message)
} catch (QueueListenerUtilException e) {
LOG.error("Could not process given message try to rollback transaction");
mdc.setRollbackOnly();
throw e;
}
LOG.debug("Sending message has been started");
this.simCntrlUtil.writeToQueue(answer, message, msgProp);
LOG.info(" onMessage has been completed");
}
I suppose that if bean throws RuntimeException the transaction will be rollbacked and after timeout will be delivered again. In server log I see message
"Resources rolled back due to setRollbackOnly() being called"
But message is not delivered again after timeout. I think that corresponded options are set in WebSphere configuration.
this
Automatically stop endpoints on repeated message failure
Enable
Sequential failed message threshold
100
Delay between failing message retries
10000
milliseconds
The transaction is managed by container (I did not change dedault initialization)
Could you please help me to understand : why I don't see message again?
Thank you in advance
I have the following listener method:
#Override
public void onMessage(Message message, Channel channel) {
try {
// do something bad :)
} catch (Exception e){
try {
long dt = null != message.getMessageProperties()
? message.getMessageProperties().getDeliveryTag()
: 0;
channel.basicReject(dt, true);
} catch(IOException io) {
logger.error("IO-COMMON", io);
}
}
}
The issue is basic reject doesn't work, I don't know why. How to reject it gracefully? I think that if I reject a message, it should be requeued and reside is sth like cache, before going to next worker. But in fact this message just seems to be lost.
You need to set the acknowledgemode to MANUAL if you are doing your own acks. I am not sure why it's not working for you; DEBUG/TRACE logging might help.
You should consider letting the container handle the acks - use acknowledgemode=AUTO; the container will normally requeue the message for any exception thrown or ack it if the listener returns normally.
You can set defaultRequeueRejected to false (it is true by default) and the message will be discarded (or routed to a DLX/DLQ).
You can also throw an AmqpRejectAndDontRequeueException to override the default mechanism of requeuing failed messages.
If the ack mode is NONE - there are no acks and RabbitMQ automatically acks the message as soon as it's sent.
There is a class 'MyConsumer' which receives messages from a queue, and processes them. There are two requirements:
If there is a message contains invalid content, MyConsumer should not acknowledge it, but can process later messages
The unconsumed message will be deliver again when MyConsumer restarts
I tried with spring-jms, with the listener-container supports, but can't find a solution fits the first requirement.
My code:
<amq:queue id="destination" physicalName="org.springbyexample.jms.test"/>
<amq:connectionFactory id="jmsFactory" brokerURL="tcp://localhost:11111"/>
<bean id="jmsConsumerConnectionFactory"
class="org.springframework.jms.connection.SingleConnectionFactory"
p:targetConnectionFactory-ref="jmsFactory"/>
<bean id="jmsConsumerTemplate" class="org.springframework.jms.core.JmsTemplate"
p:connectionFactory-ref="jmsConsumerConnectionFactory"
p:defaultDestination-ref="destination"/>
<bean id="jmsMessageListener" class="test.MyConsumer"/>
<bean id="errorHandler" class="test.MyErrorHandler"/>
<jms:listener-container container-type="default"
connection-factory="jmsConsumerConnectionFactory"
error-handler="errorHandler"
acknowledge="client">
<jms:listener destination="org.springbyexample.jms.test" ref="jmsMessageListener"/>
</jms:listener-container>
Class MyConsumer:
#Override
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("!!!!!!!!! get message: " + textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
if (theNumberOfMessageIs(3)) {
throw new RuntimeException("something is wrong");
}
}
You may notice that the acknowledge in listener-container is client, actually it has 3 values:
auto (default)
client
transacted
I tried all of them, but none fits my requirement. My test scenario:
producer put 3 messages to queue
start a thread to monitor the message count in queue, when the count changes, print it
start consumer, it will receive messages from queue, and processes them
wait a while, put another 3 messages to queue
For auto:
MyConsumer will acknowledge after receiving each message, no matter throwing exception or not
For client:
MyConsumer will acknowledge only if no exception thrown in onMessage. For the 3rd message, it throws exception, there will be a message in the queue unconsummed. But when it get the 4th message and doesn't throw exception, the 3rd message in queue will be disapeared
For transacted:
If exception thrown in MyConsumer, the message will not be acknowledged and be re-delivered several times. After that, the message is disappeared from queue
But none of them fit the requirement 1.
I wonder: if I need to look for other solution than Spring-jms, or my usage is not correct?
auto The DefaultMessageListenerContainer is really designed for transactions - with auto, as you have found, the message is always acknowledged. You can use a SimpleMessagseListenerContainer which will work as you desire, but it has other limitations; see the JavaDocs.
client That's just the way JMS works when you ack #4, #3 is automatically acked too - see the Message JavaDocs. Client mode is used to reduce ack traffic (by, say, acking every 10 messages).
transacted That's a function of the broker, you can configure AMQ to send the bad message to a Dead Letter Queue after some number of retries.
You would need some process to move messages from the DLQ back to the main queue for later retry (perhaps during initialization on restart).
Using WMQ you can achieve the requirement using BackOut feature using BOTHRESH and BOQNAME QUEUE configuration parameters, where BOTHRESH define how many times you will try consume the message and after that parameter BOQNAME define the name of QUEUE that your message you be redelivery. In this case you can use a DLQ QUEUE where you can move messages to main QUEUE after some time or use you main QUEUE as DLQ QUEUE that enable message rotate in you consumer.
Hope that helps.