JMS - Message redlivery on fail - java

I've got this scenario:
a JMS message elaboration via an MDB might fail (throws a RuntimeException in this case)
the message should be redelivered, though after a delay (ideal, but not strictly necessary: after an increasing delay depending on the number of fails)
after x failures, the message should be disregarded and never sent again
Right now, the behaviour I have is that the failed message is redelivered instantly for 10 times, and I haven't been able to customize this.
Is there a way I can achieve this via #JMSdefinition (or other annotations as well) or setting the correct properties in the message? If so, how to do?

You can schedule the message with _AMQ_SCHED_DELIVERY property:
Queue q = (Queue) ServiceLocator.getInstance().getDestination("QUEUE");
QueueConnectionFactory factory = (QueueConnectionFactory) ServiceLocator.getInstance().getConnectionFactory(
"java:/ConnectionFactory");
QueueConnection connection = factory.createQueueConnection();
QueueSession session = null;
QueueSender sender = null;
session = connection.createQueueSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
sender = session.createSender(q);
ObjectMessage msg = session.createObjectMessage();
if (redelivedCount > 0) {
msg.setIntProperty("redelivedCount", redelivedCount);
// schedule to run in 10 secs
msg.setLongProperty("_AMQ_SCHED_DELIVERY", System.currentTimeMillis() + 10000);
}
msg.setStringProperty("action", action);
msg.setObject(params);
sender.send(msg);

Related

How to schedule a message in ActiveMQ Artemis

I have a Artemis' bootstrap.xml config file here
<broker xmlns="http://activemq.org/schema" schedulerSupport="true">
<jaas-security domain="activemq"/>
<server configuration="file:/opt/activemq/apache-artemis-home/etc//broker.xml"/>
<web bind="http://10.0.34.96:8161" path="web">
<app url="activemq-branding" war="activemq-branding.war"/>
<app url="artemis-plugin" war="artemis-plugin.war"/>
<app url="console" war="console.war"/>
</web>
</broker>
Even the artemis server restart, the schedule message still not work. The consumer gets the message from the TEST queue immediately.
try {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://10.0.34.96:61616");
Connection connection = factory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// Create Destination queue
Destination queue = session.createQueue("TEST");
MessageProducer producer = session.createProducer(queue);
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
long schedDelivery = System.currentTimeMillis() + 50000;
String msg = "Hello World (test SCHED_DELIVERY) "+schedDelivery;
TextMessage message = session.createTextMessage(msg);
// message.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY, 100000);
message.setLongProperty("_HQ_SCHED_DELIVERY", schedDelivery);
System.out.println("Producer Sent: " + msg);
producer.send(message);
session.close();
connection.close();
}
catch (Exception ex) {
System.out.println("Exception Occured");
}
Any one know how to set the ScheduledMessage.AMQ_SCHEDULED_DELAY or _HQ_SCHED_DELIVERY property ?
You may use ActiveMQMessage.setJMSDeliveryTime(schedDelivery) or maybe this will work: message.setLongProperty("_AMQ_SCHED_DELIVERY", schedDelivery)
Using _HQ_SCHED_DELIVERY with the new Artemis clients will not work. That is the old property used by/for legacy HornetQ clients. You need to use _AMQ_SCHED_DELIVERY. For convenience this property is defined in org.apache.activemq.artemis.api.core.Message so you can use something like this.
Also, keep in mind that the scheduled delivery time is an absolute time and not a relative time.
Putting this together you can use something like this:
import org.apache.activemq.artemis.api.core.Message;
...
long time = System.currentTimeMillis();
time += 5000;
jmsMessage.setLongProperty(Message.HDR_SCHEDULED_DELIVERY_TIME.toString(), time);
Or you could also use something like:
jmsMessage.setLongProperty("_AMQ_SCHED_DELIVERY", System.currentTimeMillis() + 5000);
ActiveMQ Artemis ships with an example at examples/features/standard/scheduled-message which demonstrates all this.
You can read more about scheduled messages in the documentation.
Lastly, you don't need to set schedulerSupport="true" in bootstrap.xml. That setting is strictly for ActiveMQ "Classic" and not ActiveMQ Artemis. Also, ScheduledMessage.AMQ_SCHEDULED_DELAY is likewise for ActiveMQ "Classic".

Remove message from a queue in ActiveMq

