qpid Producer and Consumer on the same Session/Topic? - jms

Brocker C++ , Client java jms
It is correct to send a message to a topic and just after create a consumer on this Topic ?
connection = connectionFactory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
topic = (Destination) context.lookup("topicSend");
Destination tempTopic = (Destination) context.lookup("topicSend");
MessageProducer messageProducer = session.createProducer(topic);
messageProducer.send(messageToSend);
... and just subsequent create a Consumer on the SAME Session and topic (topicSend is equal as tempTopic
MessageConsumer messageConsumer = session.createConsumer(tempTopic, MESSAGE_SELECTOR);

Yes. That should be fine. Just be sure to close your producer if you're done with it.

I will suggest to use separate Session for producer and consumer to avoid any issues.

Related

JMS consumer not receiving messages

I just finished a tutorial on JMS, so it's super new to me and I'm trying to understand the basics. I'm using ActiveMQ Artemis if that matters. I created two simple applications, one named Producer and the other named Consumer. I run Producer first then run Consumer. The Consumer application never terminates and does not print the messages to the console. Weirdly, if I do not manually terminate Consumer and run Producer a second time, then I see the messages that Consumer should have received printed on the console. What's going on here? How do I get Consumer to receive and print messages from Producer?
Here's Producer:
public class Producer {
public static void main(String[] args) throws Exception {
InitialContext initialContext = null;
Connection connection = null;
initialContext = new InitialContext();
ConnectionFactory cf = (ConnectionFactory) initialContext.lookup("ConnectionFactory");
connection = cf.createConnection();
Session session = connection.createSession();
Queue queue = (Queue) initialContext.lookup("queue/myQueue");
Topic topic = (Topic) initialContext.lookup("topic/myTopic");
MessageProducer queueProducer = session.createProducer(queue);
MessageProducer topicProducer = session.createProducer(topic);
TextMessage queueMessage = session.createTextMessage("This message is for the queue2");
TextMessage topicMessage = session.createTextMessage("This message is for the topic2");
queueProducer.send(queueMessage);
topicProducer.send(topicMessage);
System.out.println("Message to queue sent: "+ queueMessage.getText());
System.out.println("Message to topic sent: "+ topicMessage.getText());
initialContext.close();
}
}
Here's Consumer:
public class Consumer {
public static void main(String[] args) throws Exception {
InitialContext initialContext = null;
Connection connection = null;
initialContext = new InitialContext();
ConnectionFactory cf = (ConnectionFactory) initialContext.lookup("ConnectionFactory");
connection = cf.createConnection();
Session session = connection.createSession();
Queue queue = (Queue) initialContext.lookup("queue/myQueue");
Topic topic = (Topic) initialContext.lookup("topic/myTopic");
MessageConsumer queueConsumer = session.createConsumer(queue);
MessageConsumer topicConsumer1 = session.createConsumer(topic);
MessageConsumer topicConsumer2 = session.createConsumer(topic);
connection.start();
TextMessage messageReceivedByQueueConsumer = (TextMessage) queueConsumer.receive();
TextMessage messageReceivedByTopicConsumer1 = (TextMessage) topicConsumer1.receive();
TextMessage messageReceivedByTopicConsumer2 = (TextMessage) topicConsumer2.receive();
System.out.println("Message received by queue consumer: "+ messageReceivedByQueueConsumer.getText());
System.out.println("Message received by topic consumer 1: "+ messageReceivedByTopicConsumer1.getText());
System.out.println("Message received by topic consumer 2: "+ messageReceivedByTopicConsumer2.getText());
connection.close();
initialContext.close();
}
}
What you're observing is, in fact, the expected behavior.
Since you run the Producer application first a message is being sent to a queue and a topic when no consumer/subscriber exists on either. The message sent to the queue is stored in the queue because that's how JMS queues work. The message sent to the topic is discarded since there are no subscriptions to receive the message. Again, this is how JMS topics work.
Then when your Consumer application runs the queueConsumer receives the message sent to the queue, but since you're invoking receive() with no timeout on topicConsumer1 the application will simply block forever since there are no messages in the topic consumer's subscription. This blocking prevents the message received from the queue from being printed.
You should run your consuming application first and then run your producer while the consuming application is still running. Then you should see all the messages received and printed as you assumed they would be.

How to send jms message from one server hosting an application to another server hosting another application both are hosted in Websphere servers

I understand if both the message queues (receiver and response) are present in the same server location I can use the JNDI connection factory and queue name
jms/myqueue_qcf1
jms/myqueue1, to connect to the queue and send message to jms/myqueue_qcf2, jms/myqueue2
But in case of Interserver connectivity, will this be the same
When the firewall b/w both the servers is opened.
The MQ myqueue2 is setup as remote mq in Websphere.
Any help with code reference would be appreciable.
public void myAppListener extends MessageListener{ //getting message from MQ1 -
//sent by some other application - MQ1 is Local
//in appServer1
private static final String JMS_LC_JNDI_NAME = "jms/liftcargo_lara_qcf";
private static final String JMS_LC_QUEUE_NAME = "jms/APP.OUT.REQUEST";
public void onMessage(Message msg){
try{
TextMessage requestMessage = (TextMessage) msg;
String reqMessage = requestMessage.getText();
String correlationId = requestMessage.getJMSCorrelationID();
sendXMLToNextAppMQ(reqMessage , correlationId)
}
}
public static void sendXMLToNextAppMQ(String message, String correlID) throws JMSException { //The MQ to which the message is forwarded to is a Remote MQ, in different server appServer2
try {
InitialContext context = new InitialContext();
QueueConnectionFactory queueConnectionFactory =
(QueueConnectionFactory)context.lookup(JMS_LC_JNDI_NAME);
System.out.println("Connection Factory data :: "+queueConnectionFactory.toString());
Queue queue = (Queue) context.lookup(JMS_LC_QUEUE_NAME);
System.out.println("Check Queue Name :: "+queue.toString());
QueueConnection queueConnection = queueConnectionFactory.createQueueConnection();
QueueSession session = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
QueueSender queueSender = session.createSender(queue);
TextMessage message1 = session.createTextMessage();
message1.setText(message);
message1.setJMSType("Tunnel message from CCAM.LARA.OUT.REQUEST MQ to
LIFTCARGO.OUT.LARA.REQUEST MQ");
message1.setJMSDestination(queue);
message1.setJMSCorrelationID(correlID);
queueSender.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
queueSender.send(message1);
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (JMSException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
In Method sendXMLToNextAppMQ (i.e., tunnel the msg recieved in MQ1 in appServer1 to MQ2 in appServer2) is there any other jndi properties needed to mention to connect appServer1 to MQ2 in appServer2 (firewall is opened b/w appServer1 & appServer2)
If your target request queue is on a different server, your application will still have the same code, but the name you supply will not be the name of a QLOCAL on the queue manager you are connected to, but instead will be a QREMOTE. If you are using JNDI to refer to your queues, you don't even have to change the name of the queue, only the referenced real MQ queue name need be changed.
For example, if you were using the command line JMSAdmin tool, you'd change the name in the QUEUE attribute only:-
DEFINE Q(myqueue2) QUEUE(Q.FOR.REQUESTS)
Then on your two queue managers, there would be definitions that look something like these:-
Local QM (QM1)
DEFINE QREMOTE(Q.FOR.REQUESTS) RNAME(WORK.QUEUE) RQMNAME(QM2) XMITQ(QM2)
DEFINE QLOCAL(QM2) USAGE(XMITQ) DESCR('Transmission queue for messages to QM2')
DEFINE QLOCAL(MY.REPLY.Q) DESCR('My application queue for responses')
DEFINE CHANNEL(TO.QM2) CHLTYPE(SDR) CONNAME('(qm2.machine.com(1414)') XMITQ(QM2)
DEFINE CHANNEL(TO.QM1) CHLTYPE(RCVR)
Remote QM (QM2)
DEFINE QLOCAL(WORK.QUEUE)
DEFINE QLOCAL(QM1) USAGE(XMITQ) DESCR('Transmission queue for messages to QM1')
DEFINE CHANNEL(TO.QM2) CHLTYPE(RCVR)
DEFINE CHANNEL(TO.QM1) CHLTYPE(SDR) CONNAME('qm1.machine.com(1414)') XMITQ(QM1)
In addition, it is good practice in a request/reply application, to provide the name of the queue where the response should be sent as part of the request message, and to code the responding application to read those fields (ReplyTo fields) and use them to send the reply message back, thus not requiring an extra QREMOTE definition on the Remote QM (QM2 in my example).

How to not consume JMS message when an error happen

I'm creating a topic with multiple consumer, each of them identified by a clientId.
The behaviour I'm seeing is :
A message come in
I throw a runtime exception in one of my consumer
I would like this consumer to try to consume again the same message but it goes straight to the next one.
Is there a way to stop the consumption after 3 try for instance ?
You could create a transacted JMS Session:
// create JMS Session from JMS Connection
session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
and use the Session.rollback() method to indicate that you need to see that message again:
public void onMessage(Message message)
{
msgsReceived++;
System.err.println("received: " + message);
if( msgsReceived<3 ) { // simulating an error case
session.rollback();
} else {
session.commit();
}
you should then see this message 3 times until you commit it the last time.

Topic not able to receive message

I have a non durable Topic client which is supposed to receive messages asynchronously using a listener.
When message is published on Topic, i can see on admin console that message is published and consumed but my client never receives it.
Client is able to establish connection properly as i can track it on console.
Any suggestions?
EDIT:
Did some more analysis and found that issue is with API used for connection.
I was able to listen to messages when i use following code:
TopicConnection conn;
TopicSession session = conn.createTopicSession(false, TopicSession.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic(monacoSubscriberEmsTopic);
conn.start();
tsubs = session.createSubscriber(topic);
tsubs.setMessageListener(listener);
But when i use following code then it doesn't work:
DefaultMessageListenerContainer listenerContainer = createMessageListenerContainer();
private DefaultMessageListenerContainer createMessageListenerContainer() {
DefaultMessageListenerContainer listenerContainer = new DefaultMessageListenerContainer();
listenerContainer.setClientId(clientID);
listenerContainer.setDestinationName(destination);
listenerContainer.setConnectionFactory(connectionFactory);
listenerContainer.setConcurrentConsumers(minConsumerCount);
listenerContainer.setMaxConcurrentConsumers(maxConsumerCount);
listenerContainer.setPubSubDomain(true);
listenerContainer.setSessionAcknowledgeModeName(sessionAcknowledgeMode);
if (messageSelector != null)
listenerContainer.setMessageSelector(messageSelector);
listenerContainer.setSessionTransacted(true);
return listenerContainer;
}
listenerContainer.initialize();
listenerContainer.start();
What is wrong with second approach?

TIBCO EMS set queue exclusive property programmatically

How can I set the exclusive/non-exclusive property of a tibco queue programmatically? I want to be able to set the queue as non-exclusive when I crate it in my application.
For example, if I use the following code to craete the queue:
QueueConnectionFactory factory = new TIBCO.EMS.QueueConnectionFactory(serverUrl);
QueueConnection connection = factory.CreateQueueConnection(userName, password);
QueueSession session = connection.CreateQueueSession(false, Session.AUTO_ACKNOWLEDGE);
TIBCO.EMS.Queue queue = session.CreateQueue(queueName);
How can I set the queue's properties?
You will need the TibjmsAdmin API to do that. The JavaDoc of the API can be found here
Then try this:
TibjmsAdmin jmsAdmin = new TibjmsAdmin("tcp://localhost:7222", "admin", "admin");
QueueInfo qi = jmsAdmin.getQueue("my.queue");
qi.setExclusive(true);
HTH,
Hendrik

Resources