Solace Client JMS : Operation Not supported on Router: Router doesn't support transacted sessions - spring

I am trying to listen to a Solace End Point using Sping Boot and when ran my app i am getting the Error:
2018-09-28 03:16:57.446 WARN 27305 --- [enerContainer-1] o.s.j.l.DefaultMessageListenerContainer : Setup of JMS message listener invoker failed for destination 'TEST1.OUT' - trying to recover. Cause: Error creating session - operation not supported on router (Capability Mismatch: Router does not support transacted sessions.)
Is there a config argument that i can set to not to use transaction sessions.
Thanks

You will need to create a JmsListenerContainerFactory that does not make use of transactions. For example:
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(
ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory listenerFactory =
new DefaultJmsListenerContainerFactory();
configurer.configure(listenerFactory, connectionFactory);
listenerFactory.setTransactionManager(null);
listenerFactory.setSessionTransacted(false);
return listenerFactory;
}
Full details can be found in the spring boot docs.
Do note that the Solace message broker supports transactions(local and XA).
To enable local transactions:
Enable allow‑transacted‑sessions in the client-profile used by your username.
Disable direct transport in your JMS connection factory.
Full details can be found in the Solace documentation.

Excellent answer.
To complement Russell answer, in the method which will handle the consume, in the annotation, we must specify the container factory bean created in the last step.
#JmsListener(destination = "TOPIC.TRX_PAYMENT", containerFactory = "jmsListenerContainerFactory")

Related

Message sent using JMS producer is not received in MQTT receiver in the same SpringBoot app

I'm just starting with ActiveMQ Artemis and have Artemis 2.17.0 installed on my machine. Created SpringBoot test app where both JMS and MQTT publishers and receivers exist. Created also small RestController so I can send messages using both JMS and MQTT producers. Receivers are quite simple and just create a log message to console. Now when I create a message using MQTT producer, both JMS and MQTT receivers get and log message to console. But when I send a message using JMS producer, the message is being received only in JMS receiver, no MQTT message in console. Tried several times. Implementation is ok I think as MQTT producer example is working fine. Is there any limitation for routing messages among protocols in Artemis in this way? Or what kind of problem it can be?
Code info about JMS implementation: https://dmarko.tcl-digitrade.com/post/2021/activemq-artemis-spring-boot/
Code info about MQTT implementation: https://dmarko.tcl-digitrade.com/post/2021/activemq-artemis-mqtt/
Apache ActiveMQ Artemis has a flexible addressing model that supports both Point-to-Point and Publish-Subscribe patterns.
By default, Spring Boot creates a JmsTemplate configured to transmit Point-to-Point while MQTT uses a Publish-Subscribe pattern, so the JMS and MQTT receivers are using different messaging patterns and this is causing your issue.
To configure a JmsTemplate for the Publish-Subscribe pattern set spring.jms.pub-sub-domain=true through Boot’s application.properties or set the JmsTemplate pubSubDomain to true, ie:
jmsTemplate.setPubSubDomain(true);
To configure a JmsListener for the Publish-Subscribe pattern set spring.jms.pub-sub-domain=true through Boot’s application.properties or set the JmsListenerContainerFactory pubSubDomain to true, ie:
#Bean
public JmsListenerContainerFactory<?> topicConnectionFactory(ConnectionFactory connectionFactory, DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
configurer.configure(factory, connectionFactory);
factory.setPubSubDomain(true);
return factory;
}
#JmsListener(destination = "${prices.mqtt.east}", containerFactory = "topicConnectionFactory")
public void receiveFromTopic(String message) {
...
}

Setup of spring-boot application with JMS, Artemis and JGroups with jdbc_ping

