I am using Glassfish JMS.
I am able to add messages to a queue.
I can see the messages using the QueueBrowser object.
However the MessageConsumer (nor the QueueReceiver) cannot receice any message and return null.
Message expiration is set to 0 and I remember to open the connection.
Any ideas?
Here is the code:
Session session = null;
Connection conn = null;
try
{
InitialContext jndi = new InitialContext();
ConnectionFactory qFactory = (ConnectionFactory)jndi.
lookup("myConnectionFactory");
conn = qFactory.createConnection();
conn.start();
Queue queue = (Queue)jndi.lookup("myQueueName");
session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
QueueReceiver mc = ((QueueSession)session).createReceiver(queue);
Object m = mc.receive(2000);
//m is NULL!
QueueBrowser browser = session.createBrowser(queue);
for(Enumeration e = browser.getEnumeration(); e.hasMoreElements(); )
{
//there are many messages here...
}
That would be good to have the client code.
Similar thing happened to me when not properly committing/closing the connection on the sender side. The message would be visible when using the admin console, however, not available yet to the MDB.
Hope it helps.
Does this code run in the appserver? If it does, I'd obtain the required objects via annotations, and for a message receiver I'd use a MDB.
If this is a piece of standalone code, I had a hell of a time getting a JNDI based client working, I reverted to using the "raw" Java API.
I witnessed the same behavior happening after the first session commit, meaning that before the messages where received correctly. In my case the issue was that I was re-creating the receiver while keeping the same session.
As pointed out in this article:
Creating temporary destinations, consumers, producers and connections
are all synchronous request-response operations with the broker and so
should be avoided for processing each request as it results in lots of
chat with the JMS broker.
The solution was as simple as reusing the same receiver.
Related
How can I check whether a queue exists on a JMS server using the Java API? I don't want to send or receive any data to the queue for now, just verify that the queue exists. Also, the queue may be empty.
Here is my code sample. I have removed the error handling for simplicity.
Connection connection = null;
Session session = null;
connection = factory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//I was hoping this next line would throw an exception if the queue does not exist
Queue queue = session.createQueue(queueName);
My JMS server is TIBCO EMS. I'm hoping for a solution that works on versions 5-7.
Solution
I followed the recommendation in the accepted answer but created a browser instead. The following line threw an exception as desired:
QueueBrowser browser = session.createBrowser(queue);
This is dependent on the provider, but you wont know in most cases until you create the session type, such as session.createConsumer. Simply creating a consumer this way will not consume any messages until you do a receive. And it is here the behavior may change from provider to provider and configuration of the server.
For example with ActiveMQ, assuming there are no permissions blocking the user you are connecting with, the queue is created automatically when you create the session type.
With WebSphere MQ, the queue has to be defined by an admin. If it does not exist, the queue manager will return an exception with a reason code of 2085 (UNKNOWN_OBJECT_NAME).
Outside of this, you'd need to see if the particular provider had a way to access a list of queues. Using the above examples, ActiveMQ you can get the list of queues using JMX, with WebSphere MQ, you can do this if you have permissions to send PCF commands to the queue manager.
Try creating a consumer or producer off the Session passing in the queue object you just created:
session.createConsumer(queue);
This should throw an InvalidDestinationException if the queue (or topic) does not exist.
I am trying to write a test for a class used to send a JMS-message to ActiveMQ. What I am trying to accomplish is to get a method in the class under test to send the message to an ActiveMQ instance in localhost, and then pick the message up in the test and verify that it is correct.
I have chosen this as my broker url: vm://localhost?broker.persistent=true, which means that a local ActiveMQ instance will be created, and the messages stored in a KahaDB (which is also created.) (I tried using broker.persistent=false, but since the method under test has a finally-clause that closes the connection, the in-memory messages are then lost before I can retrieve them.)
In order to retrieve the message and verify it, I have the following code:
//call method under test to send a message
//create a ConnectionFactory with url vm://localhost?broker.persistent=true
final Connection connection = connectionFactory.createConnection();
connection.start();
final Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
final Destination dest = session.createQueue("my.queue");
final MessageConsumer messageConsumer = session.createConsumer(dest);
Message message = messageConsumer.receive(1000);
messageConsumer.close();
session.close();
connection.close();
My problem is that upon running this code, the messages are not being removed from KahaDb! Upon multiple test runs, the message added the first time will be read again and again. Am I missing something here, or is this a bug in KahaDB/ActiveMQ? I am using ActiveMQ 5.7.0.
Try
final Session session =
connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
otherwise you get a "transacted" session.
Otherwise, if you really want to have a "transacted" session, you have to call
// 2nd parameter is ignored, if the session is transacted
final Session session =
connection.createSession(true, -1);
// Read messages
session.commit();
messageConsumer.close();
session.close();
connection.close();
in order to remove all messages you have read during this session.
For your reference, there is an excellent overview from Javaworld regarding Transactions and redelivery in JMS. It covers additional possibilities as well (using Session.CLIENT_ACKNOWLEDGE to acknowledge messages individually, for example).
You've created a transacted session but never called commit. In this case when the close method is called the in-flight transaction is rolled back and so the message that you received is placed back into the queue and will be redelivered to another consumer. You can test this by querying the redelivered count on the message and see that it increases each time. To consume the message call session.commit() before closing the session.
I have a producer which connects to ActiveMQ broker to send me messages to the client.
Since it expects some response from the client, it first creates a temp queue and associates it to the JMS replyto header.
It then sends the message over to the broker and waits for the response on temp queue from the client.
Receives the response from the client over the temp queue, performs required actions and then exits.
This works fine most of the times, but sporadically the application throws error messsages saying " Cannot use queue created from another connection ".
I am unable to identify what could cause this to happen as the temp queue is being created from the current session itself.
Did anyone else come across this situation and knows how to fix it?
Code snippet:
Connection conn = myJmsTemp. getConnectionFactory().createConnection();
ses = conn.createSession(transacted,ackMode);
responseQueue = ses.createTemporaryQueue();
...
MyMessageCreator msgCrtr = new MyMessageCreator(objects,responseQueue);
myJmsTemp.send(dest, msgCrtr);
myJmsTemp.setReceiveTimeout(timeout);
ObjectMessage response = (ObjectMessage)myJmsTemplate.receive(responseQueue);
Here MyMessageCreator implements MessageCreator interface.
All am trying to do is send a message to the broker and wait for a response from the client over the temp queue. Also am using a pooled connection factory to get the connection.
You get an error like this if you have a client that is trying to subscribe as a consumer on a temporary destination that was created by a different connection instance. The JMS spec defines that only the connection that created the temp destination can consume from it, so that's why the limitation exists. As for the reason you are seeing it its hard to say without seeing your code that encounters the error.
Given that your update says you are using the Pooled connection factory I'd guess that this is the root of you issue. If the consume call happens to use a different connection from the Pool than the one that created the temp destination then you would see the error that you mentioned.
I'm having a bit of trouble with understanding JMS from a performance perspective. We have this very straightforward code in our application:
QueueConnection connection = null;
QueueSession session = null;
QueueSender sender = null;
TextMessage msg = null;
try {
// The JNDIHelper uses InitialContext to look up things
QueueConnectionFactory qcf = JNDIHelper.lookupFactory();
Queue destQueue = JNDIHelper.lookupQueue();
// These objects are created for every message, which is quite slow
connection = qcf.createQueueConnection();
session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
sender = session.createSender(destQueue);
// This is the actual message
msg = session.createTextMessage(xmlMsg);
sender.setTimeToLive(0);
sender.send(msg);
}
finally {
// Close all objects again
JMSUtilities.safeClose(sender);
JMSUtilities.safeClose(session);
JMSUtilities.safeClose(connection);
}
The code is correct, but probably some of the above artefacts could be reused for several messages. These are our configurations:
We use Oracle Weblogic 10.3.3
Weblogic connects to IBM MQ 7.0 (Problem also appears with 6.0) for JMS
The above logic is executed by a single thread on a backend server. It would be simple to keep some objects (QueueConnection, QueueSession, QueueSender) in memory as there is no concurrency involved.
My questions
Which types of objects can be shared among several messages? (of course we'd include error recovery, restoring those shared objects)
What are best practices to improve performance?
Here are some relevant parts of the jms spec:
section 2.8 Multithreading
JMS Object Supports Concurrent Use
Destination YES
ConnectionFactory YES
Connection YES
Session NO
MessageProducer NO
MessageConsumer NO
section 4.4.14 Serial Execution of Client Code
JMS does not cause concurrent execution of client code unless a client explicitly requests it. One way this is done is to define that a session serializes all asynchronous delivery of messages
So as already mentioned reuse as much as possible. Reuse the ConnectionFactory, Connection and Destinations for all Threads. For each Thread reuse consumers and producers.
If you are reusing a JMS connection beware, that the JMS Provider will multiplex different sessions on that connections. So even if it is safe to reuse connections it might be faster to create a connection for every session you need.
The only thing you need to create again and again is the msg itself - if you are sending to the same queue.
So yes, you can remember the Connection, Session and Sender.
Define "to share".
If you mean to share among different threads this is very dangerous. You can safely share QueueConnectionFactory object as well as the JMS Connection object. You must not share Session, Sender/Consumer or Message objects. Thats the way how TIBCO EMS works I am not sure about IBM platform but I guess this is very same.
If you can be sure your "send" method is not called by different threads you can encapulate this into a MySender class with Connection, Session and Sender member variables. But watch out! Do properly close the resources on exit. Thats what Heiko Rupp recommends. Somthing like this:
class MySender {
private QueueConnection connection = null;
private QueueSession session = null;
private QueueSender sender = null;
public MySender(...) { /* initialize conn/sess/sender */ }
public send(String xmlMsg) { /* sender.send(session.createTextMessage(xmlMsg)) */ }
public close() { /* close all resources */ }
}
Regarding performance. There is no much room for improvement in JMS standard. Keep messages small and optimize server setting. Use durable destinations only when you need it etc. Read documentation for your platform. But on the client side there is not much room. Some platforms offers additional features to JMS that allows some extra performance gain (batch sends etc) but it depends on the platform. I dont know IBM.
I am able to query for queues by invoking a GET_DESTINATIONS operation using JMX. With that I will receive the queue info (attributes).
I would like now to query the messages that are stored in this queue, is that possible? Could someone give me some direction?
I have already tried using this code
ConnectionFactory connectionFactory = new
com.sun.messaging.QueueConnectionFactory();
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue myQueue = session.createQueue(string);
QueueBrowser browser = session.createBrowser(myQueue);
Enumeration msgs = browser.getEnumeration();
if (!msgs.hasMoreElements()) {
System.out.println("No messages in queue");
} else {
while (msgs.hasMoreElements()) {
Message tempMsg = (Message) msgs.nextElement();
System.out.println("Message: " + tempMsg);
}
}
connection.close();
But for some reason O can't access the same queue as using JMX. I didn't made any research on that because I want to use JMX as the access standard.
I am still trying to find any JMX operation that could help me, but I am not finding anything that could help me.
Could you please give me some hints what can I look for?
thank you,
Oscar
Edit: just to let you know: I don't want to consume the queues, I want a similar behavior to the Browser, in which I can read the messages without removing them from the queue.
QueueBrowser browser = null;
try{
Queue myQueue = session.createQueue(getName());
//Create the browser and session to be able to iterate
browser = session.createBrowser(myQueue);
Enumeration msgs = browser.getEnumeration();
This code will give you the messages, then just iterate through it and you can get get infos about the message and its content