IBM MQ call failed with compcode '2' ('MQCC_FAILED') reason '2027' ('MQRC_MISSING_REPLY_TO_Q') Exception With Spring Boot - spring

I have a spring boot application which uses JMS template to communicate with queue services.
Here is my producer definition:
<bean id="jmsConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory">
<bean class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter">
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
<property name="targetConnectionFactory">
<bean class="com.ibm.mq.jms.MQQueueConnectionFactory">
<property name="transportType" value="1"/>
<property name="queueManager" value="${queueManager}"/>
<property name="hostName" value="${connName}"/>
<property name="port" value="${connPort}" />
<property name="channel" value="${channel}"/>
</bean>
</property>
</bean>
</property>
<property name="sessionCacheSize" value="10"/>
<property name="cacheConsumers" value="false"/>
</bean>
<bean id="jmsTemplate" class="org.springframework.integration.jms.DynamicJmsTemplate">
<property name="connectionFactory" ref="jmsConnectionFactory"/>
<property name="messageConverter">
<bean class="org.springframework.jms.support.converter.MappingJackson2MessageConverter"></bean>
</property>
</bean>
<!-- JMS outbound gateway -->
<int-jms:outbound-gateway
id="myJmsOutboundGateway"
connection-factory="jmsConnectionFactory"
request-destination-expression="headers.request_destination_queue"
request-channel="jmsOutboundRequestChannel"
reply-destination-expression="headers.reply_destination_queue"
reply-channel="jmsOutboundReplyChannel"
explicit-qos-enabled="true"/>
Here is consumer definition which stays in other project to listen queue:
<!-- Transation flow start -->
<bean id="inboundQueue" class="com.ibm.mq.jms.MQQueue">
<constructor-arg index="0" value="${queueManager}"/>
<constructor-arg index="1" value="#{'inQueue'}"/>
<property name="targetClient" value="1" />
</bean>
<int:channel id="jmsOutboundChannel" />
<int-jms:outbound-channel-adapter
id="acquirerOutboundAdapter"
destination-name="destinationQueue"
jms-template="jmsTemplate"
channel="jmsOutboundChannel"/>
When I run the project and try to send message over IBM MQ, I get this exception:
JMS attempted to perform an MQPUT or MQPUT1; however IBM MQ reported an error.
Use the linked exception to determine the cause of this error.; nested exception is com.ibm.mq.MQException: JMSCMQ0001: IBM MQ call failed with compcode '2' ('MQCC_FAILED') reason '2027' ('MQRC_MISSING_REPLY_TO_Q').
at org.springframework.jms.support.JmsUtils.convertJmsAccessException(JmsUtils.java:274)
at org.springframework.jms.support.JmsAccessor.convertJmsAccessException(JmsAccessor.java:185)
at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:507)
at org.springframework.jms.core.JmsTemplate.send(JmsTemplate.java:584)
at org.springframework.jms.core.JmsTemplate.convertAndSend(JmsTemplate.java:691)
at
When we checked headers in message in debug mode, we saw that header which told as missing stays in message headers:
jms_destination=queue:///destinationQueue
jms_timestamp=1650004155460
JMS_IBM_PutApplType=28
JMS_IBM_Format=
id=3d22ed9e-d665-8c9a-7995-bb1a2b0c36eb
acquirerId=404045
jms_messageId=ID:414d5120514d3120202020202020202079ca536203fb1940
JMS_IBM_MsgType=1
JMSXUserID=mqm
priority=4
acq_rsp_ts=1650004162979
ReplyToQ=destinationQueue
JMS_IBM_Character_Set=UTF-8
JMS_IBM_Encoding=273
reply_destination_queue=destionationQueue
jms_replyTo=queue://QM1/destionationQueue
startTime=1650004162965
JMS_IBM_PutTime=06291556
request_destination_queue=inQueue
JMSXAppID=core.SoftPOSApplication
jms_redelivered=false
JMS_IBM_PutDate=20220415
jms_correlationId=210595709001
ReplyToQMgr=destinationQueue
Why are we facing this problem although header stays in headers? How can we solve this problem?

If the message type has been set to one of "Request", MQ will check for the presence of a reply to queue in the MQ header; obviously you would want the reply to queue to be present on the queue manager but I don't believe the reply to queue actually has to exist at the time the application puts the message for the put to succeed. Here is a snippet of JMS code that would demonstrate that:
Destination sendTo = session.createQueue("IN.Q");
Destination replyTo = session.createQueue("IN.Q.REPLY");
MessageProducer producer = session.createProducer(sendTo);
Message msg = session.createTextMessage("test");
msg.setIntProperty("JMS_IBM_MsgType",com.ibm.mq.constants.MQConstants.MQMT_REQUEST);
// Uncomment the line below to prevent a '2027' ('MQRC_MISSING_REPLY_TO_Q') error
// msg.setJMSReplyTo(replyTo);
producer.send(msg);
I appreciate this isn't a complete answer but hopefully it will help you understand what is happening and focus on debugging the Spring configuration.

