// 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.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.junit.Test; import com.rabbitmq.client.Channel; import com.rabbitmq.client.GetResponse; import com.rabbitmq.client.test.BrokerTestCase; import com.rabbitmq.client.test.server.HATests; import com.rabbitmq.tools.Host; public class Policies extends BrokerTestCase { private static final int DELAY = 100; // MILLIS @Override protected void createResources() throws IOException { setPolicy("AE", "^has-ae", "\"alternate-exchange\":\"ae\""); setPolicy("DLX", "^has-dlx", "\"dead-letter-exchange\":\"dlx\",\"dead-letter-routing-key\":\"rk\""); setPolicy("TTL", "^has-ttl", "\"message-ttl\":" + DELAY); setPolicy("Expires", "^has-expires", "\"expires\":" + DELAY); setPolicy("MaxLength", "^has-max-length", "\"max-length\":1"); channel.exchangeDeclare("has-ae", "fanout"); Map<String, Object> args = new HashMap<String, Object>(); args.put("alternate-exchange", "ae2"); channel.exchangeDeclare("has-ae-args", "fanout", false, false, args); } @Test public void alternateExchange() throws IOException, InterruptedException { String q = declareQueue(); channel.exchangeDeclare("ae", "fanout", false, true, null); channel.queueBind(q, "ae", ""); basicPublishVolatile("has-ae", ""); assertDelivered(q, 1); clearPolicies(); basicPublishVolatile("has-ae", ""); assertDelivered(q, 0); } // i.e. the argument takes priority over the policy @Test public void alternateExchangeArgs() throws IOException { String q = declareQueue(); channel.exchangeDeclare("ae2", "fanout", false, true, null); channel.queueBind(q, "ae2", ""); basicPublishVolatile("has-ae-args", ""); assertDelivered(q, 1); } @Test public void deadLetterExchange() throws IOException, InterruptedException { Map<String, Object> args = ttlArgs(0); String src = declareQueue("has-dlx", args); String dest = declareQueue(); channel.exchangeDeclare("dlx", "fanout", false, true, null); channel.queueBind(dest, "dlx", ""); basicPublishVolatile(src); Thread.sleep(DELAY); GetResponse resp = channel.basicGet(dest, true); assertEquals("rk", resp.getEnvelope().getRoutingKey()); clearPolicies(); basicPublishVolatile(src); Thread.sleep(DELAY); assertDelivered(dest, 0); } // again the argument takes priority over the policy @Test public void deadLetterExchangeArgs() throws IOException, InterruptedException { Map<String, Object> args = ttlArgs(0); args.put("x-dead-letter-exchange", "dlx2"); args.put("x-dead-letter-routing-key", "rk2"); String src = declareQueue("has-dlx-args", args); String dest = declareQueue(); channel.exchangeDeclare("dlx2", "fanout", false, true, null); channel.queueBind(dest, "dlx2", ""); basicPublishVolatile(src); Thread.sleep(DELAY); GetResponse resp = channel.basicGet(dest, true); assertEquals("rk2", resp.getEnvelope().getRoutingKey()); } @Test public void tTL() throws IOException, InterruptedException { String q = declareQueue("has-ttl", null); basicPublishVolatile(q); Thread.sleep(2 * DELAY); assertDelivered(q, 0); clearPolicies(); basicPublishVolatile(q); Thread.sleep(2 * DELAY); assertDelivered(q, 1); } // Test that we get lower of args and policy @Test public void tTLArgs() throws IOException, InterruptedException { String q = declareQueue("has-ttl", ttlArgs(3 * DELAY)); basicPublishVolatile(q); Thread.sleep(2 * DELAY); assertDelivered(q, 0); clearPolicies(); basicPublishVolatile(q); Thread.sleep(2 * DELAY); assertDelivered(q, 1); basicPublishVolatile(q); Thread.sleep(4 * DELAY); assertDelivered(q, 0); } @Test public void expires() throws IOException, InterruptedException { String q = declareQueue("has-expires", null); Thread.sleep(2 * DELAY); assertFalse(queueExists(q)); clearPolicies(); q = declareQueue("has-expires", null); Thread.sleep(2 * DELAY); assertTrue(queueExists(q)); } // Test that we get lower of args and policy @Test public void expiresArgs() throws IOException, InterruptedException { String q = declareQueue("has-expires", args("x-expires", 3 * DELAY)); Thread.sleep(2 * DELAY); assertFalse(queueExists(q)); clearPolicies(); q = declareQueue("has-expires", args("x-expires", 3 * DELAY)); Thread.sleep(2 * DELAY); assertTrue(queueExists(q)); } @Test public void maxLength() throws IOException, InterruptedException { String q = declareQueue("has-max-length", null); basicPublishVolatileN(q, 3); assertDelivered(q, 1); clearPolicies(); basicPublishVolatileN(q, 3); assertDelivered(q, 3); } @Test public void maxLengthArgs() throws IOException, InterruptedException { String q = declareQueue("has-max-length", args("x-max-length", 2)); basicPublishVolatileN(q, 3); assertDelivered(q, 1); clearPolicies(); basicPublishVolatileN(q, 3); assertDelivered(q, 2); } @Override protected void releaseResources() throws IOException { clearPolicies(); channel.exchangeDelete("has-ae"); channel.exchangeDelete("has-ae-args"); } private final Set<String> policies = new HashSet<String>(); private void setPolicy(String name, String pattern, String definition) throws IOException { // We need to override the HA policy that we use in HATests, so // priority 1. But we still want a valid test of HA, so add the // ha-mode definition. if (HATests.HA_TESTS_RUNNING) { definition += ",\"ha-mode\":\"all\""; } Host.rabbitmqctl("set_policy --priority 1 " + name + " " + pattern + " {" + escapeDefinition(definition) + "}"); policies.add(name); } private String escapeDefinition(String definition) { return definition.replaceAll(",", "\\\\,").replaceAll("\"", "\\\\\\\""); } private void clearPolicies() throws IOException { for (String policy : policies) { Host.rabbitmqctl("clear_policy " + policy); } policies.clear(); } private Map<String, Object> ttlArgs(int ttl) { return args("x-message-ttl", ttl); } private Map<String, Object> args(String name, Object value) { Map<String, Object> args = new HashMap<String, Object>(); args.put(name, value); return args; } private String declareQueue() throws IOException { return channel.queueDeclare().getQueue(); } private String declareQueue(String name, Map<String, Object> args) throws IOException { return channel.queueDeclare(name, false, true, false, args).getQueue(); } private boolean queueExists(String name) throws IOException { Channel ch2 = connection.createChannel(); try { ch2.queueDeclarePassive(name); return true; } catch (IOException ioe) { return false; } } private void basicPublishVolatileN(String q, int count) throws IOException { for (int i = 0; i < count; i++) { basicPublishVolatile(q); } } }