Spring MessageListenerAdapter and invalid ReplyTo destination

View: New views
8 Messages — Rating Filter:   Alert me  

Spring MessageListenerAdapter and invalid ReplyTo destination

by Bryan Talbot :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I have a stand-alone spring program (no J2EE container) listening on a queue and sending response messages to the destination in the ReplyTo header. The application is configured to use spring's MessageListenerAdapter and DefaultMessageListenerContainer.

Everything works fine until a message is received that has an invalid destination in the ReplyTo header. In my use case, the requester has disconnected (timeout) and the temporary destination created for reply has been closed by the router.

When this happens, the MessageListenerAdapter throws the JMSException back to the DefaultMessageListenerContainer which attempts to redeliver the problem message again -- this continues indefinitely.

This seems like a common use case. What is the best way to handle this condition?

I have a work-around but it is certainly not pretty. My work around involves sub-classing the MessageListenerAdapter and overriding the sendResponse() method to check for certain JMSExceptions. Unfortunately it looks like I have to check the exception's message string to see if it's an exception that should be ignored.

protected void sendResponse( Session session, Destination destination, Message response ) 
        throws JMSException 
    {
        try
        {
            super.sendResponse( session, destination, response );
        }
        catch( JMSException e )
        {
            if( e.getMessage().matches( "com.swiftmq.swiftlet.queue.UnknownQueueException: queue 'tmp\\$[^']+' is unknown" ) )
            {
                logger.warn( "Discarding response message - " + e.getMessage() );
                return;
            }
            throw e;
        }
    }

Thanks for any suggestions

-Bryan

Re: Spring MessageListenerAdapter and invalid ReplyTo destination

by IIT Software :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Why don't you just check the JMSRedelivered flag of the incoming message and discard it if the flag is set?

Re: Spring MessageListenerAdapter and invalid ReplyTo destination

by Bryan Talbot :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

There are legitimate reasons for a message to be redelivered other than from a failure to send a response.  Those cases would still need to be supported.

Re: Spring MessageListenerAdapter and invalid ReplyTo destination

by IIT Software :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

There are legitimate reasons for a message to be redelivered other than from a failure to send a response.  
I don't mind any other case than a rollback of a transaction. The JMSRedelivered flag will be set if the message has already been passed in a former transaction or non-transacted delivery to the application via onMessage/receive. If you don't do rollbacks yourself, the JMSRedelivered flag should work fine.

Actually you will receive an InvalidDestinationException (extends JMSException) if you try to send to a temp queue which has been deleted in the meantime. This exception will be thrown if you try to create a producer on it. However, deletion of such a temp destination is asynchronously and can happen any time, e.g. after the producer has been created and while it writes to the queue and before commit. In that case it is possible that you will get a simple JMSException.

I would catch InvalidDestinationException and would check the JMSRedelivered flag.

Re: Spring MessageListenerAdapter and invalid ReplyTo destination

by Bryan Talbot :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

IIT Software wrote:
Actually you will receive an InvalidDestinationException (extends JMSException) if you try to send to a temp queue which has been deleted in the meantime. This exception will be thrown if you try to create a producer on it. However, deletion of such a temp destination is asynchronously and can happen any time, e.g. after the producer has been created and while it writes to the queue and before commit. In that case it is possible that you will get a simple JMSException.

I would catch InvalidDestinationException and would check the JMSRedelivered flag.
SwiftMQ doesn't seem to throw an InvalidDestinationException in this case -- I get a plain JMSException instead.  

Here's a simple straight JMS program to demonstrate this against SwiftMQ 7.4.1 (works the same with 7.3.2):


package com.age.jms;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.InitialContext;


public class InvalidDestinationSender
{
        public static final void main( String [] args )
            throws Exception
        {
        Connection connection = null;
            try
            {
            final String destinationName = args[0];
            final InitialContext jndiContext = new InitialContext();
            final ConnectionFactory cf = (ConnectionFactory)jndiContext.lookup( "ConnectionFactory" );
            jndiContext.close();

            connection = cf.createConnection();
            final Session session = connection.createSession( false, Session.AUTO_ACKNOWLEDGE );
            connection.start();
            System.out.println( "Createing queue named: " + destinationName );
            final Destination dest = session.createQueue( destinationName );
               
            // I don't expect this code to be reached when the destination is invalid
            final MessageProducer mp = session.createProducer( dest );
            final TextMessage msg = session.createTextMessage();
            msg.setText( "the hero you deserve" );
            mp.send( msg );
            }
            finally
            {
            if( null != connection )
            {
                connection.close();
            }
            }
        }
}



$> java -classpath bin:lib/swiftmq-7.4.1.jar:lib/jms.jar com.age.jms.InvalidDestinationSender noqueue
Createing queue named: noqueue
Exception in thread "main" javax.jms.JMSException: com.swiftmq.swiftlet.queue.UnknownQueueException: queue 'noqueue@router1' is unknown
        at com.swiftmq.jms.ExceptionConverter.convert(Unknown Source)
        at com.swiftmq.jms.v630.SessionImpl.createSender(Unknown Source)
        at com.swiftmq.jms.v630.SessionImpl.createProducer(Unknown Source)
        at com.age.jms.InvalidDestinationSender.main(InvalidDestinationSender.java:32)

Re: Spring MessageListenerAdapter and invalid ReplyTo destination

by IIT Software :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

            final Destination dest = session.createQueue( destinationName );
               
            // I don't expect this code to be reached when the destination is invalid
Method session.createQueue just creates a javax.jms.Queue object (the address of a queue), not the queue itself nor does it interact with the router to verify whether the queue exists.

Just verified that. It is a bug on our side that a JMSException instead of a InvalidDestinationException is thrown if you call createProducer with an invalid destinatopm. Will be fixed in the next release.

Re: Spring MessageListenerAdapter and invalid ReplyTo destination

by IIT Software :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

This has also been fixed in 7.5.0, released today.

Re: Spring MessageListenerAdapter and invalid ReplyTo destination

by batalbot :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Thanks for the update.  I tested with 7.5.0 and it's now throwing the proper exception.

-Bryan