// 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; import com.rabbitmq.client.*; import com.rabbitmq.client.impl.nio.NioParams; import com.rabbitmq.tools.Host; import org.junit.After; import org.junit.Assume; import org.junit.Before; import org.junit.Rule; import org.junit.rules.TestRule; import org.junit.rules.TestWatcher; import org.junit.runner.Description; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.net.ssl.SSLContext; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeoutException; import static org.junit.Assert.*; import static org.junit.Assume.*; public class BrokerTestCase { private static final Logger LOGGER = LoggerFactory.getLogger(BrokerTestCase.class); @Rule public TestRule watcher = new TestWatcher() { protected void starting(Description description) { LOGGER.info( "Starting test: {}.{} (nio? {})", description.getTestClass().getSimpleName(), description.getMethodName(), TestUtils.USE_NIO ); } @Override protected void finished(Description description) { LOGGER.info("Test finished: {}.{}", description.getTestClass().getSimpleName(), description.getMethodName()); } }; protected ConnectionFactory connectionFactory = newConnectionFactory(); protected ConnectionFactory newConnectionFactory() { ConnectionFactory connectionFactory = TestUtils.connectionFactory(); if(TestUtils.USE_NIO) { connectionFactory.setNioParams(nioParams()); } connectionFactory.setAutomaticRecoveryEnabled(isAutomaticRecoveryEnabled()); return connectionFactory; } protected NioParams nioParams() { return new NioParams(); } protected boolean isAutomaticRecoveryEnabled() { return true; } protected Connection connection; protected Channel channel; @Before public void setUp() throws IOException, TimeoutException { assumeTrue(shouldRun()); openConnection(); openChannel(); createResources(); } @After public void tearDown() throws IOException, TimeoutException { if(shouldRun()) { closeChannel(); closeConnection(); openConnection(); openChannel(); releaseResources(); closeChannel(); closeConnection(); } } /** * Whether to run the test or not. * Subclasses can check whether some broker features * are available or not, and choose not to run the test. * @return */ protected boolean shouldRun() throws IOException { return true; } /** * Should create any AMQP resources needed by the test. Will be * called by BrokerTestCase's implementation of setUp, after the * connection and channel have been opened. */ protected void createResources() throws IOException, TimeoutException { } /** * Should destroy any AMQP resources that were created by the * test. Will be called by BrokerTestCase's implementation of * tearDown, after the connection and channel have been closed and * reopened specifically for this method. After this method * completes, the connection and channel will be closed again. */ protected void releaseResources() throws IOException { } protected void restart() throws IOException, TimeoutException { tearDown(); bareRestart(); setUp(); } protected void bareRestart() throws IOException { Host.invokeMakeTarget( "stop-rabbit-on-node start-rabbit-on-node"); } public void openConnection() throws IOException, TimeoutException { if (connection == null) { connection = connectionFactory.newConnection(); } } public void closeConnection() throws IOException { if (connection != null) { connection.abort(); connection = null; } } public void openChannel() throws IOException { channel = connection.createChannel(); } public void closeChannel() throws IOException { if (channel != null) { channel.abort(); channel = null; } } public void checkShutdownSignal(int expectedCode, IOException ioe) { ShutdownSignalException sse = (ShutdownSignalException) ioe.getCause(); checkShutdownSignal(expectedCode, sse); } public void checkShutdownSignal(int expectedCode, ShutdownSignalException sse) { Method method = sse.getReason(); channel = null; if (sse.isHardError()) { connection = null; AMQP.Connection.Close closeMethod = (AMQP.Connection.Close) method; assertEquals(expectedCode, closeMethod.getReplyCode()); } else { AMQP.Channel.Close closeMethod = (AMQP.Channel.Close) method; assertEquals(expectedCode, closeMethod.getReplyCode()); } } public void expectError(int error) { try { channel.basicQos(0); fail("Expected channel error " + error); } catch (IOException ioe) { // If we get a channel close back when flushing it with the // synchronous basicQos above. checkShutdownSignal(error, ioe); } catch (AlreadyClosedException ace) { // If it has already closed of its own accord before we got there. checkShutdownSignal(error, ace); } } protected void assertDelivered(String q, int count) throws IOException { assertDelivered(q, count, false); } protected void assertDelivered(String q, int count, boolean redelivered) throws IOException { GetResponse r; for (int i = 0; i < count; i++) { r = basicGet(q); assertNotNull(r); assertEquals(redelivered, r.getEnvelope().isRedeliver()); } assertNull(basicGet(q)); } protected GetResponse basicGet(String q) throws IOException { return channel.basicGet(q, true); } protected void basicPublishPersistent(String q) throws IOException { basicPublishPersistent("persistent message".getBytes(), q); } protected void basicPublishPersistent(byte[] msg, String q) throws IOException { basicPublishPersistent(msg, "", q); } protected void basicPublishPersistent(String x, String routingKey) throws IOException { basicPublishPersistent("persistent message".getBytes(), x, routingKey); } protected void basicPublishPersistent(byte[] msg, String x, String routingKey) throws IOException { channel.basicPublish(x, routingKey, MessageProperties.PERSISTENT_TEXT_PLAIN, msg); } protected void basicPublishVolatile(String q) throws IOException { basicPublishVolatile("not persistent message".getBytes(), q); } protected void basicPublishVolatile(byte[] msg, String q) throws IOException { basicPublishVolatile(msg, "", q); } protected void basicPublishVolatile(String x, String routingKey) throws IOException { basicPublishVolatile("not persistent message".getBytes(), x, routingKey); } protected void basicPublishVolatile(byte[] msg, String x, String routingKey) throws IOException { basicPublishVolatile(msg, x, routingKey, MessageProperties.TEXT_PLAIN); } public void basicPublishVolatile(byte[] msg, String x, String routingKey, AMQP.BasicProperties properties) throws IOException { channel.basicPublish(x, routingKey, properties, msg); } protected void declareAndBindDurableQueue(String q, String x, String r) throws IOException { declareDurableQueue(q); channel.queueBind(q, x, r); } protected void declareDurableDirectExchange(String x) throws IOException { channel.exchangeDeclare(x, "direct", true); } protected void declareDurableQueue(String q) throws IOException { channel.queueDeclare(q, true, false, false, null); } protected void declareTransientQueue(String q) throws IOException { channel.queueDeclare(q, false, false, false, null); } protected void declareTransientQueue(String q, Map<String, Object> args) throws IOException { channel.queueDeclare(q, false, false, false, args); } protected void declareDurableTopicExchange(String x) throws IOException { channel.exchangeDeclare(x, "topic", true); } protected void declareTransientTopicExchange(String x) throws IOException { channel.exchangeDeclare(x, "topic", false); } protected void declareTransientFanoutExchange(String x) throws IOException { channel.exchangeDeclare(x, "fanout", false); } protected void deleteExchange(String x) throws IOException { channel.exchangeDelete(x); } protected void deleteQueue(String q) throws IOException { channel.queueDelete(q); } protected void clearAllResourceAlarms() throws IOException, InterruptedException { clearResourceAlarm("memory"); clearResourceAlarm("disk"); } protected void setResourceAlarm(String source) throws IOException, InterruptedException { Host.invokeMakeTarget("set-resource-alarm SOURCE=" + source); } protected void clearResourceAlarm(String source) throws IOException, InterruptedException { Host.invokeMakeTarget("clear-resource-alarm SOURCE=" + source); } protected void block() throws IOException, InterruptedException { Host.rabbitmqctl("set_vm_memory_high_watermark 0.000000001"); setResourceAlarm("disk"); } protected void unblock() throws IOException, InterruptedException { Host.rabbitmqctl("set_vm_memory_high_watermark 0.4"); clearResourceAlarm("disk"); } protected String generateQueueName() { return "queue" + UUID.randomUUID().toString(); } protected String generateExchangeName() { return "exchange" + UUID.randomUUID().toString(); } protected SSLContext getSSLContext() throws NoSuchAlgorithmException { SSLContext c = null; // pick the first protocol available, preferring TLSv1.2, then TLSv1, // falling back to SSLv3 if running on an ancient/crippled JDK for(String proto : Arrays.asList("TLSv1.2", "TLSv1", "SSLv3")) { try { c = SSLContext.getInstance(proto); return c; } catch (NoSuchAlgorithmException x) { // keep trying } } throw new NoSuchAlgorithmException(); } }