Related

How to configure Delegating Session Factory in spring sftp inbound channel adaptor

We want to delegate the session-factory at run time to spring sftp inbound channel adapter. For that we have done the below configuration.
We have gone through the spring-sftp integration docs but we are not sure how to set the session-factory attribute value via java. Could you please suggest us how to delegate the session-factory run time in spring sftp inbound channel adapter using Delegating Session Factory.
XML Configuration
<beans>
<bean id="defaultSftpSessionFactoryOne" class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
<property name="host" value="**.***.**.***" />
<property name="port" value="**" />
<property name="user" value="######" />
<property name="password" value="######" />
<property name="allowUnknownKeys" value="true" />
</bean>
<bean id="defaultSftpSessionFactoryTwo" class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
<property name="host" value="**.***.**.***" />
<property name="port" value="**" />
<property name="user" value="######" />
<property name="password" value="######" />
<property name="allowUnknownKeys" value="true" />
</bean>
<bean id="delegatingSessionFactory" class="org.springframework.integration.file.remote.session.DelegatingSessionFactory">
<constructor-arg>
<bean id="factoryLocator"
class="org.springframework.integration.file.remote.session.DefaultSessionFactoryLocator">
<constructor-arg name="factories">
<map>
<entry key="one" value-ref="defaultSftpSessionFactoryOne"></entry>
<entry key="two" value-ref="defaultSftpSessionFactoryTwo"></entry>
</map>
</constructor-arg>
</bean>
</constructor-arg>
</bean>
<int:channel id="receiveChannel" />
<int-sftp:inbound-channel-adapter id="sftpInbondAdapter" auto-startup="false"
channel="receiveChannel" session-factory="delegatingSessionFactory"
local-directory="C:\\Users\\sftp" remote-directory="/tmp/archive"
auto-create-local-directory="true" delete-remote-files="false"
filename-regex=".*\.txt$">
<int:poller cron="0/10 * * * * ?">
</int:poller>
</int-sftp:inbound-channel-adapter>
java code
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
DelegatingSessionFactory<String> dsf = (DelegatingSessionFactory<String>) ac.getBean("delegatingSessionFactory");
SessionFactory<String> one = dsf.getFactoryLocator().getSessionFactory("one");
SessionFactory<String> two = dsf.getFactoryLocator().getSessionFactory("two");
dsf.setThreadKey("two");
SourcePollingChannelAdapter spca = (SourcePollingChannelAdapter) ac.getBean("sftpInbondAdapter");
spca.start();
The delegating session factory was really intended for outbound adapters and gateways. Typically, inbound adapters don't switch to different servers.
Setting the thread key on the main thread like that does nothing.
You need to set/clear the key on the thread that invokes the adapter; this is shown for outbound adapters in the documentation.
For inbound adapters you need to do it on the poller thread.
I don't know what criteria you will use to select the factory, but you can use a smart poller to do it.

Spring Integration JMS Issue

