Weird behavior when counting messages on JMS queue - java

I am using Active MQ and the Java JMS.
I want to count the number of messages on the queue.
One approach is counting the messeages with a browser:
Queue queue = (Queue) session.createQueue(subject);
QueueBrowser queueBrowser = session.createBrowser(queue);
Enumeration<?> e = queueBrowser.getEnumeration();
int numMsgs = 0;
// count number of messages
while (e.hasMoreElements()) {
// Message m = (Message) e.nextElement();
e.nextElement();
numMsgs++;
}
But for a queue with 5000 pending requests, this only return 500.
Another approach is this (iterate all the messeages in the queue):
Message message= consumer.receive(500);
while(message!= null)
{
if (message instanceof TextMessage)
{
TextMessage textMessage = (TextMessage) message;
// BytesMessage Byte
System.out.println("Received message '"+ textMessage.getText() + "'");
}
if(message!=null)
Messages_list.add(message);
message = consumer.receive(1);
}
But this also dont give the right amount of messages pending.
How can i confidently iterate akk the messages waiting in the queue?

There is a bug in ActiveMQ that is preventing the browse from returning the actual number of messages. In this case the browse is only returning a single page of messages, which is set by the maxPageSize property and documented here: http://activemq.apache.org/per-destination-policies.html
ActiveMQ currently has a bug report on this issue and it is being tracked here: https://issues.apache.org/jira/browse/AMQ-4181. This issue has been resolved and is currently scheduled to be fixed in ActiveMQ 5.8.0.

Since you are using ActiveMQ, you can make use of ActiveMQ's StatisticsPlugin: http://activemq.apache.org/statisticsplugin.html
Similarly, to query the statistics for a destination just send a message to the destination name prepended with ActiveMQ.Statistics.Destination. For example, to retrieve the statistics for a queue whose name is TEST.FOO, send an empty message to the queue named ActiveMQ.Statistics.Destination.TEST.FOO
Specifically, you might be interested in enqueueCount.
I'm omitting example code here, since the example code on the plugin's webpage are concise and good.

Related

IBM MQQueue Safest way to get all messages

I have a simple java program to transfer messages from queue A to queue B using IBM MQ.
My program works fine, but what I am concerned about is losing messages. I know that .get() removes the message from queue A. So of course, there is a brief moment where I have "got" a message from queue A, and I have not yet placed it in queue B. If my program were to crash during this time - the message would be lost.
To combat this, I am writing the current message to the logs. Then, if the program crashes, we can enter the message back into the queue manually.
However - what if the program crashes from an IOException? Now the message is gone from queue A, has not been .put() in queue B, and hasn't been written to the logs.
The way I see it, I have two options:
Browse the message first: I know that I can browse the message before "getting" it, although I'm a bit confused about how this affects the number of messages in the queue, and if it creates a duplicate etc.
Write the message back to queue A: In theory, if we "get" the message from queue A, we shouldn't have a problem "putting" it back to queue A, if for some reason we can't connect to queue B.
Could someone clarify the proper way to browse a message first - or perhaps suggest a third option I haven't thought of?
while (true) {
try {
// Clear the MQMessage
theMessage.messageId = MQConstants.MQMI_NONE;
theMessage.correlationId = MQConstants.MQCI_NONE;
// Get the message from queue A
queueA.get(theMessage, gmo);
// Read the message from queue A
byte[] messageBytes = new byte[theMessage.getMessageLength()];
theMessage.readFully(messageBytes);
String messageText = new String(messageBytes);
// Store the message to the logs in case of crash
// Put the message in queue B
queueB.put(theMessage);
} catch (MQException e) {
// Break the loop if we get an MQException
// Hopefully, it is a reason code 2033 (out of messages)
} catch (IOException e) {
// Something went wrong reading the message
}
}
Generally, if you want to keep track of messages read and written, you should use transactional reading/writing.
MQGetMessageOptions gmo = new MQGetMessageOptions();
gmo.waitInterval = 1000;
gmo.options = MQGMO_WAIT;
gmo.options += MQGMO_FAIL_IF_QUIESCING;
gmo.options += MQGMO_SYNCPOINT;
MQPutMessageOptions pmo = new MQPutMessageOptions();
pmo.options += MQPMO_SYNCPOINT;
// create message instance
MQMessage message = new MQMessage();
message.correlationId = MQCI_NONE;
message.messageId = MQMI_NONE;
// read message
queueA.get(message, gmo);
// write message
queueB.put(message, pmo);
// commit transaction
qmgr.commit();
In that case, if transaction will not be committed, all read messages will return to the source queue and all written messages will disappear from target queues. It may be good idea to commit not every message, but every 10 or 100 depending on their amount.
If you're not going to use distributed transactions (e.g. saving some information from MQ messages to database), that would suffice. Otherwise I'd recommend to switch to JMS because of its better transaction support.

