JMS Queue receive causes application to crash - java

I've created a very simple JMS Queue example to send and receive messages. I have it set up to receive the messages after a certain number have been sent and then do work on them. After it receives all of the messages, trying to send more messages causes the application to crash.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Resource;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.jms.*;
#Startup
#Singleton
public class JMSQueue {
/** SLF4J logger. */
#SuppressWarnings("unused")
private final Logger log = LoggerFactory.getLogger(JMSQueue.class);
#Resource(mappedName = "jms/__defaultQueue")
private Queue queue;
#Resource(mappedName = "jms/__defaultQueueConnectionFactory")
private QueueConnectionFactory factory;
private int count = 0;
private QueueConnection connection;
private QueueSession session;
private MessageProducer producer;
private QueueReceiver receiver;
public void init(){
try {
connection = factory.createQueueConnection();
session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
producer = session.createProducer(queue);
receiver = session.createReceiver(queue);
connection.start();
} catch (JMSException e) {
log.error("JMS Queue Initialization failed.", e);
}
}
public void sendMessage() throws JMSException {
String messageBody = "ping" + count;
Message request = session.createTextMessage(messageBody);
request.setJMSReplyTo(queue);
producer.send(request);
count++;
if (count >= 10) {
count = 0;
Message response = receiver.receive();
while (response != null){
String responseBody = ((TextMessage) response).getText();
log.debug("jms - " + responseBody);
try {
response = receiver.receive();
} catch(JMSException e){
response = null;
}
}
}
}
}
I run init once to create the connection, producer, and receiver, and then I run sendMessage 10 times. On the tenth time it spits out the output of all ten received messages. If I then hit sendMessage a couple of times after that, my application crashes. I have tried changing it to create and close the connection after each message which didn't change anything. I'm running a glassfish application web server and trying to use the queue to be notified of every rest call that users try to access.

Turns out the issue was that the receive was hanging indefinitely due to there not being a timeout. Adding a timeout of 1 millisecond solved the issue.

Related

Multiple queues receiving same message from virtual topic creates a deadletter entry for one queue only

