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);
}
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.
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.
I have a onMessage method where I'm reciving an ObjectMessage from the Queue and using that information to populate and persist a JPA entity object. But when something goes wrong while persisting the entity object it is re-executing the onMessage(). My guess is it is pushing the ObjectMessage back the queue and hence the onmessage is getting executed again. This way I'm entering an infinite loop. How can stop onMessage() to get execute again or control the no of times it gets executed. Here is the code I have.
Error is happening at saveAuditData(auditInfo).
public void onMessage(Message inMessage) {
log.debug("Entering onMessage() Method.");
AuditInfo auditInfo = null;
try {
ObjectMessage om = (ObjectMessage) inMessage;
auditInfo = (AuditInfo) om.getObject();
log.debug("Message received : " + auditInfo.getApiUsed());
log.debug("Calling saveAuditData().");
saveAuditData(auditInfo);
log.debug("Leaving onMessage() Method.");
}
catch (Exception e) {
e.printStackTrace();
log.debug("Error persisting Audit Info.",e);
log.debug("Printing Audit Info:");
log.debug(auditInfo.toString());
}
}
private void saveAuditData(AuditInfo auditInfo) {
log.debug("Entering saveAuditData() Method.");
log.debug("Populating Audit Object.");
IdmAudit idmAudit = new IdmAudit();
idmAudit.setApiUsed("API");
idmAudit.setAppClientIpAddress("localhost");
idmAudit.setAuditActivity("activity1");
idmAudit.setAuditData(auditInfo.getAuditData());
idmAudit.setAuditGroup(AUDIT_GROUP);
idmAudit.setAuditType("Type");
idmAudit.setIdmAuditCreationDate(new Date());
idmAudit.setLocationCd("Location");
idmAudit.setPurgeDate(null);
idmAudit.setSubscriberId(new BigDecimal(0));
idmAudit.setSuccessInd("Y");
idmAudit.setUserId(new BigDecimal(0));
idmAudit.setAuditSource("Source");
idmAudit.setVersionNumber(new BigDecimal(0));
log.debug("Saving Audit.");
entityManager.persist(idmAudit);
entityManager.flush();
log.debug("Leaving saveAuditData() Method.");
}
When a container-managed transaction is started by the container to process a JMS message, any failure in JDBC connections or exception thrown in the thread will result into a rollback of the global XA transaction. So the message goes back to the queue and will be retry later according to the queue configuration: period between retries, maximum number of retry before moving the message to a dead-letter queue.
So you have the following options:
Choose "Bean managed" transaction mode in your MDB deployment descriptor and use UserTransaction from lookup to java:comp/UserTransaction to call begin, commit or rollback manually, so care your exception handling.
Keep "Container managed" transaction but query the redelivery count property on the JMS message to decide what to do next: either try again something that can fail or either skip this step and save your data in database. You can get redelivery info on your message from Message.getJMSRedelivered() or Message.getLongProperty("JMSXDeliveryCount") if your JMS provider delivers it.
Or else, move your saveAuditData method to a EJB StatelessBean with transaction support RequiresNew in deployment descriptor so that a new transaction is created and your data is saved whatever happens to your MDB transaction. This option can be combined with the previous one.
You can simply mark the onMessage method with the TransactionType annotation:
#TransactionAttribute(value=TransactionAttributeType.REQUIRES_NEW)
public void onMessage(Message message) {
.....
}
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