Update
It was my mistake I forget the ssl debugging running, it is super fast now and working like magic
I have a Spring Boot application that connects to IBM MQ using spring JMS. I realized that the jmsTemplate is super slow compared to not using Spring at all.
I am sure I have something not configured correctly. Hope Someone can help.
I create a connection factory using IBM MQ 8 jar files.
#Bean
public ConnectionFactory connectionFactory() {
properties.getCipherSpec());
MQConnectionFactory factory = new MQConnectionFactory();
try {
factory.setHostName(properties.getHost());
factory.setPort(properties.getPort());
factory.setQueueManager(properties.getQueueManager());
factory.setChannel(properties.getChannel());
factory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
factory.setClientReconnectTimeout(CLIENT_RECONNECT_TIMEOUT);
factory.setClientReconnectOptions(WMQConstants.WMQ_CLIENT_RECONNECT);
if (properties.isEnableSsl()) {
factory.setSSLCipherSuite(properties.getCipherSpec());
factory.setSSLSocketFactory(socketFactory());
}
factory.setUseConnectionPooling(true);
} catch (Exception e) {
throw new RuntimeException(e);
}
return factory;
}
Then I am creating a caching Connection factory and setting Target Connection Factory to the connection factory above.
#Bean(name = "cachingConnectionFactory")
public CachingConnectionFactory cachingConnectionFactory(){
CachingConnectionFactory factory = new CachingConnectionFactory();
factory.setSessionCacheSize(50);
factory.setTargetConnectionFactory(connectionFactory());
factory.setReconnectOnException(true);
factory.afterPropertiesSet();
return factory;
}
and then in the Service class I am using the cache connection factory bean to create JmsTemplate per each thread and use the normal send receive operations.
#Autowired
private CachingConnectionFactory connectionFactory;
#PostConstruct
#DependsOn(value = "cachingConnectionFactory")
public void setJmsConnectionFactory(){
this.jmsQueueTemplate = new JmsTemplate(this.cachingConnectionFactory);
}
Any help will be appreciated ...
Related
I have a legacy Spring 4.2.1.RELEASE application that connects to ActiveMQ 5.x as a listener and now we're adding connectivity to ActiveMQ Artemis. For Artemis we're using durable subscriptions because we don't want message loss on a topic when the subscribers go down and shared subscriptions because we wanted the option of clustering or using concurrency to asynchronously process the messages in the subscription. I have separate ConnectionFactorys and ListenerContainers, but from this WARN log that keeps repeating it looks like the Artemis DMLC can't start due to the following NPE:
java.lang.NullPointerException
at org.springframework.jms.listener.AbstractMessageListenerContainer.createConsumer(AbstractMessageListenerContainer.java:856)
at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.createListenerConsumer(AbstractPollingMessageListenerContainer.java:213)
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.initResourcesIfNecessary(DefaultMessageListenerContainer.java:1173)
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1149)
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1142)
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:1039)
at java.lang.Thread.run(Unknown Source)
On the surface it looks like it can't find the method createSharedDurableConsumer. Looking at the AbstractMessageListenerContainer I have, line 856 is calling method.invoke
/** The JMS 2.0 Session.createSharedDurableConsumer method, if available */
private static final Method createSharedDurableConsumerMethod = ClassUtils.getMethodIfAvailable(
Session.class, "createSharedDurableConsumer", Topic.class, String.class, String.class);
...
Method method = (isSubscriptionDurable() ?
createSharedDurableConsumerMethod : createSharedConsumerMethod);
try {
return (MessageConsumer) method.invoke(session, destination, getSubscriptionName(), getMessageSelector());
}
Artemis configuration:
#Configuration
public class ArtemisConfig {
#Autowired
private Environment env;
#Bean
public ConnectionFactory artemisConnectionFactory() {
ActiveMQConnectionFactory artemisConnectionFactory = ActiveMQJMSClient
.createConnectionFactoryWithHA(JMSFactoryType.CF, createTransportConfigurations());
artemisConnectionFactory.setUser(env.getRequiredProperty("artemis.username"));
artemisConnectionFactory.setPassword(env.getRequiredProperty("artemis.password"));
artemisConnectionFactory.setCallTimeout(env.getRequiredProperty("artemis.call.timeout.millis", Long.class));
artemisConnectionFactory.setConnectionTTL(env.getRequiredProperty("artemis.connection.ttl.millis", Long.class));
artemisConnectionFactory
.setCallFailoverTimeout(env.getRequiredProperty("artemis.call.failover.timeout.millis", Long.class));
artemisConnectionFactory.setInitialConnectAttempts(
env.getRequiredProperty("artemis.connection.attempts.initial", Integer.class));
artemisConnectionFactory
.setReconnectAttempts(env.getRequiredProperty("artemis.connection.attempts.reconnect", Integer.class));
artemisConnectionFactory.setRetryInterval(env.getRequiredProperty("artemis.retry.interval.millis", Long.class));
artemisConnectionFactory
.setRetryIntervalMultiplier(env.getRequiredProperty("artemis.retry.interval.multiplier", Double.class));
artemisConnectionFactory.setBlockOnAcknowledge(true);
artemisConnectionFactory.setBlockOnDurableSend(true);
artemisConnectionFactory.setCacheDestinations(true);
artemisConnectionFactory.setConsumerWindowSize(0);
artemisConnectionFactory.setMinLargeMessageSize(1024 * 1024);
CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(artemisConnectionFactory);
cachingConnectionFactory
.setSessionCacheSize(env.getRequiredProperty("artemis.session.cache.size", Integer.class));
cachingConnectionFactory.setReconnectOnException(true);
return cachingConnectionFactory;
}
#Bean
public DefaultJmsListenerContainerFactory artemisContainerFactory(ConnectionFactory artemisConnectionFactory,
JmsTransactionManager artemisJmsTransactionManager,
MappingJackson2MessageConverter mappingJackson2MessageConverter) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setCacheLevel(DefaultMessageListenerContainer.CACHE_CONSUMER);
factory.setConnectionFactory(artemisConnectionFactory);
factory.setDestinationResolver(new DynamicDestinationResolver());
factory.setMessageConverter(mappingJackson2MessageConverter);
factory.setSubscriptionDurable(Boolean.TRUE);
factory.setSubscriptionShared(Boolean.TRUE);
factory.setSessionAcknowledgeMode(Session.SESSION_TRANSACTED);
factory.setSessionTransacted(Boolean.TRUE);
factory.setTransactionManager(artemisJmsTransactionManager);
return factory;
}
private TransportConfiguration[] createTransportConfigurations() {
String connectorFactoryFqcn = NettyConnectorFactory.class.getName();
Map<String, Object> primaryTransportParameters = new HashMap<>(2, 1F);
String primaryHostname = env.getRequiredProperty("artemis.primary.hostname");
Integer primaryPort = env.getRequiredProperty("artemis.primary.port", Integer.class);
primaryTransportParameters.put("host", primaryHostname);
primaryTransportParameters.put("port", primaryPort);
return new TransportConfiguration[] {
new TransportConfiguration(connectorFactoryFqcn, primaryTransportParameters),
new TransportConfiguration(connectorFactoryFqcn, backupTransportParameters) };
}
}
My pom uses version 2.10.0 of Artemis.
How do I fix this?
The JMS 2.0 spec is backwards compatible with JMS 1.1 so make sure you only have the JMS 2 spec on your classpath. My hunch is that the reflection calls in the Spring code are getting messed up because they're hitting the JMS 1.1 spec classes instead of the proper JMS 2 spec classes.
I am updating Spring from 4.x.x to Spring 5.0.3. The project uses ActiveMQ version 5.15.3. When I try to deploy the application with the newest version of Spring I get this error:
Caused by: java.lang.NoSuchMethodError: org.springframework.web.servlet.handler.AbstractHandlerMapping.obtainApplicationContext()Lorg/springframework/context/ApplicationContext;
at org.springframework.web.servlet.handler.AbstractHandlerMapping.detectMappedInterceptors(AbstractHandlerMapping.java:269)
at org.springframework.web.servlet.handler.AbstractHandlerMapping.initApplicationContext(AbstractHandlerMapping.java:243)
at org.springframework.web.servlet.handler.SimpleUrlHandlerMapping.initApplicationContext(SimpleUrlHandlerMapping.java:102)
at org.springframework.context.support.ApplicationObjectSupport.initApplicationContext(ApplicationObjectSupport.java:120)
at org.springframework.web.context.support.WebApplicationObjectSupport.initApplicationContext(WebApplicationObjectSupport.java:77)
at org.springframework.context.support.ApplicationObjectSupport.setApplicationContext(ApplicationObjectSupport.java:74)
at org.springframework.context.support.ApplicationContextAwareProcessor.invokeAwareInterfaces(ApplicationContextAwareProcessor.java:121)
at org.springframework.context.support.ApplicationContextAwareProcessor.postProcessBeforeInitialization(ApplicationContextAwareProcessor.java:97)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:409)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1620)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
... 53 more
I noticed that ActiveMQ has Spring version "4.3.9" as a dependency. This version does not have the method "obtainApplicationContext" in "AbstractHandlerMapping" and hence the problem. Is there a way exclude the Spring libraries from the activemq-all bundle?
I thought this was my problem too but I eventually got my Spring webapp deployed on TomEE to successfully connect and use ActiveMQ hosted and running internally to that Tomcat container.
I'm using Spring 5.0.3-RELEASE and activemq-client 5.15.3. I didn't need everything in the maven shaded uber jar activemq-all.
#Configuration
public class MyConfig {
#Bean
public SingleConnectionFactory connectionFactory() {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("vm://localhost");
((ActiveMQConnectionFactory) connectionFactory)
// See http://activemq.apache.org/objectmessage.html why we set trusted packages
.setTrustedPackages(new ArrayList<String>(Arrays.asList("com.mydomain", "java.util")));
return new SingleConnectionFactory(connectionFactory);
}
#Bean
#Scope("prototype")
public JmsTemplate jmsTemplate() {
return new JmsTemplate(connectionFactory());
}
#Bean
public Queue myQueue() throws JMSException {
Connection connection = connectionFactory().createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue("message-updates");
return queue;
}
}
#Component
public class MyQueueImpl implements MyQueue {
#Inject
private JmsTemplate jmsTemplate;
#Inject
private Queue myQueue;
#PostConstruct
public void init() {
jmsTemplate.setReceiveTimeout(JmsTemplate.RECEIVE_TIMEOUT_NO_WAIT);
}
#Override
public void enqueue(Widget widget) {
jmsTemplate.send(myQueue, new MessageCreator() {
#Override
public Message createMessage(Session session) throws JMSException {
return session.createObjectMessage(widget);
}
});
}
#Override
public Optional<Widget> dequeue() {
Optional<Widget> widget = Optional.empty();
ObjectMessage message = (ObjectMessage) jmsTemplate.receive(myQueue);
try {
if (message != null) {
widget = Optional.ofNullable((Widget) message.getObject());
message.acknowledge();
}
} catch (JMSException e) {
throw new UncategorizedJmsException(e);
}
return widget;
}
}
Thanks Matthew K above. I found that too. ActiveMQ-all have packed a version of spring (currently that 4.x version) inside. There are some none-backwards compatible changes between that and spring v.5. I came across a new method in one of the other spring classes myself. It can cause this kind of issue (no such method exception in my case).
I had this issue with activeMQ 5.15.4 and spring 5.0.7. In the end I solved it with using the finer grained jars instead. I had to use all these: activemq-broker,activemq-client,activemq-pool,activemq-kahadb-store,activemq-spring
I have implemented ActiveMQ message broker in my application but whenever I'm connected to ActiveMQ server in my console logs I'm always seeing the below messages:
10:28:05.282 [ActiveMQ InactivityMonitor WriteCheckTimer] DEBUG o.a.a.t.AbstractInactivityMonitor - WriteChecker: 10000ms elapsed since last write check.
10:28:05.282 [ActiveMQ InactivityMonitor Worker] DEBUG o.a.a.t.AbstractInactivityMonitor - Running WriteCheck[tcp://10.211.127.203:61616]
Looks it keep on polling the listener queue it seems. I want my listener to be active so that whenever the message comes to the queue, the application can process it. At the same time, i do not want this message to pile up my log file.
My message configuration:
#Configuration
#EnableJms
#ImportResource("classpath*:beans.xml")
public class MessagingConfiguration {
#Autowired
MongoCredentialEncryptor encryptor;
#Value("${activemq.broker.url}")
private String BROKER_URL = "tcp://localhost:61616";
#Value("${activemq.request.queue}")
private String REQUEST_QUEUE = "test.request";
#Value("${activemq.response.queue}")
private String RESPONSE_QUEUE = "test.response";
#Value("${activemq.borker.username}")
private String BROKER_USERNAME = "admin";
#Value("${activemq.borker.password}")
private String BROKER_PASSWORD = "admin";
#Autowired
ListenerClass messageListener;
#Autowired
JmsExceptionListener jmsExceptionListener;
#Bean
public ActiveMQConnectionFactory connectionFactory() {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
connectionFactory.setBrokerURL(BROKER_URL);
connectionFactory.setUserName(BROKER_USERNAME);
//connectionFactory.setPassword(BROKER_PASSWORD);
connectionFactory.setPassword(encryptor.decrypt(BROKER_PASSWORD));
connectionFactory.setTrustAllPackages(true);
connectionFactory.setRedeliveryPolicy(redeliveryPolicy());
return connectionFactory;
}
#Bean
public RedeliveryPolicy redeliveryPolicy() {
RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
redeliveryPolicy.setBackOffMultiplier(3); // Wait 5 seconds first re-delivery, then 15, 45 seconds
redeliveryPolicy.setInitialRedeliveryDelay(5000);
redeliveryPolicy.setMaximumRedeliveries(3);
redeliveryPolicy.setUseExponentialBackOff(true);
return redeliveryPolicy;
}
#Bean
public ConnectionFactory cachingConnectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setTargetConnectionFactory(connectionFactory());
connectionFactory.setExceptionListener(jmsExceptionListener);
connectionFactory.setSessionCacheSize(100);
connectionFactory.setCacheConsumers(false);
connectionFactory.setCacheProducers(false);
return connectionFactory;
}
#Bean
public JmsTemplate jmsTemplate() {
JmsTemplate template = new JmsTemplate();
template.setConnectionFactory(connectionFactory());
template.setDefaultDestinationName(REQUEST_QUEUE);
template.setSessionAcknowledgeModeName("CLIENT_ACKNOWLEDGE");
template.setMessageConverter(converter());
return template;
}
#Bean
public DefaultMessageListenerContainer jmsListenerContainer() {
DefaultMessageListenerContainer factory = new DefaultMessageListenerContainer();
factory.setConnectionFactory(connectionFactory());
factory.setConcurrency("1-1");
factory.setDestinationName(RESPONSE_QUEUE);
factory.setMessageListener(messageListener);
factory.setExceptionListener(jmsExceptionListener);
return factory;
}
#Bean
MessageConverter converter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
}
Solution:
I found a solution to my problem by disabling the InactivityMonitor by following this link ActiveMQ InactivityMonitor. I tested it the connection is still alive and its not keep on polling the queue. But i would like to know is there any downfall by disabling InactivityMonitor? Is there any better solution for this problem.
activemq.broker.url=failover:tcp://localhost:61616?wireFormat.maxInactivityDuration=0
The fix is simple, change your logging settings to not write at debug level or filter that one logger to not be at debug level. The logs are keeping you updated on the fact that the client is successfully pinging the remote broker to ensure the connection is alive. If you disable the monitoring then you can miss connection drop events are things like half closed sockets etc.
In production you really don't want to be turning off connection checking features that cause your code to miss the fact that it is not going to be able to receive anything
Example configuration modification:
<logger name="org.apache.activemq.transport" level="WARN"/>
There is nothing harm if this log is getting printed, InactivityMonitor just checks whether the connection between broker and client is active or not.
If it finds that the connection is inactive for the given time, then InactivityMonitor closes the connection between client and broker.
We can set the timeout for InactivityMonitor by specifying wireFormat.maxInactivityDuration="<time in ms>" in the tcp URLs in transportConnectors section of activemq.xml file.
For detailed information visit this : InactivityMonitor
I'm having difficulty finding a Spring way to initial an exchange that's sending the incoming message to more then 1 queue - on my Spring-boot application:
I can't find a good way to define a seconds exchange-queue binding.
I'm using RabbitTemplate as the producer client.
The RabbitMQ 6 page tutorial doesn't really help with that since:
the only initial several temporary queues from the Consumer on-demand (while I need to the Producer to do the binding - to persistant queues)
The examples are for basic java usage - not using Spring capabilities.
I also failed to find how to implement it via The spring AMQP pages.
what I got so far, is trying to inject the basic java binding to the spring way of doing it - but it's not working....
#Bean
public ConnectionFactory connectionFactory() throws IOException {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
Connection conn = connectionFactory.createConnection();
Channel channel = conn.createChannel(false);
channel.exchangeDeclare(SPRING_BOOT_EXCHANGE, "fanout");
channel.queueBind(queueName, SPRING_BOOT_EXCHANGE, ""); //first bind
channel.queueBind(queueName2, SPRING_BOOT_EXCHANGE, "");// second bind
return connectionFactory;
}
Any help would be appreciated
Edited
I think the problem arise with the fact that every time I restart my server it tries to redefine the exchange-query-binding - while they persist in the broker...
I managed to define them manually via the brokers UI console - so the Producer only aware of the exchange name, and the Consumer only aware to it's relevant queue.
Is there a way to define those element progrematically - but in such a way so it won't be redefined\overwritten if already exist from previous restarts?
We use an approach similar to the following to send data from one specific input channel to several input queues of other consumers:
#Bean
public IntegrationFlow integrationFlow(final RabbitTemplate rabbitTemplate, final AmqpHeaderMapper amqpHeaderMapper) {
IntegrationFlows
.from("some-input-channel")
.handle(Amqp.outboundAdapter(rabbitTemplate)
.headerMapper(headerMapper))
.get()
}
#Bean
public AmqpHeaderMapper amqpHeaderMapper() {
final DefaultAmqpHeaderMapper headerMapper = new DefaultAmqpHeaderMapper();
headerMapper.setRequestHeaderNames("*");
return headerMapper;
}
#Bean
public ConnectionFactory rabbitConnectionFactory() {
return new CachingConnectionFactory();
}
#Bean
public RabbitAdmin rabbitAdmin(final ConnectionFactory rabbitConnectionFactory) {
final RabbitAdmin rabbitAdmin = new RabbitAdmin(rabbitConnectionFactory);
rabbitAdmin.afterPropertiesSet();
return rabbitAdmin;
}
#Bean
public RabbitTemplate rabbitTemplate(final ConnectionFactory rabbitConnectionFactory, final RabbitAdmin rabbitAdmin) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate();
rabbitTemplate.setConnectionFactory(connectionFactory);
final FanoutExchange fanoutExchange = new FanoutExchange(MY_FANOUT.getFanoutName());
fanoutExchange.setAdminsThatShouldDeclare(rabbitAdmin);
for (final String queueName : MY_FANOUT.getQueueNames) {
final Queue queue = new Queue(queueName, true);
queue.setAdminsThatShouldDeclare(rabbitAdmin);
final Binding binding = BindingBuilder.bind(queue).to(fanoutExchange);
binding.setAdminsThatShouldDeclare(rabbitAdmin);
}
rabbitTemplate.setExchange(fanoutExchange);
}
and for completeness here's the enum for the fanout declaration:
public enum MyFanout {
MY_FANOUT(Lists.newArrayList("queue1", "queue2"), "my-fanout"),
private final List<String> queueNames;
private final String fanoutName;
MyFanout(final List<String> queueNames, final String fanoutName) {
this.queueNames = requireNonNull(queueNames, "queue must not be null!");
this.fanoutName = requireNonNull(fanoutName, "exchange must not be null!");
}
public List<String> getQueueNames() {
return this.queueNames;
}
public String getFanoutName() {
return this.fanoutName;
}
}
Hope it helps!
Thanks!
That was the answer I was looking for.
also - for the sake of completeness - I found a way to it 'the java way' inside Spring Bean:
#Bean
public ConnectionFactory connectionFactory() throws IOException {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
Connection conn = connectionFactory.createConnection();
Channel channel = conn.createChannel(false);
// declare exchnage
AMQP.Exchange.DeclareOk resEx = channel.exchangeDeclare(AmqpTemp.SPRING_BOOT_EXCHANGE_test, ExchangeTypes.FANOUT, true, false, false, null);
// declares queues
AMQP.Queue.DeclareOk resQ = channel.queueDeclare(AmqpTemp.Q2, true, false, false, null);
resQ = channel.queueDeclare(AmqpTemp.Q3, true, false, false, null);
// declare binding
AMQP.Queue.BindOk resB = channel.queueBind(AmqpTemp.Q2, AmqpTemp.SPRING_BOOT_EXCHANGE_test, "");
resB = channel.queueBind(AmqpTemp.Q3, AmqpTemp.SPRING_BOOT_EXCHANGE_test, "");
// channel.queueBind(queueName2, SPRING_BOOT_EXCHANGE, "");
return connectionFactory;
}
The problems I was having before, were do to the fact that I created some queues in my initial play with the code - and when I tried to reuse the same queue names it caused exception since they were initially defined differently - so - lesson learnt: rename the queues from the names you used when you 'played' with the code.
I have a spring application that has to consume messages from some JMS queues. The number of queues has to be configurable, and because of this we have to manually create the consumers by reading a config file. So I can have x queues of type1 and y queues of type2, and all the connection details are specified in this config file.
I would say it is a rather complicated code, and I need to point out the following facts: I manually create the spring DefaultMessageListenerContainer and call start and stop on it, the transaction manager is distributed between the JMS and JDBC resources. Also, the application runs on WebLogic and the JMS queues are in WebLogic too.
The flow is that the app reads messages from the queues, tries to put the messages into the database, but if the database is down, the transaction (shared between both JMS and JDBC) is rolled back, so the messages is put back into the queue - this is the failover mechanism when database is down.
The issue that I am experiencing is that when I stop the application while it performs the failover mechanism, there are some JMS consumer threads that are not stopped. This way I get to leak threads and overload the system.
So my question is how can I make sure that when the application stops, it stops all the consumer threads? Calling stop on the message listener container doesn't seem to do the job.
Below are some code snippets:
config:
[
{
"factoryInitial": "weblogic.jndi.WLInitialContextFactory",
"providerUrl": "t3://localhost:7001",
"securityPrincipal": "user",
"securityCredentials": "password",
"connectionFactory": "jms/QCF",
"channels": {
"type1": "jms/queue1"
}
}
]
java:
public class JmsConfig {
private Map<String, List<DefaultMessageListenerContainer>> channels = new HashMap<>();
private Map<String, MessageListener> messageConsumers;
private PlatformTransactionManager transactionManager;
public JmsConfig(Map<String, MessageListener> messageConsumers, PlatformTransactionManager transactionManager) throws Exception {
this.messageConsumers = messageConsumers;
this.transactionManager = transactionManager;
List<JmsServerConfiguration> serverConfigurationList = readJsonFile();
for (JmsServerConfiguration jmsServerConfiguration : serverConfigurationList) {
Properties environment = createEnvironment(jmsServerConfiguration);
JndiTemplate jndiTemplate = new JndiTemplate();
jndiTemplate.setEnvironment(environment);
ConnectionFactory connectionFactory = createConnectionFactory(jndiTemplate, jmsServerConfiguration);
populateMessageListenerContainers(jmsServerConfiguration, jndiTemplate, connectionFactory);
}
}
#PreDestroy
public void stopListenerContainers() {
for (Map.Entry<String, List<DefaultMessageListenerContainer>> channel : channels.entrySet()) {
for (DefaultMessageListenerContainer listenerContainer : channel.getValue()) {
listenerContainer.stop();
}
}
}
private void populateMessageListenerContainers(
JmsServerConfiguration jmsServerConfiguration,
JndiTemplate jndiTemplate, ConnectionFactory connectionFactory) throws Exception {
Set<Map.Entry<String, String>> channelsEntry = jmsServerConfiguration.getChannels().entrySet();
for (Map.Entry<String, String> channel : channelsEntry) {
Destination destination = createDestination(jndiTemplate, channel.getValue());
DefaultMessageListenerContainer listenerContainer =
createListenerContainer(connectionFactory, destination, messageConsumers.get(channel.getKey()));
if (!channels.containsKey(channel.getKey())) {
channels.put(channel.getKey(),
new ArrayList<DefaultMessageListenerContainer>());
}
channels.get(channel.getKey()).add(listenerContainer);
}
}
private Properties createEnvironment(JmsServerConfiguration jmsServerConfiguration) {
Properties properties = new Properties();
properties.setProperty("java.naming.factory.initial", jmsServerConfiguration.getFactoryInitial());
properties.setProperty("java.naming.provider.url", jmsServerConfiguration.getProviderUrl());
properties.setProperty("java.naming.security.principal", jmsServerConfiguration.getSecurityPrincipal());
properties.setProperty("java.naming.security.credentials", jmsServerConfiguration.getSecurityCredentials());
return properties;
}
private ConnectionFactory createConnectionFactory(JndiTemplate jndiTemplate,
JmsServerConfiguration jmsServerConfiguration) throws Exception {
JndiObjectFactoryBean connectionFactory = new JndiObjectFactoryBean();
connectionFactory.setJndiTemplate(jndiTemplate);
connectionFactory.setJndiName(jmsServerConfiguration.getConnectionFactory());
connectionFactory.setExpectedType(ConnectionFactory.class);
connectionFactory.afterPropertiesSet();
return (ConnectionFactory) connectionFactory.getObject();
}
private Destination createDestination(JndiTemplate jndiTemplate, String jndiName) throws Exception {
JndiObjectFactoryBean destinationFactory = new JndiObjectFactoryBean();
destinationFactory.setJndiTemplate(jndiTemplate);
destinationFactory.setJndiName(jndiName);
destinationFactory.setExpectedType(Destination.class);
destinationFactory.afterPropertiesSet();
return (Destination) destinationFactory.getObject();
}
private DefaultMessageListenerContainer createListenerContainer(
ConnectionFactory connectionFactory, Destination destination,
MessageListener messageListener) {
DefaultMessageListenerContainer listenerContainer = new DefaultMessageListenerContainer();
listenerContainer.setConcurrentConsumers(3);
listenerContainer.setConnectionFactory(connectionFactory);
listenerContainer.setDestination(destination);
listenerContainer.setMessageListener(messageListener);
listenerContainer.setTransactionManager(transactionManager);
listenerContainer.setSessionTransacted(true);
listenerContainer.afterPropertiesSet();
listenerContainer.start();
return listenerContainer;
}
}
So the issue was solved by calling listenerContainer.shutdown(); instead of stop().