We are using Spring Integration to consume messages from a queue and the requirement is to send the message to Error queue if there is any issue in the processing of the message consumed.
Flow works fine but one issue we see is when there is any exception thrown in processing of message, the message is redirected to the Error queue which we have configured but it is appended by the entire stack trace of the exception.
We are looking only for the original message to be delivered to the queue. Below is the configuration we have done,
<bean id="errorQ" class="com.ibm.mq.jms.MQQueue">
<constructor-arg value="${error.queue}" />
</bean>
<bean id="inQ" class="com.ibm.mq.jms.MQQueue">
<constructor-arg value="${inbound.queue}" />
</bean>
<int:channel id="error" />
<int:channel id="inbound" />
<int-jms:message-driven-channel-adapter
id="jmsIn" channel="inbound" container="messageListenerContainer"
acknowledge="transacted"></int-jms:message-driven-channel-adapter>
<int-jms:outbound-channel-adapter id="jmsError"
channel="error" connection-factory="mqConnectionFactory"
destination="errorQ" delivery-persistent="true"></int-jms:outbound-channel-adapter>
<int:service-activator id="service"
input-channel="inbound" ref="messageListener" method="someMethodInListerner">
<int:request-handler-advice-chain>
<ref bean="retryWithBackoffAdviceSession" />
</int:request-handler-advice-chain>
<bean id="retryWithBackoffAdviceSession"
class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice">
<property name="retryTemplate">
<bean class="org.springframework.retry.support.RetryTemplate">
<property name="backOffPolicy">
<bean class="org.springframework.retry.backoff.ExponentialBackOffPolicy">
<property name="initialInterval" value="${initialInterval}" />
<property name="multiplier" value="${multiplier}" />
<property name="maxInterval" value="${maxInterval}" />
</bean>
</property>
<property name="retryPolicy">
<bean class="org.springframework.retry.policy.SimpleRetryPolicy">
<property name="maxAttempts" value="${redelivery}" />
</bean>
</property>
</bean>
</property>
<property name="recoveryCallback">
<bean
class="org.springframework.integration.handler.advice.ErrorMessageSendingRecoverer">
<constructor-arg ref="error" />
</bean>
</property>
</bean>
The message sent to the error is an ErrorMessage with payload MessagingException.
The MessagingException has two properties cause and failedMessage.
So, if you want to send the original failed message to errorQ, you will need to add a transformer to the error flow...
<int:transformer ... expression="payload.failedMessage" />
EDIT
<int:chain input-channel="error">
<int:transformer expression="payload.failedMessage" />
<int-jms:outbound-channel-adapter ... />
</int:chain>
EDIT2
Generally, when using these techniques, it's useful to convey the reason for the failure. You can add the exception's message as a header...
<int:chain input-channel="error">
<int:header-enricher>
<int:header name="failureReason" expression="payload.cause.message" />
</int:header-enricher>
<int:transformer expression="payload.failedMessage" />
<int-jms:outbound-channel-adapter ... />
</int:chain>

Managing JMS transaction with Spring

I'm trying to manage JMS transaction with Spring and HornetQ.
This is the code I wrote:
public void receive() {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
Message msg = jmsTemplate.receive(queue);
boolean success = false;
if (msg != null) {
try {
success = handleMessage(msg);
if (success) {
msg.acknowledge(); // session is still open within the transaction
}
} catch (JMSException e) {
transactionManager.rollback(status);
}
if (success)
transactionManager.commit(status);
else
transactionManager.rollback(status):
}
}
I'm doing a synchronous read from the queue, with timeout set to 0, since I don't wanna block on the read. Because of this I have to check if something was actually received.
This is an excerpt of my applicationContext.xml:
<bean id="inVMConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:/ConnectionFactory</value>
</property>
</bean>
<bean id="cachedConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="inVMConnectionFactory" />
</bean>
<bean id="producer" class="it.ubiquity.gestoreprofilazione.onweb.OnWebProducer" scope="singleton">
<property name="queue" ref="retryQueue" />
<property name="connectionFactory" ref="cachedConnectionFactory" />
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="cachedConnectionFactory" />
<property name="sessionTransacted" value="true" />
<property name="sessionAcknowledgeMode" value="#{T(javax.jms.Session).CLIENT_ACKNOWLEDGE}" />
<property name="pubSubDomain" value="false" />
<property name="receiveTimeout" value="# {T(org.springframework.jms.core.JmsTemplate).RECEIVE_TIMEOUT_NO_WAIT}" />
</bean>
<bean id="jmsTransactionManager" class="org.springframework.jms.connection.JmsTransactionManager">
<property name="connectionFactory" ref="cachedConnectionFactory" />
</bean>
<bean id="consumer" class="it.ubiquity.gestoreprofilazione.onweb.OnWebConsumer" scope="singleton">
<property name="queue" ref="retryQueue" />
<property name="jmsTemplate" ref="jmsTemplate" />
<property name="transactionManager" ref="jmsTransactionManager" />
</bean>
The problem I have is quite strange: the first time I receive a message, handleMessage fail, so I rollback the transaction. Then nothing more happens. If I check with the JMX console, I can see there's one message on the queue. Now, if I restart JBoss, the messages is receive again and again, as expected.
Maybe there's something wrong with my configuration, but why it works after a reboot?
HornetQ 2.2.10
JBoss 5.1.0
Spring 3.1.2
UPDATE
With debugging enabled I see the first time:
DEBUG [org.springframework.jms.connection.JmsTransactionManager]
(baseScheduler-1) Creating new transaction with name [null]:
PROPAGATION_REQUIRED,ISOLATION_DEFAULT
and after the first rollback, on subsequent receive and rollback I see:
DEBUG [org.springframework.jms.connection.JmsTransactionManager]
(baseScheduler-1) Participating in existing transaction
Instead, after restarting JBoss, I read that the transaction is actually rolled back:
DEBUG [org.springframework.jms.connection.JmsTransactionManager]
(baseScheduler-1) Initiating transaction rollback 2012-11-05
09:54:14,436 DEBUG
[org.springframework.jms.connection.JmsTransactionManager]
(baseScheduler-1) Rolling back JMS transaction on Session
So, why the rollback it's not happening the first time, and as soon I restart the server it happens all the time? What am I doing wrong?
Ok, finally I've managed to understand my mistake :)
In the outer if's else I should have committed the transaction. That's why the mechanism works after restarting JBoss: there is no transaction hanging on.

