delay delivery of message in activeMQ in spring boot - spring-boot

I want to send a message at any time 't' that will be received by the receiver after 'x' sec.
for doing so, I have written sender code
#Autowired
private JmsTemplate jmsTemplate;
private Queue queue = new ActiveMQQueue("topicName");
public void show(String message) {
try {
System.out.println("Sending message " + message);
jmsTemplate.convertAndSend(queue, message, new MessagePostProcessor() {
#Override
public Message postProcessMessage(Message message) throws JMSException {
System.out.println("postProcessMessage executed ");
message.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY, 3 * 60 * 1000);
System.out.println("long time " + message
.getLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY));
return message;
}
});
System.out.println("Sending done " + message + " at " + System.currentTimeMillis());
} catch (Exception er) {
er.printStackTrace();
}
}
and Reciever code
#JmsListener(destination = "topicName")
public void reciever(String message) {
System.out.println("receiving message " + message + " at " + System.currentTimeMillis());
}
But message received by receiver instant .without any delay.
output is
Sending message this is a message postProcessMessage executed long
time 180000 receiving message this is a message at 1514391984964
Sending done this is a message at 1514391984970
configuration file is
#Bean
JmsTemplate createJMSTemplate(ConnectionFactory connectionFactory) {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setConnectionFactory(connectionFactory);
return jmsTemplate;
}
#Bean
ConnectionFactory myActiveMQConnectionFactory() {
RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
redeliveryPolicy.setBackOffMultiplier(1);
redeliveryPolicy.setUseExponentialBackOff(false);
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
connectionFactory.setRedeliveryPolicy(redeliveryPolicy);
NetworkConnector networkConnector = new DiscoveryNetworkConnector();
networkConnector.setConsumerTTL(2);
return connectionFactory;
}

Delayed message is not supported by activemq with default config, you should turn it on at first.
adding schedulerSupport in activemq.conf
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}" schedulerSupport="true">

Related

TopicSubscriber receive() not working Spring JMS - consumer closed

I am trying to receive messages using a TopicSubscriber created using execute method of JMSTemplate in Spring JMS. The objective is to test receiving of messages from a topic both synchronously and asynchronously. However, when trying to receive message from a durable topic subscriber, I get an error saying Consumer is closed.
I am new to Spring JMS. Can someone specify how to configure a JMSListener inside the test class? I have created two JMSListeners but they do not seem to work.
Here is my Test Class Code:
#SpringBootTest
class AqJmsExampleApplicationTests {
private ConnectionFactory connectionFactory;
private JmsTemplate jmsTemplate;
#BeforeEach
void setUp() {
// initialize jmstemplate with connectionFactory
// ......
}
#Test
void test() {
this.jmsTemplate.setExplicitQosEnabled(true);
this.jmsTemplate.setPubSubDomain(true);
TopicSubscriber tSub = this.jmsTemplate.execute(s -> {
return s.createDurableSubscriber(
(Topic)this.jmsTemplate
.getDestinationResolver()
.resolveDestinationName(s,
"testTopic4", true), "tSub");
}, true);
this.jmsTemplate.setPriority(7);
this.jmsTemplate.send("testTopic4", s -> {
return s.createTextMessage("hi this is first topic message");
});
try {
Message msg = tSub.receive(20000);
System.out.println("The message received is: " +
((TextMessage)msg).getText());
} catch (JMSException e) {
System.out.println("Got exception: " + e);
e.printStackTrace();
}
}
static class MyTestBean {
#JmsListener(destination = "testTopic4",
id = "top41",
subscription = "testTopic4"
)
public void receiveMessageTopic41(Message msg) throws JMSException{
System.out.println("Received for async subscriber 1<" +
((TextMessage)msg).getText() + ">");
}
#JmsListener(destination = "testTopic4",
id = "top42",
subscription = "testTopic4"
)
public void receiveMessageTopic42(Message msg) throws JMSException{
System.out.println("Received for async subscriber 2<" +
((TextMessage)msg).getText() + ">");
}
}
The error received is the following:
Got exception: javax.jms.IllegalStateException: JMS-115: Consumer is closed
Any help would be appreciated. Thanks!