I have setup an Artemis HA-Custer example locally on my computer to learn how it's basically working. Now I want to prepare it to be pushed in a kubernetes cluster. Therefore I want to change the way of the initial membership discovery for the broker nodes, so I can use it in cloud, too. I want to use JMS and JGroups with "jdbc_ping". Actually I am not sure, if I am doing it right, so maybe you can tell me if not.
So far the brokers have successfully put their infos in the db-table and are apparently connected. When I try the following connectionFactory from my java application, it starts without errors and connects with the brokers. But in some points I am not sure, if it acts correctly.
#Bean
public ConnectionFactory connectionFactory() {
TransportConfiguration transportConfiguration = new TransportConfiguration(NettyConnectorFactory.class.getName());
ConnectionFactory cf = ActiveMQJMSClient.createConnectionFactoryWithHA(JMSFactoryType.CF, transportConfiguration);
return cf;
}
So the single point of question is now, how to setup the connectionFactory for the use of JGroups correctly.
UPDATE:
INFO 24528 --- [enerContainer-1] o.s.j.l.DefaultMessageListenerContainer : JMS message listener invoker needs to establish shared Connection
ERROR 24528 --- [enerContainer-1] o.s.j.l.DefaultMessageListenerContainer : Could not refresh JMS Connection for destination 'TestA' - retrying using FixedBackOff{interval=5000, currentAttempts=0, maxAttempts=unlimited}. Cause: Failed to create session factory; nested exception is ActiveMQInternalErrorException[errorType=INTERNAL_ERROR message=AMQ219004: Failed to initialise session factory]
The ActiveMQ Artemis documentation covers this:
Lastly, the jgroups scheme is supported which provides an alternative to the udp scheme for server discovery. The URL pattern is either jgroups://channelName?file=jgroups-xml-conf-filename where jgroups-xml-conf-filename refers to an XML file on the classpath that contains the JGroups configuration or it can be jgroups://channelName?properties=some-jgroups-properties. In both instance the channelName is the name given to the jgroups channel created.
In your code you can do something like this:
#Bean
public ConnectionFactory connectionFactory() {
return new ActiveMQConnectionFactory("jgroups://channelName?file=jgroups-xml-conf-filename");
}
In your case the client will need access to the same database that the broker's are using in order to use that information for discovery.

IBM MQ ignores JMS ExceptionListener registered by Camel + Spring Boot based app

We develop a microservice based on Apache Camel JMS component and Spring Boot. IBM MQ is used as messaging middleware. There is an issue with exception listener - IBM MQ classes can't find registered exception listener and print it's own stack trace in system out when connection with MQ is broken:
com.ibm.msg.client.jms.internal.JmsProviderExceptionListener
The exception is ignored as no exception listener is registered: '
Message : com.ibm.msg.client.jms.DetailedJMSException: JMSWMQ1107: A problem with this connection has occurred.
An error has occurred with the IBM MQ JMS connection.
Use the linked exception to determine the cause of this error.
org.springframework.jms.connection.CachingConnectionFactory is used for connection setup.
#Bean
protected final ConnectionFactory createMqJmsConnectionFactory() {
MQQueueConnectionFactory mqFactory = new MQQueueConnectionFactory();
// Factory setup
CachingConnectionFactory cachingFactory = new CachingConnectionFactory(mqFactory);
return cachingFactory;
}
CachingConnectionFactory's ancestor implementes javax.jms.ExceptionListener and, as I found on Spring forum, registers itself as exceptionListener. From stack trace I can see that onException() method is invoked after exception, resets connection and writes log.
So we have a situation when IBM MQ ignores CachingConnectionFactory as exception listener. Camel JMS component has exceptionListener endpoint configuration option - I assume, adding of the CachingConnectionFactory here would be redundantly.
Any other settings need to be done in order to register CachingConnectionFactory as exception listener?

Losing JMS Messages with Spring JMS and ActiveMQ when application server is suddenly stopped

