Search code examples
javahornetqjms-topic

How to know when connection to a JMS Topic is lost?


I have a Java rich client application that registers a durable subscription on a remote HornetQ JMS Topic on startup. However if the server restarts the connection is lost and can only be restored by restarting the client application. This leads to confusing situations where JMS messages are not received and after a client restart a lot of messages are received at once.

A simple solution to restore connection would be to run a timer to periodically check if the connection is still valid and try to reconnect otherwise. Alternatively the server could send a heartbeat to the client and try to reconnect if no heartbeat is received after a certain period (like mentioned in this answer).

But both seem like clumsy approaches to this problem and therefore I would like to know if there is a better solution to find out that the connection is not available anymore?


Solution

  • To get notified on the disconnection you have to register an ExceptionListener on your TopicConnection before you start the connection.

    private void subscribe() throws JMSException {
    
        // get context / create connection and session / etc.
        TopicConnection connection = ...
    
        connection.setExceptionListener(this::handleExceptions);
        connection.start();
    }
    

    In the ExceptionListener you can check the error code of the received JMSException. (The error codes are vendor-specific)
    In the case of HornetQ the error code DISCONNECT is received after the connection is lost.

    private static final String HORNETQ_DISCONNECT_ERROR_CODE = "DISCONNECT";
    
    private void handleExceptions(final JMSException jmsException) {
    
        final String errorCode = jmsException.getErrorCode();
        if (HORNETQ_DISCONNECT_ERROR_CODE.equals(errorCode)) {
            tryConnect();
        }
    }
    

    Then you can start a self-canceling Timer that tries to reconnect every x seconds until it succeeds.

    private static final long SUBSCRIBE_RETRY_TIME_OUT_IN_MILLIS = 60000;
    
    private void tryConnect() {
    
        final Timer timer = new Timer("JMS-Topic-Reconnection-Timer", true);
        final TimerTask timerTask = new TimerTask() {
    
            @Override
            public void run() {
                try {
                    subscribe();
                    // cancel the timer, after the subscription succeeds
                    timer.cancel();
                }
                catch (final Exception e) {
                    logger.info("reconnect to jms topic failed: {}", e.getMessage());
                }
            }
        };
        timer.schedule(timerTask, SUBSCRIBE_RETRY_TIME_OUT_IN_MILLIS, SUBSCRIBE_RETRY_TIME_OUT_IN_MILLIS);
    }