How to create multiple MQQueueConnectionFactory for multiple Queue Manager

I am trying to configure one #JMSListener which will listen to multiple queue managers in my Springboot application which is listening to IBM MQ. Below are the 2 bean i have created and the requirement is to listen to both queue actively :
#Value("${ibm.mq.queueManager.A}")
String jmsMQConnectionFactoryA;
#Value("${ibm.mq.queueManager.B}")
String jmsMQConnectionFactoryB;
#Bean
#Primary
public MQQueueConnectionFactory jmsMQConnectionFactoryB() {
MQQueueConnectionFactory mqQueueConnectionFactory = new MQQueueConnectionFactory();
URL url = JMSConfiguration.getURL(this.urlFileLocation);
try {
mqQueueConnectionFactory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
mqQueueConnectionFactory.setClientReconnectOptions(WMQConstants.WMQ_CLIENT_RECONNECT_Q_MGR);
mqQueueConnectionFactory.setCCDTURL(url);
mqQueueConnectionFactory.setQueueManager(jmsMQConnectionFactoryB);
} catch (Exception e) {
e.printStackTrace();
}
return mqQueueConnectionFactory;
}
#Bean
public MQQueueConnectionFactory jmsMQConnectionFactoryB() {
MQQueueConnectionFactory mqQueueConnectionFactory = new MQQueueConnectionFactory();
URL url = JMSConfiguration.getURL(this.urlFileLocation);
try {
mqQueueConnectionFactory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
mqQueueConnectionFactory.setClientReconnectOptions(WMQConstants.WMQ_CLIENT_RECONNECT_Q_MGR);
mqQueueConnectionFactory.setCCDTURL(url);
mqQueueConnectionFactory.setQueueManager(jmsMQConnectionFactoryA);
} catch (Exception e) {
e.printStackTrace();
}
return mqQueueConnectionFactory;
}
EDIT 1 :
Added 2 containerFactory using above connections and 2 JMSTemplate (as i need to publish message as well) Below is the updated working code
#Bean
JmsListenerContainerFactory<?> jmsContainerFactoryA() {
DefaultJmsListenerContainerFactory factory = new
DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(jmsMQConnectionFactoryA());
factory.setRecoveryInterval((long) 60000);
factory.setSessionAcknowledgeMode(2);
factory.setSessionTransacted(true);
// factory.setMaxMessagesPerTask(concurrencyLimit);
return factory;
}
#Bean
JmsListenerContainerFactory<?> jmsContainerFactoryB() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(jmsMQConnectionFactoryB());
factory.setRecoveryInterval((long) 60000);
factory.setSessionAcknowledgeMode(2);
factory.setSessionTransacted(true);
// factory.setMaxMessagesPerTask(concurrencyLimit);
return factory;
}
#Bean
public MQQueue jmsResponseQueue() {
MQQueue queue = null;
try {
queue = new MQQueue("QueueOut");
queue.setBaseQueueName(wmq_out_base_queue);
} catch (JMSException e) {
e.printStackTrace();
}
return queue;
}
#Bean
public JmsTemplate jmsTemplateB() {
JmsTemplate template = new JmsTemplate();
template.setConnectionFactory(jmsMQConnectionFactoryB());
template.setDefaultDestination(jmsResponseQueue());
template.setMessageConverter(oxmMessageConverter());
return template;
}
#Bean
public JmsTemplate jmsTemplateA() {
JmsTemplate template = new JmsTemplate();
template.setConnectionFactory(jmsMQConnectionFactoryA());
template.setDefaultDestination(jmsResponseQueue());
template.setMessageConverter(oxmMessageConverter());
return template;
}
Class2.java
#Value("${wmq_out_base_queue}")
String wmq_out_base_queue;
#Autowired
JmsTemplate jmsTemplateA;
#Autowired
JmsTemplate jmsTemplateB;
#JmsListener(containerFactory="jmsContainerFactoryA",destination = "${wmq_in_base_queue}")
public void reciveMessageA(Message message) {
LOGGER.info("Received message is: " + message);
this.jmsTemplateA.convertAndSend(wmq_out_base_queue, "Some Message");
}
#JmsListener(containerFactory="jmsContainerFactoryB",destination = "${wmq_in_base_queue}")
public void reciveMessageB(Message message) {
LOGGER.info("Received message is: " + message);
this.jmsTemplateA.convertAndSend(wmq_out_base_queue, "Some Message2");
}
You need to disable JMS auto configuration and configure 2 connection factories, 2 container factories using those connection factories and 2 JmsTemplates (if you are also publishing).
I am trying to configure one #JMSListener which will listen to multiple queue managers
In order to have a #JmsListener to do this you need to add 2 #JmsListener annotations to the method, with each one having the containerFactory property set to the respective container factory.

