Does JMS QueueBrowser getEnumeration requires Connection Start - java

I want to know if its required to call JMS Connection start() before we do QueueBrowser browse(). Could not find anything in javadoc about start() as a pre-op to browse() and each vendor samples for browse seems to be different. Some of them calls while other’s don’t.
I ask this as ActiveMQ does not browse messages if I don’t perform start().
ConnectionFactory factory = new ActiveMQConnectionFactory("tcp://**:**");
Connection connection = factory.createConnection();
connection.start();
Session session = connection.createSession(false, 1);
TextMessage message = session.createTextMessage();
message.setText("This is a sample message");
Queue dest = new ActiveMQQueue("Sample");
MessageProducer producer = session.createProducer(dest);
producer.send(message);
QueueBrowser browser = session.createBrowser(dest);
Enumeration<Message> messages = browser.getEnumeration();
/* Iteration code here
* If connection.start() is'nt called, no element in returned collection
* If connection.start() is called, the returned collection contains
* queue elements.
*/
..
Could not find java doc talking anything specific to start before peek on the queue. Any idea ?

Yes, Connection.Start() is required. QueueBrowser is similar to MessageConsumer with only difference being QueueBrowser does not remove message from JMS provider. Without application calling Connection.Start method JMS provider will not deliver messages.

Related

RabbitMQ not failing send when messages are rejected due to queue size

I am using RMQ and it's JMS client to publish messages to RMQ (this is a requirement i have, I can't use their java client instead of JMS client).
So, basically I do this:
RMQConnectionFactory factory = new RMQConnectionFactory() ;
factory.setUsername(props.getProperty("rmq.username"));
factory.setPassword(props.getProperty("rmq.password"));
factory.setHost(props.getProperty("rmq.host"));
factory.setVirtualHost(props.getProperty("rmq.virtualHost"));
factory.setPort(Integer.parseInt(props.getProperty("rmq.port")));
Connection connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
String queueName = managerProps.getProperty("rmq.queue.name");
Queue queue = session.createQueue(queueName);
producer = session.createProducer(queue);
TextMessage msg = session.createTextMessage(text);
msg.setText(text);
producer.send(msg);
I have a policy set up on RMQ overflow: reject-publish, so if it's over the limit RMQ is supposed to send a nack when the queue is full, but I don't seem to get it.
The question is - how do I determine if the message was rejected? I assume the producer.send(msg) to be synchronous and throw exception if the message is not published, but I don't get any exceptions, it just looks like everything got published.
JMS spec has a send(msg, CompletionListener) with a listener with two methods onCompletion and onException, but it doesn't look like RMQ JMS client implemented this method.
Is there another way to make sure that message made it through?
RabbitMQ use Publisher Confirms to guarantee that a message isn't lost, so if your Queue overflow behavior is reject-publish, the confirm channel will got a nack. It is also contains in many AMQP client.
But in JMS client, I have check the code in rabbitmq-jms-client, and no send implementaion contains CompletionListener. So if you want to enjoy reliable publish, please use AMQP client.
I did some digging, the CompletionListener is part of JMS 2.0 and RMQ only implements JMS 1.1, that's the reason it's not there.
But it looks like I can do something with transactions. I would need to change the code like this:
RMQConnectionFactory factory = new RMQConnectionFactory() ;
// ... skipping the code here
connection.start();
// set session to be transacted
session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
String queueName = managerProps.getProperty("rmq.queue.name");
Queue queue = session.createQueue(queueName);
producer = session.createProducer(queue);
TextMessage msg = session.createTextMessage(text);
msg.setText(text);
producer.send(msg);
// commit transaction
session.commit();
This will work if the queue is not full, but will throw an exception after a rejected message with this:
Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method(reply-code=406, reply-text=PRECONDITION_FAILED - partial tx completion, class-id=90, method-id=20)
I can then catch the exception and do what I need to do to resend/save the message.

How to set Persistence to JMS client?

