// Copyright (c) 2007-Present Pivotal Software, Inc. All rights reserved. // // This software, the RabbitMQ Java client library, is triple-licensed under the // Mozilla Public License 1.1 ("MPL"), the GNU General Public License version 2 // ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see // LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, // please see LICENSE-APACHE2. // // This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, // either express or implied. See the LICENSE file for specific language governing // rights and limitations of this software. // // If you have any questions regarding licensing, please contact us at // info@rabbitmq.com. package com.rabbitmq.client.test.functional; import static org.junit.Assert.*; import org.junit.Test; import com.rabbitmq.client.AMQP; import com.rabbitmq.client.Channel; import com.rabbitmq.client.ConfirmListener; import com.rabbitmq.client.DefaultConsumer; import com.rabbitmq.client.GetResponse; import com.rabbitmq.client.MessageProperties; import com.rabbitmq.client.ShutdownSignalException; import com.rabbitmq.client.test.BrokerTestCase; import java.io.IOException; import java.util.Collections; import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.TimeoutException; public class Confirm extends BrokerTestCase { private final static int NUM_MESSAGES = 1000; private static final String TTL_ARG = "x-message-ttl"; @Override public void setUp() throws IOException, TimeoutException { super.setUp(); channel.confirmSelect(); channel.queueDeclare("confirm-test", true, true, false, null); channel.queueDeclare("confirm-durable-nonexclusive", true, false, false, null); channel.basicConsume("confirm-test", true, new DefaultConsumer(channel)); channel.queueDeclare("confirm-test-nondurable", false, true, false, null); channel.basicConsume("confirm-test-nondurable", true, new DefaultConsumer(channel)); channel.queueDeclare("confirm-test-noconsumer", true, true, false, null); channel.queueDeclare("confirm-test-2", true, true, false, null); channel.basicConsume("confirm-test-2", true, new DefaultConsumer(channel)); channel.queueBind("confirm-test", "amq.direct", "confirm-multiple-queues"); channel.queueBind("confirm-test-2", "amq.direct", "confirm-multiple-queues"); } @Test public void persistentMandatoryCombinations() throws IOException, InterruptedException, TimeoutException { boolean b[] = { false, true }; for (boolean persistent : b) { for (boolean mandatory : b) { confirmTest("", "confirm-test", persistent, mandatory); } } } @Test public void nonDurable() throws IOException, InterruptedException, TimeoutException { confirmTest("", "confirm-test-nondurable", true, false); } @Test public void mandatoryNoRoute() throws IOException, InterruptedException, TimeoutException { confirmTest("", "confirm-test-doesnotexist", false, true); confirmTest("", "confirm-test-doesnotexist", true, true); } @Test public void multipleQueues() throws IOException, InterruptedException, TimeoutException { confirmTest("amq.direct", "confirm-multiple-queues", true, false); } /* For testQueueDelete and testQueuePurge to be * relevant, the msg_store must not write the messages to disk * (thus causing a confirm). I'd manually comment out the line in * internal_sync that notifies the clients. */ @Test public void queueDelete() throws IOException, InterruptedException, TimeoutException { publishN("","confirm-test-noconsumer", true, false); channel.queueDelete("confirm-test-noconsumer"); channel.waitForConfirmsOrDie(60000); } @Test public void queuePurge() throws IOException, InterruptedException, TimeoutException { publishN("", "confirm-test-noconsumer", true, false); channel.queuePurge("confirm-test-noconsumer"); channel.waitForConfirmsOrDie(60000); } /* Tests rabbitmq-server #854 */ @Test public void confirmQueuePurge() throws IOException, InterruptedException, TimeoutException { channel.basicQos(1); for (int i = 0; i < 20000; i++) { publish("", "confirm-durable-nonexclusive", true, false); if (i % 100 == 0) { channel.queuePurge("confirm-durable-nonexclusive"); } } channel.waitForConfirmsOrDie(90000); } @Test public void basicReject() throws IOException, InterruptedException, TimeoutException { basicRejectCommon(false); channel.waitForConfirmsOrDie(60000); } @Test public void queueTTL() throws IOException, InterruptedException, TimeoutException { for (int ttl : new int[]{ 1, 0 }) { Map<String, Object> argMap = Collections.singletonMap(TTL_ARG, (Object)ttl); channel.queueDeclare("confirm-ttl", true, true, false, argMap); publishN("", "confirm-ttl", true, false); channel.waitForConfirmsOrDie(60000); channel.queueDelete("confirm-ttl"); } } @Test public void basicRejectRequeue() throws IOException, InterruptedException, TimeoutException { basicRejectCommon(true); /* wait confirms to go through the broker */ Thread.sleep(1000); channel.basicConsume("confirm-test-noconsumer", true, new DefaultConsumer(channel)); channel.waitForConfirmsOrDie(60000); } @Test public void basicRecover() throws IOException, InterruptedException, TimeoutException { publishN("", "confirm-test-noconsumer", true, false); for (long i = 0; i < NUM_MESSAGES; i++) { GetResponse resp = channel.basicGet("confirm-test-noconsumer", false); resp.getEnvelope().getDeliveryTag(); // not acking } channel.basicRecover(true); Thread.sleep(1000); channel.basicConsume("confirm-test-noconsumer", true, new DefaultConsumer(channel)); channel.waitForConfirmsOrDie(60000); } @Test public void select() throws IOException { channel.confirmSelect(); try { Channel ch = connection.createChannel(); ch.confirmSelect(); ch.txSelect(); fail(); } catch (IOException ioe) { checkShutdownSignal(AMQP.PRECONDITION_FAILED, ioe); } try { Channel ch = connection.createChannel(); ch.txSelect(); ch.confirmSelect(); fail(); } catch (IOException ioe) { checkShutdownSignal(AMQP.PRECONDITION_FAILED, ioe); } } @Test public void waitForConfirms() throws IOException, InterruptedException, TimeoutException { final SortedSet<Long> unconfirmedSet = Collections.synchronizedSortedSet(new TreeSet<Long>()); channel.addConfirmListener(new ConfirmListener() { public void handleAck(long seqNo, boolean multiple) { if (!unconfirmedSet.contains(seqNo)) { fail("got duplicate ack: " + seqNo); } if (multiple) { unconfirmedSet.headSet(seqNo + 1).clear(); } else { unconfirmedSet.remove(seqNo); } } public void handleNack(long seqNo, boolean multiple) { fail("got a nack"); } }); for (long i = 0; i < NUM_MESSAGES; i++) { unconfirmedSet.add(channel.getNextPublishSeqNo()); publish("", "confirm-test", true, false); } channel.waitForConfirmsOrDie(60000); if (!unconfirmedSet.isEmpty()) { fail("waitForConfirms returned with unconfirmed messages"); } } @Test public void waitForConfirmsWithoutConfirmSelected() throws IOException, InterruptedException { channel = connection.createChannel(); // Don't enable Confirm mode publish("", "confirm-test", true, false); try { channel.waitForConfirms(60000); fail("waitForConfirms without confirms selected succeeded"); } catch (IllegalStateException _e) {} catch (TimeoutException e) { e.printStackTrace(); } } @Test public void waitForConfirmsException() throws IOException, InterruptedException, TimeoutException { publishN("", "confirm-test", true, false); channel.close(); try { channel.waitForConfirmsOrDie(60000); fail("waitAcks worked on a closed channel"); } catch (ShutdownSignalException sse) { if (!(sse.getReason() instanceof AMQP.Channel.Close)) fail("Shutdown reason not Channel.Close"); //whoosh; everything ok } catch (InterruptedException e) { // whoosh; we should probably re-run, though } } /* Publish NUM_MESSAGES messages and wait for confirmations. */ public void confirmTest(String exchange, String queueName, boolean persistent, boolean mandatory) throws IOException, InterruptedException, TimeoutException { publishN(exchange, queueName, persistent, mandatory); channel.waitForConfirmsOrDie(60000); } private void publishN(String exchangeName, String queueName, boolean persistent, boolean mandatory) throws IOException { for (long i = 0; i < NUM_MESSAGES; i++) { publish(exchangeName, queueName, persistent, mandatory); } } private void basicRejectCommon(boolean requeue) throws IOException { publishN("", "confirm-test-noconsumer", true, false); for (long i = 0; i < NUM_MESSAGES; i++) { GetResponse resp = channel.basicGet("confirm-test-noconsumer", false); long dtag = resp.getEnvelope().getDeliveryTag(); channel.basicReject(dtag, requeue); } } protected void publish(String exchangeName, String queueName, boolean persistent, boolean mandatory) throws IOException { channel.basicPublish(exchangeName, queueName, mandatory, false, persistent ? MessageProperties.PERSISTENT_BASIC : MessageProperties.BASIC, "nop".getBytes()); } }