/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.hawtjms.jms.consumer;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import io.hawtjms.test.support.AmqpTestSupport;
import io.hawtjms.test.support.Wait;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.broker.jmx.QueueViewMBean;
import org.junit.After;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Test that Session CLIENT_ACKNOWLEDGE works as expected.
*/
public class JmsClientAckTest extends AmqpTestSupport {
protected static final Logger LOG = LoggerFactory.getLogger(JmsClientAckTest.class);
private Connection connection;
@Override
@After
public void tearDown() throws Exception {
connection.close();
super.tearDown();
}
@Test(timeout = 60000)
public void testAckedMessageAreConsumed() throws Exception {
connection = createAmqpConnection();
connection.start();
Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
Queue queue = session.createQueue(name.getMethodName());
MessageProducer producer = session.createProducer(queue);
producer.send(session.createTextMessage("Hello"));
final QueueViewMBean proxy = getProxyToQueue(name.getMethodName());
assertEquals(1, proxy.getQueueSize());
// Consume the message...
MessageConsumer consumer = session.createConsumer(queue);
Message msg = consumer.receive(1000);
assertNotNull(msg);
msg.acknowledge();
assertTrue("Queued message not consumed.", Wait.waitFor(new Wait.Condition() {
@Override
public boolean isSatisified() throws Exception {
return proxy.getQueueSize() == 0;
}
}));
connection.close();
}
@Test(timeout = 60000)
public void testLastMessageAcked() throws Exception {
connection = createAmqpConnection();
connection.start();
Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
Queue queue = session.createQueue(name.getMethodName());
MessageProducer producer = session.createProducer(queue);
producer.send(session.createTextMessage("Hello"));
producer.send(session.createTextMessage("Hello2"));
producer.send(session.createTextMessage("Hello3"));
final QueueViewMBean proxy = getProxyToQueue(name.getMethodName());
assertEquals(3, proxy.getQueueSize());
// Consume the message...
MessageConsumer consumer = session.createConsumer(queue);
Message msg = consumer.receive(1000);
assertNotNull(msg);
msg = consumer.receive(1000);
assertNotNull(msg);
msg = consumer.receive(1000);
assertNotNull(msg);
msg.acknowledge();
assertTrue("Queued message not consumed.", Wait.waitFor(new Wait.Condition() {
@Override
public boolean isSatisified() throws Exception {
return proxy.getQueueSize() == 0;
}
}));
}
@Test(timeout = 60000)
public void testUnAckedMessageAreNotConsumedOnSessionClose() throws Exception {
connection = createAmqpConnection();
connection.start();
Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
Queue queue = session.createQueue(name.getMethodName());
MessageProducer producer = session.createProducer(queue);
producer.send(session.createTextMessage("Hello"));
final QueueViewMBean proxy = getProxyToQueue(name.getMethodName());
assertEquals(1, proxy.getQueueSize());
// Consume the message...but don't ack it.
MessageConsumer consumer = session.createConsumer(queue);
Message msg = consumer.receive(1000);
assertNotNull(msg);
session.close();
assertEquals(1, proxy.getQueueSize());
// Consume the message...and this time we ack it.
session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
consumer = session.createConsumer(queue);
msg = consumer.receive(2000);
assertNotNull(msg);
msg.acknowledge();
assertTrue("Queued message not consumed.", Wait.waitFor(new Wait.Condition() {
@Override
public boolean isSatisified() throws Exception {
return proxy.getQueueSize() == 0;
}
}));
}
@Test(timeout = 60000)
public void testAckedMessageAreConsumedByAsync() throws Exception {
connection = createAmqpConnection();
connection.start();
Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
Queue queue = session.createQueue(name.getMethodName());
MessageProducer producer = session.createProducer(queue);
producer.send(session.createTextMessage("Hello"));
final QueueViewMBean proxy = getProxyToQueue(name.getMethodName());
assertEquals(1, proxy.getQueueSize());
// Consume the message...
MessageConsumer consumer = session.createConsumer(queue);
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
try {
message.acknowledge();
} catch (JMSException e) {
LOG.warn("Unexpected exception on acknowledge: {}", e.getMessage());
}
}
});
assertTrue("Queued message not consumed.", Wait.waitFor(new Wait.Condition() {
@Override
public boolean isSatisified() throws Exception {
return proxy.getQueueSize() == 0;
}
}));
}
@Test(timeout = 60000)
public void testUnAckedAsyncMessageAreNotConsumedOnSessionClose() throws Exception {
connection = createAmqpConnection();
connection.start();
Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
Queue queue = session.createQueue(name.getMethodName());
MessageProducer producer = session.createProducer(queue);
producer.send(session.createTextMessage("Hello"));
final QueueViewMBean proxy = getProxyToQueue(name.getMethodName());
assertEquals(1, proxy.getQueueSize());
// Consume the message...
MessageConsumer consumer = session.createConsumer(queue);
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
// Don't ack the message.
}
});
session.close();
assertEquals(1, proxy.getQueueSize());
// Now we consume and ack the Message.
session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
consumer = session.createConsumer(queue);
Message msg = consumer.receive(2000);
assertNotNull(msg);
msg.acknowledge();
assertTrue("Queued message not consumed.", Wait.waitFor(new Wait.Condition() {
@Override
public boolean isSatisified() throws Exception {
return proxy.getQueueSize() == 0;
}
}));
}
@Test(timeout=90000)
public void testAckMarksAllConsumerMessageAsConsumed() throws Exception {
connection = createAmqpConnection();
connection.start();
Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
Queue queue = session.createQueue(name.getMethodName());
final int MSG_COUNT = 30;
final AtomicReference<Message> lastMessage = new AtomicReference<Message>();
final CountDownLatch done = new CountDownLatch(MSG_COUNT);
MessageListener myListener = new MessageListener() {
@Override
public void onMessage(Message message) {
lastMessage.set(message);
done.countDown();
}
};
MessageConsumer consumer1 = session.createConsumer(queue);
consumer1.setMessageListener(myListener);
MessageConsumer consumer2 = session.createConsumer(queue);
consumer2.setMessageListener(myListener);
MessageConsumer consumer3 = session.createConsumer(queue);
consumer3.setMessageListener(myListener);
MessageProducer producer = session.createProducer(queue);
for (int i = 0; i < MSG_COUNT; ++i) {
producer.send(session.createTextMessage("Hello: " + i));
}
final QueueViewMBean proxy = getProxyToQueue(name.getMethodName());
assertEquals(MSG_COUNT, proxy.getQueueSize());
assertTrue("Failed to consume all messages.", done.await(20, TimeUnit.SECONDS));
assertNotNull(lastMessage.get());
assertEquals(MSG_COUNT, proxy.getInFlightCount());
lastMessage.get().acknowledge();
assertTrue("Queued message not consumed.", Wait.waitFor(new Wait.Condition() {
@Override
public boolean isSatisified() throws Exception {
return proxy.getQueueSize() == 0;
}
}));
}
@Test(timeout=60000)
public void testUnackedAreRecovered() throws Exception {
connection = createAmqpConnection();
connection.start();
Session consumerSession = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
Queue queue = consumerSession.createQueue(name.getMethodName());
MessageConsumer consumer = consumerSession.createConsumer(queue);
Session producerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = producerSession.createProducer(queue);
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
TextMessage sent1 = producerSession.createTextMessage();
sent1.setText("msg1");
producer.send(sent1);
TextMessage sent2 = producerSession.createTextMessage();
sent1.setText("msg2");
producer.send(sent2);
TextMessage sent3 = producerSession.createTextMessage();
sent1.setText("msg3");
producer.send(sent3);
consumer.receive(5000);
Message rec2 = consumer.receive(5000);
consumer.receive(5000);
rec2.acknowledge();
TextMessage sent4 = producerSession.createTextMessage();
sent4.setText("msg4");
producer.send(sent4);
Message rec4 = consumer.receive(5000);
assertTrue(rec4.equals(sent4));
consumerSession.recover();
rec4 = consumer.receive(5000);
assertNotNull(rec4);
assertTrue(rec4.equals(sent4));
assertTrue(rec4.getJMSRedelivered());
rec4.acknowledge();
}
@Test(timeout=60000)
public void testRecoverRedelivery() throws Exception {
final CountDownLatch redelivery = new CountDownLatch(6);
connection = createAmqpConnection();
connection.start();
final Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
Queue queue = session.createQueue(name.getMethodName());
MessageConsumer consumer = session.createConsumer(queue);
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
try {
LOG.info("Got message: " + message.getJMSMessageID());
if (message.getJMSRedelivered()) {
LOG.info("It's a redelivery.");
redelivery.countDown();
}
LOG.info("calling recover() on the session to force redelivery.");
session.recover();
} catch (JMSException e) {
e.printStackTrace();
}
}
});
connection.start();
MessageProducer producer = session.createProducer(queue);
producer.send(session.createTextMessage("test"));
assertTrue("we got 6 redeliveries", redelivery.await(20, TimeUnit.SECONDS));
}
}