I have a Spring JMS application that has a JMS Listener that connects to an Active MQ queue on application startup. This JMS listener is a part of the an application that takes a message, enriches it with content, and then delivers it to a topic on the same ActiveMQ broker.
The sessionTransacted is set to True. I'm not performing any database transactions, so I do not have #Transactional set anywhere. From what I've read, the sessionTransacted property sets a local transaction around the JMS Listener's receive method and therefore it will not pull the message off the queue until the transaction is complete. I've tested this using a local ActiveMQ instance, and on my local tomcat container, and it worked as expected.
However, when I deploy to our PERF environment and retry the same test, I notice that the message that was currently in-flight when the server was shutdown, is pulled from queue prior to completing the receive method.
What I would like to know is if there is anything obvious that I should be looking for? Are there certain JMS headers that would cause this behaviour to occur? Please let me know if there is anymore information that I can provide.
I'm using Spring 4.1.2.RELEASE with Apache ActiveMQ 5.8.0, on a Tomcat 7 container running Java 8.
UPDATE - Adding my Java JMS Configurations. Please note that I substituted what I had in my PERF properties file into the relevant areas for clarity.
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() throws Throwable {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
factory.setMaxMessagesPerTask(-1);
factory.setConcurrency(1);
factory.setSessionTransacted(Boolean.TRUE);
return factory;
}
#Bean
public CachingConnectionFactory connectionFactory(){
RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
redeliveryPolicy.setInitialRedeliveryDelay(1000);
redeliveryPolicy.setRedeliveryDelay(1000);
redeliveryPolicy.setMaximumRedeliveries(6);
redeliveryPolicy.setUseExponentialBackOff(Boolean.TRUE);
redeliveryPolicy.setBackOffMultiplier(5);
ActiveMQConnectionFactory activeMQ = new ActiveMQConnectionFactory(environment.getProperty("queue.username"), environment.getProperty("queue.password"), environment.getProperty("jms.broker.endpoint"));
activeMQ.setRedeliveryPolicy(redeliveryPolicy);
activeMQ.setPrefetchPolicy(prefetchPolicy());
CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(activeMQ);
cachingConnectionFactory.setCacheConsumers(Boolean.FALSE);
cachingConnectionFactory.setSessionCacheSize(1);
return cachingConnectionFactory;
}
#Bean
public JmsMessagingTemplate jmsMessagingTemplate(){
ActiveMQTopic activeMQ = new ActiveMQTopic(environment.getProperty("queue.out"));
JmsMessagingTemplate jmsMessagingTemplate = new JmsMessagingTemplate(connectionFactory());
jmsMessagingTemplate.setDefaultDestination(activeMQ);
return jmsMessagingTemplate;
}
protected ActiveMQPrefetchPolicy prefetchPolicy(){
ActiveMQPrefetchPolicy prefetchPolicy = new ActiveMQPrefetchPolicy();
int prefetchValue = 0;
prefetchPolicy.setQueuePrefetch(prefetchValue);
return prefetchPolicy;
}
Thanks,
Juan
It turns out that there were different versions of our application deployed on our PERF environment. Once the application was updated, then it worked as expected.

Spring JMS Message Redelivery not working as expected for CLIENT_ACKNOWLEDGE mode

My environment: spring 4.1, JBoss EAP 6.4, IBM MQ 8.0:
Messages are not redelivered in the case where Listener throws RuntimeException.
I have the following in JmsConfig:
#Bean
DefaultMessageListenerContainer defaultMessageListenerContainer(QueueConnectionFactory connectionFactory, JndiDestinationResolver dr, MessageListener ml) {
DefaultMessageListenerContainer mlc = new DefaultMessageListenerContainer();
mlc.setConnectionFactory(connectionFactory);
mlc.setMessageListener(ml);
mlc.setDestinationName(jndiInQueue);
mlc.setDestinationResolver(dr);
mlc.setSessionTransacted(true);
mlc.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
return mlc;
}
If I use a JmsTransactionManager and pass it to the above method and use like so:
mlc.setTransactionManager(tm)
Following warnings are written to the log:
It is not valid to commit a non-transacted session, and the behavior is the same, no redelivery.
ConnectionFactory is obtained via JNDI, I wonder if sourcing the ConnectionFactory through jndi has something to do with this?
From the AbstractMessageListenerContainer Javadocs:
In order to consistently arrange for redelivery with any container variant, consider "CLIENT_ACKNOWLEDGE" mode or - preferably - setting "sessionTransacted" to "true" instead
There is a similar question on SO.
Flip your ack mode to Session.SESSION_TRANSACTED instead of CLIENT_ACKNOWLEDGE.
Client Ack mode doesn't work as most folks want it.. and is a common "gotcha" in JMS. It acknowledges current message AND all previous messages in the session. It is not per-message acknowledgement.
Edit:
Also check related post-- IBM MQ may require you to use the "XA" versions of the connection factory class.
ref: Websphere Liberty profile - transacted Websphere MQ connection factory

Resources