Websphere Liberty profile - transacted Websphere MQ connection factory - websphere

Trying to get my Liberty profile server to create a transacted jmsConnectionFactory to my instance of Websphere message queue.
Liberty profile v 8.5.5.5
Websphere MQ 7.x
I've tried to change the jmsQueueConnectionFactory to jmsXAQueueConnectionFactory but it does not help -> Then it seems to just ignore it and doesn't connect to the MQ
server.xml
<?xml version="1.0" encoding="UTF-8"?>
<server description="new server">
<!-- Enable features -->
<featureManager>
<feature>wmqJmsClient-1.1</feature>
<feature>jndi-1.0</feature>
<feature>jsp-2.2</feature>
<feature>localConnector-1.0</feature>
</featureManager>
<variable name="wmqJmsClient.rar.location" value="D:\wlp\wmq\wmq.jmsra.rar"/>
<jmsQueueConnectionFactory jndiName="jms/wmqCF" connectionManagerRef="ConMgr6">
<properties.wmqJms
transportType="CLIENT"
hostName="hostname"
port="1514"
channel="SYSTEM.DEF.SVRCONN"
queueManager="QM"
/>
</jmsQueueConnectionFactory>
<connectionManager id="ConMgr6" maxPoolSize="2"/>
<applicationMonitor updateTrigger="mbean"/>
<application id="App"
location="...\app.war"
name="App" type="war"/>
<!-- To access this server from a remote client add a host attribute to the following element, e.g. host="*" -->
<httpEndpoint id="defaultHttpEndpoint"
httpPort="9080"
httpsPort="9443"/>
</server>
log
2015-04-23 17:07:14,981 [JmsConsumer[A0]] WARN ultJmsMessageListenerContainer - Setup of JMS message listener invoker failed for destination 'A0' - trying to recover. Cause: Could not commit JMS transaction; nested exception is com.ibm.msg.client.jms.DetailedIllegalStateException: JMSCC0014: It is not valid to call the 'commit' method on a nontransacted session. The application called a method that must not be called on a nontransacted session. Change the application program to remove this behavior.
2015-04-23 17:07:14,983 [JmsConsumer[A0]] INFO ultJmsMessageListenerContainer - Successfully refreshed JMS Connection
Camel code
public static JmsComponent mqXAComponentTransacted(InitialContext context, String jndiName) throws JMSException, NamingException {
return JmsComponent.jmsComponentTransacted((XAQueueConnectionFactory) context.lookup(jndiName));
}

Ended up with:
public static JmsComponent mqXAComponentTransacted(InitialContext context, String connectionFactoryJndiName, String userTransactionJndiName) throws JMSException, NamingException {
LOG.info("Setting up JmsComponent using jndi lookup");
final JtaTransactionManager jtaTransactionManager = new JtaTransactionManager((UserTransaction) context.lookup(userTransactionJndiName));
final ConnectionFactory connectionFactory = (ConnectionFactory) context.lookup(connectionFactoryJndiName);
final JmsComponent jmsComponent = JmsComponent.jmsComponentTransacted(connectionFactory, (PlatformTransactionManager) jtaTransactionManager);
jmsComponent.setTransacted(false);
jmsComponent.setCacheLevel(DefaultMessageListenerContainer.CACHE_NONE);
return jmsComponent;
}

Related

ActiveMQ - Redelivery policy and Dead letter queue for topic

