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.
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
When does MQ throw the JMS Exception 'MQJMS1022: failed to redirect message' ?
Our application(running on JDK 1.5) is connected to MQ 7.5.
While reading the message from MQ the following exception is thrown.
javax.jms.JMSException: MQJMS1022: failed to redirect message
at com.ibm.mq.jms.services.ConfigEnvironment.newException(ConfigEnvironment.java:530)
at com.ibm.mq.jms.MQQueueReceiver.receiveAsync(MQQueueReceiver.java:734)
at com.ibm.mq.jms.SessionAsyncHelper.run(SessionAsyncHelper.java:269)
at java.lang.Thread.run(Thread.java:682)
There is no stack trace from our application code.
Does anyone know what could be the reason.
The MQ documentation is very poor and i couldn't find much info about the error.
It is a really good idea to also dump out the LinkedException.
i.e.
catch (JMSException e)
{
System.err.println("getLinkedException()=" + e.getLinkedException());
System.err.println(e.getLocalizedMessage());
e.printStackTrace();
}
The LinkedException will contain the MQ Reason Code.
Exception: MQJMS1022: failed to redirect message :
can be caused if there is a poison message, and application
attempts to back it out. If there is no backout queue defined on a
queue manager, MQ has nowhere to put the message, hence the exception.
Please confirm if you have defined the backout queue. The knowledge center page about handling poison messages may be helpful
I have a simple database connection method that I want to add a condition that if the connection fails then an email is sent to a list of people telling that it failed to connect.
My current method is this:
public Connection createInitialConnection() throws ClassNotFoundException, SQLException, PollingException
{
DBConnectionDetails conDetails = new DBConnectionDetails("INITIAL");
Class.forName(conDetails.getDriver());
Connection connInitial = DriverManager.getConnection(conDetails.getUrl(),
conDetails.getUser(), conDetails.getPassword());
logger.info("Initial connection created" + conDetails.getUrl());
return connInitial;
}
Currently, there is no checking to see if the connection was successful, if it does not connect then the program just keeps going.
I'm not sure about the best way to do this would be. An if/else or try/catch?
Logging libraries like log4j allow you to add a logging appender that sends emails for each log entry (of course you can filter by severity, category, etc.). So when you attempt to get a connection and an exception is thrown it should get caught by the exception handler and logged, and the logger will send emails.
That assumes your application has some kind of exception handler that logs uncaught exceptions. Where that happens depends on your application's architecture. It could be in a servlet, in try-catch code in the jsp, or in a dedicated handler in a webapp framework like struts or spring-mvc.
If you implement this you will have a way to be notified when any exceptions are getting logged, it won't be limited to database connectivity problems.
You can send email on catch block.
try{
Class.forName(conDetails.getDriver());
Connection connInitial = DriverManager.getConnection(conDetails.getUrl(),
conDetails.getUser(), conDetails.getPassword());
}
catch(SQLException | ClassNotFoundException ex){ // Java 7 needed for multicatch
// Send the Email.
}
Maybe you should try to catch SQLExcepton and then, send notification like this :
try {
Connection connInitial = DriverManager.getConnection(conDetails.getUrl(), conDetails.getUser(), conDetails.getPassword());
} catch (SQLException e) {
//send notification here
}
catch block. It is always better than if-else
try{
// your Logic to test the connection
}
catch(Exception ex)
{
//Code to send the mail if the connection is failed.
}
You can do with try { } Catch .. This in case you have an execption like no network or somthing. after that you need to check if Connection != null.. This is all you need
Sending an email in the catch block might be OK for your application, but then you're faced with how to configure the email. Who should receive it? Are multiple recipients possible? How should the senders, subject and format be configured and passed to your mailer? What if the mailer fails? You may find yourself reinventing an existing solution if you go that route.
If you're using Log4j, it might be a good idea to configure a SMTPAppender for your existing logger or create a logger for errors which need to be sent over email. You can configure Log4j to use the appender for only SQL exceptions if you like (ie. log4j: Log output of a specific class to a specific appender).
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.
I am trying to handle flow control situation on producer end.
I have a queue on a qpid-broker with a max queue-size set. Also have flow_stop_count and flow_resume_count set on the queue.
now at the producer keeps on continuously producing messages until this flow_stop_count is reached. Upon breach of this count, an exception is thrown which is handled by Exception listener.
Now sometime later the consumer on queue will catch up and the flow_resume_count will be reached. The question is how does the producer know of this event.
Here's a sample code of the producer
connection connection = connectionFactory.createConnection();
connection.setExceptionListenr(new MyExceptionListerner());
connection.start();
Session session = connection.createSession(false,Session.CLIENT_ACKNOWLEDGE);
Queue queue = (Queue)context.lookup("Test");
MessageProducer producer = session.createProducer(queue);
while(notStopped){
while(suspend){//---------------------------how to resume this flag???
Thread.sleep(1000);
}
TextMessage message = session.createTextMessage();
message.setText("TestMessage");
producer.send(message);
}
session.close();
connection.close();
and for the exception listener
private class MyExceptionListener implements ExceptionListener {
public void onException(JMSException e) {
System.out.println("got exception:" + e.getMessage());
suspend=true;
}
}
Now the exceptionlistener is a generic listener for exceptions, so it should not be a good idea to suspend the producer flow through that.
What I need is perhaps some method on the producer level , something like produer.isFlowStopped() which I can use to check before sending a message. Does such a functionality exist in qpid api.
There is some documentation on the qpid website which suggest this can be done. But I couldn't find any examples of this being done anywhere.
Is there some standard way of handling this kind of scenario.
From what I have read from the Apache QPid documentation it seems that the flow_resume_count and flow_stop_count will cause the producers to start getting blocked.
Therefore the only option would be to software wise to poll at regular intervals until the messages start flowing again.
Extract from here.
If a producer sends to a queue which is overfull, the broker will respond by instructing the client not to send any more messages. The impact of this is that any future attempts to send will block until the broker rescinds the flow control order.
While blocking the client will periodically log the fact that it is blocked waiting on flow control.
WARN AMQSession - Broker enforced flow control has been enforced
WARN AMQSession - Message send delayed by 5s due to broker enforced flow control
WARN AMQSession - Message send delayed by 10s due to broker enforced flow control
After a set period the send will timeout and throw a JMSException to the calling code.
ERROR AMQSession - Message send failed due to timeout waiting on broker enforced flow control.
From this documentation it implicates that the software managing the producer would then have to self manage. So basically when you receive an exception that the queue is overfull you will need to back off and most likely poll and reattempt to send your messages.
You can try setting the capacity (size in bytes at which the queue is thought to be full ) and flowResumeCapacity (the queue size at which producers are unflowed) properties for a queue.
send() will then be blocked if the size exceeds the capacity value.
You can have a look at this test case file in the repo to get an idea.
Producer flow control is not yet implemented on the JMS client.
See https://issues.apache.org/jira/browse/QPID-3388