Why consumer stopped if message doesn't contain message selector condition?

I recently work with jms and I have such a question. I have to received message 1)All messages 2)Only where type = 'LIQUID'. I created two consumers
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue(QUEUE_FOR_RECEIVED);
QueueBrowser queueBrowser = session.createBrowser(queue);
Enumeration enumeration = queueBrowser.getEnumeration();
MessageConsumer consumer = session.createConsumer(queue);
MessageConsumer liquidConsumer = session.createConsumer(queue, "type = 'LIQUID'");
First received all messages, second only with type = 'LIQUID'. But second consumer just stopped application if message doesn't contain type='LIQUID'
while (enumeration.hasMoreElements()) {
ObjectMessage ss = (ObjectMessage) consumer.receive();
System.out.println(ss.getObject());
ObjectMessage msg = (ObjectMessage) liquidConsumer.receive(); // here consumer stopped if message doesn't contain type ='LIQUID'
System.out.println(msg.getObject());
enumeration.nextElement();
}
How it can be improved?
The reason the application stopped is because javax.jms.MessageConsumer.receive() is a blocking call. In other words, it will block further execution until a result is returned. If the queue doesn't contain any messages which match the selector then the call to javax.jms.MessageConsumer.receive() will block indefinitely. That's the expected, documented behavior.
If you don't want to block indefinitely here you could:
Receive messages asynchronously (e.g. using a javax.jms.MessageListener implementation)
Use javax.jms.MessageConsumer.receive(int) and pass a timeout to receive so that the call returns if no messages are received after the given timeout.
Use javax.jms.MessageConsumer.receiveNoWait() which will attempt to receive the next matching message and if no matching message is immediately available it will return.

How To count Number of queues in ACTIVEMQ

I am trying to create an application which keeps on checking the number of queues up and running in activemq.
And Any way to check whether queue's are working or not i.e. if corrupted and not able to process messages.
Kindly suggest how to do it.
Thanks in Advance.
You can try following code.
public static void main(String[] args) throws Exception
{
// get the initial context
InitialContext ctx = new InitialContext();
// lookup the queue object
Queue queue = (Queue) ctx.lookup("queue/queue0");
// lookup the queue connection factory
QueueConnectionFactory connFactory = (QueueConnectionFactory) ctx.
lookup("queue/connectionFactory");
// create a queue connection
QueueConnection queueConn = connFactory.createQueueConnection();
// create a queue session
QueueSession queueSession = queueConn.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
// create a queue browser
QueueBrowser queueBrowser = queueSession.createBrowser(queue);
// start the connection
queueConn.start();
// browse the messages
Enumeration e = queueBrowser.getEnumeration();
int numMsgs = 0;
// count number of messages
while (e.hasMoreElements()) {
Message message = (Message) e.nextElement();
numMsgs++;
}
System.out.println(queue + " has " + numMsgs + " messages");
// close the queue connection
queueConn.close();
}
You can ask for stats using the statistics plugin on the broker and the plain JMS api. I.e. to count the number of messages on FOO.BAR, send an empty message to ActiveMQ.Statistics.Destination.TEST.FOO and specify the replyTo header. In the response message, which is of typ MapMessage, you can find the message counter.
Another way is to browse only the first message of the queue using a simple queue browser (similar to the way praveen_programmer suggests) and check the timestamp of that message. If it's older than some threshold, you might have a problem with that consumer. I.e. no messages has been processed in the last hour/minute/day.
Yet another way is to use JMX, or preferably the jolokia REST/HTTP management api.
Just query the destination using http and you get a queue depth back:
To query the queue "q" on localhost, use the following api (you need to supply the user/password for the web console):
http://localhost:8161/api/jolokia/read/org.apache.activemq:type=Broker,brokerName=localhost,destinationType=Queue,destinationName=q
Take a look at Advisory messages. You need to enable them in your config , but you can get a lot of useful about your current activemq instances info through simple JMS messaging. http://activemq.apache.org/advisory-message.html I was using them to highlight slow producer and consumer scenarios.