I'm working with ActiveMQ Artemis 2.17 and Spring Boot 2.5.7.
I'm publishing messages on topic and queue and consuming it. All is done through JMS.
All queues (anycast or multicast) are durables. My topic (multicast address) has 2 durable queues in order to have 2 separate consumers.
For my topic, the 2 consumers use durable and shared subscriptions (JMS 2.0).
All processing is transactional, managed through Atomikos transaction manager (I need it for a 2 phases commit with the database).
I have a problem with the redelivery policy and DLQ. When I throw an exception during the processing of the message, the redelivery policy applies correctly for the queue (anycast queue) and a DLQ is created with the message. However, for the topic (multicast queue), the redelivery policy does not apply and the message is not sent into a DLQ.
Here is my ActiveMQ Artemis broker configuration:
<?xml version='1.0'?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<configuration xmlns="urn:activemq"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:activemq /schema/artemis-configuration.xsd">
<core xmlns="urn:activemq:core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:activemq:core ">
<name>0.0.0.0</name>
<!-- Codec and key used to encode the passwords -->
<!-- TODO : set master-password configuration into the Java code -->
<password-codec>org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec;key=UBNTd0dS9w6f8HDIyGW9
</password-codec>
<!-- Configure the persistence into a database (postgresql) -->
<persistence-enabled>true</persistence-enabled>
<store>
<database-store>
<bindings-table-name>BINDINGS_TABLE</bindings-table-name>
<message-table-name>MESSAGE_TABLE</message-table-name>
<page-store-table-name>PAGE_TABLE</page-store-table-name>
<large-message-table-name>LARGE_MESSAGES_TABLE</large-message-table-name>
<node-manager-store-table-name>NODE_MANAGER_TABLE</node-manager-store-table-name>
<jdbc-lock-renew-period>2000</jdbc-lock-renew-period>
<jdbc-lock-expiration>20000</jdbc-lock-expiration>
<jdbc-journal-sync-period>5</jdbc-journal-sync-period>
<!-- Configure a connection pool -->
<data-source-properties>
<data-source-property key="driverClassName" value="org.postgresql.Driver"/>
<data-source-property key="url" value="jdbc:postgresql://localhost/artemis"/>
<data-source-property key="username" value="postgres"/>
<data-source-property key="password" value="ENC(-3eddbe9664c85ec7ed63588b000486cb)"/>
<data-source-property key="poolPreparedStatements" value="true"/>
<data-source-property key="initialSize" value="2"/>
<data-source-property key="minIdle" value="1"/>
</data-source-properties>
</database-store>
</store>
<!-- Configure the addresses, queues and topics default behaviour -->
<!-- See: https://activemq.apache.org/components/artemis/documentation/latest/address-model.html -->
<address-settings>
<address-setting match="#">
<dead-letter-address>DLA</dead-letter-address>
<auto-create-dead-letter-resources>true</auto-create-dead-letter-resources>
<dead-letter-queue-prefix/>
<dead-letter-queue-suffix>.DLQ</dead-letter-queue-suffix>
<expiry-address>ExpiryQueue</expiry-address>
<auto-create-expiry-resources>false</auto-create-expiry-resources>
<expiry-queue-prefix/>
<expiry-queue-suffix>.EXP</expiry-queue-suffix>
<expiry-delay>-1</expiry-delay>
<max-delivery-attempts>5</max-delivery-attempts>
<redelivery-delay>250</redelivery-delay>
<redelivery-delay-multiplier>2.0</redelivery-delay-multiplier>
<redelivery-collision-avoidance-factor>0.5</redelivery-collision-avoidance-factor>
<max-redelivery-delay>10000</max-redelivery-delay>
<max-size-bytes>100000</max-size-bytes>
<page-size-bytes>20000</page-size-bytes>
<page-max-cache-size>5</page-max-cache-size>
<max-size-bytes-reject-threshold>-1</max-size-bytes-reject-threshold>
<address-full-policy>PAGE</address-full-policy>
<message-counter-history-day-limit>0</message-counter-history-day-limit>
<default-last-value-queue>false</default-last-value-queue>
<default-non-destructive>false</default-non-destructive>
<default-exclusive-queue>false</default-exclusive-queue>
<default-consumers-before-dispatch>0</default-consumers-before-dispatch>
<default-delay-before-dispatch>-1</default-delay-before-dispatch>
<redistribution-delay>0</redistribution-delay>
<send-to-dla-on-no-route>true</send-to-dla-on-no-route>
<slow-consumer-threshold>-1</slow-consumer-threshold>
<slow-consumer-policy>NOTIFY</slow-consumer-policy>
<slow-consumer-check-period>5</slow-consumer-check-period>
<!-- We disable the automatic creation of queue or topic -->
<auto-create-queues>false</auto-create-queues>
<auto-delete-queues>true</auto-delete-queues>
<auto-delete-created-queues>false</auto-delete-created-queues>
<auto-delete-queues-delay>30000</auto-delete-queues-delay>
<auto-delete-queues-message-count>0</auto-delete-queues-message-count>
<config-delete-queues>OFF</config-delete-queues>
<!-- We disable the automatic creation of address -->
<auto-create-addresses>false</auto-create-addresses>
<auto-delete-addresses>true</auto-delete-addresses>
<auto-delete-addresses-delay>30000</auto-delete-addresses-delay>
<config-delete-addresses>OFF</config-delete-addresses>
<management-browse-page-size>200</management-browse-page-size>
<default-purge-on-no-consumers>false</default-purge-on-no-consumers>
<default-max-consumers>-1</default-max-consumers>
<default-queue-routing-type>ANYCAST</default-queue-routing-type>
<default-address-routing-type>ANYCAST</default-address-routing-type>
<default-ring-size>-1</default-ring-size>
<retroactive-message-count>0</retroactive-message-count>
<enable-metrics>true</enable-metrics>
<!-- We automatically force group rebalance and a dispatch pause during group rebalance -->
<default-group-rebalance>true</default-group-rebalance>
<default-group-rebalance-pause-dispatch>true</default-group-rebalance-pause-dispatch>
<default-group-buckets>1024</default-group-buckets>
</address-setting>
</address-settings>
<!-- Define the protocols accepted -->
<!-- See: https://activemq.apache.org/components/artemis/documentation/latest/protocols-interoperability.html -->
<acceptors>
<!-- Acceptor for only CORE protocol -->
<!-- We enable the cache destination as recommended into the documentation. See: https://activemq.apache.org/components/artemis/documentation/latest/using-jms.html -->
<acceptor name="artemis">
tcp://0.0.0.0:61616?protocols=CORE,tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;useEpoll=true,cacheDestinations=true
</acceptor>
</acceptors>
<!-- Define how to connect to another broker -->
<!-- TODO : est-ce utile ? -->
<connectors>
<connector name="netty-connector">tcp://localhost:61616</connector>
<connector name="broker1-connector">tcp://localhost:61616</connector>
<connector name="broker2-connector">tcp://localhost:61617</connector>
</connectors>
<!-- Configure the High-Availability and broker cluster for the high-availability -->
<ha-policy>
<shared-store>
<master>
<failover-on-shutdown>true</failover-on-shutdown>
</master>
</shared-store>
</ha-policy>
<cluster-connections>
<cluster-connection name="gerico-cluster">
<connector-ref>netty-connector</connector-ref>
<static-connectors>
<connector-ref>broker1-connector</connector-ref>
<connector-ref>broker2-connector</connector-ref>
</static-connectors>
</cluster-connection>
</cluster-connections>
<!-- <cluster-user>cluster_user</cluster-user>-->
<!-- <cluster-password>cluster_user_password</cluster-password>-->
<!-- should the broker detect dead locks and other issues -->
<critical-analyzer>true</critical-analyzer>
<critical-analyzer-timeout>120000</critical-analyzer-timeout>
<critical-analyzer-check-period>60000</critical-analyzer-check-period>
<critical-analyzer-policy>HALT</critical-analyzer-policy>
<page-sync-timeout>72000</page-sync-timeout>
<!-- the system will enter into page mode once you hit this limit.
This is an estimate in bytes of how much the messages are using in memory
The system will use half of the available memory (-Xmx) by default for the global-max-size.
You may specify a different value here if you need to customize it to your needs.
<global-max-size>100Mb</global-max-size>
-->
<!-- Security configuration -->
<security-enabled>false</security-enabled>
<!-- Addresses and queues configuration -->
<!-- !!! DON'T FORGET TO UPDATE 'slave-broker.xml' FILE !!! -->
<addresses>
<address name="topic.test_rde">
<multicast>
<queue name="rde_receiver_1">
<durable>true</durable>
</queue>
<queue name="rde_receiver_2">
<durable>true</durable>
</queue>
</multicast>
</address>
<address name="queue.test_rde">
<anycast>
<queue name="queue.test_rde">
<durable>true</durable>
</queue>
</anycast>
</address>
</addresses>
</core>
</configuration>
The JMS configuration into the Spring Boot is the following:
#Bean
public DynamicDestinationResolver destinationResolver() {
return new DynamicDestinationResolver() {
#Override
public Destination resolveDestinationName(Session session, String destinationName, boolean pubSubDomain) throws JMSException {
if (destinationName.startsWith("topic.")) {
pubSubDomain = true;
}
else
pubSubDomain =false;
return super.resolveDestinationName(session, destinationName, pubSubDomain);
}
};
}
#Bean
public JmsListenerContainerFactory<?> queueConnectionFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer,
JmsErrorHandler jmsErrorHandler) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
configurer.configure(factory, connectionFactory);
factory.setPubSubDomain(false);
factory.setSessionTransacted(true);
factory.setErrorHandler(jmsErrorHandler);
return factory;
}
#Bean
public JmsListenerContainerFactory<?> topicConnectionFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer,
JmsErrorHandler jmsErrorHandler) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
// This provides all boot's default to this factory, including the message converter
configurer.configure(factory, connectionFactory);
factory.setPubSubDomain(true);
factory.setSessionTransacted(true);
factory.setSubscriptionDurable(true);
factory.setSubscriptionShared(true);
factory.setErrorHandler(jmsErrorHandler);
return factory;
}
The messages publisher:
#GetMapping("api/send")
public void sendDataToJms() throws InterruptedException {
OrderRest currentService = this.applicationContext.getBean(OrderRest.class);
for (Long i = 0L; i < 1L; i++) {
currentService.sendTopicMessage(i);
currentService.sendQueueMessage(i);
Thread.sleep(200L);
}
}
#Transactional
public void sendTopicMessage(Long id) {
Order myMessage = new Order("--" + id.toString() + "--", new Date());
jmsTemplate.convertAndSend("topic.test_rde", myMessage);
}
#Transactional
public void sendQueueMessage(Long id) {
Order myMessage = new Order("--" + id.toString() + "--", new Date());
jmsTemplate.convertAndSend("queue.test_rde", myMessage);
}
And the listeners:
#Transactional
#JmsListener(destination = "topic.test_rde", containerFactory = "topicConnectionFactory", subscription = "rde_receiver_1")
public void receiveMessage_rde_1(#Payload Order order, #Headers MessageHeaders headers, Message message, Session session) {
log.info("---> Consumer1 - rde_receiver_1 - " + order.getContent());
throw new ValidationException("Voluntary exception", "entity", List.of(), List.of());
}
#Transactional
#JmsListener(destination = "queue.test_rde", containerFactory = "queueConnectionFactory")
public void receiveMessage_rde_queue(#Payload Order order, #Headers MessageHeaders headers, Message message, Session session) {
log.info("---> Consumer1 - rde_receiver_queue - " + order.getContent());
throw new ValidationException("Voluntary exception", "entity", List.of(), List.of());
}
Is it normal that the redelivery policy and the DLQ mechanismd do only apply for a queue (anycat queue)?
Is it possible to also apply it for topic (multicast queue) and shared durable subscriptions?
If not, how can I have a topic behaviour but with redelivery and DLQ mechanisms? Should I use the divert solution of ActiveMQ?
Thank a lot for your help.
Regards.
I have found the problem. It comes from Atomikos that does not support the JMS v2.0 but only JMS 1.1.
So, it's not possible to get a shared durable subscription on a topic that support at the same time the 2-PC and the redelivery policy.