I'm am using Virtual Destinations to implement Publish Subscribe model in ActiveMQ 5.15.13.
I have a virtual topic VirtualTopic and there are two queues bound to it. Each queue has its own redelivery policy. Let's say Queue 1 will retry message 2 times in case there is an exception while processing the message and Queue 2 will retry message 3 times. Post retry message will be sent to deadletter queue. I'm also using Individual Dead letter Queue strategy so that each queue has it's own deadletter queue.
I've observed that when a message is sent to VirtualTopic, the message with same message id is delivered to both the queues. I'm facing an issue where if the consumers of both queues are not able to process the message successfully. The message destined for Queue 1 is moved to deadletter queue after retrying for 2 times. But there is no deadletter queue for Queue 2, though message in Queue 2 is retried for 3 times.
Is it the expected behavior?
Code:
public class ActiveMQRedelivery {
private final ActiveMQConnectionFactory factory;
public ActiveMQRedelivery(String brokerUrl) {
factory = new ActiveMQConnectionFactory(brokerUrl);
factory.setUserName("admin");
factory.setPassword("password");
factory.setAlwaysSyncSend(false);
}
public void publish(String topicAddress, String message) {
final String topicName = "VirtualTopic." + topicAddress;
try {
final Connection producerConnection = factory.createConnection();
producerConnection.start();
final Session producerSession = producerConnection.createSession(false, AUTO_ACKNOWLEDGE);
final MessageProducer producer = producerSession.createProducer(null);
final TextMessage textMessage = producerSession.createTextMessage(message);
final Topic topic = producerSession.createTopic(topicName);
producer.send(topic, textMessage, PERSISTENT, DEFAULT_PRIORITY, DEFAULT_TIME_TO_LIVE);
} catch (JMSException e) {
throw new RuntimeException("Message could not be published", e);
}
}
public void initializeConsumer(String queueName, String topicAddress, int numOfRetry) throws JMSException {
factory.getRedeliveryPolicyMap().put(new ActiveMQQueue("*." + queueName + ".>"),
getRedeliveryPolicy(numOfRetry));
Connection connection = factory.createConnection();
connection.start();
final Session consumerSession = connection.createSession(false, CLIENT_ACKNOWLEDGE);
final Queue queue = consumerSession.createQueue("Consumer." + queueName +
".VirtualTopic." + topicAddress);
final MessageConsumer consumer = consumerSession.createConsumer(queue);
consumer.setMessageListener(message -> {
try {
System.out.println("in listener --- " + ((ActiveMQDestination)message.getJMSDestination()).getPhysicalName());
consumerSession.recover();
} catch (JMSException e) {
e.printStackTrace();
}
});
}
private RedeliveryPolicy getRedeliveryPolicy(int numOfRetry) {
final RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
redeliveryPolicy.setInitialRedeliveryDelay(0);
redeliveryPolicy.setMaximumRedeliveries(numOfRetry);
redeliveryPolicy.setMaximumRedeliveryDelay(-1);
redeliveryPolicy.setRedeliveryDelay(0);
return redeliveryPolicy;
}
}
Test:
public class ActiveMQRedeliveryTest {
private static final String brokerUrl = "tcp://0.0.0.0:61616";
private ActiveMQRedelivery activeMQRedelivery;
#Before
public void setUp() throws Exception {
activeMQRedelivery = new ActiveMQRedelivery(brokerUrl);
}
#Test
public void testMessageRedeliveries() throws Exception {
String topicAddress = "testTopic";
activeMQRedelivery.initializeConsumer("queue1", topicAddress, 2);
activeMQRedelivery.initializeConsumer("queue2", topicAddress, 3);
activeMQRedelivery.publish(topicAddress, "TestMessage");
Thread.sleep(3000);
}
#After
public void tearDown() throws Exception {
}
}
I recently came across this problem. To fix this there are 2 attributes that needs to be added to individualDeadLetterStrategy as below
<deadLetterStrategy>
<individualDeadLetterStrategy destinationPerDurableSubscriber="true" enableAudit="false" queuePrefix="DLQ." useQueueForQueueMessages="true"/>
</deadLetterStrategy>
Explanation of attributes:
destinationPerDurableSubscriber - To enable a separate destination per durable subscriber.
enableAudit - The dead letter strategy has a message audit that is enabled by default. This prevents duplicate messages from being added to the configured DLQ. When the attribute is enabled, the same message that isn't delivered for multiple subscribers to a topic will only be placed on one of the subscriber DLQs when the destinationPerDurableSubscriber attribute is set to true i.e. say two consumers fail to acknowledge the same message for the topic, that message will only be placed on the DLQ for one consumer and not the other.

Low disk space causes RabbitMQ to loose messages instead of blocking the connection

