// 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;
}
}
}