Delay in send Message method in JMSTemplate at createMessage

I am newbie to MQ, We are usgin spring boot with JMS + IBM MQ Framework. We are facing a very random issue where there is 5 - 15 min delay to execute send method of jmsQueueTemplate as below.
Random delay is Between //POINT 1 and //POINT 2
try {
logger.info("Calling Send Method ");
//POINT 1
jmsQueueTemplate.send(new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
JMSTextMessage message = null;
try {
//POINT 2
logger.info("Session Object ........ " + session.toString());
logger.info("Session Status - Is Session Transacted ?........ " + session.getTransacted());
logger.info("Session Status acknowledge mode........ " + session.getAcknowledgeMode());
logger.info("Ready to send Sample message........ ");
logger.info("Send ConnectionFactory is: " + jmsQueueTemplate.getConnectionFactory().toString());
logger.info("Send destination is: " + jmsQueueTemplate.getDefaultDestination());
logger.info("Reply destination is: " + destination);
logger.info("Sent message correlationId is: " + correlationId);
logger.info("##########################################################");
message = (JMSTextMessage) session.createTextMessage();
String fosXmlBatchRequest = XMLUtil.createBatchFosRequestXML(oBatchFosRequest);
message.setText(fosXmlBatchRequest);
message.setJMSCorrelationID(correlationId);
logger.info(transactionType + " : Sending message is:");
logger.info(message.getText());
logger.info("##########################################################");
message.setJMSReplyTo(destination);
} catch (Exception e) {
logger.info("Exception Occured :" + e.getMessage());
}
return message;
}
});
} catch(Exception e) {
logger.info("Exception while Sending Message To FOS",e);
logger.info(e.toString(),e);
logger.error("Exception while Sending Message To FOS",e);
logger.error(e.toString(),e);
}
we are not handling session object and letting spring take care of session creation . I am not sure when exacty is the delay getting introced ? Are we loosing session object ? Any suggestion pls.
Our spring configuration is as below .
import javax.jms.Destination;
import javax.jms.JMSException;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.listener.DefaultMessageListenerContainer;
import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.MessageType;
import org.springframework.stereotype.Component;
import com.ibm.mq.jms.MQQueue;
import com.ibm.mq.jms.MQQueueConnectionFactory;
/**
*
* #author krna
* This class defines configuration for JMS Queues required for FOS update.
*/
#Configurable
#Component
public class JMSConfiguration {
private final Logger log = LogManager.getLogger(JMSConfiguration.class);
#Autowired
MessageListenerReciever messageListenerReciever1;
#Autowired
MessageListenerReciever messageListenerReciever2;
private String hostName1;
private String hostName2;
private String hostName3;
private String writePort;
private String readPort;
private String channel;
private String transportType;
private String updateQueue;
private String replyQueue;
private String queueGateway;
#Autowired
JMSConfiguration(Environment environment){
this.hostName1 = environment.getRequiredProperty("jms.cf.write.hostName1");
this.hostName2 = environment.getRequiredProperty("jms.cf.read.hostName2");
this.hostName3 = environment.getRequiredProperty("jms.cf.read.hostName3");
this.writePort = environment.getRequiredProperty("jms.cf.write.port");
this.readPort = environment.getRequiredProperty("jms.cf.read.port");
this.channel = environment.getRequiredProperty("jms.cf.channel");
this.transportType = environment.getRequiredProperty("jms.cf.transportType");
this.updateQueue = environment.getRequiredProperty("jms.queue.update");
this.replyQueue = environment.getRequiredProperty("jms.queue.reply");
this.queueGateway = environment.getRequiredProperty("jms.queue.gateway");
}
public MQQueueConnectionFactory connectionFactory1() {
MQQueueConnectionFactory connectionFactory = new MQQueueConnectionFactory();
try {
connectionFactory.setHostName(hostName1);
connectionFactory.setPort(Integer.parseInt(writePort));
connectionFactory.setChannel(channel);
connectionFactory.setTransportType(Integer.parseInt(transportType));
} catch (NumberFormatException | JMSException e) {
log.error(e.toString(),e);
}
return connectionFactory;
}
public MQQueueConnectionFactory connectionFactory2() {
MQQueueConnectionFactory connectionFactory = new MQQueueConnectionFactory();
try {
connectionFactory.setHostName(hostName2);
connectionFactory.setPort(Integer.parseInt(readPort));
connectionFactory.setChannel(channel);
connectionFactory.setTransportType(Integer.parseInt(transportType));
} catch (NumberFormatException | JMSException e) {
log.error(e.toString(),e);
}
return connectionFactory;
}
public MQQueueConnectionFactory connectionFactory3() {
MQQueueConnectionFactory connectionFactory = new MQQueueConnectionFactory();
try {
connectionFactory.setHostName(hostName3);
connectionFactory.setPort(Integer.parseInt(readPort));
connectionFactory.setChannel(channel);
connectionFactory.setTransportType(Integer.parseInt(transportType));
} catch (NumberFormatException | JMSException e) {
log.error(e.toString(),e);
}
return connectionFactory;
}
#Bean
public Destination jmsDestinationResolverSender() throws JMSException {
return new MQQueue(updateQueue);
}
#Bean
public Destination jmsDestinationResolverReceiver() throws JMSException {
return new MQQueue(replyQueue);
}
#Bean
public Destination jmsDestinationResolverReplyTo() throws JMSException {
return new MQQueue(queueGateway, replyQueue);
}
#Bean
public JmsTemplate jmsQueueTemplate() throws JMSException {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setConnectionFactory(connectionFactory1());
jmsTemplate.setDefaultDestination(jmsDestinationResolverSender());
jmsTemplate.afterPropertiesSet();
log.info("Send ConnectionFactory is:" + jmsTemplate.getConnectionFactory());
log.info("Send destination is:" + jmsTemplate.getDefaultDestination());
log.info("Send Delivery Delay is :" + jmsTemplate.getDeliveryDelay());
log.info("Send Delivery Mode is:" + jmsTemplate.getDeliveryMode());
return jmsTemplate;
}
#Bean
public DefaultMessageListenerContainer listenerContainer() throws JMSException {
DefaultMessageListenerContainer defMsgListCont = new DefaultMessageListenerContainer();
defMsgListCont.setConnectionFactory(connectionFactory3());
defMsgListCont.setDestination(jmsDestinationResolverReceiver());
defMsgListCont.setMessageListener(messageListenerReciever1);
defMsgListCont.afterPropertiesSet();
return defMsgListCont;
}
#Bean
public DefaultMessageListenerContainer listenerContainer2() throws JMSException {
DefaultMessageListenerContainer defMsgListCont = new DefaultMessageListenerContainer();
defMsgListCont.setConnectionFactory(connectionFactory2());
defMsgListCont.setDestination(jmsDestinationResolverReceiver());
defMsgListCont.setMessageListener(messageListenerReciever2);
defMsgListCont.afterPropertiesSet();
return defMsgListCont;
}
// Serialize message content to json using TextMessage
#Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
}
Update: It happened again today. One more Interesting this is sourcetimestamp on MQ msg is different from the time it was send. Sourcetimestamp is showing correct time but execution is delayed