I have a java application that publishes messages to a RabbitMQ server.
When the available disk space drops below rabbit's low watermark I get unexpected behavior.
The expected behavior is that the connection will become blocking, making my application hang on the call to Channel.basicPublish.
The actual behavior is that the connection appears to be blocking in the management console, but calls to Channel.basicPublish return with no errors and the messages that were supposed to be published are lost.
This behavior undermines the most important feature of RabbitMQ, which is robustness.
Below is a minimal version of my application for testing. All it does is publish a message every second with an incrementing index (1, 2, 3, ...). The messages are received just fine by the RabbitMQ server, until I set the low watermark to a very high value, by putting the following line in the rabbitmq.config file:
[
{rabbit, [{disk_free_limit, 60000000000}]}
].
After restarting the server, I get a low disk space notification in the management console, the connection is marked as 'blocking', and no more messages are received by the server. However, the application keeps running and sending messages as if nothing is wrong. When I reduce the watermark back to a normal value messages are received by the server again, but all the messages that were sent while the connection was blocking are lost.
Am I doing something wrong?
Is this a bug in RabbitMQ?
If so, is there a workaround?
OS: Windows 8 64bit
RabbitMQ server version: 3.1.1
RabbitMQ Java client version: 3.1.0
Test application code:
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class Main {
private final static Logger logger = LoggerFactory.getLogger(Main.class);
private final static String QUEUE_NAME = "testQueue";
private static Channel channel = null;
private static void connectToRabbitMQ() throws IOException {
ConnectionFactory factory = new ConnectionFactory();
Connection connection = factory.newConnection();
channel = connection.createChannel();
channel.queueDeclare(
QUEUE_NAME,
true, // Durable - survive a server restart
false, // Not exclusive to this connection
false, // Do not autodelete when no longer in use
null // Arguments
);
}
private static void disposeChannel()
{
if (channel == null) {
return;
}
try {
channel.close();
} catch (Exception e) {
} finally {
channel = null;
}
}
public static void main(String[] args) throws Exception {
boolean interrupted = false;
int messageNumber = 1;
while (!interrupted) {
byte[] message = Integer.toString(messageNumber).getBytes();
try {
if (channel == null) {
connectToRabbitMQ();
}
channel.basicPublish(
"",
QUEUE_NAME,
MessageProperties.MINIMAL_PERSISTENT_BASIC,
message
);
logger.info("Published message number {}", messageNumber);
messageNumber++;
} catch (Exception e) {
logger.info("Unable to connect to RabbitMQ...");
disposeChannel();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
logger.info("Interrupted");
interrupted = true;
}
}
}
}

Synchronous Consumer with JMS Queue

I want to process all the messages from a JMS Queue in Glassfish 3 in a synchronous way so I have tried to change the property Maximum Active Consumers from -1 to 1 in JMS Physical Destination in Glassfish window. I think setting this I will have only one Consumer executing OnMessage() at the same time. The problem I have reached its that when I change that property I got this error:
[I500]: Caught JVM Exception: org.xml.sax.SAXParseException: Content is not allowed in prolog.
[I500]: Caught JVM Exception: com.sun.messaging.jms.JMSException: Content is not allowed in prolog.
sendMessage Error [C4038]: com.sun.messaging.jms.JMSException: Content is not allowed in prolog.
If anyone know another way to make the method onmessage() synchronous will be appreciated. This is my Consumer Class:
#MessageDriven(mappedName = "QueueListener", activationConfig = {
#ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"),
#ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue")
})
public class MessageBean implements MessageListener {
#Override
public void onMessage(Message message) {
long t1 = System.currentTimeMillis();
write("MessageBean has received " + message);
try{
TextMessage result=(TextMessage)message;
String text=result.getText();
write("OTAMessageBean message ID has resolved to " + text);
int messageID=Integer.valueOf(text);
AirProcessing aP=new AirProcessing();
aP.pickup(messageID);
}
catch(Exception e){
raiseError("OTAMessageBean error " + e.getMessage());
}
long t2 = System.currentTimeMillis();
write("MessageBean has finished in " + (t2-t1));
}
}
I had the same problem, the only solution I found was to set up a Schedule which polls the messages from the queue every ten seconds:
#Stateless
public class MyReceiver {
#Resource(mappedName = "jms/MyQueueFactory")
private QueueConnectionFactory connectionFactory;
#Resource(mappedName = "jms/MyQueue")
private Queue myQueue;
private QueueConnection qc;
private QueueSession session;
private MessageConsumer consumer;
#PostConstruct
void init() {
try {
qc = connectionFactory.createQueueConnection();
session = qc.createQueueSession(false, Session.CLIENT_ACKNOWLEDGE);
consumer = session.createConsumer(myQueue);
qc.start();
} catch (JMSException e) {
throw new RuntimeException(e);
}
}
#PreDestroy
void cleanup() throws JMSException {
qc.close();
}
#Schedule(hour = "*", minute = "*", second = "*/10", persistent = false)
public void onMessage() throws JMSException {
Message message;
while ((message = consumer.receiveNoWait()) != null) {
ObjectMessage objMsg = (ObjectMessage) message;
Serializable content;
try {
content = objMsg.getObject();
//Do sth. with "content" here
message.acknowledge();
} catch (JMSException ex) {
ex.printStackTrace();
}
}
}
}
JMS is async by nature, you don't have a specific config for telling io to behave synchronously. You can simulate it by adding message delivery and consumption confirmations everywhere, but that's not really how JMS is intended to work. Try RMIenter link description here or maybe HTTP (or something on top of it like a SOAP or REST web service)

