Reputation: 1083
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 {...}
Upvotes: 0
Views: 2939
Reputation: 174769
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}")
Upvotes: 1