Actuator JMS Health Check Return False Positive With Java Configuration

I have run into an issue where the Actuator probe fails for JMS health even though my routes can connect and produce message to JMS. So in short Actuator is saying it is down but it is working.
Tech stack and tech notes:
Spring-boot: 2.3.1.RELEASE
Camel: 3.4.1
Artemis: 2.11.0
Artemis has been setup to use a user name and password(artemis/artemis).
Using org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory for connection factory.
My route is as simple as chips:
<route id="timer-cluster-producer-route">
<from uri="timer:producer-ticker?delay=5000"/>
<setBody>
<groovy>
result = ["Name":"Johnny"]
</groovy>
</setBody>
<marshal>
<json library="Jackson"/>
</marshal>
<to uri="ref:jms-producer-cluster-event" />
</route>
XML Based Artemis Configuration
With Spring-boot favoring java based configuration I am busy migrating our XML beans accordingly.Thus I took a working beans.xml file pasted into the project and fired up the route and I could send messages flowing and the health check returned OK.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
<bean id="jmsConnectionFactory"
class="org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616" />
<property name="user" value="artemis"/>
<property name="password" value="artemis"/>
<property name="connectionLoadBalancingPolicyClassName" value="org.apache.activemq.artemis.api.core.client.loadbalance.RoundRobinConnectionLoadBalancingPolicy"/>
</bean>
<!--org.messaginghub.pooled.jms.JmsPoolConnectionFactory-->
<!--org.apache.activemq.jms.pool.PooledConnectionFactory-->
<bean id="jmsPooledConnectionFactory"
class="org.apache.activemq.jms.pool.PooledConnectionFactory"
init-method="start" destroy-method="stop">
<property name="maxConnections" value="64" />
<property name="MaximumActiveSessionPerConnection"
value="500" />
<property name="connectionFactory" ref="jmsConnectionFactory" />
</bean>
<bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">
<property name="connectionFactory"
ref="jmsPooledConnectionFactory" />
<property name="concurrentConsumers" value="1" />
<property name="artemisStreamingEnabled" value="true"/>
</bean>
<bean id="jms"
class="org.apache.camel.component.jms.JmsComponent">
<property name="configuration" ref="jmsConfig"/>
</bean>
<!-- <bean id="activemq"
class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="configuration" ref="jmsConfig" />
</bean>-->
</beans>
Spring-boot Auto (Black)Magic Configuration
I then used the application.yaml file to configure the artemis connection by using this method as outlined in the Spring-boot documentation. This also worked when my application.yaml file contained the following configuration:
artemis:
user: artemis
host: localhost
password: artemis
pool:
max-sessions-per-connection: 500
enabled: true
max-connections: 16
This worked like a charm.
Brave Attempt At Java Configuration.
So I then went for gold and tried the Java based configuration as outlined below:
#SpringBootApplication
#ImportResource("classpath:/camel/camel.xml")
public class ClusterProducerApplication {
public static void main(String[] args) {
SpringApplication.run(ClusterProducerApplication.class, args);
}
#Bean
public JmsComponent jms() throws JMSException {
// Create the connectionfactory which will be used to connect to Artemis
ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory();
cf.setBrokerURL("tcp://localhost:61616");
cf.setUser("artemis");
cf.setPassword("artemis");
//Create connection pool using connection factory
PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory();
pooledConnectionFactory.setMaxConnections(2);
pooledConnectionFactory.setConnectionFactory(cf);
//Create configuration which uses connection factory
JmsConfiguration jmsConfiguration = new JmsConfiguration();
jmsConfiguration.setConcurrentConsumers(2);
jmsConfiguration.setArtemisStreamingEnabled(true);
jmsConfiguration.setConnectionFactory(pooledConnectionFactory);
// Create the Camel JMS component and wire it to our Artemis configuration
JmsComponent jms = new JmsComponent();
jms.setConfiguration(jmsConfiguration);
return jms;
}
}
So when camel starts up I see the following warning logged on start up:
020-07-28 12:33:38.631 WARN 25329 --- [)-192.168.1.158] o.s.boot.actuate.jms.JmsHealthIndicator : JMS health check failed
javax.jms.JMSSecurityException: AMQ229031: Unable to validate user from /127.0.0.1:42028. Username: null; SSL certificate subject DN: unavailable
After the 5sec delay the timer kicks in and message are being produced. I logged into the Artemis console and I can browse the messages and can see them being created. However when I run a get on actuator health I see the following:
"jms": {
"status": "DOWN",
"details": {
"error": "javax.jms.JMSSecurityException: AMQ229031: Unable to validate user from /127.0.0.1:42816. Username: null; SSL certificate subject DN: unavailable"
}
},
This feels like a big of a bug to me.
Observations about connection pooling implementations.
I noticed that AMQ connection pooling has been moved into the following maven dependency:
<dependency>
<groupId>org.messaginghub</groupId>
<artifactId>pooled-jms</artifactId>
</dependency>
I thought let me give that a try as well. It show the same behaviour as outlined above with one more interesting thing. When using org.messaginghub.pooled-jms as the connection pool(recommended by spring-boot docs as well) the following is logged on startup.
2020-07-28 12:41:37.255 INFO 26668 --- [ main] o.m.pooled.jms.JmsPoolConnectionFactory : JMS ConnectionFactory on classpath is not a JMS 2.0+ version.
Which is weird as according to the official repo the connector is JMS 2.0 compliant.
Quick Summary:
It appears that actuator does not pick up the credentials of the connection factory when configuring the JMS component via Java. While a work around exists at the moment by using the spring-boot application.yaml configuration it limits the way you can configure JMS clients on Camel.
So after some digging and reaching out to the Spring-boot people on GitHub, I found what the issue is. When using Java configuration I am configuring the JMS component of Camel with a connection factory. However Spring-boot is completely unaware of this as it is a Camel component. Thus the connection factory used by the JMS needs to be exposed to Spring-boot for it to work.
The fix is relatively simple. See code below:
#Configuration
public class ApplicationConfiguration {
private ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory();
#Bean
public JmsComponent jms() throws JMSException {
// Create the connectionfactory which will be used to connect to Artemis
cf.setBrokerURL("tcp://localhost:61616");
cf.setUser("artemis");
cf.setPassword("artemis");
// Setup Connection pooling
PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory();
pooledConnectionFactory.setMaxConnections(2);
pooledConnectionFactory.setConnectionFactory(cf);
JmsConfiguration jmsConfiguration = new JmsConfiguration();
jmsConfiguration.setConcurrentConsumers(2);
jmsConfiguration.setArtemisStreamingEnabled(true);
jmsConfiguration.setConnectionFactory(pooledConnectionFactory);
// Create the Camel JMS component and wire it to our Artemis connectionfactory
JmsComponent jms = new JmsComponent();
jms.setConfiguration(jmsConfiguration);
return jms;
}
/*
This line will expose the connection factory to Spring-boot.
*/
#Bean
public ConnectionFactory jmsConnectionFactory() {
return cf;
}
}