How to handle JmsException /How do I set the redeliveryPolicy in ActiveMQ?

Let see one scenario is there when jms send message to consumer and we have to save that to db,but when db is down we cant able to save that to db due to db server is down.So how to acknowledge jms to send message again?
This issue raised by many people.I resolved this using below code snippet.
#Configuration
public class AppConfiguration {
#Bean
public JmsListenerContainerFactory<?> jmsContainerFactory(DefaultJmsListenerContainerFactoryConfigurer configurer) {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
connectionFactory.setClientID(clientId);
connectionFactory.setBrokerURL(brokerUrl);
CachingConnectionFactory cf = new CachingConnectionFactory(connectionFactory);
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(cf);
factory.setSubscriptionDurable(true);
configurer.configure(factory, cf);
return factory;
}
}
#Component("ApprovalSubsCriber")
public class ApprovalSubscriber implements SessionAwareMessageListener<TextMessage> {
#Override
#JmsListener(destination = "topic/jmsReplyTest1", containerFactory = "jmsContainerFactory", subscription = "ApprovalSubsCriber")
public void onMessage(TextMessage message, Session session) throws JMSException {
// This is the received message
System.out.println("Receive: " + message.getText());
// Let's prepare a reply message - a "ACK" String
ActiveMQTextMessage textMessage = new ActiveMQTextMessage();
textMessage.setText("ACK");
System.out.println(session.getAcknowledgeMode());
if ("notexception".equals(message.getText())) {
session.commit();
} else {
session.rollback();//If exception comes
}
}
}

