Using topics but not all the consumers take the message - spring

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!

Related

JMS Topic Subscriber in Spring using JMS template/ Message Subscriber

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.

Activemq degrade response time

I`m using activemq 5.10 with spring 4.1.1.
I find a degrade problem with the response time of messages. After 4 days, the response time starts to grow up from 15ms to 200ms and more.
The app works fine with approximately 1000 messages per second and then all run slower.
Here is part of the xml beans:
<amq:systemUsage>
<amq:memoryUsage>
<amq:memoryUsage limit="512 mb">
</amq:memoryUsage>
</amq:memoryUsage>
<amq:storeUsage>
<amq:storeUsage limit="50 mb"></amq:storeUsage>
</amq:storeUsage>
<amq:tempUsage>
<amq:tempUsage limit="50 mb"></amq:tempUsage>
</amq:tempUsage>
</amq:systemUsage>
<amq:broker brokerName="myBroker" id="broker"
persistent="false" deleteAllMessagesOnStartup="true" enableStatistics="false"
useLoggingForShutdownErrors="true">
<amq:transportConnectors>
<amq:transportConnector
uri="nio://${Ip}:${Port}?jms.useAsyncSend=true?jms.useCompression=true"
disableAsyncDispatch="false" />
</amq:transportConnectors>
<amq:destinationPolicy>
<amq:policyMap>
<amq:policyEntries>
<amq:policyEntry queue=">" optimizedDispatch="true" />
</amq:policyEntries>
</amq:policyMap>
</amq:destinationPolicy>
</amq:broker>
<!-- A JMS connection factory for ActiveMQ -->
<bean id="ConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"
p:brokerURL="nio://${Ip}:${Port}" />
<bean id="pooledJmsConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory"
destroy-method="stop">
<property name="connectionFactory" ref="ConnectionFactory" />
<property name="maxConnections" value="10" />
</bean>
<bean id="Listener" class="xx.com.xxx.MessageListener" />
<jms:listener-container container-type="default"
connection-factory="ConnectionFactory" acknowledge="auto">
<jms:listener destination="${QueueName}"
ref="sisBusMessageListener" method="onMessage" />
</jms:listener-container>
And here is the Java code:
public class MessageListener extends GenericMessageListener {
public void onMessage(Message request) {
MyExecutor mythread = new MyExecutor(request, new DateTime());
executor.execute(mythread );
}
}
public class MyExecutor {
public void init() {
try {
connectionFactory = ApplicationHelper.getBean("ConnectionFactory");
connectionFactory.setAlwaysSessionAsync(false);
connection = connectionFactory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
} catch (JMSException e) {
logs...
}
}
public void returnMessage(Message request, Object responseFromExternalSystem) throws JMSException {
MapMessage response = session.createMapMessage();
response.setJMSCorrelationID(request.getJMSCorrelationID());
//code that set info on map message is here
replyProducer = session.createProducer(request.getJMSReplyTo());
replyProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
replyProducer.send(response);
}
}
The solution was re-use the connections and sessions creating only one instanace and creating an other instance in some cases(errors, desconnections...)

Spring Jms #JmsListener annotation doesnt work

I have such method
#JmsListener(containerFactory = "jmsListenerContainerFactory", destination = "myQName")
public void rceive(MySerializableObject message) {
log.info("received: {}", message);
}
and such config on xml
<jms:annotation-driven />
<bean id="jmsListenerContainerFactory" class="org.springframework.jms.config.DefaultJmsListenerContainerFactory">
<property name="connectionFactory" ref="pooledConnectionFactory" />
<property name="concurrency" value="3-10" />
</bean>
<bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="${brokerURL}" />
</bean>
<bean id="pooledConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory">
<property name="maxConnections" value="5" />
<property name="maximumActiveSessionPerConnection" value="500" />
<property name="connectionFactory" ref="jmsConnectionFactory" />
</bean>
<bean id="jmsContainerFactory" class="org.springframework.jms.config.DefaultJmsListenerContainerFactory">
<property name="connectionFactory" ref="pooledConnectionFactory" />
<property name="concurrency" value="3-10" />
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate" p:connectionFactory-ref="pooledConnectionFactory" />
Seems consumer was not created. I can send messages but cannot receive them.
Any idea whats wrong here?
Just tested your config and it works well. Only difference that I make a class with #JmsListener as a <bean> in that context:
<bean class="org.springframework.integration.jms.JmsListenerAnnotationTests$TestService"/>
#ContextConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#DirtiesContext
public class JmsListenerAnnotationTests {
#Autowired
private JmsTemplate jmsTemplate;
#Autowired
private TestService testService;
#Test
public void test() throws InterruptedException {
this.jmsTemplate.convertAndSend("myQName", "foo");
assertTrue(this.testService.receiveLatch.await(10, TimeUnit.SECONDS));
assertNotNull(this.testService.received);
assertEquals("foo", this.testService.received);
}
public static class TestService {
private CountDownLatch receiveLatch = new CountDownLatch(1);
private Object received;
#JmsListener(containerFactory = "jmsListenerContainerFactory", destination = "myQName")
public void receive(String message) {
this.received = message;
this.receiveLatch.countDown();
}
}
}
<jms:annotation-driven /> makes the #JmsListener infrastructure available, but to force Spring to see those methods your classes should be beans anyway.
For example <component-scan> for the package with #Service classes.
Cheers!

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?

Resources