0

This is a separate yet related question to my earlier post here: Safely Terminating a Spring JMS application

My JMS application that I have using spring boot processes everything correctly and shuts down with no errors. To get this to work I changed a bean from:

@Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
    DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
    factory.setConnectionFactory(mqConnectionFactory());
    factory.setDestinationResolver(destinationResolver());
    factory.setConcurrency("1");
    factory.setErrorHandler(errorHandler());
    factory.setSessionTransacted(true);
    factory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
    return factory;
}

To:

@Bean
    public DefaultMessageListenerContainer defaultMessageListenerContainer() {
        DefaultMessageListenerContainer jmsListenerContainer = new DefaultMessageListenerContainer();
        jmsListenerContainer.setConnectionFactory(mqConnectionFactory());
        jmsListenerContainer.setDestinationResolver(destinationResolver());
        jmsListenerContainer.setDestinationName(queueName);
        jmsListenerContainer.setConcurrency("1");
        jmsListenerContainer.setErrorHandler(errorHandler());
        jmsListenerContainer.setSessionTransacted(true);
        jmsListenerContainer.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
        jmsListenerContainer.setAutoStartup(false);
        return jmsListenerContainer;
    }

The problem with this is, I could have created just a "hotfix", as my knowledge about spring is little. the line in the changed bean jmsListenerContainer.setAutoStartup(false); was added when I stumbled upon this post: http://forum.spring.io/forum/spring-projects/integration/79176-illegalstateexception-no-message-listener-specified as without the autoStartup set to false I get this after every processed message in my logs:

java.lang.IllegalStateException: No message listener specified - see property 'messageListener'
    at org.springframework.jms.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:691) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:651) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.doReceiveAndExecute(AbstractPollingMessageListenerContainer.java:315) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:253) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1150) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1142) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:1039) [spring-jms-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at java.lang.Thread.run(Unknown Source) [?:1.8.0_74]

Everything still is processed and shut down correctly, I just see this in my logs. Not sure if there is a conflict in my config file or not that may be the root of this. I just want to make sure the changes won't cause problems, even though everything currently works as intended with no errors.

Lastly here is my entire configuration class:

@Configuration
@EnableJms
public class MQConfig {

    private static final Logger logger = LogManager.getLogger(MQConfig.class.getName());

    @Value("${mq.hostName}")
    String host;
    @Value("${mq.port}")
    Integer port;
    @Value("${mq.queueManager}")
    String queueManager;
    @Value("${mq.queueName}")
    String queueName;
    @Value("${mq.channel}")
    String channel;

    @Autowired
    Environment environment;

    @Bean
    public DefaultMessageListenerContainer defaultMessageListenerContainer() {
        DefaultMessageListenerContainer jmsListenerContainer = new DefaultMessageListenerContainer();
        jmsListenerContainer.setConnectionFactory(mqConnectionFactory());
        jmsListenerContainer.setDestinationResolver(destinationResolver());
        jmsListenerContainer.setDestinationName(queueName);
        jmsListenerContainer.setConcurrency("1");
        jmsListenerContainer.setErrorHandler(errorHandler());
        jmsListenerContainer.setSessionTransacted(true);
        jmsListenerContainer.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
        jmsListenerContainer.setAutoStartup(false);
        return jmsListenerContainer;
    }

    @Bean
    public MQConnectionFactory mqConnectionFactory() {
        MQConnectionFactory mqConnectionFactory = new MQConnectionFactory();

        try {
            mqConnectionFactory.setHostName(host);
            mqConnectionFactory.setPort(port);
            mqConnectionFactory.setQueueManager(queueManager);
            mqConnectionFactory.setTransportType(1);
        } catch (JMSException ex) {
            logger.error(ex.getStackTrace());
        }

        return mqConnectionFactory;
    }

    @Bean
    public DynamicDestinationResolver destinationResolver() {
        DynamicDestinationResolver destinationResolver = new DynamicDestinationResolver();

        try {
            Connection connection = mqConnectionFactory().createConnection();
            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            destinationResolver.resolveDestinationName(session, queueName, false);
        } catch (JMSException ex) {
            logger.error(ex.getStackTrace());
        }

        return destinationResolver;
    }

    @Bean
    public MQQueue mqQueue() {
        MQQueue mqQueue = new MQQueue();

        try {
            mqQueue.setBaseQueueName(queueName);
            mqQueue.setBaseQueueManagerName(queueManager);
        } catch (JMSException ex) {
            logger.error(ex.getStackTrace());
        }

        return mqQueue;
    }

    @Bean
    public JmsErrorHandler errorHandler(){
        return new JmsErrorHandler();
    }

    @Bean
    public MQManager mqHoldManager(){
        return new MQManager(host, port, queueName,
                queueManager, channel);
    }

    @Bean
    public MQInitializer mqInitializer(){
        return new MQInitializer(environment);
    }


}

Thoughts? Is this messy?

EDIT: JMS Listener

@Component
public class MQConsumer {

    @Resource(name = "mqHoldManager")
    private MQManager mqHoldManager;
    @Resource(name = "defaultMessageListenerContainer")
    private DefaultMessageListenerContainer listenerContainer;

    @Autowired
    MQInitializer mqInitializer;    /* To ensure this bean executes before Listener Setup not necessarily for any particular usage*/

    final Logger logger = LogManager.getLogger(MQConsumer.class.getName());
    private static ReportManager reportManager = new ReportManager();
    private static boolean isFirstQueue = true;

    @JmsListener(destination = "${mq.queueName}")
    public void processOrder(String message) throws Exception {...}
Community
  • 1
  • 1
LumberSzquatch
  • 1,054
  • 3
  • 23
  • 46

1 Answers1

1

I am confused.

DefaultJmsListenerContainerFactory is for use with annotated POJO methods:

@JmsListener(...)
public void foo(String bar) {...}

The factory is used to create the listener container for the method.

With your replacement configuration, I don't see you ever configuring a message listener into the DefaultMessageListenerContainer.

Usually you would have container.setMessageListner(myListener) where myListener is a MessageListener, a SessionAwareMessageListener or a MessageListenerAdapter that wraps a POJO listener.

Setting autoStartup to false and never starting the container does nothing except add the container bean to the context.

I don't see how you can ever get any messages with that configuration.

EDIT

Are you using Spring Boot? If so, it will create a default jmsListenerContainerFactory for you - that is my best guess.

In which case, your stop code is not really stopping the actual container - just the "dummy" one that was never started.

I suggest you give your @JmsListener an id, @Autowire the JmsListenerEndpointRegistry and call registry.getListenerContainer("myListener").stop().

@JmsListener(id = "myListener", destination = "${mq.queueName}")
Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • I added my JMSListener class, not sure if that would help explain the seemingly strange behavior, as I am successfully reading from our queue and creating an output file. Everything looks in order. I'm still learning the intricacies of spring so I have put alot of this together through multiple sources. So your saying my change is unnecessary and I should access the container in one of the ways you cited? – LumberSzquatch May 09 '16 at 16:28
  • My point is that without a `jmsListenerContainerFactory`, `@JmsListener` won't work - hence my confusion when you say you __replaced__ it with a listener container. You are getting that error message because `defaultMessageListenerContainer` has no listener - turning it off avoids the message because it's no longer listening but I don't see how (a) your listener is getting invoked without a container and (b) how your "stop" logic works since the `defaultMessageListenerContainer` is never started. – Gary Russell May 09 '16 at 16:33
  • Then I'm confused as well...Because all the code in my listener gets executed. But if I'm confused, and your confused, does this seem as volatile? Or could there be more going on? – LumberSzquatch May 09 '16 at 16:39
  • So, I'm guessing you're using Spring Boot - see my edit. – Gary Russell May 09 '16 at 16:45
  • I am using Spring Boot. I was starting to wonder if boot was doing the magic. So this is good to know. That may clean my code up. So the container bean that I have is or isn't adding anything to what I think it is (and what I think it is is the container to my listener)? Then I also suppose the JmsListenerEndpointRegistry is something that is provided or set up? These question feel rudimentary, but I'm finding it difficult to piece all this together. – LumberSzquatch May 09 '16 at 16:53
  • Disregard my last question. I discovered my answer. I know these types of comments are against convention, but thank you for your help, I feel like I have learned a lot from your help these past two posts. – LumberSzquatch May 09 '16 at 16:59