RabbitTemplate.ReturnCallback() is called always in dynamic queue publish

In our code we have attached RabbitTemplate.ReturnCallback() to get notification of not routed messages from consumer and it is working fine for the fixed queue.
But in case of synchronous publish by using rabbitTemplate.sendAndReceive, after receiving message when consumer tries to send response through dynamic queue , ReturnCallback is called always.
Is it the expected behavior?
Do we have any way to handle not routed message for dynamic queue.
Below are the beans created in my project , here we have attached one consumer to the queue "test", which receive the published "Message" from queue "test" then put a response to "reply-to" queue.
#Bean
SimpleMessageListenerContainer container( ConnectionFactory connectionFactory ) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory( connectionFactory );
container.setQueueNames( "test" );
container.setMessageListener( exampleListener() );
return container;
}
#Bean
public ConnectionFactory rabbitConnectionFactory() {
return new CachingConnectionFactory( "localhost" );
}
#Bean
public RabbitTemplate rabbitTemplate( ConnectionFactory connectionFactory ) {
RabbitTemplate rabbitTemplate = new RabbitTemplate( connectionFactory );
rabbitTemplate.setMandatory( true );
((CachingConnectionFactory)rabbitTemplate.getConnectionFactory()).setPublisherReturns( true );
rabbitTemplate.setReturnCallback( new RabbitTemplate.ReturnCallback() {
#Override
public void returnedMessage( Message arg0, int arg1, String arg2, String arg3, String arg4 ) {
System.out.println( "Returned message " + arg0 );
System.out.println( "Returned replyText " + arg2 );
}
} );
return rabbitTemplate;
}
#Bean
public RabbitAdmin rabbitAdmin( ConnectionFactory connectionFactory ){
return new RabbitAdmin( connectionFactory );
}
#Bean
public MessageListener exampleListener() {
return new MessageListener() {
public void onMessage( Message message ) {
System.out.println( "received: " + message );
RabbitTemplate rabbitTemplate = rabbitTemplate( rabbitConnectionFactory() );
// sending message to reply-to queue( dynamic queue in case of synchronous publish)
// even though the queue exist and messaage successfully published , RabbitTemplate.ReturnCallback() is called
rabbitTemplate.convertAndSend( message.getMessageProperties().getReplyTo(), "response from consumer" );
}
};
}
Rest end point to publish message synchronously to queue "test"
/**
* publish Message to Queue.
*
* #return response
*/
#RequestMapping ( value = "/publish", method = RequestMethod.POST )
public String sendMsgToQueue( ) {
MessageProperties messageProperties = new MessageProperties();
messageProperties.setCorrelationId("101".getBytes());
rabbitTemplate.setReplyTimeout( 30000 ); // timeout
return rabbitTemplate.sendAndReceive( "test", new SimpleMessageConverter().toMessage( "message to test", messageProperties ) ).toString();
}

Resources