4
votes

I am using JNDI lookup for the getting the connection object for Websphere MQ Broker on tomcat server. I am using JmsTemplate for sending the messages to the Queue on WMQ Broker and trying to avoid the Spring Xml based configuration and for that reason i have configured the Spring boot's application.properties file to specify the JNDI look up name.below is the property from application.properties file.

spring.jms.jndi-name= java:comp/env/XXXX

i am using a Spring bean to define the JmsTemplate and below is the code for it.

@Configuration
public class JmsMessageTemplateBean {

    //@Value("${spring.jms.jndi-name}")
    //private ConnectionFactory connectionFactory;

    @Bean
    public JmsTemplate jmsTemplate() throws Exception{
        JmsTemplate jmsMessagingTemplate = new JmsTemplate();
        jmsMessagingTemplate.setDefaultDestinationName("Some Queue");
        jmsMessagingTemplate.setConnectionFactory(connectionFactory);
        return jmsMessagingTemplate;
    }
}

i have a couple of questions:

1.How to read the JNDI property from application.properties file and set the Connection object to Jms Template in the above bean.

2.I have observed that the connection object from the JNDI lookup is MQQueueConnectionFactory and from what i have researched JmsTemplate supports javax.jms.ConnectionFactory object. is there a way to convert the MQQueueConnectionfactory object to javax.jms.Connectionfactory.

Appreciate your answers.

1
You are making it way to complex. Remove your bean, you already get a JmsTemplate with Spring Boots auto configuration. The MQQueueConnectionFactory is a ConnectionFactory. - M. Deinum
@ M. Deinum Thanks for your response. With Spring's auto boot configuration i got the JmsTemplate but it's connectionfactory is by default looking at localhost broker.How can i configure JmsTemplate to use spring.jms.jndi-name specified in application.properties file. - maram05
If you really have specified that property that should be used to do a lookup. Also make sure you don't have something like activemq in your class path. You have to be using Spring Boot 1.2 or higher for this to work. - M. Deinum

1 Answers

2
votes

I also had a hard time figuring out how to implement a Spring Boot JMS Listener, listening to an ActiveMQ queue within a JBoss application server.

ActiveMQ is supported by Spring Boot autoconfiguration, but since it was inside the JBoss server Spring Boot was failing to connect ActiveMQ. In fact you need to define connectionFactory and jmsListenerContainerFactory beans yourself by doing a lookup on the JNDI provider.

@Configuration
@EnableJms
public class ActiveMqConnectionFactoryConfig {

  @Value("${broker.url}")
  String brokerUrl;

  @Value("${borker.username}")
  String userName;

  @Value("${borker.password}")
  String password;

  @Value("${queue}")
  String queueName;

  private static final String INITIAL_CONTEXT_FACTORY = "org.jboss.naming.remote.client.InitialContextFactory";
  private static final String CONNECTION_FACTORY = "jms/RemoteConnectionFactory";


  @Bean
  public ConnectionFactory connectionFactory() {
    try {
      System.out.println("Retrieving JMS queue with JNDI name: " + CONNECTION_FACTORY);
      JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
      jndiObjectFactoryBean.setJndiName(CONNECTION_FACTORY);

      jndiObjectFactoryBean.setJndiEnvironment(getEnvProperties());
      jndiObjectFactoryBean.afterPropertiesSet();

      return (QueueConnectionFactory) jndiObjectFactoryBean.getObject();

    } catch (NamingException e) {
      System.out.println("Error while retrieving JMS queue with JNDI name: [" + CONNECTION_FACTORY + "]");
    } catch (Exception ex) {
      System.out.println("Error");
    }
    return null;
  }

  Properties getEnvProperties() {
    Properties env = new Properties();
    env.put(Context.INITIAL_CONTEXT_FACTORY, INITIAL_CONTEXT_FACTORY);
    env.put(Context.PROVIDER_URL, brokerUrl);
    env.put(Context.SECURITY_PRINCIPAL, userName);
    env.put(Context.SECURITY_CREDENTIALS, password);
    return env;
  }

  @Bean
  public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory) {

    DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
    factory.setConnectionFactory(connectionFactory);
    JndiDestinationResolver jndiDestinationResolver = new JndiDestinationResolver();

    jndiDestinationResolver.setJndiEnvironment(getEnvProperties());
    factory.setDestinationResolver(jndiDestinationResolver);
    return factory;
  }

Then if you want to consume the queue you just define your JMS consumer class with a method annotated with @JmsListener(destination = "${queue}")

 @JmsListener(destination = "${queue}")
  public void receive(Message message) {
    System.out.println("Received Message: " + message);
  }

Hope that helps save a few hours of research ;) Cheers