/* * 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. */ using System; using System.Threading; using System.Collections; using Apache.NMS; using Apache.NMS.Util; using Apache.NMS.Test; using Apache.NMS.Policies; using NUnit.Framework; namespace Apache.NMS.ActiveMQ.Test { [TestFixture] public class MessageListenerRedeliveryTest : NMSTestSupport { private Connection connection; private volatile int counter; private ISession session; private ArrayList received; private ArrayList dlqMessages; private int maxDeliveries; private CountDownLatch gotOneMessage; private CountDownLatch gotTwoMessages; private CountDownLatch gotOneDlqMessage; private CountDownLatch gotMaxRedeliveries; [SetUp] public override void SetUp() { this.connection = (Connection) CreateConnection(); this.connection.RedeliveryPolicy = GetRedeliveryPolicy(); this.gotOneMessage = new CountDownLatch(1); this.gotTwoMessages = new CountDownLatch(2); this.gotOneDlqMessage = new CountDownLatch(1); this.maxDeliveries = GetRedeliveryPolicy().MaximumRedeliveries; this.gotMaxRedeliveries = new CountDownLatch(maxDeliveries); this.received = new ArrayList(); this.dlqMessages = new ArrayList(); this.counter = 0; } [TearDown] public override void TearDown() { this.session = null; if(this.connection != null) { this.connection.Close(); this.connection = null; } base.TearDown(); } protected IRedeliveryPolicy GetRedeliveryPolicy() { RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy(); redeliveryPolicy.InitialRedeliveryDelay = 1000; redeliveryPolicy.MaximumRedeliveries = 3; redeliveryPolicy.BackOffMultiplier = (short)2; redeliveryPolicy.UseExponentialBackOff = true; return redeliveryPolicy; } private void OnMessageListener(IMessage message) { counter++; if(this.counter <= 4) { session.Rollback(); } else { message.Acknowledge(); session.Commit(); } } private void OnTracedReceiveMessage(IMessage message) { try { received.Add(((ITextMessage) message).Text); } catch (Exception e) { Assert.Fail("Error: " + e.Message); } if (++counter < maxDeliveries) { throw new Exception("force a redelivery"); } // new blood counter = 0; gotTwoMessages.countDown(); } private void OnDlqMessage(IMessage message) { dlqMessages.Add(message); gotOneDlqMessage.countDown(); } private void OnRedeliveredMessage(IMessage message) { gotMaxRedeliveries.countDown(); throw new Exception("Test Forcing a Rollback"); } [Test] public void TestQueueRollbackConsumerListener() { connection.Start(); this.session = connection.CreateSession(AcknowledgementMode.Transactional); ITemporaryQueue queue = session.CreateTemporaryQueue(); IMessageProducer producer = session.CreateProducer(queue); IMessage message = session.CreateTextMessage("Test Message"); producer.Send(message); session.Commit(); IMessageConsumer consumer = session.CreateConsumer(queue); consumer.Listener += new MessageListener(OnMessageListener); Thread.Sleep(500); // first try.. should get 2 since there is no delay on the // first redeliver.. Assert.AreEqual(2, counter); Thread.Sleep(1000); // 2nd redeliver (redelivery after 1 sec) Assert.AreEqual(3, counter); Thread.Sleep(2000); // 3rd redeliver (redelivery after 2 seconds) - it should give up after // that Assert.AreEqual(4, counter); // create new message producer.Send(session.CreateTextMessage("Test Message Again")); session.Commit(); Thread.Sleep(500); // it should be committed, so no redelivery Assert.AreEqual(5, counter); Thread.Sleep(1500); // no redelivery, counter should still be 5 Assert.AreEqual(5, counter); session.Close(); } [Test] public void TestQueueRollbackSessionListener() { connection.Start(); this.session = connection.CreateSession(AcknowledgementMode.Transactional); IQueue queue = session.CreateTemporaryQueue(); IMessageProducer producer = CreateProducer(session, queue); IMessage message = CreateTextMessage(session); producer.Send(message); session.Commit(); IMessageConsumer consumer = session.CreateConsumer(queue); consumer.Listener += new MessageListener(OnMessageListener); Thread.Sleep(1000); // first try Assert.AreEqual(2, counter); Thread.Sleep(1500); // second try (redelivery after 1 sec) Assert.AreEqual(3, counter); Thread.Sleep(3000); // third try (redelivery after 2 seconds) - it should give up after that Assert.AreEqual(4, counter); // create new message producer.Send(CreateTextMessage(session)); session.Commit(); Thread.Sleep(1000); // it should be committed, so no redelivery Assert.AreEqual(5, counter); Thread.Sleep(2000); // no redelivery, counter should still be 4 Assert.AreEqual(5, counter); session.Close(); } [Test] public void TestQueueSessionListenerExceptionRetry() { connection.Start(); ISession session = connection.CreateSession(AcknowledgementMode.AutoAcknowledge); IQueue queue = session.CreateTemporaryQueue(); IMessageProducer producer = CreateProducer(session, queue); IMessage message = CreateTextMessage(session, "1"); producer.Send(message); message = CreateTextMessage(session, "2"); producer.Send(message); IMessageConsumer consumer = session.CreateConsumer(queue); consumer.Listener += new MessageListener(OnTracedReceiveMessage); Assert.IsTrue(gotTwoMessages.await(TimeSpan.FromSeconds(20)), "got message before retry expiry"); for (int i = 0; i < maxDeliveries; i++) { Assert.AreEqual("1", received[i], "got first redelivered: " + i); } for (int i = maxDeliveries; i < maxDeliveries * 2; i++) { Assert.AreEqual("2", received[i], "got first redelivered: " + i); } session.Close(); } [Test] public void TestQueueSessionListenerExceptionDlq() { connection.Start(); session = connection.CreateSession(AcknowledgementMode.AutoAcknowledge); IQueue queue = session.CreateTemporaryQueue(); IMessageProducer producer = CreateProducer(session, queue); IMessage message = CreateTextMessage(session); producer.Send(message); IDestination dlqDestination = session.GetQueue("ActiveMQ.DLQ"); connection.DeleteDestination(dlqDestination); IMessageConsumer dlqConsumer = session.CreateConsumer(dlqDestination); dlqConsumer.Listener += new MessageListener(OnDlqMessage); IMessageConsumer consumer = session.CreateConsumer(queue); consumer.Listener += new MessageListener(OnRedeliveredMessage); Assert.IsTrue(gotMaxRedeliveries.await(TimeSpan.FromSeconds(20)), "got message before retry expiry"); // check DLQ Assert.IsTrue(gotOneDlqMessage.await(TimeSpan.FromSeconds(20)), "got dlq message"); // check DLQ message cause is captured message = dlqMessages[0] as IMessage; Assert.IsNotNull(message, "dlq message captured"); String cause = message.Properties.GetString("dlqDeliveryFailureCause"); Assert.IsTrue(cause.Contains("JMSException"), "cause 'cause' exception is remembered"); Assert.IsTrue(cause.Contains("Test"), "is correct exception"); Assert.IsTrue(cause.Contains("RedeliveryPolicy"), "cause policy is remembered"); session.Close(); } private void OnMessageThenRollback(IMessage message) { gotOneMessage.countDown(); try { session.Rollback(); } catch (Exception) { } throw new Exception("Test force a redelivery"); } [Test] public void TestTransactedQueueSessionListenerExceptionDlq() { connection.Start(); session = connection.CreateSession(AcknowledgementMode.Transactional); IQueue queue = session.CreateTemporaryQueue(); IMessageProducer producer = CreateProducer(session, queue); IMessage message = CreateTextMessage(session); producer.Send(message); session.Commit(); IDestination dlqDestination = session.GetQueue("ActiveMQ.DLQ"); connection.DeleteDestination(dlqDestination); IMessageConsumer dlqConsumer = session.CreateConsumer(dlqDestination); dlqConsumer.Listener += new MessageListener(OnDlqMessage); IMessageConsumer consumer = session.CreateConsumer(queue); consumer.Listener += new MessageListener(OnMessageThenRollback); Assert.IsTrue(gotOneMessage.await(TimeSpan.FromSeconds(20)), "got message before retry expiry"); // check DLQ Assert.IsTrue(gotOneDlqMessage.await(TimeSpan.FromSeconds(20)), "got dlq message"); // check DLQ message cause is captured message = dlqMessages[0] as IMessage; Assert.IsNotNull(message, "dlq message captured"); String cause = message.Properties.GetString("dlqDeliveryFailureCause"); Assert.IsTrue(cause.Contains("JMSException"), "cause 'cause' exception is remembered"); Assert.IsTrue(cause.Contains("Test force"), "is correct exception"); Assert.IsTrue(cause.Contains("RedeliveryPolicy"), "cause policy is remembered"); session.Close(); } private ITextMessage CreateTextMessage(ISession session, String text) { return session.CreateTextMessage(text); } private ITextMessage CreateTextMessage(ISession session) { return session.CreateTextMessage("Hello"); } private IMessageProducer CreateProducer(ISession session, IDestination queue) { IMessageProducer producer = session.CreateProducer(queue); producer.DeliveryMode = GetDeliveryMode(); return producer; } protected MsgDeliveryMode GetDeliveryMode() { return MsgDeliveryMode.Persistent; } } }