I set up a connection with Weblogic IBM Webpsphere MQ through JMS with using a secure channel using SSL.
My application on Weblogic received message from MQ.
Sending answer to reply queue.
The response header is present MQMD, it fills java. In parameter Persistence JMS send value "1". Other system need to received value "0" at Persistence. How to set this parameter to java?
I guess that parameter is javax.jms.deliverymode. But how to set it i don't know.
Anyway thank you for help.
The corresponding property on JMS is the delivery mode (Int parameter to be set) to set Persistent and non persistent messages.
You can refer this URL from IBM for details
You should try like this:
public String sendMessage(ConnectionFactory connectionFactory,
Destination destination,
Destination jmsReplyTo,
CorrelationType correlationType,
CallOptions<String> callOptions,
String rqUid,
JMSAbstract transport) throws JMSException {
Connection connection = null;
Session session = null;
MessageProducer producer = null;
try {
connection = connectionFactory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
producer = session.createProducer(destination);
// Set JMS DeliverMode (1/2)
producer.setDeliveryMode(1);
// create message
Message message = createTextMessage(session, jmsReplyTo, correlationType, callOptions, rqUid, transport);
// send message
producer.send(message);
return correlationType.getCorrelationId(message);
} finally {
closeResource(connection, session, null, producer, rqUid);
}
}
It`s just a java example. Also you can set persistence flag in Queue configuration in IBM WebSphere. I mean MQQueue have method setPersistence. If you using IBM java objects in your project, you can set persistence by calling that method:
MQQueue mqQueue = new MQQueue("QueueName");
mqQueue.setPersistence(1);
I The answer of 0x5a4d is ok but better to use this like IBM best practices
//Persistentmode = 1
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
//Persistentmode = 2
producer.setDeliveryMode(DeliveryMode.PERSISTENT);

Why doesnt JMS MessageProducer method setTimeToLive or send() work?

I tried to use either of those 2 methods such that the message that I sent via that producer will have an expiration. For instance, I had set the time to live to 5 seconds (5 000 ms) but even after 5 s, after I subscribe I still get the message from the consumer. I wonder why...
The specification says as below,
When a message's expiration time is reached, a provider should discard
it. The JMS API does not define any form of notification of message
expiration. Clients should not receive messages that have expired;
however, the JMS API does not guarantee that this will not happen.
So its totally implementation specific. Your Publisher/Subscriber should be implemented in such a way to discard the expired messages as per the JMS specification, if not you are bound to receive those messages even after expiry time.
Maybe you're doing something wrong. Try my test
String url = "tcp://localhost:61616";
BrokerService broker = new BrokerService();
broker.addConnector(url);
broker.start();
ConnectionFactory cf = new ActiveMQConnectionFactory(url);
Connection conn = cf.createConnection();
Session s = conn.createSession(true, Session.AUTO_ACKNOWLEDGE);
ActiveMQQueue q = new ActiveMQQueue("test");
MessageProducer p = s.createProducer(q);
p.send(s.createTextMessage("!!!!"), DeliveryMode.NON_PERSISTENT, 0, 1000); // ttl = 1s
Thread.sleep(2000);
MessageConsumer c = s.createConsumer(q);
System.out.println("Received: " + c.receiveNoWait());
System.exit(1);
it uses activemq-all-5.6.0.jar, TTL = 1s, you will see that if you sleep more than 1s after you sent the message it disappears from the queue.

Synchronous behavior with JMS Topic

I have below pseudo code of flow which uses queue to send the message and then listen to topic synchronously. The underlying JMS provider is Tibco EMS.
//Send to Queue
Connection connection = createConnection(); // get the JMS connection
Session session = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue("sample.queue");
MessageProducer messageProducer = session.createProducer(queue);
Message message = createMessage(); //create JMS message
messageProducer.send(message);
Now, I listen to a topic using same session object created and wait till there is a response.
Topic topic = session.createTopic("sample.topic");
MessageConsumer messageConsumer = session.createConsumer(topic);
//wait for the reply.
Message responseMessage = messageConsumer.receive(60000);
if(responseMessage != null) {
System.out.println("Message received..");
}
The problem that I am facing is that the message object is coming out as null. I tested with a jms monitoring tool and the topic does have some message, but the above code is not able to pick it up even after 60 secs.
Any idea what am I missing here ?
Figured it out. Just before calling messageConsumer.receive();, a call to connection.start() to start the delivery of the message.

JMS client does not receive messages

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.

Categories