Does JMS receiveNoWait() guarantee message delivery when messages are available? - java

Hello I am writing some kind of simple testing scenario where I execute the following source code:
Here is my send() method:
public void send() throws JMSException {
Session session = null;
MessageProducer producer = null;
try {
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue("TEST.FOO");
producer = session.createProducer(destination);
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
byte[] uselessData = new byte[1024];
BytesMessage message = session.createBytesMessage();
message.writeBytes(uselessData);
producer.send(message);
} finally {
producer.close();
session.close();
}
}
Here is my receive() method:
public void receive() throws JMSException {
Session session = null;
MessageConsumer consumer = null;
try {
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue("TEST.FOO");
consumer = session.createConsumer(destination);
Message hugeMessage = consumer.receiveNoWait();
if (hugeMessage == null) {
System.out.println("Message was not received");
unsucsesfullCount++;
} else {
if (hugeMessage instanceof BytesMessage) {
System.out.println("Message received");
}
}
} finally {
consumer.close();
session.close();
}
}
I execute:
send();
receive();
The message value after receiveNoWait() is always null.
My question here is does the receiveNoWait() guarantee message delivery when there are messages in the broker? The send() is executed successfully so there is at least one message in the destination.
I've searched in the specification but there is not really clear definition if a message, which is available at the broker side should be explicitly received by receiveNoWait() at client side.
Also I want to ask, if receiveNoWait() does not have message available, should it trigger some refresh consumer process in the broker, so the next receiveNoWait() will receive the message?
The example code I provided run on ActiveMQ but my question is more conceptual than provider specific, because I have the same observation for other JMS providers.

No, the specification does not guarantee that any call to receiveNoWait will return a message, it might and then again it might not. When using receiveNoWait you must always check for a null return and act accordingly.
In the case of ActiveMQ the client will return a message if the broker has dispatched one to it and it is immediately available in the consumer prefetch buffer otherwise it just returns null.
Other implementations my indeed send a poll request to the broker, Qpid JMS for instance uses an AMQP link drain request to ask that the broker send it any messages that are available for dispatch and the broker will either send them or signal that the link is drained and there are no messages ready.
In short it's completely up to the client and broker how they implement receiveNoWait but no matter what you always need to account for the chance that you won't get a message returned to you from that method.

Related

Reply-To Queue is not updating in IBM MQ while putting message type as request

jmsTemplate.sendAndReceive("Queue2", session -> {
TextMessage msg = session.createTextMessage();
msg.setText(message);
msg.setJMSReplyTo(config.getReplyQ()); //Not updated but auto generated queue updated
msg.setJMSCorrelationID("asd_123584_lkj"); //Updated in Destination Queue
msg.setJMSType("MQSTR");
System.out.println("Message : "+msg);
return msg;
});
public Destination getReplyQ() throws JMSException {
MQQueue replyToQ = new MQQueue(queueManager, replyQueue);
Destination replyTo = (Destination) replyToQ;
return replyTo;
}
I read some articles that says using JMS will update the RFH but not MQMD and this Reply-To Queue is part of MQMD and I didn't find the right class to update the MQMD header and send the message to MQ and update the reply-to queue.
jmsTemplate.send(config.getQ(), session -> {
TextMessage msg = session.createTextMessage();
msg.setText(message);
msg.setJMSReplyTo(config.getReplyQ());
msg.setJMSCorrelationID("asd_123584_lkj");
System.out.println("Message : " + msg);
return msg;
});
Above method will set the JMSReplyTo.
Here the definition of sendAndReceive is present which stated that "a temporary queue is created as part of this operation and is set in the JMSReplyTO header of the message"
And to ignore the RFH2 header setTargetClient(WMQConstants.WMQ_CLIENT_NONJMS_MQ) can be used like below:
public Destination getQ() throws JMSException {
MQQueue replyToQ = new MQQueue(queueManager, queues);
replyToQ.setTargetClient(WMQConstants.WMQ_CLIENT_NONJMS_MQ);
Destination rt = (Destination) replyToQ;
return rt;
}

How to receive JSON message from a Solace JMS queue, the queue is already created

