Camel JMS with CLIENT_ACKNOWLEDGE mode not working - jms

I am unable to acknowledge JMS message with CLIENT_ACKNOWLEDGE mode in camel. After digging a into the stack trace I found that message.acknowledge() in AbstractMessageListenerContainer
is always being executed which causes the auto-ack behavior. Am I configured anything wrong?
org.springframework.jms.listener.AbstractMessageListenerContainer.commitIfNecessary(Session, Message)
protected void commitIfNecessary(Session session, Message message) throws JMSException {
// Commit session or acknowledge message.
if (session.getTransacted()) {
// Commit necessary - but avoid commit call within a JTA transaction.
if (isSessionLocallyTransacted(session)) {
// Transacted session created by this container -> commit.
JmsUtils.commitIfNecessary(session);
}
}
else if (message != null && isClientAcknowledge(session)) {
message.acknowledge();
}
}
Spring Configuration
<bean id="jms" class="org.apache.camel.component.jms.JmsComponent">
<property name="connectionFactory" ref="cachedConnectionFactory" />
<property name="asyncConsumer" value="true" />
<property name="acknowledgementModeName" value="CLIENT_ACKNOWLEDGE" />
</bean>
<bean id="cachedConnectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="connectionFactory" />
<property name="sessionCacheSize" value="30" />
</bean>
Camel Route
from(jmsTerminalRequest).routeId("generic-jms-inbound").setExchangePattern(ExchangePattern.InOnly).threads(5, 20, "generic-jms-inbound").bean(clientAckProcessor).to("...")

What you can do is specifying acknowledgement mode within route consuming endpoint definition:
from("...?maxConcurrentConsumers=20&acknowledgementModeName=CLIENT_ACKNOWLEDGE")
.bean(clientAckProcessor)
.to("...")

Related

Spring Integration, jms Inbound gateway for WMQ; Unable to consume messages

