There might be a stupid simple answer to this, but I'm trying to use ActiveMQ to pass messages between producers and consumers. I will have many producers and many consumers, but I want each message to be delivered only once among the consumers. This would seem to mean that I cannot use Topics, since they would deliver messages to all consumers who are listening, and I want only one Consumer to receive each message.
My problem is that I am able to receive messages, but the messages are not dequeued. So if I restart my consumer process, all of the messages are reprocessed. This answer seems pertinent but does not seem to apply since I can't create durable queue consumers, only durable topic consumers (unless I'm missing something in the API docs).
My code is as follows.
TopicConnectionFactory factory = new ActiveMQConnectionFactory(props.getProperty("mq.url"));
Connection conn = factory.createConnection();
Session session = conn.createSession(true, Session.CLIENT_ACKNOWLEDGE);
Queue queue = session.createQueue(props.getProperty("mq.source_queue"));
conn.start();
MessageConsumer consumer = session.createConsumer(queue);
Then later on
Message msg = consumer.receive();
msg.acknowledge();
if (!(msg instanceof TextMessage)) continue;
String msgStr = ((TextMessage)msg).getText();
This is my current code. I have tried with Session.AUTO_ACKNOWLEDGE and without msg.acknowledge(). Any working permutation of this code seems to retrieve the messages, but when I restart my consumer, all of the messages get received again, even if they have been received prior to the restart.
You created the session as a transacted Session and therefore need to call, session.commit if you want to inform the broker that all messages are now consumed and don't need to be redelivered. If you don't set the first argument to createSession to true then the Ack mode is respected otherwise its ignored, one of the oddities of the JMS API I'm afraid. If you do this:
ConnectionFactory factory = new ActiveMQConnectionFactory(props.getProperty("mq.url"));
Connection conn = factory.createConnection();
Session session = conn.createSession(false, Session.CLIENT_ACKNOWLEDGE);
Queue queue = session.createQueue(props.getProperty("mq.source_queue"));
conn.start();
MessageConsumer consumer = session.createConsumer(queue);
Then this would work:
Message msg = consumer.receive();
msg.acknowledge();
Otherwise you need to do:
Message msg = consumer.receive();
session.commit();
But keep in mind that for a single message transactions don't really make sense to client ack with no transaction is a better option.
Related
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.
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.
My JMS consumer produces any number(Say n) of messages on JMS queue during the day. First I am evaluating the synchronous processing of message
Say at 23.0 clock, now I want to consume all messages. Here is main method
Here's how to do it sequentially(not concurrently) :-
Do I need to call consumer.receive() method n times (till returns consumer.receive() return null )on single consumer?
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("vm://localhost");
// Create a Connection
Connection connection = connectionFactory.createConnection();
connection.start();
// Create a Session
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// Create the destination (Topic or Queue)
Destination destination = session.createQueue("TEST.FOO");
// Create a MessageConsumer from the Session to the Topic or Queue
MessageConsumer consumer = session.createConsumer(destination);
// Wait for a message
Message message = consumer.receive();
How to do it concurrently :- I want to process the 20 messages concurrently
Do I need to create 20 thread where each thread creates its own consumer and the receive the message?
To process 20 messages sequentially, and you know you will receive at least 20 messages, put the MessageConsumer.receive() call into a loop 20 times. Note that MessageConsumer.receive() without a timeout argument will not return null if there are no messages on the queue. It will block until it receives a message, or until close() is called. If you use MessageConsumer.receive(longTimeoutValue), it will wait for longTimeoutValue to receive a message, and will return null if no message is received by then.
For concurrent message processing, the ActiveMQ docs provide a sample of how to use multiple consumers here: http://activemq.apache.org/hello-world.html, which you can modify for your purposes. The sample creates a new connection per thread, but according to http://activemq.apache.org/multiple-consumers-on-a-queue.html, you are only required to create a session and consumer per thread.
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.
I want to build a simple consumer program (in java) to get all messages stocked in an ActiveMQ subject.
I have a producer which send TextMessage in the queue.
But I don't know how to start to write my consumer for retrieving old messages and wait for new one.
If you have an example, thanks!
This is my Producer: http://pastebin.com/uRy9D8mY
This is my Consumer: http://pastebin.com/bZh4r66e
When I run my producer before my consumer, then run the consumer, I got nothing.
When I run my consumer then my producer, I add 72 messages in the queue but my consumer got only 24 message...
I suggest reading this tutorial (as does Apache ActiveMQ) SUN Jms tutorial
There are many ways to write JMS/ActiveMQ programs, using various frameworks such as Spring, or by using plain java.
Essentially, write a listener class like this:
public class MyListener implements MessageListener{
public void onMessage(Message message){
// Read and handle message here.
}
}
Since you already are producing message, I assume you have connection up and running.
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
consumer = session.createConsumer("MyQueue");
listener = new MyListener ();
consumer.setMessageListener(listener);
connection.start();
// At this point, messages should arrive from the queue to your listener.
Then there are some error handling code not included in this example, but you should be able to figure it out with the help of the tutorial and JMS documentation.
Using the code given below you can read all the messages en-queued in the queue.
In this code the while loop is an unending loop it will iterate all
the messages in the queue.
Once there is no messages in the queue it will wait for 5 seconds then automatically stops the connection and breaks
the loop.
If you required an unending consumer which will read all the messages whenever newly added to the queue, then remove the else part, so the program will not terminate.
ConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");
Connection con = factory.createConnection();
Session session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue("tmp_queue2");
MessageConsumer consumer = session.createConsumer(queue);
con.start();
while (true) {
Message msg = consumer.receive(5000);
if (msg instanceof TextMessage) {
TextMessage tm = (TextMessage) msg;
System.out.println(tm.getText());
}
else{
System.out.println("Queue Empty");
con.stop();
break;
}
}
Hope this consumer program will helps people who were new to ActiveMQ.