Setting up listener using IBM MQ in eclipse using wlp for java springboot application

Setting up listener using IBM MQ in eclipse using wlp for java springboot application
Hi, I am trying to set up listener using wlp in my local in eclipse,
following is the code :
pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.jms</groupId>
<artifactId>javax.jms-api</artifactId>
<version>2.0</version>
</dependency>
java class:
#JmsListener(containerFactory = "jmsListenerContainerFactory", destination = test.queue)
public void recieve(Message message) {
log.info("inside message receiver");
try {
if (message instanceof TextMessage) {
message.acknowledge();
String json = ((TextMessage) message).getText();
/** To solve Json injection fortify issue **/
String sanitisedJsonMessage = JsonSanitizer.sanitize(json);
Gson gson = new Gson();
//business logic
} else {
log.error("ERROR ::: invalid message type");
message.acknowledge();
}
} catch (JsonSyntaxException | JMSException ex) {
log.error("ERROR: " + ex + ex.getMessage());
}
}
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
ConnectionFactory connectionfactory;
DefaultJmsListenerContainerFactory listenerFactory;
try {
log.info("inside listener factory");
#Cleanup
Context context = null;
listenerFactory = new DefaultJmsListenerContainerFactory();
context = new InitialContext();
connectionfactory = (ConnectionFactory) context.lookup(jms/ConnectionFactory);
listenerFactory.setConnectionFactory(connectionfactory);
listenerFactory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
} catch (NamingException ex) {
log.error("ERROR: error looking up queue connection factory jndi {}", ex);
}
return listenerFactory;
}
Now I tried to set up my server.xml in wlp as per ibm guidelines as follows:
<server description="new server">
<!-- Enable features -->
<featureManager>
<feature>javaee-7.0</feature>
<feature>jndi-1.0</feature>
<feature>jaxws-2.2</feature>
<feature>localConnector-1.0</feature>
<feature>transportSecurity-1.0</feature>
<feature>servlet-3.1</feature>
<feature>mdb-3.2</feature>
<feature>wmqJmsClient-2.0</feature>
<feature>jsp-2.3</feature>
</featureManager>
<!-- Define an Administrator and non-Administrator -->
<basicRegistry id="basic">
<user name="admin" password="adminpwd" />
<user name="nonadmin" password="nonadminpwd" />
</basicRegistry>
<!-- Assign 'admin' to Administrator -->
<administrator-role>
<user>admin</user>
</administrator-role>
<keyStore id="defaultKeyStore" password="Liberty" />
<httpEndpoint host="*" httpPort="9081" httpsPort="9444"
id="defaultHttpEndpoint" />
<!-- Automatically expand WAR files and EAR files -->
<applicationManager autoExpand="true" />
<applicationMonitor updateTrigger="mbean" />
<enterpriseApplication
id="mqtest-ear"
location="mqtest-ear.ear"
name="mqtest-ear" />
<variable name="wmqJmsClient.rar.location"
value="path\to\wlp\wmq\wmq.jmsra.rar" />
<jmsQueueConnectionFactory
jndiName="jms/ConnectionFactory">
<properties.wmqJms transportType="CLIENT"
hostName="<test.correcthostname>" port="<test.correctportname>"
channel="<test.correctchannelname>" queueManager="<test.correctqmgrname>" useSSL="true"
headerCompression="SYSTEM" messageCompression="RLE"
sslCipherSuite="SSL_RSA_WITH_AES_256_CBC_SHA256"
targetClientMatching="true" />
<connectionManager></connectionManager>
</jmsQueueConnectionFactory>
<jmsQueue id="JMSQueue" jndiName="jms/InQueue">
<properties.wmqJms baseQueueName="test.queue"
baseQueueManagerName="<test.correctqmgrname>" receiveConversion="CLIENT_MSG"
putAsyncAllowed="DESTINATION" targetClient="MQ"
readAheadAllowed="ENABLED" />
</jmsQueue>
<!-- <resourceAdapter
location="${wmqJmsClient.rar.location}" id="resourceAdapter">
</resourceAdapter> -->
<keyStore id="keyAndTrustStore" password="password"
location="path\to\keyandtruststore"
type="PKCS12">
</keyStore>
I have downloaded latest resource adapter and 9.0.0.8-IBM-MQ-Java-InstallRA.jar,
When I run the application , I get constant error:
2020-01-10 11:21:16 ERROR o.s.j.l.DefaultMessageListenerContainer - Could not refresh JMS Connection for destination 'test.queue' - retrying using FixedBackOff{interval=5000, currentAttempts=12, maxAttempts=unlimited}. Cause: MQJCA1011: Failed to allocate a JMS connection.
what Can I do to make listener work
After trying and troubleshoting a lot, I found a solution to set up ibm mq message listener using springboot and jms locally in websphere liberty server version 19.0.0.6.
I used following steps to make listener work:
download resource adaptor 9.0.0.8-IBM-MQ-Java-InstallRA.jar
Run the jar, and a folder named wmq will be generated with required .rar file in it.
Place the wmq folder inside wlp server : path\to\wlp-webProfile8-19.0.0.6\wlp\usr\servers\defaultServer
if using keystores for ssl , place trust and key store .jks or .p12 file inside wlp server path\to\wlp-webProfile8-19.0.0.6\wlp\usr\servers\defaultServer\resources\security
create a new file name jvm.options with following details:
-Dcom.ibm.mq.cfg.useIBMCipherMappings=false
-Djavax.net.debug="all"
-Djdk.tls.client.protocols="TLSv1.2"
-Dhttps.protocols="TLSv1.2"
-Djavax.net.ssl.trustStore="path\to\keyandtruststore.jks"
-Djavax.net.ssl.trustStorePassword="secret"
-Djavax.net.ssl.keyStore="path\to\keyandtruststore.jks"
-Djavax.net.ssl.keyStorePassword="secret"
save file inside following location : path\to\wlp-webProfile8-19.0.0.6\wlp\usr\servers\defaultServer
now add required dependencies in pom.xml
I am using spring boot version 2.1.4.release and following are important dependecies which will be required:
<dependency>
<groupId>com.ibm.mq</groupId>
<artifactId>mq-jms-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
now change the wlp server.xml as follows:
<!-- Enable features -->
<featureManager>
<feature>webProfile-8.0</feature>
<feature>localConnector-1.0</feature>
<feature>jndi-1.0</feature>
<feature>jaxws-2.2</feature>
<feature>transportSecurity-1.0</feature>
<feature>wmqJmsClient-2.0</feature>
<feature>jsp-2.3</feature>
<feature>servlet-4.0</feature>
<feature>jms-2.0</feature>
<feature>javaee-8.0</feature>
</featureManager>
<!-- Define an Administrator and non-Administrator -->
<basicRegistry id="basic">
<user name="admin" password="adminpwd"/>
<user name="nonadmin" password="nonadminpwd"/>
</basicRegistry>
<!-- Assign 'admin' to Administrator -->
<administrator-role>
<user>admin</user>
</administrator-role>
<!-- Automatically expand WAR files and EAR files -->
<applicationManager autoExpand="true"/>
<applicationMonitor updateTrigger="mbean"/>
<variable name="wmqJmsClient.rar.location" value="path\to\wlp-webProfile8-19.0.0.6\wlp\usr\servers\defaultServer\wmq\wmq.jmsra.rar"/>
<jmsQueueConnectionFactory jndiName="jms/ConnectionFactory" id="QueueConnectionFactory ">
<properties.wmqJms channel="channelName" headerCompression="NONE" hostName="host name" messageCompression="RLE" port="1414" queueManager="qmgrName" sslCipherSuite="TLS_RSA_WITH_AES_256_CBC_SHA256" targetClientMatching="true" transportType="CLIENT" temporaryModel="SYSTEM.DEFAULT.MODEL.QUEUE" pollingInterval="5s" rescanInterval="5s"/>
<connectionManager connectionTimeout="180s" maxPoolSize="20" minPoolSize="1" reapTime="180s" agedTimeout="0" maxIdleTime="30m"/>
</jmsQueueConnectionFactory>
<jmsQueue id="JMSQueue" jndiName="jms/testInQueue">
<properties.wmqJms putAsyncAllowed="DISABLED" readAheadAllowed="ENABLED" receiveConversion="CLIENT_MSG" targetClient="JMS" baseQueueName="queueName" baseQueueManagerName="qmgrName" failIfQuiesce="true" persistence="APP"/>
</jmsQueue>
<!-- <wmqJmsClient nativeLibraryPath="C:\Users\n78724\CRAS\resource adapter\lib"/> -->
<resourceAdapter id="resourceAdapter"
location="${wmqJmsClient.rar.location}">
<customize></customize>
</resourceAdapter>
<ssl id="keyAndTrustStore" keyStoreRef="defaultKeyStore" sslProtocol="TLSv1.2" trustStoreRef="defaultTrustStore"/>
<keyStore id="defaultKeyStore" location="path\to\wlp-webProfile8-19.0.0.6\wlp\usr\servers\defaultServer\resources\security\keyandtruststore.jks" password="secret"/>
<keyStore id="defaultTrustStore" location="path\to\wlp-webProfile8-19.0.0.6\wlp\usr\servers\defaultServer\resources\security\keyandtruststore.jks" password="secret"/>
<enterpriseApplication id="Test-ear" location="Test-ear.ear" name="Test-ear"/>
start the server
hope it helps.