ACTIVEMQ- publisher subscriber hello world example

There are two programs: subscriber and publisher...
Subscriber is able to put the message onto the topic and the message is sent successfully.
When I check the activemq server on my browser it shows 1 msg enqueued . But when I run the consumer code, it is not receiving the message
Here is the producer code:
import javax.jms.*;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
public class producer {
private static String url = ActiveMQConnection.DEFAULT_BROKER_URL;
public static void main(String[] args) throws JMSException {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
Connection connection = connectionFactory.createConnection();
connection.start();
// JMS messages are sent and received using a Session. We will
// create here a non-transactional session object. If you want
// to use transactions you should set the first parameter to 'true'
Session session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic("testt");
MessageProducer producer = session.createProducer(topic);
// We will send a small text message saying 'Hello'
TextMessage message = session.createTextMessage();
message.setText("HELLO JMS WORLD");
// Here we are sending the message!
producer.send(message);
System.out.println("Sent message '" + message.getText() + "'");
connection.close();
}
}
After I run this code the output at the console is:
26 Jan, 2012 2:30:04 PM org.apache.activemq.transport.failover.FailoverTransport doReconnect
INFO: Successfully connected to tcp://localhost:61616
Sent message 'HELLO JMS WORLD'
And here is the consumer code:
import javax.jms.*;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
public class consumer {
// URL of the JMS server
private static String url = ActiveMQConnection.DEFAULT_BROKER_URL;
// Name of the topic from which we will receive messages from = " testt"
public static void main(String[] args) throws JMSException {
// Getting JMS connection from the server
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
Connection connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic("testt");
MessageConsumer consumer = session.createConsumer(topic);
MessageListener listner = new MessageListener() {
public void onMessage(Message message) {
try {
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
System.out.println("Received message"
+ textMessage.getText() + "'");
}
} catch (JMSException e) {
System.out.println("Caught:" + e);
e.printStackTrace();
}
}
};
consumer.setMessageListener(listner);
connection.close();
}
}
After I run this code it doesnt show anything.
Can someone help to me to overcome this problem?
Your issue is that your consumer is running and then shutting down immediately.
Try adding this into your consumer:
consumer.setMessageListener(listner);
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
connection.close();
This will wait until you hit a key before stopping.
Other things to consider:
Use a finally block for the close
Java naming conventions encourage using uppercase for the first letter of a class
The main problem (besides the app closing down to quickly) is that you are sending to a Topic. Topics don't retain messages so if you run your application that produces and then run the consumer, the consumer won't receive anything because it was not subscribed to the topic at the time the message was sent. If you fix the shutdown issue and then run the consumer in one terminal and then run the producer you should then see the message received by your consumer. If you want message retention then you need to use a Queue which will hold onto the message until someone consumes it.
Your producer class is correct. It runs smoothly.
But, your consumer is incorrect & you have to modify it.
First, add setClientID("any_string_value") after creating connection object;
eg: Connection connection = connectionFactory.createConnection();
// need to setClientID value, any string value you wish
connection.setClientID("12345");
secondly, use createDurableSubscriber() method instead of createConsumer() for transmitting message via topic.
MessageConsumer consumer = session.createDurableSubscriber(topic,"SUB1234");
Here is the modified comsumer class:
package mq.test;
import javax.jms.*;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
public class consumer {
// URL of the JMS server
private static String url = ActiveMQConnection.DEFAULT_BROKER_URL;
// Name of the topic from which we will receive messages from = " testt"
public static void main(String[] args) throws JMSException {
// Getting JMS connection from the server
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
Connection connection = connectionFactory.createConnection();
// need to setClientID value, any string value you wish
connection.setClientID("12345");
try{
connection.start();
}catch(Exception e){
System.err.println("NOT CONNECTED!!!");
}
Session session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic("test_data");
//need to use createDurableSubscriber() method instead of createConsumer() for topic
// MessageConsumer consumer = session.createConsumer(topic);
MessageConsumer consumer = session.createDurableSubscriber(topic,
"SUB1234");
MessageListener listner = new MessageListener() {
public void onMessage(Message message) {
try {
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
System.out.println("Received message"
+ textMessage.getText() + "'");
}
} catch (JMSException e) {
System.out.println("Caught:" + e);
e.printStackTrace();
}
}
};
consumer.setMessageListener(listner);
//connection.close();
}
}
Now, your code will run successfully.
just some:
work with a queue not a topic. messages in topics will be discarded when no consumer is available, they are NOT persistend.
add connection.start() after setting the message listener. you should start a connection when all consumers/producers are properly set up.
wait some time before before closing the connection again.
the topic will probably be your most important source of failure.