ActiveMQ with more than one Listener

I am using it with spring. Now I have the following setting:
<!--异步调用消息 -->
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="cacheConnectionFactory"></property>
<!-- <property name="destination" ref="ptpQueue"></property> -->
<property name="destination" ref="topicQueue"></property>
<property name="messageListener" ref="objHelper"></property>
<property name="sessionAcknowledgeModeName" value="AUTO_ACKNOWLEDGE"/>
</bean>
My messageListener only listens ref="objHelper" but now I want it to listen on both ref="objHelper" and ref="bexHelper" !
Both my objHelper and bexHelper have implemented MessageListener and have a method onMessage(){………… } but I have no idea how to do this.
The Spring MessageListenerContainer can only be configured with exactly one message listener.
So this is not possible.
Why do you need to use 2 message listeners on the same message listener container?

Twice dequeued JMS messages using ActiveMq and Atomikos

I use ActiveMq as JMS server and Atomikos as transaction manager.
On ActiveMq Admin web-interface I see that one message was enqueued, but 2 (!) messages were dequeued.
However, jms consumer process message only once, there is no duplication in processing. When I use simple Spring JmsTransactionManager, there are one enqueued and one dequeued messages. The problem appeares only for Atomikos JTA transaction manager.
What is the problem? How to configure Atomikos not to see twice dequeued messages?
My configuration is below. It almost the same as in tutorial. BTW, Atomikos's spring integration example works well but it uses Spring 2.0 whereas I use Spring 3.1.
<bean id="activeMqXaConnectionFactory" class="org.apache.activemq.spring.ActiveMQXAConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616"/>
</bean>
<bean id="atomikosQueueConnectionFactory" class="com.atomikos.jms.QueueConnectionFactoryBean" init-method="init">
<property name="resourceName" value="xaMq"/>
<property name="xaQueueConnectionFactory" ref="activeMqXaConnectionFactory"/>
</bean>
<bean id="singleConnectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory" ref="atomikosQueueConnectionFactory"/>
</bean>
<bean id="jmsDefaultContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="singleConnectionFactory"/>
<property name="messageListener" ref="consumer"/>
<property name="concurrentConsumers" value="1"/>
<property name="destinationName" value="q.jtaxa"/>
<property name="receiveTimeout" value="3000"/>
<property name="recoveryInterval" value="5000"/>
<property name="transactionManager" ref="transactionManager"/>
<property name="sessionTransacted" value="true"/>
</bean>
<!-- Transactions -->
<!--Construct Atomikos UserTransactionManager, needed to configure Spring-->
<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init"
destroy-method="close">
<!-- when close is called, should we force transactions to terminate or not? -->
<property name="forceShutdown" value="true"/>
</bean>
<!-- Also use Atomikos UserTransactionImp, needed to configure Spring -->
<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
<property name="transactionTimeout" value="300"/>
</bean>
<!-- Configure the Spring framework to use JTA transactions from Atomikos -->
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="atomikosTransactionManager"/>
<property name="userTransaction" ref="atomikosUserTransaction"/>
<property name="nestedTransactionAllowed" value="true"/>
</bean>
Consumer class is:
#Component
public class Consumer implements MessageListener {
#Override
public void onMessage(Message message) {
if (message instanceof TextMessage) {
try {
TextMessage textMsg = (TextMessage) message;
System.out.println(" " + textMsg.getText());
} catch (JMSException ex) {
ex.printStackTrace();
}
}
}
}
I found what was wrongly configured. It was "atomikosQueueConnectionFactory". I did accroding to the tutorial, but it should be just AtomikosConnectionFactoryBean class not QueueConnectionFactoryBean.
I deleted atomikosQueueConnectionFactory and added atomikosConnectionFactory
<bean id="atomikosConnectionFactory" class="com.atomikos.jms.AtomikosConnectionFactoryBean" init-method="init">
<property name="uniqueResourceName" value="amq1"/>
<property name="xaConnectionFactory" ref="mqXaConnectionFactory"/>
</bean>
It works fine after it.
I found proper configuration here.

Resources