Spring Integration with WebSphere JMS IBM MQ provider

We have a WebSphere JMS Queue and QueueConnectionFactory with provider as IBM MQ. we can not connect to IBM MQ directly.
I have the below configuration - I have bean jmsConnectionFactory that creates factory using InitialContext as expected. THE_QUEUE is JNDI name of my queue
<int-jms:inbound-channel-adapter channel="transformedChannel" connection-factory="jmsConnectionFactory"
destination-name="THE_QUEUE">
<int:poller fixed-delay="500" />
</int-jms:inbound-channel-adapter>
It is failing with error
Caused by: com.ibm.msg.client.jms.DetailedInvalidDestinationException:
JMSWMQ2008: Failed to open MQ queue 'THE_QUEUE'. JMS attempted to
perform an MQOPEN, but WebSphere MQ reported an error. Use the linked
exception to determine the cause of this error. Check that the
specified queue and queue manager are defined correctly.
My outbound channel configuration
<int-jms:outbound-channel-adapter id="respTopic"
connection-factory="jmsConnectionFactory"
destination-name="THE_REPLYQ" channel="process-channel"/>
If I use java code - it works
creating MessageProducer from session.createProducer and send message,
create MessageConsumer on queuesession.createConsumer(outQueue); and receive()
Please van you help, on how can I create jms inbound and outbound adapters for these queues using spring integration and process messages
EDIT:
#Bean
public ConnectionFactory jmsConnectionFactory(){
ConnectionFactory connectionFactory = null ;
Context ctx = null;
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY,"com.ibm.websphere.naming.WsnInitialContextFactory");
p.put(Context.PROVIDER_URL, "iiop://hostname.sl");
p.put("com.ibm.CORBA.ORBInit", "com.ibm.ws.sib.client.ORB");
try {
ctx = new InitialContext(p);
if (null != ctx)
System.out.println("Got naming context");
connectionFactory = (QueueConnectionFactory) ctx.lookup
("BDQCF");
}...
#Bean
public JmsListenerContainerFactory<?> mydbFactory(ConnectionFactory jmsConnectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
configurer.configure(factory, jmsConnectionFactory);
return factory;
}
THe code and configuration works for a queue that uses WebSphere default JMS provider
EDIT2 : Code added after comment
<int:channel id="jmsInputChannel" />
<jee:jndi-lookup id="naarconnectionFactory" jndi-name="MQ_QUEUE" resource-ref="false">
<jee:environment>
java.naming.factory.initial=com.ibm.websphere.naming.WsnInitialContextFactory
java.naming.provider.url=iiop://host.ws
</jee:environment>
</jee:jndi-lookup>
<int-jms:inbound-channel-adapter id="jmsIn" channel="jmsInputChannel"
connection-factory="jmsNAARConnectionFactory" destination-name="naarconnectionFactory">
<int:poller fixed-delay="500" />
</int-jms:inbound-channel-adapter>
You can't just use the JNDI name there - you must perform a JNDI lookup to resolve it to a Destination - see the Spring JMS Documentation.