Subscribe to multiple topic destination in ActiveMQ Messaging

A publisher publishes messages to different destinations. My client needs to subscribe and get all those messages in those destinations one by one.Means i want to consume messages from multiple topics. Also I want the topic messages (different destinations) to be received in a button action, not by using Message Listener. Can anyone please help on this?
Part of my code is.
MessageConsumer consumer = null;
if (isDurableSubscription) {
// the subscription Name assigned to a durable subscription must be unique within a given client ID.
consumer = session.createDurableSubscriber( topic, subscriptionName );
} else {
consumer = session.createConsumer( topic );
}
log.finest("consumer = " + consumer );
consumer.setMessageListener( this );
conn.start();
}
public void onMessage(Message message) {
if ( message instanceof TextMessage ) {
try {
TextMessage txtMessage = (TextMessage) message;
String text = txtMessage.getText();
this.msg = text;
System.out.println(text);
log.finest("Message processed ...");
session.commit();
}
Also i want the topic messages (different destinations) to be
received in a button action, not by using Message Listener.
The whole point of a JMS provider is to listen to messages published by a producer and have an async communication channel in which the producer and the listener are decoupled. When you say you want to receive messages in a button action, it's equivalent of saying "I don't really care when the publisher produced the message, but I'll listen when I feel like" - which doesn't fit the use of a JMS. May be a queue where you have messages and pick one after the other based on some user action.
The publisher will not mark the message as delivered (based on how you have configured it) until the client acknowledges it and in your case (even if it were possible), it may be a long time and the message might expire. One way to achieve this, with JMS, is to have your internal data structure where you keep all your messages (after picking them up from the topic using a listener) and then process it on a button action. But you'll lose all the benefits of a JMS provider (durability, loss of messages upon client shut down, and the likes).

JMS Queue with 2 Listeners

I have a question as regards JMS Queues.
I have implemented a jms sender/reciever using a shared queue as its the only queue avaialbe to me and the only one i can use.
The problem that i am now faced with is that i as this first come first served i cannot guarentee that the messages that i am sending from my producer will be consumed by my consumer and not the other sharing this queue and vice versa. So i am consuming the other apps messages and they are consuming mine.
Is there a way i can just listen for my messages and not consume them from the queue or is this more of a topic implementation?
Or perhaps i can explicitly set an identifier that only my consumer will pick up.
My main code:
public class AsyncReceiver implements MessageListener, ExceptionListener
{
public static void main(String[] args) throws Exception
{
//create queue factory
factory = new TibjmsQueueConnectionFactory(serverUrl);
//create queue connection
queueConn = factory.createQueueConnection(userName, password);
queueConn.setExceptionListener(this);
//create queue session
session = queueConn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
//receive message by QueueReceiver
final Queue queue = session.createQueue(queueName);
final QueueReceiver queueReceiver = session.createReceiver(queue);
queueReceiver.setMessageListener(this);
queueConn.start ();
}
You should use JMS selector. Every JMS message can contain properties that you initiate at sender's side.
Message consumer can register to JMS destination (either queue or topic) specifying selector - SQL-like statement that explains which messages does it want to consume. So you can specify your application specific property and then receive relevant messages only.
You could use message selector as shown below
queueReceiver = queueSession.createReceiver(responseQueue, "JMSCorrelationID='"
+ requestMessage.getJMSCorrelationID() +"'");
Here i am using the JMS correlation ID to identify the correct message I need.
Please note that the filter will only work on Message Header and Message properties.
It will not work on the message content.
Details on Message Header & Properties: http://docs.oracle.com/javaee/1.4/tutorial/doc/JMS4.html#wp79367
If you need to filter based on message content, you may have to use QueueSession.MANUAL_ACKNOWLEDGE mode and acknowledge messages that you want. In this case the application has to have the logic to identify messages based on content. Not the ideal design though.

Categories

Resources