I'm testing open MQ for send and receive messages in my project. I have no problem to configure it to send a synchronous message, but i can't find any way in the official documentation to configure the message to be consumed 15 minutes after the producer send a message, and continue call the consumer if an error appears.
offical documentation: http://dlc.sun.com/pdf/819-7757/819-7757.pdf
my method whom send a message
public void sendMessage(EntradaPrecomven entrada){
try{
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");
env.put(Context.PROVIDER_URL, "file:///C:/mqteste");
// Create the initial context.
Context ctx = new InitialContext(env);
// Look up the connection factory object in the JNDI object store.
autenticisFactory = (ConnectionFactory) ctx.lookup(CF_LOOKUP_NAME);
mdbConn = autenticisFactory.createConnection();
mdbSession = mdbConn.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = (Destination) ctx.lookup(DEST_LOOKUP_NAME);
MessageProducer myProducer = mdbSession.createProducer(destination);
ObjectMessage outMsg = mdbSession.createObjectMessage(entrada);
outMsg.setJMSRedelivered(Boolean.TRUE);
myProducer.send(outMsg);
consumidor = mdbSession.createConsumer(destination);
MessageMDB myListener = new MessageMDB();
consumidor.setMessageListener(myListener);
mdbConn.start();
mdbConn.close();
}catch(Exception e){
try {
mdbSession.rollback();
} catch (JMSException e1) {}
}
}
My listener:
#Override
public void onMessage(Message msg) {
ObjectMessage objMessage = (ObjectMessage) msg;
try {
System.out.println("Received Phone Call:" + objMessage.getJMSRedelivered());
throw new JMSException("TESTE");
} catch (JMSException e) {
e.printStackTrace();
}
}
So, when i call mdbConn.start() the sendMessage() is called, but i want to call 15 minutes after the call. And whatever it sendMessage() does, the message is always removed from the queue. How can i keep the messagen in queue to be called later ?
Thanks!
The message is removed from the broker queue due to the fact that the session you are using is set to auto acknowledge.
mdbSession = mdbConn.createSession(false, Session.AUTO_ACKNOWLEDGE);
This will automatically send an acknowledgement to the broker that the listener has received a message for the consumer with which it is associated once the onMessage() method has executed to completion. This then results in the message being removed from the queue.
If you manually take over the acknowledgement process you can choose to only acknowledge the receipt of the message at a time of your choosing (be that 15 minutes later or whatever criteria you have for the consuming client).
Setting the Session Session.CLIENT_ACKNOWLEDGE will allow you to do this but then you will have to manually send an acknowledge in your consumer code. By calling acknowledge on the message msg.acknowledge() inside your onMessage() method within your listener.
This will then acknowledge the receipt of messages consumed within that session and remove them from the queue.
Pages 46 and 65 in the pdf you quoted are useful for more information on this as is the api
Related
I am trying to connect to solace queues and simply read messages from them. However, I am able to read the messages but the messages are not getting removed from the queue.
Below is the code which I tried:
public void clearMessages() throws Exception {
// Programmatically create the connection factory using default settings
// Create connection to the Solace router
SolXAConnectionFactoryImpl connectionFactory = returnConnFactory();
XAConnection connection = connectionFactory.createXAConnection();
XASession session = connection.createXASession();
Queue queue = session.createQueue(QUEUE_NAME);
connection.start();
MessageConsumer messageConsumer = session.createConsumer(queue);
//session.b
messageConsumer.setMessageListener(new MessageListener() {
#Override
public void onMessage(Message message) {
if(message instanceof SolTextMessage) {
SolTextMessage solTextMessage =(SolTextMessage)message;
try {
System.out.println("Message cleared is : "+solTextMessage.getText());
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else {
System.out.println("Message Content: %s"+ SolJmsUtility.dumpMessage(message));
}
latch.countDown();
}
});
latch.await(120,TimeUnit.SECONDS);
connection.stop();
messageConsumer.close();
session.close();
connection.close();
}
Here latch is the object of CountDownLatch which is initialized as:
CountDownLatch latch = new CountDownLatch(2);
You need to commit the XA transaction in order for messages to be consumed.
In JMS, the function call is XAResource.commit(xid, true)
Also, is there a reason to use the CountDownLatch?
If you would like to consume messages synchronously, you can choose not to set a message listener and call MessageConsumer.receive()
Solace does provide a basic sample showing how to make use of XA transactions.
Refer to XATransactions.java in the samples directory of the API.
Note that the sample code is manually managing the XA transaction by calling the relevant XAResource methods such as XAResource.commit().
XA transactions are usually used within Java EE application servers that contain a transaction manager to manage the lifecycle of XA transactions.
I have a JAVA application which creates consumers that listen to rabbitmq . I need to know the started consumer is still working fine and if not then i need to restart the consumer.
Is their any way i can do that. Currently my main application creates an Executor thread pool and passes this executor while creating new connection.
ExecutorService executor = Executors.newFixedThreadPool(30);
Connection connection = factory.newConnection(executor);
The main method then create 30 consumerApp object by calling constructor with new channel as argument and call the listen() method
for(int i=0;i<=30;i++) {
ConsumerApp consumer = new ConsumerApp(i,connection.createChanell());
consumer.listen() }
The listen method in consumerApp listen to a queue and start a DefaultConsumer Object which simply prints the received message
listen() {
try {
channel.queueDeclare("test-queue-name", false, false, false, null);
}
catch {
System.out.println("Exception on creating Queue")
}
Consumer consumer = new DefaultConsumer(this.channel) {
#Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println(" [x] Received Message in consumer '"+consumerId+" "+ message + "'");
}
};
//Now starting the consumer
try {
channel.basicConsume(QUEUE_NAME, true, consumer);
}
catch (ShutdownSignalException | IOException ex) {
ex.printStackTrace();
}
}
I want to know is their any way i can check the consumer is active . My idea is to catch the shutdown signal exception and recreate the consumer object and recall the listen method . Is this necessary as rabbitmq auto recovers and connnect back. ? But how can i ensure this ?
Is this any way achievable using the threadpool passed to rabbitmq connector.
I am using latest version of rabbitmq client 5.3.0
Consumer has different methods that can help you track the state of your consumer. You're likely to be interested in handleConsumeOk and in handleCancel.
Automatic connection recovery will indeed re-register consumers after a connection failure, but that doesn't prevent you from following their state manually to e.g. expose some information on JMX.
We're using hornetQ, and got a separate java application which connects to the queue to read from it.
We're having a Connection to it, creating a JMS session from the connection,
then obtaining a JMS MessageConsumer from the session, then assigning a custom MessageListener to the MessageConsumer.
We call message.acknowledge() for every processed message.
This all works very well, but in the case where we kill our java-application in the middle of processing, before hitting acknowledge(), the message is removed from hornetQ, yet never processed, and lost forever.
It seems that our current java application JMS setup somehow works by removing from the queue, and then rolling back/re-inserting it, if something goes wrong.
But if we kill our application, there won't be any roll-back or re-inserting, as it happens within our client/java-code.
Is there a setting or delivery-mode or something I can configure in the client, that causes the HornetQ to treat every message as "not consumed"(and still in the queue) untill the client sends the ack, so that a rollback arent neccecary?
EDIT: Also tried using transactional mode, calling:
connection.createSession(true, acknowledgementType)
Our MessageListener:
#Override
public void onMessage(Message message) {
LOGGER.debug("Message received. Going to handle message.");
try {
final TextMessage messageReceived = (TextMessage) message;
final String text = messageReceived.getText();
LOGGER.debug(String.format("Message content:\n%s", text));
final boolean isMessageSent = eventDispatcher.handleEvent(text);
if (isMessageSent) {
LOGGER.error("Behold!");
message.acknowledge();
session.commit();
} else {
session.rollback();
}
} catch (JMSException jmse) {
LOGGER.error("Failed to get text from a message from the events queue.", jmse);
} catch (JAXBException jaxbe) {
LOGGER.error("Failed to deserialize contents of the message from the events queue.", jaxbe);
} catch (Exception ex) {
LOGGER.error("An error occurred when receiving an event from the events queue:", ex);
}
I have set up a simple request/reply type scenario (JavaEE 7/Netbeans 7 with Glassfish) with code as follows:
This is a stateless session bean performing the JMS producer role
destination and factory are injected into the EJB.
public void doStuff(int id) {
try {
Connection connection = factory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(destination);
ObjectMessage message = session.createObjectMessage();
MyObject obj = new MyObject(id);
connection.start();
TemporaryQueue replyQueue = session.createTemporaryQueue();
MessageConsumer consumer = session.createConsumer(replyQueue);
message.setJMSReplyTo(replyQueue);
message.setObject(obj);
producer.send(message, DeliveryMode.PERSISTENT, Message.DEFAULT_PRIORITY, 1800000);
Message reply = consumer.receive();
producer.close();
session.close();
connection.close();
} catch (JMSException ex) {
}
}
My problem is that when consumer.receive() is called, execution blocks as expected - however the consumer of this message never runs. The consumer of the original message (it's an MDB) is located in another netbeans project deployed on the same Glassfish server.
I have tried debugging both projects (both the producer and consumer) and it seems like while my producer project is blocking, my consumer project doesn't do anything at all.
If I change consumer.recieve() to something like consumer.receive(20000) then as expected nothing happens for 20 seconds - but as soon as the timeout expires all of a sudden I get a debugger breakpoint hit on my consumer project and it processes as normal. But of course no reply is sent back to my producer project because it's no longer listening!
My other project (the consumer project) looks something like this:
#MessageDriven(activationConfig = {
#ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "jms/myQueue")
})
---------------------
#Override
public void onMessage(Message message) {
try {
ObjectMessage objMessage = (ObjectMessage) message;
MyObject obj = (MyObject) objMessage.getObject();
Connection connection = factory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(message.getJMSReplyTo());
connection.start();
try {
myMethod(obj);
} catch (Exception e) {
Message response = session.createTextMessage(e.getMessage());
producer.send(message.getJMSReplyTo(), response);
}
} catch (JMSException ex) {
}
}
How can I get the receive() method to work correctly? I need it to pause for a reply, but while pausing I need my other project to continue working - and I'd assume they're on different threads anyway if they're two separately deployed projects.
I eventually discovered that I was getting this issue because my doStuff() method was running a Container Managed Transaction.
In addition to this, you can only perform one action per transaction (eg: Only one of the request/receive)
So when I was calling producer.send(), the message wasn't actually sent because the transaction had not finished yet (the method was not finished). It then went on to the receive call and blocked there since no message was sent.
I worked around this by making my doStuff() method a Bean Managed Transaction, I injected a UserTransaction object with the #Resource annotation on the bean.
I went to bound the send action by UserTransaction.begin and UserTransaction.commit and I repeated this to bind my second receive action - it worked as expected.
This provided me with a clear explanation on CMTs: http://docs.oracle.com/cd/E19798-01/821-1841/bncij/index.html
Requirement: I want messages to be persist in queue till onMessage() executed successfully. If any exception occur during execution of onMessage() and if it is not handle then message should be redelivered to listener.
I am having Glassfish v2 as a application server. I am using OpenMQConnectionFactory and JmsTemplate to send message on queue.
Please note that I am not using MDB.
<bean id="openMQConnectionFactory"
class="com.is.br.util.OpenMqConnectionFactoryBean">
<property name="imqAddressList" value="mq://localhost:7676" />
<property name="imqDefaultUsername" value="admin" />
<property name="imqDefaultPassword" value="admin" />
</bean>
I tried AUTO_ACKNOWLEDGE as acknowledge mode but in listener when exception thrown message is not redelivered.
MessageProducer.java
public void sendMessage(final String responseStream) {
System.out.println("Enter into IsJmsProducer.sendMessage method");
try {
MessageCreator creator = new MessageCreator() {
public Message createMessage(Session session) {
ObjectMessage message = null;
try {
message = session.createObjectMessage(responseStream);
} catch (Exception e) {
System.out.println("Unable create a JMSMessage");
}
return message;
}
};
System.out.println("Sending message to destination: " + this.destination.toString());
this.jmsTemplate.setSessionAcknowledgeMode(Session.AUTO_ACKNOWLEDGE);
this.jmsTemplate.send(this.destination, creator);
System.out.println("SendMessage to queue successfully.");
} catch (Exception ex) {
System.out.println("SendMessage to queue Fail." + ex);
}
System.out.println("Exit from IsJmsProducer.sendMessage method");
}
SampleJMSConsumer.java
public class SampleJMSConsumer implements MessageListener {
#Override
public void onMessage(Message message) {
throw new RuntimeException();
}
}
Then I tried with this.jmsTemplate.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE); and in listener I called message.acknowledge(); and in catch block I called session.recover() still message is not redeliver.
SampleJMSConsumer.java
public class SampleJMSConsumer implements MessageListener {
#Override
public void onMessage(Message message) {
ObjectMessage objectMessage = (ObjectMessage) message;
Object object;
try {
object = objectMessage.getObject();
if (object instanceof String) {
System.out.println("Message received - " + object.toString());
throw new JMSException("JMS exception");
}
message.acknowledge();
} catch (JMSException e) {
session.recover();
}
}
}
When I run the program in debug mode and I send message on queue in broker admin console I am able to see the number of messages but as soon as onMessage() called number of messages get reduce by one. That means message is consumed and deleted from queue. Is that message consider as "delivered"?
Please help me to understand why message is not redeliver when exception occur?
Thanks in advance.
I think this is by design, delivered when onmessage gets called. If you want do something about the exception you might handle it using try catch.
Assume the message was put on the queue once again, you wouls likely hit the same exception when consumed anyways.
The ack mechanisms should imo be about assuring correct delivery. Maybe what you are after is a reject mechanism where you ask the prpoducerside to send a new message?
Client Acknowledge is suited for you. In your onMessage() method, once processing is over, you need to call Acknowledge otherwise if there is any exception, then you don't call Acknowledge().
Session.Recovery() stops and restarts message delivery. The delivery of the message will be from the last unacknowledged message.
I would suggest to verify what is the default session mode for OpenMQ. It could happen that, once you opened a connection, you can't change it, so that has to be specified upon connection openin.
session created in consumer should set session mode to AUTO_ACK / DUPS_OK_ACK. You haven't shared your code for starting consumer. You are setting session mode in Producer but not Consumer.