0

Even though the message is received by the MessageListener, I don't want to remove from the Queue, I want to do some processing in onMessage method and based on the result:

I want to commit(); for Success - so the message will be completely removed from the Queue.

For Failures - don't commit - rollback(); so the message will be redelivered (some times by default) and then goes to Dead letter Queue (DLQ). That’s OK for us.

I use: SpringBoot and hornetq (spring-boot-starter-hornetq-1.4.7.RELEASE). Settings:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerContainerFactory;
import org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter;
import org.springframework.jndi.JndiObjectFactoryBean;
import org.springframework.jndi.JndiTemplate;

import javax.jms.ConnectionFactory;
import javax.naming.Context;
import javax.naming.NamingException;
import java.util.Properties;

import static com.test.hornetq.Receiver.LOG;
import static javax.jms.Session.SESSION_TRANSACTED;

@Configuration
public class JmsConfig {

    private String host;
    private String port;
    private String connectionFactoryJndiName;
    private String jndiInit;
    private String user;
    private String password;
    private String jmsReceiverConcurrency;

    public JmsConfig(final Environment env) {
        host = env.getProperty("host");
        port = env.getProperty("port");
        connectionFactoryJndiName = env.getProperty("connectionfactory.jndiname");
        jndiInit = env.getProperty("jndiInit");
        user = env.getProperty("user");
        password = env.getProperty("password");
        jmsReceiverConcurrency = env.getProperty("jmsReceiverConcurrency");
    }

    @Bean
    public JndiTemplate jndiTemplate() {
        final JndiTemplate jndiTemplate = new JndiTemplate();
        jndiTemplate.setEnvironment(getProperties());

        return jndiTemplate;
    }

    @Bean
    public JndiObjectFactoryBean jmsConnectionFactory() throws NamingException {
        final JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
        jndiObjectFactoryBean.setJndiTemplate(jndiTemplate());
        jndiObjectFactoryBean.setJndiName(connectionFactoryJndiName);
        jndiObjectFactoryBean.afterPropertiesSet();

        return jndiObjectFactoryBean;
    }

    @Bean
    @Primary
    public ConnectionFactory connectionFactory() throws NamingException {
        final UserCredentialsConnectionFactoryAdapter adapter = new UserCredentialsConnectionFactoryAdapter();
        adapter.setTargetConnectionFactory((ConnectionFactory) jmsConnectionFactory().getObject());
        adapter.setUsername(user);
        adapter.setPassword(password);

        return adapter;
    }

    @Bean
    public JmsListenerContainerFactory<?> myJmsContainerFactory() throws NamingException {
        final DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory());
        factory.setSubscriptionDurable(false);
        factory.setConcurrency(jmsReceiverConcurrency);
        factory.setMaxMessagesPerTask(1);
        factory.setSessionTransacted(true);
        factory.setSessionAcknowledgeMode(SESSION_TRANSACTED);
        factory.setErrorHandler(t -> {
            LOG.error("Error in listener!", t);
        });


        return factory;
    }

    private Properties getProperties() {
        final Properties jndiProps = new Properties();
        jndiProps.setProperty(Context.INITIAL_CONTEXT_FACTORY, jndiInit);
        jndiProps.setProperty(Context.PROVIDER_URL, "http-remoting://" + host + ":" + port);
        jndiProps.setProperty(Context.SECURITY_PRINCIPAL, user);
        jndiProps.setProperty(Context.SECURITY_CREDENTIALS, password);
        return jndiProps;
    }

}

And receiver:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Session;

@Component
public class Receiver {

    @JmsListener(destination = "${destination.name}", containerFactory = "myJmsContainerFactory")
    public void onReceive(final MapMessage message, Session session) throws JMSException {
        try {
            System.out.println(">>>> " + message);
            session.rollback();
        } catch (Exception ex) {
            System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>THROW ");
            throw ex;
        }
    }
}

But when I do rollback(); nothing happen and message doesn't comeback.

makson
  • 1,975
  • 3
  • 23
  • 31

1 Answers1

0

The code work. The problem was in hornetq settings in server side.

<pre-acknowledge>true</pre-acknowledge>

Extra Acknowledge Modes

Please note, that if you use pre-acknowledge mode, then you will lose transactional semantics for messages being consumed, since clearly they are being acknowledged first on the server, not when you commit the transaction. This may be stating the obvious but we like to be clear on these things to avoid confusion!

makson
  • 1,975
  • 3
  • 23
  • 31