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?
Related
I have a simple Spring application for JMS Producer/Subscriber using ActiveMQ with below configuration :
Application Context xml :
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616" />
<property name="userName" value="user" />
<property name="password" value="password" />
</bean>
<bean id="messageDestination" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg value="messageQueue1" />
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<property name="sessionAcknowledgeModeName" value="CLIENT_ACKNOWLEDGE">
</property>
</bean>
<bean id="springJmsProducer" class="SpringJmsProducer">
<property name="destination" ref="messageDestination" />
<property name="jmsTemplate" ref="jmsTemplate" />
</bean>
<bean id="springJmsConsumer" class="SpringJmsConsumer">
<property name="destination" ref="messageDestination" />
<property name="jmsTemplate" ref="jmsTemplate" />
</bean>
and Below is Spring producer
public class SpringJmsProducer {
private JmsTemplate jmsTemplate;
private Destination destination;
public JmsTemplate getJmsTemplate() {
return jmsTemplate;
}
public void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
public Destination getDestination() {
return destination;
}
public void setDestination(Destination destination) {
this.destination = destination;
}
public void sendMessage(final String msg) {
jmsTemplate.send(destination, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(msg);
}});
}
}
below is Spring Consumer:
public class SpringJmsConsumer {
private JmsTemplate jmsTemplate;
private Destination destination;
public JmsTemplate getJmsTemplate() {
return jmsTemplate;
}
public void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
public Destination getDestination() {
return destination;
}
public void setDestination(Destination destination) {
this.destination = destination;
}
public String receiveMessage() throws JMSException {
TextMessage textMessage =(TextMessage) jmsTemplate.receive(destination);
return textMessage.getText();
}
}
Issue : When i start producer and post messages, and then i start consumer, Consumer is not reading old messages but only reading messages posted after consumer was started. Could anyone please help me how to make this durable subscriber so that messages in queue which are not acknowledged should be read by consumer and also i need to implement Synchronous Consumer not Asynchronous.
I have tried all possible solution but none is working. Any help is highly appreciated
if you want consumer receive messages sent to the topic before he starts you have 2 choice :
1. Use Activemq Retroactive Consumer
Background A retroactive consumer is just a regular JMS Topic consumer
who indicates that at the start of a subscription every attempt should
be used to go back in time and send any old messages (or the last
message sent on that topic) that the consumer may have missed.
See the Subscription Recovery Policy for more detail.
You mark a Consumer as being retroactive as follows:
topic = new ActiveMQTopic("TEST.Topic?consumer.retroactive=true");
http://activemq.apache.org/retroactive-consumer.html
2. Use Durable Subscribers :
Note that the Durable Subscriber receive messages sent to the topic before he starts at the 2nd run
http://activemq.apache.org/manage-durable-subscribers.html
This is possible with DefaultMessageListenerContainer Asynchronously
<bean id="jmsContainer" destroy-method="shutdown"
class="org.springframework.jms.listener.DefaultMessageListenerContainer" >
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="messageDestination" />
<property name="messageListener" ref="messageListenerAdapter" />
<property name="sessionAcknowledgeModeName" value="CLIENT_ACKNOWLEDGE" />
<property name="subscriptionDurable" value="true" />
<property name="clientId" value="UniqueClientId" />
</bean>
<bean id="messageListenerAdapter"
class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<constructor-arg ref="springJmsConsumer" />
</bean>
<bean id="springJmsConsumer" class="SpringJmsConsumer">
</bean>
AND Update your consumer :
public class SpringJmsConsumer implements javax.jms.MessageListener {
public void onMessage(javax.jms.Message message) {
// treat message;
message.acknowledge();
}
}
UPDATE to use
if you want a Synchronous Durable Subscriber, an example
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicSubscriber;
public class SpringJmsConsumer {
private Connection conn;
private TopicSubscriber topicSubscriber;
public SpringJmsConsumer(ConnectionFactory connectionFactory, Topic destination ) {
conn = connectionFactory.createConnection("user", "password");
Session session = conn.createSession(false, Session.CLIENT_ACKNOWLEDGE);
topicSubscriber = session.createDurableSubscriber(destination, "UniqueClientId");
conn.start();
}
public String receiveMessage() throws JMSException {
TextMessage textMessage = (TextMessage) topicSubscriber.receive();
return textMessage.getText();
}
}
And update springJmsConsumer
<bean id="springJmsConsumer" class="SpringJmsConsumer">
<constructor-arg ref="connectionFactory" />
<constructor-arg ref="messageDestination" />
</bean>
Note that connection failures are not managed by this code.
I want to create a sender to generate message and the send it to all consumers.
I am using topic, but something is wrong, if for example I have 3 consumers, only one takes the message in a random way.
I donĀ“t know what is wrog. Here is my server configuration
<amq:broker brokerName="granicaBroker" id="broker"
persistent="false" deleteAllMessagesOnStartup="true" enableStatistics="false"
useLoggingForShutdownErrors="true">
<amq:networkConnectors>
<amq:networkConnector name="linkToBrokerB"
uri="static:(tcp://xxx.xx.xxx.xx:61617)" networkTTL="3" duplex="true" />
</amq:networkConnectors>
<amq:transportConnectors>
<amq:transportConnector
uri="nio://xxx.xx.xxx.xx:61616?jms.useAsyncSend=true?jms.useCompression=true"
disableAsyncDispatch="false" />
</amq:transportConnectors>
</amq:broker>
<bean id="destination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="JMS.TOPIC.NOTIFICATION" />
</bean>
<bean id="producerTemplate" class="org.springframework.jms.core.JmsTemplate"
p:connectionFactory-ref="connectionFactory"
p:defaultDestination-ref="destination" />
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"
p:brokerURL="nio://xxx.xx.xxx.xx:61616" />
And my producer class(just the part to send the message)
#Autowired
protected JmsTemplate jmsTemplate;
final String text = applicationEvent.getMsg();
jmsTemplate.send(new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
TextMessage message = session.createTextMessage(text);
return message;
}
});
My client context configuration:
p:brokerURL="nio://xxx.xx.xxx.xx:61616" />
<bean id="simpleMessageListener" class="notifications.NotifierControllerImpl"/>
<jms:listener-container container-type="default"
connection-factory="connectionFactory" acknowledge="auto">
<jms:listener destination="JMS.TOPIC.NOTIFICATION" ref="simpleMessageListener"
method="onMessage" />
</jms:listener-container>
And the java client class
public class NotifierControllerImpl implements MessageListener{
#Override
public void onMessage(Message message) {
try {
if (message instanceof TextMessage) {
TextMessage tm = (TextMessage)message;
System.out.println(tm.getText());
}
} catch (JMSException e) {
System.out.println(e.toString());
}
}
}
The destination needs to be a topic not a queue; use ActiveMQTopic not ActiveMQQueue.
I change jms:listener-container part
Here is the code:
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="simpleMessageListener" />
</bean>
And it works!
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("...")
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();
The following code in my DAO works perfectly fine.
public void insert(final Person person) {
transactionTemplate.execute(new TransactionCallback<Void>() {
public Void doInTransaction(TransactionStatus txStatus) {
try {
getJdbcTemplate().execute("insert into person(username, password) values ('" + person.getUsername() + "','" + person.getPassword() + "')");
} catch (RuntimeException e) {
txStatus.setRollbackOnly();
throw e;
}
return null;
}
});
}
Following is my spring config.
<bean id="derbyds" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="org.apache.derby.jdbc.EmbeddedDriver" />
<property name="username" value="app" />
<property name="password" value="app" />
<property name="url" value="jdbc:derby:mytempdb" />
</bean>
<bean id="persondaojdbc" class="com.napp.dao.impl.PersonDaoJdbcImpl">
<property name="dataSource" ref="derbyds" />
<property name="transactionTemplate">
<bean class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager" />
</bean>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="derbyds"/>
</bean>
What i wanted to know is how does the
<bean id="persondaojdbc" class="com.napp.dao.impl.PersonDaoJdbcImpl">
<property name="dataSource" ref="derbyds" />
<property name="transactionTemplate">
<bean class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager" />
</bean>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="derbyds"/>
</bean>
Now, its imperative that both the TransactionManager and the code (in my case jdbc template) operate on the same connection. I am assuming both of them are getting the Connection objects from the DataSource. DataSource pools connections and its a chance when you call getConnection multiple times, you will get different Connection obejcts. How does spring make sure that the TransactionManager and JdbcTemplate end up getting the same connection objects. My understanding is that, if that doesn't happen, rollbacks, or commits wont work, correct? Could someone throw more light on this.
If you look at the code for JdbcTemplate (one of the execute(...) methods) you will see
Connection con = DataSourceUtils.getConnection(getDataSource());
Which tries to retrieve a Connection from a ConnectionHolder registered with a TransactionSynchronizationManager.
If there is no such object, it just gets a connection from the DataSource and registers it (if it is in a transactional environment, ie. you have a transaction manager). Otherwise, it immediately returns the registered object.
This is the code (stripped of logs and stuff)
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
conHolder.requested();
if (!conHolder.hasConnection()) {
conHolder.setConnection(dataSource.getConnection());
}
return conHolder.getConnection();
}
// Else we either got no holder or an empty thread-bound holder here.
Connection con = dataSource.getConnection();
// flag set by the TransactionManager
if (TransactionSynchronizationManager.isSynchronizationActive()) {
// Use same Connection for further JDBC actions within the transaction.
// Thread-bound object will get removed by synchronization at transaction completion.
ConnectionHolder holderToUse = conHolder;
if (holderToUse == null) {
holderToUse = new ConnectionHolder(con);
}
else {
holderToUse.setConnection(con);
}
holderToUse.requested();
TransactionSynchronizationManager.registerSynchronization(
new ConnectionSynchronization(holderToUse, dataSource));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != conHolder) {
TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
}
}
return con;
You'll notice that the JdbcTemplate tries to
finally {
DataSourceUtils.releaseConnection(con, getDataSource());
}
release the Connection, but this only happens if you're in a non-transactional environment, ie.
if it is not managed externally (that is, not bound to the thread).
Therefore, in a transactional world, the JdbcTemplate will be reusing the same Connection object.