Receiving JMS messages via Spring integration inbound adapter randomly fails

I am new to this Spring Integration and JMS and i started playing with it. In here i want to create plain jms message via activemq and receive it through spring inbound adapter(message driven).
following is my spring config file
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:stream="http://www.springframework.org/schema/integration/stream"
xmlns:jms="http://www.springframework.org/schema/integration/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/stream
http://www.springframework.org/schema/integration/stream/spring-integration-stream.xsd>
http://www.springframework.org/schema/integration/jms
http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd">
<!-- jms beans -->
<beans:bean id="jms.msgQueue" class="org.apache.activemq.command.ActiveMQQueue">
<beans:constructor-arg value="MSG_QUEUE" />
</beans:bean>
<beans:bean name="jms.connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<beans:property name="brokerURL" value="tcp://localhost:61616" />
</beans:bean>
<!-- spring integration beans -->
<channel id="channels.jms.allMessages">
<queue capacity="1000" />
</channel>
<jms:message-driven-channel-adapter id="adapters.jms.msgAdapter"
connection-factory="jms.connectionFactory"
destination="jms.msgQueue"
channel="channels.jms.allMessages" />
and this is my testing class
package com.bst.jms;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.Message;
import org.springframework.integration.core.PollableChannel;
public class TestActiveMQ {
public static void main(String[] args){
try{
AbstractApplicationContext context = new ClassPathXmlApplicationContext("app-context.xml");
ConnectionFactory connectionFactory = (ConnectionFactory)context.getBean("jms.connectionFactory");
Destination destination = (Destination)context.getBean("jms.msgQueue");
PollableChannel msgChannel = (PollableChannel) context.getBean("channels.jms.allMessages", PollableChannel.class );
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(destination);
TextMessage textMessage = session.createTextMessage();
textMessage.setText("Message from JMS");
producer.send(textMessage);
System.out.println("--------------- Message Sending ------------------------");
Message<?> received = msgChannel.receive();
String payload = (String) received.getPayload();
System.out.println("Receving message = " + payload);
}catch(JMSException ex){
System.out.println("----------- JMS Exception --------------");
}
}
}
But the thing is i can not guarantee the delivery. some times the program can not receive the message and some tomes it succeeds with some warnings like
Setup of JMS message listener invoker failed for destination 'queue://MSG_QUEUE' - trying to recover. Cause: Connection reset
Could not refresh JMS Connection for destination 'queue://MSG_QUEUE' - retrying in 5000 ms. Cause: Could not connect to broker URL: tcp://localhost:61616. Reason: java.net.ConnectException: Connection refused: connect
Could not refresh JMS Connection for destination 'queue://MSG_QUEUE' - retrying in 5000 ms. Cause: Could not connect to broker URL: tcp://localhost:61616. Reason: java.net.ConnectException: Connection refused: connect
This occurs few times before it succeeds.
Do you guys have any idea about this.
appreciate your help.
thanks,
keth
This just means the broker isn't running when the listener container starts. When using a tcp:// URL you should run the broker in it's own context (or another JVM) before creating this context.
I have tested these code in my STS its working fine .
The only problem in your side is , first start message Broker (say ActiveMQ) then run your project, you can get your required output.
Thanks.

Resources