// 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.AMQConnection; import com.rabbitmq.client.impl.ConnectionParams; import com.rabbitmq.client.impl.Frame; import com.rabbitmq.client.impl.FrameHandler; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.net.InetAddress; import java.net.SocketException; import java.net.SocketTimeoutException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeoutException; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; /** * Test suite for AMQConnection. */ public class AMQConnectionTest { // private static final String CLOSE_MESSAGE = "terminated by test"; /** The mock frame handler used to test connection behaviour. */ private MockFrameHandler _mockFrameHandler; private ConnectionFactory factory; private MyExceptionHandler exceptionHandler; @Before public void setUp() throws Exception { _mockFrameHandler = new MockFrameHandler(); factory = TestUtils.connectionFactory(); exceptionHandler = new MyExceptionHandler(); factory.setExceptionHandler(exceptionHandler); } @After public void tearDown() throws Exception { factory = null; _mockFrameHandler = null; } @Test public void negativeTCPConnectionTimeout() { ConnectionFactory cf = TestUtils.connectionFactory(); try { cf.setConnectionTimeout(-10); fail("expected an exception"); } catch (IllegalArgumentException _ignored) { // expected } } @Test public void negativeProtocolHandshakeTimeout() { ConnectionFactory cf = TestUtils.connectionFactory(); try { cf.setHandshakeTimeout(-10); fail("expected an exception"); } catch (IllegalArgumentException _ignored) { // expected } } @Test public void tcpConnectionTimeoutGreaterThanHandShakeTimeout() { ConnectionFactory cf = TestUtils.connectionFactory(); cf.setHandshakeTimeout(3000); cf.setConnectionTimeout(5000); } @Test public void protocolHandshakeTimeoutGreaterThanTCPConnectionTimeout() { ConnectionFactory cf = TestUtils.connectionFactory(); cf.setConnectionTimeout(5000); cf.setHandshakeTimeout(7000); cf.setConnectionTimeout(0); cf.setHandshakeTimeout(7000); } @Test public void negativeRpcTimeoutIsForbidden() { ConnectionFactory cf = TestUtils.connectionFactory(); try { cf.setChannelRpcTimeout(-10); fail("expected an exception"); } catch (IllegalArgumentException _ignored) { // expected } } /** Check the AMQConnection does send exactly 1 initial header, and deal correctly with * the frame handler throwing an exception when we try to read data */ @Test public void connectionSendsSingleHeaderAndTimesOut() throws TimeoutException { IOException exception = new SocketTimeoutException(); _mockFrameHandler.setExceptionOnReadingFrames(exception); assertEquals(0, _mockFrameHandler.countHeadersSent()); try { ConnectionParams params = factory.params(Executors.newFixedThreadPool(1)); new AMQConnection(params, _mockFrameHandler).start(); fail("Connection should have thrown exception"); } catch(IOException signal) { // As expected } assertEquals(1, _mockFrameHandler.countHeadersSent()); // _connection.close(0, CLOSE_MESSAGE); List<Throwable> exceptionList = exceptionHandler.getHandledExceptions(); assertEquals(Collections.<Throwable>singletonList(exception), exceptionList); } /** Check we can open a connection once, but not twice. * @throws IOException */ // public void testCanOpenConnectionOnceOnly() throws IOException { // AMQConnection connection = new AMQConnection(_mockFrameHandler); // connection.open(); // try { // connection.open(); // fail("We shouldn't have been able to open this connection more than once."); // } catch(IOException ex) { // // as expected // } // } /** * Test that we catch timeout between connect and negotiation of the connection being finished. */ @Test public void connectionHangInNegotiation() { this._mockFrameHandler.setTimeoutCount(10); // to limit hang assertEquals(0, this._mockFrameHandler.countHeadersSent()); try { ConnectionParams params = factory.params(Executors.newFixedThreadPool(1)); new AMQConnection(params, this._mockFrameHandler).start(); fail("Connection should have thrown exception"); } catch(IOException signal) { // expected } catch(TimeoutException te) { // also fine: continuation timed out first } assertEquals(1, this._mockFrameHandler.countHeadersSent()); List<Throwable> exceptionList = exceptionHandler.getHandledExceptions(); assertEquals("Only one exception expected", 1, exceptionList.size()); assertEquals("Wrong type of exception returned.", SocketTimeoutException.class, exceptionList.get(0).getClass()); } @Test public void clientProvidedConnectionName() throws IOException, TimeoutException { String providedName = "event consumers connection"; Connection connection = factory.newConnection(providedName); assertEquals(providedName, connection.getClientProvidedName()); connection.close(); List<Address> addrs1 = Arrays.asList(new Address("127.0.0.1"), new Address("127.0.0.1", 5672)); connection = factory.newConnection(addrs1, providedName); assertEquals(providedName, connection.getClientProvidedName()); connection.close(); Address[] addrs2 = {new Address("127.0.0.1"), new Address("127.0.0.1", 5672)}; connection = factory.newConnection(addrs2, providedName); assertEquals(providedName, connection.getClientProvidedName()); connection.close(); ExecutorService xs = Executors.newSingleThreadExecutor(); connection = factory.newConnection(xs, providedName); assertEquals(providedName, connection.getClientProvidedName()); connection.close(); connection = factory.newConnection(xs, addrs1, providedName); assertEquals(providedName, connection.getClientProvidedName()); connection.close(); connection = factory.newConnection(xs, addrs2, providedName); assertEquals(providedName, connection.getClientProvidedName()); connection.close(); } /** Mock frame handler to facilitate testing. */ private static class MockFrameHandler implements FrameHandler { /** How many times has sendHeader() been called? */ private int _numHeadersSent; private int timeout; /** An optional exception for us to throw on reading frames */ private IOException _exceptionOnReadingFrames; private int timeoutCount = 0; /** count how many headers we've sent * @return the number of sent headers */ public int countHeadersSent() { return _numHeadersSent; } public void setExceptionOnReadingFrames(IOException exception) { _exceptionOnReadingFrames = exception; } public void setTimeoutCount(int timeoutCount) { this.timeoutCount = timeoutCount; } public Frame readFrame() throws IOException { if (_exceptionOnReadingFrames != null) { throw _exceptionOnReadingFrames; } if (this.timeoutCount > 0) { if (--this.timeoutCount == 0) throw new IOException("Mock Framehandler: too many timeouts."); } return null; // simulate a socket timeout } public void sendHeader() throws IOException { _numHeadersSent++; } @Override public void initialize(AMQConnection connection) { connection.startMainLoop(); } public void setTimeout(int timeoutMs) throws SocketException { this.timeout = timeoutMs; } public void writeFrame(Frame frame) throws IOException { // no need to implement this: don't bother writing the frame } public void close() { // nothing to do } public int getTimeout() throws SocketException { return this.timeout; } public InetAddress getAddress() { return null; } public int getPort() { return -1; } public void flush() throws IOException { // no need to implement this: don't bother writing the frame } public InetAddress getLocalAddress() { return null; } public int getLocalPort() { return -1; } } /** Exception handler to facilitate testing. */ private class MyExceptionHandler implements ExceptionHandler { private final List<Throwable> _handledExceptions = new ArrayList<Throwable>(); public void handleUnexpectedConnectionDriverException(Connection conn, Throwable ex) { _handledExceptions.add(ex); } public void handleReturnListenerException(Channel ch, Throwable ex) { fail("handleReturnListenerException: " + ex); } public void handleFlowListenerException(Channel ch, Throwable ex) { fail("handleFlowListenerException: " + ex); } public void handleConfirmListenerException(Channel ch, Throwable ex) { fail("handleConfirmListenerException: " + ex); } public void handleBlockedListenerException(Connection conn, Throwable ex) { fail("handleBlockedListenerException: " + ex); } public void handleConsumerException(Channel ch, Throwable ex, Consumer c, String consumerTag, String methodName) { fail("handleConsumerException " + consumerTag + " " + methodName + ": " + ex); } public void handleConnectionRecoveryException(Connection conn, Throwable ex) { _handledExceptions.add(ex); } public void handleChannelRecoveryException(Channel ch, Throwable ex) { _handledExceptions.add(ex); } public void handleTopologyRecoveryException(Connection conn, Channel ch, TopologyRecoveryException ex) { _handledExceptions.add(ex); } public List<Throwable> getHandledExceptions() { return _handledExceptions; } } }