I am trying to receive JSON messages from a Solace JMS queue but I am not receiving any message. Below is my code
#Service
public class QueueConsumer {
final String QUEUE_NAME = "test.Request.Q.V01";
// Latch used for synchronizing between threads
final CountDownLatch latch = new CountDownLatch(1);
public void run(String... args) throws Exception {
String host = "test.solace.com";
String vpnName = "TEST_VPN";
String username = "testVpn";
String password = "test123";
System.out.printf("QueueConsumer is connecting to Solace messaging at %s...%n", host);
SolConnectionFactory connectionFactory = SolJmsUtility.createConnectionFactory();
connectionFactory.setHost(host);
connectionFactory.setVPN(vpnName);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setDynamicDurables(true);
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(false, SupportedProperty.SOL_CLIENT_ACKNOWLEDGE);
System.out.printf("Connected to the Solace Message VPN '%s' with client username '%s'.%n", vpnName, username);
Queue queue = session.createQueue(QUEUE_NAME);
MessageConsumer messageConsumer = session.createConsumer(queue);
messageConsumer.setMessageListener(new MessageListener() {
#Override
public void onMessage(Message message) {
try {
if (message instanceof SolaceMsg) {
System.out.printf("TextMessage received: '%s'%n", ((SolaceMsg) message).getClass());
} else {
System.out.println("Message received.");
}
System.out.printf("Message Content:%n%s%n", SolJmsUtility.dumpMessage(message));
message.acknowledge();
latch.countDown(); // unblock the main thread
} catch (JMSException ex) {
System.out.println("Error processing incoming message.");
ex.printStackTrace();
}
}
});
System.out.println("Start receiving messages....");
connection.start();
System.out.println("Awaiting message...");
latch.await();
connection.stop();
messageConsumer.close();
session.close();
connection.close();
}
public static void main(String... args) throws Exception {
new QueueConsumer().run(args);
}
}
My message type is JSON ad below, and I have created a POJO for this.
{
"customerDetails": {
"customerID": "0001234",
"customerName": "John"
}
}
I am getting one warning saying Response - 400 Queue already exists as it is an existing queue, and I am not receiving any messages. What am I doing wrong here?
Your code snippet looks correct. You can log on to the PubSub+ Manager of your event broker to verify that the client is binding to the correct queue and that the messages were successfully published to the queue and are waiting to be consumed. You can also enable Solace JMS API logging to understand more about what the application is doing: https://docs.solace.com/Solace-JMS-API/Code-and-Compile-Guideli.htm

ActiveMQ with Spring Boot. Messages persistance is not working

