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.
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 have ejb-jar.xml which contain <message-selector> destinationInstance IN(a', 'b','c')</message-selector>.
How we can implement message selector in standalone java program.
JMSMessageListener has been registered on queue and whenever there is message on queue , onMessage method of listener will be called.
I Have Try this.? Does It work...
public void messageSelector() {
List<String> url=StCotants.getUrls();
for(String obj:url){
ConnectionFactory factory = new ActiveMQConnectionFactory(obj);
System.out.println(factory);
try{
Connection connection = factory.createConnection();
connection.start();
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue("queue");
MessageConsumer consumer = session.createConsumer(queue,"destinationInstance IN('a', 'b','c')");
XyzMDBClient xyzClientlistener=new XyzMDBClient();
consumer.setMessageListener(xyzClientlistener);
}
catch(Exception e){
e.printStackTrace();
}
}
Back in time I was looking for examples on how to use message selectors and I found this site very usefull: How to use Message Selectors to filter messages
Very concise article about what selectors are and how to use them.
Message Selectors allow you to filter the messages that a MessageConsumer will receive. The filter is a relatively complex language that mimics the syntax of an SQL WHERE clause. The selector can use all message headers and properties for filtering, but can not use the message content.
Selectors are mostly useful for Topics that broadcast a very large number of messages to its subscribers.
They way selectors work depends on the destination type:
*On Queues, only messages that match the selector will be returned. Others stay in the queue (and thus can be read by a MessageConsumer with different selector).
*On Topics, messages that do not match the selector will be ignored as if they have not been published.
In order to create a selection, you need to pass it to the createConsumer or createDurableSubscriber invocation:
Code examples:
Session session = ...
MessageConsumer consumer1 = session.createConsumer(queue,
"(releaseYear < 1980) OR (releaseYear > 1989)");
MessageConsumer consumer2 = session.createConsumer(queue,
"(releaseYear BETWEEN 1980 AND 1989) AND title LIKE 'Michael%'");
MessageConsumer consumer3 = session.createConsumer(queue,
"(releaseYear = 1982) OR (title = 'Thriller')");
MessageConsumer consumer4 = session.createDurableConsumer(queue,
"title IN ('Off the wall', 'Thriller', 'Bad')");
When you create a consumer, you can mention the selector as second argument
session.createConsumer(queue,"destinationInstance IN('a', 'b','c')")
session.setMessageListener(...)
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 am receiving messages from aActive MQ queue.
Is there a way to receive a number of messages in one time? or is that have to be done with a loop?
Further more, if i want to take say 30 messages run a procedure, and only if that procedure works return a message.acknowledge(); for all of them.
I mean i dont want to erase those 30 from the queue if the procedure fails.
Thanks.
You'll have to do it in a loop. Usually, it's best to use message-driven beans for consuming messages, but it's not suitable for this case, because they take message by message and you cannot specify the exact number. Thus, use MessageConsumer and manual transactions:
#Resource
UserTransaction utx;
#Resource(mappedName="jms/yourConnectionFactory");
ConnectionFactory cf;
#Resource(mappedName="jms/yourQueue");
Queue queue;
..
Connection conn = null;
Session s = null;
MessageConsumer mc = null;
try {
utx.begin();
conn = cf.createConnection();
s = conn.createSession(true, Session.CLIENT_ACKNOWLEDGE); //TRANSACTIONAL SESSION!
mc = s.createConsumer(queue);
conn.start(); // START CONNECTION'S DELIVERY OF INCOMING MESSAGES
for(int i=0; i<30; i++)
{
Message msg = mc.receive();
//BUSINESS LOGIC
}
utx.commit();
} catch(Exception ex) {
..
} finally { //CLOSE CONNECTION, SESSION AND MESSAGE CONSUMER
}
I don't have any experience in ActiveMQ. But I think in case of queue listeners, basic logic should be the same independent to the queue implementation.
For your first question I don't know any way of retrieving multiple messages from a queue. I think best way would be to fetch it one by one inside a loop.
For your second question, message will not be discarded from the queue till the underlying transaction which read the message commits. So you could read whole bunch of messages in a single transaction and roll it back in case of an error. It shouldn't erase any existing messages from the queue.
May I ask why do you need 30 messages to run a procedure. Usually when we use a queue, each message should be able to process independently.
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.