/*
*
* 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 org.apache.qpid.server.queue;
import junit.framework.JUnit4TestAdapter;
import org.apache.log4j.Logger;
import org.apache.qpid.AMQException;
import org.apache.qpid.framing.BasicContentHeaderProperties;
import org.apache.qpid.framing.BasicPublishBody;
import org.apache.qpid.framing.ContentHeaderBody;
import org.apache.qpid.server.AMQChannel;
import org.apache.qpid.server.ack.UnacknowledgedMessage;
import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.store.TestableMemoryMessageStore;
import org.apache.qpid.server.util.TestApplicationRegistry;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
import java.util.Iterator;
import java.util.Map;
/**
* Tests that acknowledgements are handled correctly.
*/
public class AckTest
{
private static final Logger _log = Logger.getLogger(AckTest.class);
private SubscriptionImpl _subscription;
private MockProtocolSession _protocolSession;
private TestableMemoryMessageStore _messageStore;
private AMQChannel _channel;
private SubscriptionSet _subscriptionManager;
private AMQQueue _queue;
public AckTest() throws Exception
{
ApplicationRegistry.initialise(new TestApplicationRegistry());
}
@Before
public void setup() throws Exception
{
_messageStore = new TestableMemoryMessageStore();
_channel = new AMQChannel(5, _messageStore, null/*dont need exchange registry*/);
_protocolSession = new MockProtocolSession(_messageStore);
_protocolSession.addChannel(_channel);
_subscriptionManager = new SubscriptionSet();
_queue = new AMQQueue("myQ", false, "guest", true, new DefaultQueueRegistry(), _subscriptionManager);
}
private void publishMessages(int count) throws AMQException
{
publishMessages(count, false);
}
private void publishMessages(int count, boolean persistent) throws AMQException
{
for (int i = 1; i <= count; i++)
{
BasicPublishBody publishBody = new BasicPublishBody();
publishBody.routingKey = "rk";
publishBody.exchange = "someExchange";
AMQMessage msg = new AMQMessage(_messageStore, publishBody);
if (persistent)
{
BasicContentHeaderProperties b = new BasicContentHeaderProperties();
//This is DeliveryMode.PERSISTENT
b.setDeliveryMode((byte) 2);
ContentHeaderBody cb = new ContentHeaderBody();
cb.properties = b;
msg.setContentHeaderBody(cb);
}
else
{
msg.setContentHeaderBody(new ContentHeaderBody());
}
_subscription.send(msg, _queue);
}
}
/**
* Tests that the acknowledgements are correctly associated with a channel and
* order is preserved when acks are enabled
*/
@Test
public void ackChannelAssociationTest() throws AMQException
{
_subscription = new SubscriptionImpl(5, _protocolSession, "conTag", true);
final int msgCount = 10;
publishMessages(msgCount, true);
Map<Long, UnacknowledgedMessage> map = _channel.getUnacknowledgedMessageMap();
assertTrue(map.size() == msgCount);
assertTrue(_messageStore.getMessageMap().size() == msgCount);
Iterator<Map.Entry<Long, UnacknowledgedMessage>> it = map.entrySet().iterator();
for (int i = 1; i <= map.size(); i++)
{
Map.Entry<Long, UnacknowledgedMessage> entry = it.next();
assertTrue(entry.getKey() == i);
UnacknowledgedMessage unackedMsg = entry.getValue();
assertTrue(unackedMsg.queue == _queue);
}
assertTrue(map.size() == msgCount);
assertTrue(_messageStore.getMessageMap().size() == msgCount);
}
/**
* Tests that in no-ack mode no messages are retained
*/
@Test
public void testNoAckMode() throws AMQException
{
// false arg means no acks expected
_subscription = new SubscriptionImpl(5, _protocolSession, "conTag", false);
final int msgCount = 10;
publishMessages(msgCount);
Map<Long, UnacknowledgedMessage> map = _channel.getUnacknowledgedMessageMap();
assertTrue(map.size() == 0);
assertTrue(_messageStore.getMessageMap().size() == 0);
}
/**
* Tests that a single acknowledgement is handled correctly (i.e multiple flag not
* set case)
*/
@Test
public void singleAckReceivedTest() throws AMQException
{
_subscription = new SubscriptionImpl(5, _protocolSession, "conTag", true);
final int msgCount = 10;
publishMessages(msgCount);
_channel.acknowledgeMessage(5, false);
Map<Long, UnacknowledgedMessage> map = _channel.getUnacknowledgedMessageMap();
assertTrue(map.size() == msgCount - 1);
Iterator<Map.Entry<Long, UnacknowledgedMessage>> it = map.entrySet().iterator();
int i = 1;
while (i <= map.size())
{
Map.Entry<Long, UnacknowledgedMessage> entry = it.next();
assertTrue(entry.getKey() == i);
UnacknowledgedMessage unackedMsg = entry.getValue();
assertTrue(unackedMsg.queue == _queue);
// 5 is the delivery tag of the message that *should* be removed
if (++i == 5)
{
++i;
}
}
}
/**
* Tests that a single acknowledgement is handled correctly (i.e multiple flag not
* set case)
*/
@Test
public void multiAckReceivedTest() throws AMQException
{
_subscription = new SubscriptionImpl(5, _protocolSession, "conTag", true);
final int msgCount = 10;
publishMessages(msgCount);
_channel.acknowledgeMessage(5, true);
Map<Long, UnacknowledgedMessage> map = _channel.getUnacknowledgedMessageMap();
assertTrue(map.size() == 5);
Iterator<Map.Entry<Long, UnacknowledgedMessage>> it = map.entrySet().iterator();
int i = 1;
while (i <= map.size())
{
Map.Entry<Long, UnacknowledgedMessage> entry = it.next();
assertTrue(entry.getKey() == i + 5);
UnacknowledgedMessage unackedMsg = entry.getValue();
assertTrue(unackedMsg.queue == _queue);
++i;
}
}
/**
* Tests that a multiple acknowledgement is handled correctly. When ack'ing all pending msgs.
*/
@Test
public void multiAckAllReceivedTest() throws AMQException
{
_subscription = new SubscriptionImpl(5, _protocolSession, "conTag", true);
final int msgCount = 10;
publishMessages(msgCount);
_channel.acknowledgeMessage(0, true);
Map<Long, UnacknowledgedMessage> map = _channel.getUnacknowledgedMessageMap();
assertTrue(map.size() == 0);
Iterator<Map.Entry<Long, UnacknowledgedMessage>> it = map.entrySet().iterator();
int i = 1;
while (i <= map.size())
{
Map.Entry<Long, UnacknowledgedMessage> entry = it.next();
assertTrue(entry.getKey() == i + 5);
UnacknowledgedMessage unackedMsg = entry.getValue();
assertTrue(unackedMsg.queue == _queue);
++i;
}
}
@Test
public void testPrefetchHighLow() throws AMQException
{
int lowMark = 5;
int highMark = 10;
_subscription = new SubscriptionImpl(5, _protocolSession, "conTag", true);
_channel.setPrefetchLowMarkCount(lowMark);
_channel.setPrefetchHighMarkCount(highMark);
assertTrue(_channel.getPrefetchLowMarkCount() == lowMark);
assertTrue(_channel.getPrefetchHighMarkCount() == highMark);
publishMessages(highMark);
// at this point we should have sent out only highMark messages
// which have not bee received so will be queued up in the channel
// which should be suspended
assertTrue(_subscription.isSuspended());
Map<Long, UnacknowledgedMessage> map = _channel.getUnacknowledgedMessageMap();
assertTrue(map.size() == highMark);
//acknowledge messages so we are just above lowMark
_channel.acknowledgeMessage(lowMark - 1, true);
//we should still be suspended
assertTrue(_subscription.isSuspended());
assertTrue(map.size() == lowMark + 1);
//acknowledge one more message
_channel.acknowledgeMessage(lowMark, true);
//and suspension should be lifted
assertTrue(!_subscription.isSuspended());
//pubilsh more msgs so we are just below the limit
publishMessages(lowMark - 1);
//we should not be suspended
assertTrue(!_subscription.isSuspended());
//acknowledge all messages
_channel.acknowledgeMessage(0, true);
try
{
Thread.sleep(3000);
}
catch (InterruptedException e)
{
_log.error("Error: " + e, e);
}
//map will be empty
assertTrue(map.size() == 0);
}
@Test
public void testPrefetch() throws AMQException
{
_subscription = new SubscriptionImpl(5, _protocolSession, "conTag", true);
_channel.setPrefetchCount(5);
assertTrue(_channel.getPrefetchCount() == 5);
final int msgCount = 5;
publishMessages(msgCount);
// at this point we should have sent out only 5 messages with a further 5 queued
// up in the channel which should now be suspended
assertTrue(_subscription.isSuspended());
Map<Long, UnacknowledgedMessage> map = _channel.getUnacknowledgedMessageMap();
assertTrue(map.size() == 5);
_channel.acknowledgeMessage(5, true);
assertTrue(!_subscription.isSuspended());
try
{
Thread.sleep(3000);
}
catch (InterruptedException e)
{
_log.error("Error: " + e, e);
}
assertTrue(map.size() == 0);
}
public static junit.framework.Test suite()
{
return new JUnit4TestAdapter(AckTest.class);
}
}