I have recently started exploring Spring Integration as that is one of the option we want to evaluate for our project.
The issue i am facing is below.
I have created a JMS inbound gateway to listen to WMQ queue and i am expecting the inboudnd gateway (using DML) should pick up the messages as and when they are available on queue(event - driven).
But some how the example isn't working. It fails to pick messages from the queue. However i can see (using a tool) that there are consumers created on the queue.
Help here is really appreaciated.
Code snippet below.
<bean id="mqFactory" class="com.ibm.mq.jms.MQConnectionFactory">
<property name="hostName" value="${mq.hostName}"/>
<property name="port" value="${mq.port}"/>
<property name="queueManager" value="${mq.queueManager}"/>
<property name="channel" value="${mq.channel}"/>
<property name="transportType" value="${mq.transportType}"/>
<property name="SSLCipherSuite" value="${mq.SSLCipherSuite}"/>
</bean>
<bean id="inCachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="mqFactory" />
<property name="sessionCacheSize" value="5" />
</bean>
<bean id="requestQueue-mq" class="com.ibm.mq.jms.MQQueue">
<constructor-arg value="${mq.example.queue}"/>
</bean>
<bean id="demoBean" class="com.jpmchase.example.spring.DemoBean">
</bean>
<jms:inbound-gateway id="wMQ_in_gateway" concurrent-consumers="2" max-concurrent-consumers="5" connection-factory="inCachingConnectionFactory" request-destination="requestQueue-mq"
request-channel="demoChannel" />
<integration:channel id="demoChannel">
</integration:channel>
<integration:service-activator input-channel="demoChannel" ref="demoBean"/>
Below is the service-activator java code.
enter #MessageEndpoint
public class DemoBean {
#ServiceActivator
public String upperCase(String input) {
System.out.println("inside the service activator " + input);
return "JMS response: " + input.toUpperCase();
}
here

Restrict execution of concurrent queue in Spring JMS

Ideally after one queue completes its execution, another queue should start.
I am using Spring JMS. But at a time more than one queue able to execute by which data mismatch occurs.
So can anyone please tell how to restrict concurrent queue or unless first queue ends the 2nd queue cannot be started or all other queues are in waiting.
public class MyQueueListener implements MessageListener {
public void onMessage(Message message) {
try {
if (message instanceof ObjectMessage) {
ObjectMessage objectMessage = (ObjectMessage) message;
MyQueueObject obj= (MyQueueObject)objectMessage.getObject();
String productId = obj.getProductId();
IMyService myService;
myService.updateAllCustomerDetails(productId);
}
} catch (JMSException j) {
j.printStackTrace();
} catch(Exception e) {
e.printStackTrace();
}
}
}
<bean id="mySenderService" class="MySenderService">
<property name="jmsTemplate" ref="myjmsTemplate" />
<property name="queue" ref="myQueueDestination" />
</bean>
<bean id="myQueueDestination" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>queue/myQueue</value>
</property>
<property name="resourceRef">
<value>true</value>
</property>
</bean>
<bean id="myQueueListener" class="MyQueueListener" ></bean>
<bean id="jmsImportContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="myQueueDestination" />
<property name="messageListener" ref="myQueueListener" />
</bean>
mySenderService.sendMessages();

Spring JMS & Websphere MQ: Message is not picked up by the application

I have an application which uses spring-jms, websphere mq, websphere application server 7.
On a user interaction, I am putting a message in a Queue A. I have a Listener-A(using DefaultMessageListenerContainer in springframework) which picks up the message. That listener-A is supposed to process it and send a response to Queue A-Response. In that process, I am trying to put another message on a different Queue B for which I have a different Listener-B defined. But for some reason, after the message is put on Queue B, the Listener-B is not picking it up.
When I try to put the message manually on the Queue B, Listener-B picks the message and processes it, but when I try the above mentioned scenario, it doesn't work. Also, when I comment out the code that puts a message on Queue B in the Listener-A, then the Listener-A processes the message and sends a response back to Queue A-Response.
Any help is greatly appreciated. Thanks
Edit: Added code
Here is the spring configuration that I have
<bean id="jmsListener-Parent" abstract="true"
class="MyJmsServiceExporter">
<property name="messageTimeToLive" value="60000" />
</bean>
<bean id="transactionManager" class="org.springframework.transaction.jta.WebSphereUowTransactionManager" />
<bean id="taskExecutor" class="org.springframework.scheduling.commonj.WorkManagerTaskExecutor">
<property name="workManagerName" value="wm/default" />
</bean>
<bean name="jmsListenerContainer-Parent"
abstract="true"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="concurrentConsumers" value="1"/>
<property name="transactionManager" ref="transactionManager" />
<property name="taskExecutor" ref="taskExecutor" />
</bean>
<bean id="FirstService-Listener" parent="jmsListener-Parent">
<property name="serviceInterface" value="FirstService" />
<property name="service" ref="FirstServiceImpl" />
<property name="messageConverter" ref="MyMessageConverter" />
</bean>
<bean id="SecondService-Listener" parent="jmsListener-Parent">
<property name="serviceInterface" value="SecondService" />
<property name="service" ref="SecondServiceImpl" />
<property name="messageConverter" ref="MyMessageConverter" />
</bean>
<bean name="FirstService-ListenerContainer"
parent="jmsListenerContainer-Parent">
<property name="destination" ref="FirstQueue-Request" />
<property name="messageListener" ref="FirstService-Listener" />
</bean>
<bean name="SecondService-ListenerContainer"
parent="jmsListenerContainer-Parent">
<property name="destination" ref="SecondQueue-Request" />
<property name="messageListener" ref="SecondService-Listener" />
</bean>
<bean id="FirstService-Client"
class="MyJmsServiceInvoker">
<property name="connectionFactory" ref="connectionFactory" />
<property name="serviceInterface" value="FirstService" />
<property name="messageConverter" ref="MyMessageConverter" />
<property name="queue" ref="FirstQueue-Request" />
<property name="responseQueue" ref="FirstQueue-Response" />
<property name="timeToLive" value="60000" />
<property name="receiveTimeout" value="60000" />
</bean>
<bean id="SecondService-Client"
class="MyJmsServiceInvoker">
<property name="connectionFactory" ref="connectionFactory" />
<property name="serviceInterface" value="SecondService" />
<property name="messageConverter" ref="MyMessageConverter" />
<property name="queue" ref="SecondQueue-Request" />
<property name="responseQueue" ref="SecondQueue-Response" />
<property name="timeToLive" value="60000" />
<property name="receiveTimeout" value="60000" />
</bean>
MyJmsServiceInvoker(extends JmsInvokerProxyFactoryBean) methods:
protected Message doExecuteRequest(
Session session,
Queue queue,
Message requestMessage)
throws JMSException {
MessageProducer producer = null;
MessageConsumer consumer = null;
Message responseMessage = null;
String correlationId = null;
String responseSelector = null;
try {
LOG.info("CmsJmsServiceInvoker::doExecuteRequest");
requestMessage.setJMSType("TEXT");
requestMessage.setJMSReplyTo(responseQueue);
requestMessage.setJMSExpiration(this.getTimeToLive());
producer = session.createProducer(queue);
if (isPersistentMessage()) {
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
} else {
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
}
producer.setTimeToLive(getTimeToLive());
LOG.info("Sending requestMessage.");
producer.send(requestMessage);
correlationId = requestMessage.getJMSMessageID();
responseSelector =
"JMSCorrelationID=\'" + correlationId + "\'";
consumer = session.createConsumer(responseQueue, responseSelector);
long timeout = getReceiveTimeout();
LOG.info("Awaiting response.");
if (timeout > 0) {
responseMessage = consumer.receive(timeout);
} else {
responseMessage = consumer.receive();
}
if (responseMessage == null) {
LOG.info("Timeout encountered.");
throw new RuntimeException("Timeout");
}
} catch (JMSSecurityException jse) {
LOG.error("SecurityException encountered.", jse);
throw new RuntimeException("JMS SecurityException, jse");
} finally {
JmsUtils.closeMessageConsumer(consumer);
JmsUtils.closeMessageProducer(producer);
}
LOG.info("Returning response Message.");
return responseMessage;
}
MyJmsServiceExporter(extends JmsInvokerServiceExporter) methods:
protected void writeRemoteInvocationResult(Message requestMessage,
Session session,
RemoteInvocationResult result)
throws JMSException {
MessageProducer producer = null;
Message response = null;
if (requestMessage.getJMSReplyTo() == null) {
LOG.debug("Async: This request will not have a "
+ "response since there is no reply queue in the "
+ "JMS header.");
return;
}
producer = session.createProducer(requestMessage.getJMSReplyTo());
try {
response = createResponseMessage(requestMessage, session,
result);
producer.setTimeToLive(getMessageTimeToLive());
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
if (persistentMessage) {
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
}
producer.send(response);
if (LOG.isDebugEnabled()) {
LOG.debug("Sending response message.");
LOG.debug(response);
}
} finally {
if (producer != null) {
JmsUtils.closeMessageProducer(producer);
}
}
}
protected Message createResponseMessage(
Message request,
Session session,
RemoteInvocationResult result)
throws JMSException {
Message response = null;
String correlation = null;
correlation = request.getJMSCorrelationID();
response = super.createResponseMessage(request, session, result);
if (correlation == null) {
correlation = request.getJMSMessageID();
}
response.setJMSCorrelationID(correlation);
response.setJMSExpiration(getMessageTimeToLive());
return response;
}
public void onMessage(Message requestMessage, Session session)
throws JMSException {
RemoteInvocationResult result = null;
try {
RemoteInvocation invocation = readRemoteInvocation(requestMessage);
if (invocation != null) {
result =
invokeAndCreateResult(invocation, getProxyForService());
}
} catch (Throwable throwable) {
if (result == null) {
result = new RemoteInvocationResult(throwable);
}
throwable.printStackTrace();
} finally {
writeRemoteInvocationResult(requestMessage, session, result);
// JmsUtils.commitIfNecessary(session);
}
}
FirstServiceImpl class has a saveText method which is invoked when a message is put in FirstQueue-Request. In that method, I am trying to call SecondService method: validateText(textmessage). This message is put on SecondQueue-Request but never read.
You didn't commit session after message send.
It seems to me that you are trying to implement request-reply-by-correlationid logic while at same time working with transacted resources and manual session handling; all within same method (doExecuteRequest() or writeRemoteInvocationResult()). That's a lot of stuff for 20 lines of code. This kind of code is usually used with temporary queues..
Do you really need to write code on session level? Is there a reason why you couldn't just use jmsTemplate to send message, and messageListenerContainer to receive reply?

How to inject a message selector to message listener bean in jms-spring integration?

I'm working with JMS API (with HornetQ) and i'm using spring beans for message listener container and message listener:
<bean id="messageListener" class="core.messaging.handler.MessageListener">
<property name="postCommandService" ref="postCommandService" />
</bean>
<bean id="messageSender"
class="lsn.messaging.sender.MessageSender">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="destination" />
</bean>
<bean id="msgListenerContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer"
p:connectionFactory-ref="connectionFactory"
p:destination-ref="destination"
p:messageListener-ref="messageListener"
p:concurrentConsumers="10"
p:maxConcurrentConsumers="50"
p:receiveTimeout="5000"
p:idleTaskExecutionLimit="10"
p:idleConsumerLimit="5" />
If i want my message listener, only consume specific messages (that have same StringProperty) what should i do? Where should i define selector?
I have below solution, but i don't have MessageConsumer and so i can't add selector to it:
String redSelector = "color='red'";
MessageConsumer redConsumer = session.createConsumer(queue, redSelector);
redConsumer.setMessageListener(new SimpleMessageListener("red"));
TextMessage redMessage = session.createTextMessage("Red");
redMessage.setStringProperty("color", "red");
producer.send(redMessage);
You should be able to add it to the MessageListenerContainer this way:
p:messageSelector="color='red'"

Spring-JMS(Websphere MQ)

I have the below configurations in Spring , it is working fine but performance is too low (it takes 1 min for 20 messages). Can you please suggest me changes to increase the performance.
<bean id="jmsConnectionFactory" class="com.ibm.mq.jms.MQQueueConnectionFactory">
<property name="transportType"><value>1</value></property>
<property name="queueManager"><value></value></property>
<property name="hostName"><value></value></property>
<property name="port"><value></value></property>
<property name="channel"><value></value></property>
<property name="clientId"><value></value></property>
</bean>
<bean id="SenderJMSTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory"><ref bean="jmsConnectionFactory" /> </property>
<property name="pubSubDomain"><value>false</value></property>
<property name="defaultDestination"><ref bean="senderQueue" /></property>
</bean>
<bean id="senderQueue" class="com.ibm.mq.jms.MQQueue">
<constructor-arg value="TEST" />
<property name="baseQueueManagerName"><value>tree.queue.manager</value></property>
<property name="baseQueueName"><value>ORANGE.QUEUE</value></property>
</bean>
<bean id="jmsSender" class="org.tree.jms.spring.JMSSender">
<property name="jmsTemplate"><ref bean="SenderJMSTemplate"/></property>
</bean>
I am calling from spring as
JMSSender obj = (JMSSender) context.getBean("jmsSender");
And My Sender program is :
#Cacheable("message")
public void sendMesage() {
jmsTemplate.send(new MessageCreator() {
public Message createMessage(Session session)throws JMSException {
message = (Message) session.createTextMessage(stringBuffer.toString());
return message;
}
});
}
}
A common problem when using JMSTemplate to send messages out of JavaEE containers is the it's extremly slow since it acquires a new connection for each message (and then closes it). You would probably need a pooled/cached connection to gain speed here.
Read this article, it's written for ActiveMQ, but applies in a similar way to WebSphere MQ: http://activemq.apache.org/jmstemplate-gotchas.html
You can setup a cached connection factory in spring using something like this:
<bean id="cachedConnectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory"
p:targetConnectionFactory-ref="jmsConnectionFactory"
p:sessionCacheSize="10" />
Then use it instead of the original one for JMS connections.

Resources