I want to use ActiveMQ within Spring Boot app as embedded server. To setup ActiveMQ I used following tutorial: Spring Boot. Messaging with JMS. My app will be the broker and the consumer. There are multiple threads creating messages like this:
#Autowired
private JmsTemplate jmsTemplate;
.......
MessageCreator messageCreator = session -> session.createObjectMessage(transactionNotificationData);
jmsTemplate.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
jmsTemplate.send(QUEUE, messageCreator);
I have another class with following method:
#JmsListener(destination = QUEUE)
public void receive(Message message) throws IOException {
brokerService.getPersistenceAdapter();
try {
if (message instanceof ObjectMessage) {
ObjectMessage objMessage = (ObjectMessage) message;
NotificationData notification = (NotificationData) objMessage.getObject();
LOG.info("Received <" + notification.notification + ">");
...... do some stuff ........
// message.acknowledge();
}
} catch (JMSException e) {
e.printStackTrace();
}
During the tests I can see the messages are produced and consumed.
As you can see message.acknowledge() is commented. So I expect the message will be redelivered after rerun of my app. However it doesn't happen.
Message Acknowledgement is automatically handled by the container and it executes after the onMessage() is successfully executed,(receive() in your case),
so even when you comment message.acknowledge(); , container on its own sends the acknowledgement
you can have a look at following link for more reference
Acknowledgement from Consumer in ActiveMQ
Hope this helps!
Good luck!

Apache Camel send message JMS Consumer receives message

Have Apache Camel simple message route from folder to ActiveMQ topic:
//Create context to create endpoint, routes, processor within context scope
CamelContext context = new DefaultCamelContext();
//Create endpoint route
context.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception
{
from("file:data/outbox").to("activemq:topic:Vadim_Topic");
//from("activemq:topic:TEST").to.to("file:data/outbox");
}
});
context.start();
Thread.sleep(5000);
context.stop();
}
And JMS implementation if Topic Consumer:
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
try {
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//connection.setClientID("12345");
connection.start();
Topic topic = session.createTopic("Vadim_Topic");
MessageConsumer messageConsumer = session.createConsumer(topic);
MessageListener messageListener = new MessageListener() {
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("Received message: " + textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
};
messageConsumer.setMessageListener(messageListener);
} catch (Exception e) {
e.printStackTrace();
}
Can't understand why is my consumer can't recieve messages sent by Camel route??
I guess thet problem is then I need to subscribe my JMS Consumer on messages sent by Camel?
How can I do this if this is the case?
Camel not only allows you to send messages to a topic, it can also very easily read messages from a topic and send it to one of your POJOs.
A route that reads from your topic and sends the messages to a POJO would look like this:
from("activemq:topic:Vadim_Topic").bean(ExampleBean.class);
Camel will figure out which method to call on the POJO depending on the type of message it received, and the available method signatures. See this page for details on using POJO's in camel routes: https://camel.apache.org/bean.html

Get receipt confirmation from the listener in JMS

I'm using Spring JMS and ActiveMQ to send message from a sender to multiple listeners using ActiveMQ Topic (publish/subscribe). So far all listeners can receive message from the sender. But I want to add a functionality that when a particular listener, say listener1, gets the message, listener1 will send a receipt confirmation to the sender. I followed the comment in my old post and created a TemporaryQueue in the sender and used ReplyTo in the sender and receiver to get the confirmation message from the listener to the sender.
My sender class is:
public class CustomerStatusSender {
private JmsTemplate jmsTemplate;
private Topic topic;
public void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
public void setTopic(Topic topic) {
this.topic = topic;
}
public void simpleSend(final String customerStatusMessage) {
jmsTemplate.send(topic, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
TextMessage message = session.createTextMessage("hello world");
message.setStringProperty("content", customerStatusMessage);
message.setIntProperty("count", 10);
//send acknowledge request to a listener via a tempQueue
Destination tempQueue = session.createTemporaryQueue();
message.setJMSCorrelationID("replyMessage");
message.setJMSReplyTo(tempQueue);
return message;
}
});
}
}
The sender creates a TemporaryQueue for the listener to send back the confirmation message. Then in one of the listeners, I have the following code to send the confirmation message back to the sender:
public class CustomerStatusListener implements SessionAwareMessageListener<Message> {
public void onMessage(Message message, Session session) {
if (message instanceof TextMessage) {
try {
System.out.println("Subscriber 1 got you! The message is: "
+ message.getStringProperty("content"));
//create a receipt confirmation message and send it back to the sender
Message response = session.createMessage();
response.setJMSCorrelationID(message.getJMSCorrelationID());
response.setBooleanProperty("Ack", true);
TemporaryQueue tempQueue = (TemporaryQueue) message.getJMSReplyTo();
MessageProducer producer = session.createProducer(tempQueue);
producer.send(tempQueue, response);
} catch (JMSException ex) {
throw new RuntimeException(ex);
}
} else {
throw new IllegalArgumentException(
"Message must be of type TextMessage");
}
}
}
However, I found that the following line in the Listener class throws an error:
TemporaryQueue tempQueue = (TemporaryQueue) message.getJMSReplyTo();
MessageProducer producer = session.createProducer(tempQueue);
The exception error says:
The destination temp-queue://ID:xyz-1385491-1:2:1 does not exist.
So what's wrong here? I assume that tempQueue created by the sender is available for the listener in the same JMS session. Why the tempQueue object after calling message.getJMSReplyTo() does not return a valid TemporaryQueue?
The other question is: How do I receive the confirmation message in the sender? Should I implements MessageListener interface in the sender in order to receive the confirmation from the listener? Or should I just call receive() method to receive it synchronously?
Thanks for any suggestions!
If you are using spring-jms, why not just use a MessageListenerAdapter as your listener? - he will take care of the replyTo stuff and your listener can be a simple POJO.
In any case, you don't need to cast it to a Tempoarary queue; it's just a destination as far as the listener is concerned.
Checkout the MessageListenerAdapter` javadocs.
Also, you need to create a consumer on the temp queue on the sending side, to receive the reply. If the sending connection is closed, the temp queue will go away.
I ended up using a separate JMS Queue to send the acknowledge message from the listener-1 to the sender. For some reason, the temporaryQueue created by ActiveMQ is not available during the JMS session.

Categories