// 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.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.GetResponse;
import com.rabbitmq.client.QueueingConsumer;
/**
* This tests whether bindings are created and nuked properly.
*
* The tests attempt to declare durable queues on a secondary node, if
* present, and that node is restarted as part of the tests while the
* primary node is still running. That way we exercise any node-down
* handler code in the server.
*
*/
public class BindingLifecycleBase extends ClusteredTestBase {
protected static final String K = "K-" + System.currentTimeMillis();
protected static final int N = 1;
protected static final String Q = "Q-" + System.currentTimeMillis();
protected static final String X = "X-" + System.currentTimeMillis();
protected static final byte[] payload = ("" + System.currentTimeMillis()).getBytes();
protected static String randomString() {
return "-" + System.nanoTime();
}
protected void createQueueAndBindToExchange(Binding binding, boolean durable) throws IOException {
channel.exchangeDeclare(binding.x, "direct", durable);
channel.queueDeclare(binding.q, durable, false, false, null);
channel.queueBind(binding.q, binding.x, binding.k);
}
protected void declareDurableQueue(String q) throws IOException {
alternateChannel.queueDeclare(q, true, false, false, null);
}
protected void deleteExchangeAndQueue(Binding binding) throws IOException {
channel.queueDelete(binding.q);
channel.exchangeDelete(binding.x);
}
protected void doAutoDelete(boolean durable, int queues) throws IOException, TimeoutException {
String[] queueNames = null;
Binding binding = Binding.randomBinding();
channel.exchangeDeclare(binding.x, "direct", durable, true, null);
channel.queueDeclare(binding.q, durable, false, true, null);
channel.queueBind(binding.q, binding.x, binding.k);
if (queues > 1) {
int j = queues - 1;
queueNames = new String[j];
for (int i = 0; i < j; i++) {
queueNames[i] = randomString();
channel.queueDeclare(queueNames[i], durable, false, false, null);
channel.queueBind(queueNames[i], binding.x, binding.k);
channel.basicConsume(queueNames[i], true, new QueueingConsumer(channel));
}
}
subscribeSendUnsubscribe(binding);
if (durable) {
restart();
}
if (queues > 1 && queueNames != null) {
for (String s : queueNames) {
channel.basicConsume(s, true, new QueueingConsumer(channel));
Binding tmp = new Binding(s, binding.x, binding.k);
sendUnroutable(tmp);
}
}
channel.queueDeclare(binding.q, durable, true, true, null);
// if (queues == 1): Because the exchange does not exist, this
// bind should fail
try {
channel.queueBind(binding.q, binding.x, binding.k);
sendRoutable(binding);
}
catch (IOException e) {
checkShutdownSignal(AMQP.NOT_FOUND, e);
channel = null;
return;
}
if (queues == 1) {
deleteExchangeAndQueue(binding);
fail("Queue bind should have failed");
}
// Do some cleanup
if (queues > 1 && queueNames != null) {
for (String q : queueNames) {
channel.queueDelete(q);
}
}
}
@Override
protected void restart() throws IOException, TimeoutException {
}
protected void sendRoutable(Binding binding) throws IOException {
channel.basicPublish(binding.x, binding.k, null, payload);
GetResponse response = channel.basicGet(binding.q, true);
assertNotNull("The response should not be null", response);
}
protected void sendUnroutable(Binding binding) throws IOException {
channel.basicPublish(binding.x, binding.k, null, payload);
GetResponse response = channel.basicGet(binding.q, true);
assertNull("The response SHOULD BE null", response);
}
protected Binding setupExchangeAndRouteMessage(boolean durable) throws IOException {
Binding binding = setupExchangeBindings(durable);
sendRoutable(binding);
return binding;
}
protected Binding setupExchangeBindings(boolean durable) throws IOException {
Binding binding = Binding.randomBinding();
createQueueAndBindToExchange(binding, durable);
return binding;
}
protected void subscribeSendUnsubscribe(Binding binding) throws IOException {
String tag = channel.basicConsume(binding.q, new QueueingConsumer(channel));
sendUnroutable(binding);
channel.basicCancel(tag);
}
protected static class Binding {
final String q;
final String x;
final String k;
static Binding randomBinding() {
return new Binding(randomString(), randomString(), randomString());
}
protected Binding(String q, String x, String k) {
this.q = q;
this.x = x;
this.k = k;
}
}
// A couple of tests that are common to the subclasses (which differ on
// whether the broker is restarted)
/**
*
* The same thing as testExchangeAutoDelete, but with durable
* queues.
*
* Main difference is restarting the broker to make sure that the
* durable queues are blasted away.
*/
public void testExchangeAutoDeleteDurable() throws IOException, TimeoutException {
doAutoDelete(true, 1);
}
/**
* The same thing as testExchangeAutoDeleteManyBindings, but with
* durable queues.
*/
public void testExchangeAutoDeleteDurableManyBindings() throws IOException, TimeoutException {
doAutoDelete(true, 10);
}
}