Slow HornetQ Producer when Queue is persistent

I have tried with Persistent Queue in horntQ. I have made two separate examples (Producer, Consumer). My consumer is working well but the Producer is taking too much time to finish sending message. I have run both separately as well as together. What could be the problem?
my code is:
public class HornetProducer implements Runnable{
Context ic = null;
ConnectionFactory cf = null;
Connection connection = null;
Queue queue = null;
Session session = null;
MessageProducer publisher = null;
TextMessage message = null;
int messageSent=0;
public synchronized static Context getInitialContext()throws javax.naming.NamingException {
Properties p = new Properties( );
p.put(Context.INITIAL_CONTEXT_FACTORY,"org.jnp.interfaces.NamingContextFactory");
p.put(Context.URL_PKG_PREFIXES," org.jboss.naming:org.jnp.interfaces");
p.put(Context.PROVIDER_URL, "jnp://localhosts:1099");
return new javax.naming.InitialContext(p);
}
public HornetProducer()throws Exception{
ic = getInitialContext();
cf = (ConnectionFactory)ic.lookup("/ConnectionFactory");
queue = (Queue)ic.lookup("queue/testQueue2");
connection = cf.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
publisher = session.createProducer(queue);
connection.start();
}
public void publish(){
try{
message = session.createTextMessage("Hello!");
System.out.println("StartDate: "+new Date());
for(int i=0;i<10000;i++){
messageSent++;
publisher.send(message);
}
System.out.println("EndDate: "+new Date());
}catch(Exception e){
System.out.println("Exception in Consume: "+ e.getMessage());
}
}
public void run(){
publish();
}
public static void main(String[] args) throws Exception{
new HornetProducer().publish();
}
}
You are sending these messages persistently, and non transactionally. What means, each message sent has to be completed individually.
That means for each message you send, you have to make a network round trip to the server, and wait it finish persistency before you can send another message.
If you had multiple producers on this situation, hornetq would batch both producers and you would save a lot of time. (i.e. the server will batch many write requests).
If you want to speed up the sending of a single producer, you should use transactions probably.
for example:
I - Change your session to transactioned:
session = connection.createSession(true, Session.SESSION_TRANSACTIONED);
II - commit every N messages:
for(int i=0;i<10000;i++){
messageSent++;
publisher.send(message);
if (messageSent % 1000 == 0) session.commit();
}
session.commit();
You could also disable sync on Persistent messages. (Sending them asynchronously).

Categories