/* * 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.activemq.artemis.tests.integration.client; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.client.ClientConsumer; import org.apache.activemq.artemis.api.core.client.ClientMessage; import org.apache.activemq.artemis.api.core.client.ClientProducer; import org.apache.activemq.artemis.api.core.client.ClientSession; import org.apache.activemq.artemis.api.core.client.ClientSessionFactory; import org.apache.activemq.artemis.api.core.client.ServerLocator; import org.apache.activemq.artemis.core.config.Configuration; import org.apache.activemq.artemis.core.io.nio.NIOSequentialFileFactory; import org.apache.activemq.artemis.core.journal.LoaderCallback; import org.apache.activemq.artemis.core.journal.PreparedTransactionInfo; import org.apache.activemq.artemis.core.journal.RecordInfo; import org.apache.activemq.artemis.core.journal.impl.JournalImpl; import org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.junit.Assert; import org.junit.Test; public class RedeliveryConsumerTest extends ActiveMQTestBase { // Constants ----------------------------------------------------- private static final IntegrationTestLogger log = IntegrationTestLogger.LOGGER; // Attributes ---------------------------------------------------- ActiveMQServer server; final SimpleString ADDRESS = new SimpleString("address"); ClientSessionFactory factory; private ServerLocator locator; // Static -------------------------------------------------------- // Constructors -------------------------------------------------- // Public -------------------------------------------------------- @Test public void testRedeliveryMessageStrict() throws Exception { testDedeliveryMessageOnPersistent(true); } @Test public void testRedeliveryMessageSimpleCancel() throws Exception { testDedeliveryMessageOnPersistent(false); } @Test public void testDeliveryNonPersistent() throws Exception { testDelivery(false); } @Test public void testDeliveryPersistent() throws Exception { testDelivery(true); } public void testDelivery(final boolean persistent) throws Exception { setUp(true); ClientSession session = factory.createSession(false, false, false); ClientProducer prod = session.createProducer(ADDRESS); for (int i = 0; i < 10; i++) { prod.send(createTextMessage(session, Integer.toString(i), persistent)); } session.commit(); session.close(); session = factory.createSession(null, null, false, true, true, true, 0); session.start(); for (int loopAck = 0; loopAck < 5; loopAck++) { ClientConsumer browser = session.createConsumer(ADDRESS, null, true); for (int i = 0; i < 10; i++) { ClientMessage msg = browser.receive(1000); Assert.assertNotNull("element i=" + i + " loopAck = " + loopAck + " was expected", msg); msg.acknowledge(); Assert.assertEquals(Integer.toString(i), getTextMessage(msg)); // We don't change the deliveryCounter on Browser, so this should be always 0 Assert.assertEquals(0, msg.getDeliveryCount()); } session.commit(); browser.close(); } session.close(); session = factory.createSession(false, false, false); session.start(); ClientConsumer consumer = session.createConsumer(ADDRESS); for (int loopAck = 0; loopAck < 5; loopAck++) { for (int i = 0; i < 10; i++) { ClientMessage msg = consumer.receive(1000); Assert.assertNotNull(msg); Assert.assertEquals(Integer.toString(i), getTextMessage(msg)); // No ACK done, so deliveryCount should be always = 1 Assert.assertEquals(1, msg.getDeliveryCount()); } session.rollback(); } if (persistent) { session.close(); server.stop(); server.start(); factory = createSessionFactory(locator); session = factory.createSession(false, false, false); session.start(); consumer = session.createConsumer(ADDRESS); } for (int loopAck = 1; loopAck <= 5; loopAck++) { for (int i = 0; i < 10; i++) { ClientMessage msg = consumer.receive(1000); Assert.assertNotNull(msg); msg.acknowledge(); Assert.assertEquals(Integer.toString(i), getTextMessage(msg)); Assert.assertEquals(loopAck, msg.getDeliveryCount()); } if (loopAck < 5) { if (persistent) { session.close(); server.stop(); server.start(); factory = createSessionFactory(locator); session = factory.createSession(false, false, false); session.start(); consumer = session.createConsumer(ADDRESS); } else { session.rollback(); } } } session.close(); } protected void testDedeliveryMessageOnPersistent(final boolean strictUpdate) throws Exception { setUp(strictUpdate); ClientSession session = factory.createSession(false, false, false); RedeliveryConsumerTest.log.info("created"); ClientProducer prod = session.createProducer(ADDRESS); prod.send(createTextMessage(session, "Hello")); session.commit(); session.close(); session = factory.createSession(false, false, false); session.start(); ClientConsumer consumer = session.createConsumer(ADDRESS); ClientMessage msg = consumer.receive(1000); Assert.assertEquals(1, msg.getDeliveryCount()); session.stop(); // if strictUpdate == true, this will simulate a crash, where the server is stopped without closing/rolling back // the session, but the delivery count still persisted, so the final delivery count is 2 too. if (!strictUpdate) { // If non Strict, at least rollback/cancel should still update the delivery-counts session.rollback(true); session.close(); } server.stop(); // once the server is stopped, we close the session // to clean up its client resources session.close(); server.start(); factory = createSessionFactory(locator); session = factory.createSession(false, true, false); session.start(); consumer = session.createConsumer(ADDRESS); msg = consumer.receive(1000); Assert.assertNotNull(msg); Assert.assertEquals(strictUpdate ? 2 : 2, msg.getDeliveryCount()); session.close(); } @Test public void testInfiniteDedeliveryMessageOnPersistent() throws Exception { internaltestInfiniteDedeliveryMessageOnPersistent(false); } private void internaltestInfiniteDedeliveryMessageOnPersistent(final boolean strict) throws Exception { setUp(strict); ClientSession session = factory.createSession(false, false, false); RedeliveryConsumerTest.log.info("created"); ClientProducer prod = session.createProducer(ADDRESS); prod.send(createTextMessage(session, "Hello")); session.commit(); session.close(); int expectedCount = 1; for (int i = 0; i < 700; i++) { session = factory.createSession(false, false, false); session.start(); ClientConsumer consumer = session.createConsumer(ADDRESS); ClientMessage msg = consumer.receive(5000); assertNotNull(msg); assertEquals(expectedCount, msg.getDeliveryCount()); if (i % 100 == 0) { expectedCount++; msg.acknowledge(); session.rollback(); } session.close(); } factory.close(); server.stop(); setUp(false); for (int i = 0; i < 700; i++) { session = factory.createSession(false, false, false); session.start(); ClientConsumer consumer = session.createConsumer(ADDRESS); ClientMessage msg = consumer.receive(5000); assertNotNull(msg); assertEquals(expectedCount, msg.getDeliveryCount()); session.close(); } server.stop(); JournalImpl journal = new JournalImpl(server.getConfiguration().getJournalFileSize(), 2, 2, 0, 0, new NIOSequentialFileFactory(server.getConfiguration().getJournalLocation(), 1), "activemq-data", "amq", 1); final AtomicInteger updates = new AtomicInteger(); journal.start(); journal.load(new LoaderCallback() { @Override public void failedTransaction(long transactionID, List<RecordInfo> records, List<RecordInfo> recordsToDelete) { } @Override public void updateRecord(RecordInfo info) { if (info.userRecordType == JournalRecordIds.UPDATE_DELIVERY_COUNT) { updates.incrementAndGet(); } } @Override public void deleteRecord(long id) { } @Override public void addRecord(RecordInfo info) { } @Override public void addPreparedTransaction(PreparedTransactionInfo preparedTransaction) { } }); journal.stop(); assertEquals(7, updates.get()); } // Package protected --------------------------------------------- // Protected ----------------------------------------------------- /** * @param persistDeliveryCountBeforeDelivery * @throws Exception */ private void setUp(final boolean persistDeliveryCountBeforeDelivery) throws Exception { Configuration config = createDefaultInVMConfig().setPersistDeliveryCountBeforeDelivery(persistDeliveryCountBeforeDelivery); server = createServer(true, config); server.start(); locator = createInVMNonHALocator(); factory = createSessionFactory(locator); ClientSession session = addClientSession(factory.createSession(false, false, false)); try { session.createQueue(ADDRESS, ADDRESS, true); } catch (ActiveMQException expected) { // in case of restart } session.close(); } }