// 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 com.rabbitmq.client.AMQP; import com.rabbitmq.client.Consumer; import com.rabbitmq.client.DefaultConsumer; import com.rabbitmq.client.test.BrokerTestCase; import org.junit.Test; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeoutException; import static org.junit.Assert.fail; /** * Test queue auto-delete and exclusive semantics. */ public class QueueLifecycle extends BrokerTestCase { void verifyQueueExists(String name) throws IOException { channel.queueDeclarePassive(name); } void verifyQueueMissing(String name) throws IOException { // we can't in general check with a passive declare, since that // may return an IOException because of exclusivity. But we can // check that we can happily declare another with the same name: // the only circumstance in which this won't result in an error is // if it doesn't exist. try { channel.queueDeclare(name, false, false, false, null); } catch (IOException ioe) { fail("Queue.Declare threw an exception, probably meaning that the queue already exists"); } // clean up channel.queueDelete(name); } /** * Verify that a queue both exists and has the properties as given * * @throws IOException * if one of these conditions is not true */ void verifyQueue(String name, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> args) throws IOException { verifyQueueExists(name); // use passive/equivalent rule to check that it has the same properties channel.queueDeclare(name, durable, exclusive, autoDelete, args); } // NB the exception will close the connection void verifyNotEquivalent(boolean durable, boolean exclusive, boolean autoDelete) throws IOException { String q = "queue"; channel.queueDeclare(q, false, false, false, null); try { verifyQueue(q, durable, exclusive, autoDelete, null); } catch (IOException ioe) { if (exclusive) checkShutdownSignal(AMQP.RESOURCE_LOCKED, ioe); else checkShutdownSignal(AMQP.PRECONDITION_FAILED, ioe); return; } fail("Queue.declare should have been rejected as not equivalent"); } /** From amqp-0-9-1.xml, for "passive" property, "equivalent" rule: * "If not set and the queue exists, the server MUST check that the * existing queue has the same values for durable, exclusive, * auto-delete, and arguments fields. The server MUST respond with * Declare-Ok if the requested queue matches these fields, and MUST * raise a channel exception if not." */ @Test public void queueEquivalence() throws IOException { String q = "queue"; channel.queueDeclare(q, false, false, false, null); // equivalent verifyQueue(q, false, false, false, null); // the spec says that the arguments table is matched on // being semantically equivalent. HashMap<String, Object> args = new HashMap<String, Object>(); args.put("assumed-to-be-semantically-void", "bar"); verifyQueue(q, false, false, false, args); } // not equivalent in various ways @Test public void queueNonEquivalenceDurable() throws IOException { verifyNotEquivalent(true, false, false); } @Test public void queueNonEquivalenceExclusive() throws IOException { verifyNotEquivalent(false, true, false); } @Test public void queueNonEquivalenceAutoDelete() throws IOException { verifyNotEquivalent(false, false, true); } // Note that this assumes that auto-deletion is synchronous with // basic.cancel, // which is not actually in the spec. (If it isn't, there's a race here). @Test public void queueAutoDelete() throws IOException { String name = "tempqueue"; channel.queueDeclare(name, false, false, true, null); // now it's there verifyQueue(name, false, false, true, null); Consumer consumer = new DefaultConsumer(channel); String consumerTag = channel.basicConsume(name, consumer); channel.basicCancel(consumerTag); // now it's not .. we hope try { verifyQueueExists(name); } catch (IOException ioe) { checkShutdownSignal(AMQP.NOT_FOUND, ioe); return; } fail("Queue should have been auto-deleted after we removed its only consumer"); } @Test public void exclusiveNotAutoDelete() throws IOException { String name = "exclusivequeue"; channel.queueDeclare(name, false, true, false, null); // now it's there verifyQueue(name, false, true, false, null); Consumer consumer = new DefaultConsumer(channel); String consumerTag = channel.basicConsume(name, consumer); channel.basicCancel(consumerTag); // and still there, because exclusive no longer implies autodelete verifyQueueExists(name); } @Test public void exclusiveGoesWithConnection() throws IOException, TimeoutException { String name = "exclusivequeue2"; channel.queueDeclare(name, false, true, false, null); // now it's there verifyQueue(name, false, true, false, null); closeConnection(); openConnection(); openChannel(); verifyQueueMissing(name); } @Test public void argumentArrays() throws IOException { Map<String, Object> args = new HashMap<String, Object>(); String[] arr = new String[]{"foo", "bar", "baz"}; args.put("my-key", arr); String queueName = "argumentArraysQueue"; channel.queueDeclare(queueName, true, true, false, args); verifyQueue(queueName, true, true, false, args); } @Test public void queueNamesLongerThan255Characters() throws IOException { String q = new String(new byte[300]).replace('\u0000', 'x'); try { channel.queueDeclare(q, false, false, false, null); fail("queueDeclare should have failed"); } catch (IllegalArgumentException ignored) { // expected } } @Test public void singleLineFeedStrippedFromQueueName() throws IOException { channel.queueDeclare("que\nue_test", false, false, true, null); verifyQueue(NAME_STRIPPED, false, false, true, null); } @Test public void multipleLineFeedsStrippedFromQueueName() throws IOException { channel.queueDeclare("que\nue_\ntest\n", false, false, true, null); verifyQueue(NAME_STRIPPED, false, false, true, null); } @Test public void multipleLineFeedAndCarriageReturnsStrippedFromQueueName() throws IOException { channel.queueDeclare("q\ru\ne\r\nue_\ntest\n\r", false, false, true, null); verifyQueue(NAME_STRIPPED, false, false, true, null); } static final String NAME_STRIPPED = "queue_test"; @Override protected void releaseResources() throws IOException { channel.queueDelete(NAME_STRIPPED); } }