JMS Queue with 2 Listeners - java

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.

Related

How to use Message Selectors to filter messages In JMS

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(...)

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.

Using JMS to put a Message in a MQ Queue And Consuming Message by SPLUNK

Im trying to put a message in a MQ Queue. Here is my source code:
QueueConnection queueConn;
QueueSession queueSession;
QueueSender queueSender;
queueConn = connectionFactory.getConnection();
queueSession = queueConn.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
queueSender = queueSession.createSender(queueSession
.createQueue(KEY_CONFIG_QUEUE_NAME));
queueSender.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
TextMessage message = queueSession.createTextMessage(logBase);
queueSender.send(message);
I don't have the source code from the queue consumer, that is the one who sends the messages to SPLUNK. But at SPLUNK console, I could realize that the message is composed by JMS HEADER + my text message (logBase).
Id like the messages without JMS Header. Could someone help me to understand where the problem is? Could be at consumer? Maybe a wrong or missing SPLUNK config??
Assuming that you cannot change the source code at the consumer, there is a way to administratively do this. You can change the queue definition so that these message properties are not given to the getting application.
ALTER QLOCAL(q-name) PROPCTL(NONE)
Related Links
PROPCTL queue options
If you able and happy to change the producer, you could look into the Target Client property of the MQ JMS destination.
This informs the JMS client that the consuming application is not a JMS app so it removes the extra headers.

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).

Unable to get ActiveMQ to dequeue

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.

Categories

Resources