I have an application with several activeMq queues. I would like to list the messages in them and remove any of them from any of the queues based on the id of the message.
Here is my code so far.
public void killMessage(String id) {
try {
ActiveMQConnection activeMqConnection = (ActiveMQConnection) connectionFactory.createConnection();
activeMqConnection.start();
DestinationSource destinationSource = activeMqConnection.getDestinationSource();
Set<ActiveMQQueue> queues = destinationSource.getQueues();
QueueSession queueSession = activeMqConnection.createQueueSession(true, Session.CLIENT_ACKNOWLEDGE);
for(ActiveMQQueue queue : queues) {
QueueBrowser browser = queueSession.createBrowser(queue);
Enumeration<?> messagesInQueue = browser.getEnumeration();
while (messagesInQueue.hasMoreElements()) {
Message message = (Message) messagesInQueue.nextElement();
System.out.println("Current id: " + message.getJMSMessageID());
if(message.getJMSMessageID().equals(id)){
System.out.println("-----message id found-------");
}
}
}
activeMqConnection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
I iterate through all the queues, then I iterate through all messages in each queue. I even find the message I want to delete, but I cannot find a way to remove it from the queue.
Edit:
I also created a consumer. I am not sure how the consumer should make the messages disappear from the queue. My attempt at it, which have no effect at all, messages remain in the queue, and I get no error message and no exception is thrown which could indicate the consumer did not match a message:
if(message.getJMSMessageID().equals(id)){
System.out.println("-----message id found-------");
MessageConsumer consumer = queueSession.createConsumer(queue, "JMSMessageID='" + id + "'");
consumer.receive();
consumer.close();
}
If you want to use the JMS API to do this then you'll have to create a consumer and use a selector to consume the message with the ID that you want. A queue browser cannot consume messages; it can only browse them.
In the code you pasted you're creating a transacted session which means when you consume the message you'll need to commit the session otherwise the message will never be acknowledged. That said, you're probably better off creating a non-transacted session with AUTO_ACKNOWLEDGE instead.
Also, you probably want to call receive(int) (i.e. with a timeout) so that if the selector can't find the message for some reason your application doesn't just sit there forever waiting on the method to return.

Tibco EMS Queuebrowser pending message count slowness

I have a requirement to fetch the pending messages from tibco ems queue. Below is my program using spring jms template. However, I published 5000 messages to queue and tried fetching the count of same with out consuming, and realized it took nearly 20 minutes to browse and get the count. Please advise on any performance improvement here or any other better way to get the pending message count
jmsTemplate.execute(new SessionCallback<QueueBrowser>() {
public QueueBrowser doInJms(Session session) throws JMSException {
javax.jms.Queue queue = session.createQueue(queueName);
QueueBrowser browser = session.createBrowser(queue);
Enumeration messages = browser.getEnumeration();
int num = 0;
while(messages.hasMoreElements()) {
messages.nextElement();
num +=1;
LOG.info("num={}",num);
}
return null;
}
}, true);
From EMS 8.3 samples:
javax.jms.QueueBrowser browser = session.createBrowser(queue);
Enumeration msgs = browser.getEnumeration();
int browseCount=0;
while (msgs.hasMoreElements())
{
message = (javax.jms.Message)msgs.nextElement();
System.err.println("Browsed message: number="+message.getIntProperty("msg_num"));
browseCount++;
}

QueueBrowser does not return all messages

I list down queues and messages from each queue. The following is my code. But, QueueBrowser does not retrieve messages correctly.
Let's say, I have a queue named TestQueue which has 1000 message.
first time i run my program it shows only 200 messages.
second - 400
third - 600
forth - 800
fifth - 1000
Can you tell me how to fix this problem?
ConnectionFactory out = new ActiveMQConnectionFactory("tcp://localhost:61616?jms.prefetchPolicy.all=10000");
ActiveMQConnection connection = (ActiveMQConnection) out.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Set<ActiveMQQueue> amqs = connection.getDestinationSource().getQueues();
Iterator<ActiveMQQueue> queues = amqs.iterator();
while ( queues.hasNext() )
{
ActiveMQQueue queue_t = aqueues.next();
String q_name = queue_t.getPhysicalName();
List<ActiveMQMessage> msgList = ((ActiveMQSession) session).getUnconsumedMessages();
System.out.println( "\nQueue = " + q_name);
QueueBrowser queueBrowser = session.createBrowser(queue_t);
Enumeration e = queueBrowser.getEnumeration();
int numMsgs = 0;
while(e.hasMoreElements())
{
Message message = (Message) e.nextElement();
numMsgs++;
}
System.out.println("No of messages = " + numMsgs);
queueBrowser.close();
}
session.close();
connection.close();
From the javax.jms.QueueBrowser API:
Messages may be arriving and expiring while the scan is done. The JMS API does not require the content of an enumeration to be a static snapshot of queue content. Whether these changes are visible or not depends on the JMS provider.
Have you tried specifying the prefetch policy?
Just connect using JMX to Broker (use jconsole for example). On specific queue set MaxPageSize properties more than 200 then you can list more messages.

Apache ActiveMQ fall back mechanism

I am using Apache Active MQ with Spring..... the problem I am facing is that I am creating producer on one machine let say Machine1 and I am creating one consumer on second machine let say Machine2...
I am creating producer on Machine1 by calling a simple servlet call.... and then create a consumer on Machine2....
Problem I am facing is that suppose in anyway if my producer is not able to send any data packet in specified time duration then I want to delete my consumer and Queue from Machine2...
Is there anyway I can set my Consumer and Queue to get auto delete and perform some business logic if I do not get any packet from producer in a specified time duration....
connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER,ActiveMQConnection.DEFAULT_PASSWORD,ConnectorURL);
connection = connectionFactory.createConnection();
connection.start();
session = connection.createSession(transacted, Session.AUTO_ACKNOWLEDGE);
destination = session.createQueue(queueID+"");
connection = connectionFactory.createConnection();
connection.start();
consumer = session.createConsumer(destination);
basically this code create consumer for my application....then I assign this consumer to my application listener that listen if producer send any message to consumer....
ScenarioExecutionQueueListenerImpl executionQueueListener = new ScenarioExecutionQueueListenerImpl(scenario,result, host);
beanFactory.autowireBean(executionQueueListener);
connection.setExceptionListener(executionQueueListener);
Message message = consumer.receive();
consumer.setMessageListener(executionQueueListener);
executionQueueListener.setConsumer(consumer);
executionQueueListener.onMessage(message);
I would not setup a messagelistener for that case, but just use the consumer.receive() method. The MessageListener is more for time independent/asynchronous consuming.
public void run(){
Message m = consumer.receive(timeout_value_in_millisec);
if( m != null ){
// got a message, handle it.
processMessage(msg);
}else{
// no message received in specified time,
}
// close session, connection etc.
}
public void processMessage(Message msg){
}

Categories