/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.amqp.rabbit.connection;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.AdditionalMatchers.aryEq;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.amqp.AmqpTimeoutException;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory.CacheMode;
import org.springframework.amqp.utils.test.TestUtils;
import org.springframework.test.util.ReflectionTestUtils;
import com.rabbitmq.client.Address;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.GetResponse;
/**
* @author Mark Pollack
* @author Dave Syer
* @author Gary Russell
* @author Artem Bilan
* @author Steve Powell
*/
public class CachingConnectionFactoryTests extends AbstractConnectionFactoryTests {
@Override
protected AbstractConnectionFactory createConnectionFactory(ConnectionFactory connectionFactory) {
CachingConnectionFactory ccf = new CachingConnectionFactory(connectionFactory);
ccf.setExecutor(mock(ExecutorService.class));
return ccf;
}
@Test
public void testWithConnectionFactoryDefaults() throws Exception {
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
com.rabbitmq.client.Connection mockConnection = mock(com.rabbitmq.client.Connection.class);
Channel mockChannel = mock(Channel.class);
when(mockConnectionFactory.newConnection(any(ExecutorService.class), anyString())).thenReturn(mockConnection);
when(mockConnection.createChannel()).thenReturn(mockChannel);
when(mockChannel.isOpen()).thenReturn(true);
when(mockConnection.isOpen()).thenReturn(true);
CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory);
ccf.setExecutor(mock(ExecutorService.class));
Connection con = ccf.createConnection();
Channel channel = con.createChannel(false);
channel.close(); // should be ignored, and placed into channel cache.
con.close(); // should be ignored
Connection con2 = ccf.createConnection();
/*
* will retrieve same channel object that was just put into channel cache
*/
Channel channel2 = con2.createChannel(false);
channel2.close(); // should be ignored
con2.close(); // should be ignored
assertSame(con, con2);
assertSame(channel, channel2);
verify(mockConnection, never()).close();
verify(mockChannel, never()).close();
}
@Test
public void testWithConnectionFactoryCacheSize() throws Exception {
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
com.rabbitmq.client.Connection mockConnection = mock(com.rabbitmq.client.Connection.class);
Channel mockChannel1 = mock(Channel.class);
Channel mockChannel2 = mock(Channel.class);
Channel mockTxChannel = mock(Channel.class);
when(mockConnectionFactory.newConnection(any(ExecutorService.class), anyString())).thenReturn(mockConnection);
when(mockConnection.isOpen()).thenReturn(true);
when(mockConnection.createChannel()).thenReturn(mockChannel1, mockChannel2, mockTxChannel);
when(mockChannel1.basicGet("foo", false)).thenReturn(new GetResponse(null, null, null, 1));
when(mockChannel2.basicGet("bar", false)).thenReturn(new GetResponse(null, null, null, 1));
when(mockChannel1.isOpen()).thenReturn(true);
when(mockChannel2.isOpen()).thenReturn(true);
CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory);
ccf.setExecutor(mock(ExecutorService.class));
ccf.setChannelCacheSize(2);
Connection con = ccf.createConnection();
Channel channel1 = con.createChannel(false);
Channel channel2 = con.createChannel(false);
ChannelProxy txChannel = (ChannelProxy) con.createChannel(true);
assertTrue(txChannel.isTransactional());
verify(mockTxChannel).txSelect();
txChannel.close();
channel1.basicGet("foo", true);
channel2.basicGet("bar", true);
channel1.close(); // should be ignored, and add last into channel cache.
channel2.close(); // should be ignored, and add last into channel cache.
Channel ch1 = con.createChannel(false); // remove first entry in cache
// (channel1)
Channel ch2 = con.createChannel(false); // remove first entry in cache
// (channel2)
assertNotSame(ch1, ch2);
assertSame(ch1, channel1);
assertSame(ch2, channel2);
ch1.close();
ch2.close();
verify(mockConnection, times(3)).createChannel();
con.close(); // should be ignored
verify(mockConnection, never()).close();
verify(mockChannel1, never()).close();
verify(mockChannel2, never()).close();
}
@Test
public void testCacheSizeExceeded() throws Exception {
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
com.rabbitmq.client.Connection mockConnection = mock(com.rabbitmq.client.Connection.class);
Channel mockChannel1 = mock(Channel.class);
Channel mockChannel2 = mock(Channel.class);
Channel mockChannel3 = mock(Channel.class);
when(mockConnectionFactory.newConnection(any(ExecutorService.class), anyString())).thenReturn(mockConnection);
when(mockConnection.createChannel()).thenReturn(mockChannel1).thenReturn(mockChannel2).thenReturn(mockChannel3);
when(mockConnection.isOpen()).thenReturn(true);
// Called during physical close
when(mockChannel1.isOpen()).thenReturn(true);
when(mockChannel2.isOpen()).thenReturn(true);
when(mockChannel3.isOpen()).thenReturn(true);
CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory);
ccf.setExecutor(mock(ExecutorService.class));
ccf.setChannelCacheSize(1);
Connection con = ccf.createConnection();
Channel channel1 = con.createChannel(false);
// cache size is 1, but the other connection is not released yet so this
// creates a new one
Channel channel2 = con.createChannel(false);
assertNotSame(channel1, channel2);
// should be ignored, and added last into channel cache.
channel1.close();
// should be physically closed
channel2.close();
// remove first entry in cache (channel1)
Channel ch1 = con.createChannel(false);
// create a new channel
Channel ch2 = con.createChannel(false);
assertNotSame(ch1, ch2);
assertSame(ch1, channel1);
assertNotSame(ch2, channel2);
ch1.close();
ch2.close();
verify(mockConnection, times(3)).createChannel();
con.close(); // should be ignored
verify(mockConnection, never()).close();
verify(mockChannel1, never()).close();
verify(mockChannel2, atLeastOnce()).close();
verify(mockChannel3, atLeastOnce()).close();
}
@Test
public void testCheckoutLimit() throws Exception {
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
com.rabbitmq.client.Connection mockConnection = mock(com.rabbitmq.client.Connection.class);
Channel mockChannel1 = mock(Channel.class);
when(mockConnectionFactory.newConnection(any(ExecutorService.class), anyString())).thenReturn(mockConnection);
when(mockConnection.createChannel()).thenReturn(mockChannel1);
when(mockConnection.isOpen()).thenReturn(true);
// Called during physical close
when(mockChannel1.isOpen()).thenReturn(true);
CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory);
ccf.setExecutor(mock(ExecutorService.class));
ccf.setChannelCacheSize(1);
ccf.setChannelCheckoutTimeout(10);
Connection con = ccf.createConnection();
Channel channel1 = con.createChannel(false);
try {
con.createChannel(false);
fail("Exception expected");
}
catch (AmqpTimeoutException e) { }
// should be ignored, and added last into channel cache.
channel1.close();
// remove first entry in cache (channel1)
Channel ch1 = con.createChannel(false);
assertSame(ch1, channel1);
ch1.close();
verify(mockConnection, times(1)).createChannel();
con.close(); // should be ignored
verify(mockConnection, never()).close();
verify(mockChannel1, never()).close();
ccf.destroy();
}
@Test
public void testConnectionLimit() throws Exception {
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
com.rabbitmq.client.Connection mockConnection = mock(com.rabbitmq.client.Connection.class);
when(mockConnectionFactory.newConnection(any(ExecutorService.class), anyString())).thenReturn(mockConnection);
when(mockConnection.isOpen()).thenReturn(true);
final CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory);
ccf.setExecutor(mock(ExecutorService.class));
ccf.setCacheMode(CacheMode.CONNECTION);
ccf.setConnectionCacheSize(1);
ccf.setConnectionLimit(1);
ccf.setChannelCheckoutTimeout(10);
Connection con1 = ccf.createConnection();
try {
ccf.createConnection();
fail("Exception expected");
}
catch (AmqpTimeoutException e) { }
// should be ignored, and added to cache
con1.close();
Connection con2 = ccf.createConnection();
assertSame(con1, con2);
final CountDownLatch latch2 = new CountDownLatch(1);
final CountDownLatch latch1 = new CountDownLatch(1);
final AtomicReference<Connection> connection = new AtomicReference<Connection>();
ccf.setChannelCheckoutTimeout(30000);
Executors.newSingleThreadExecutor().execute(() -> {
latch1.countDown();
connection.set(ccf.createConnection());
latch2.countDown();
});
assertTrue(latch1.await(10, TimeUnit.SECONDS));
Thread.sleep(100);
con2.close();
assertTrue(latch2.await(10, TimeUnit.SECONDS));
assertSame(con2, connection.get());
ccf.destroy();
}
@Test
public void testCheckoutsWithRefreshedConnectionModeChannel() throws Exception {
testCheckoutsWithRefreshedConnectionGuts(CacheMode.CHANNEL);
}
@Test
public void testCheckoutsWithRefreshedConnectionModeConnection() throws Exception {
testCheckoutsWithRefreshedConnectionGuts(CacheMode.CONNECTION);
}
private void testCheckoutsWithRefreshedConnectionGuts(CacheMode mode) throws Exception {
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
com.rabbitmq.client.Connection mockConnection1 = mock(com.rabbitmq.client.Connection.class);
com.rabbitmq.client.Connection mockConnection2 = mock(com.rabbitmq.client.Connection.class);
Channel mockChannel1 = mock(Channel.class);
Channel mockChannel2 = mock(Channel.class);
Channel mockChannel3 = mock(Channel.class);
Channel mockChannel4 = mock(Channel.class);
when(mockConnectionFactory.newConnection(any(ExecutorService.class), anyString()))
.thenReturn(mockConnection1, mockConnection2);
when(mockConnection1.createChannel()).thenReturn(mockChannel1, mockChannel2);
when(mockConnection1.isOpen()).thenReturn(true);
when(mockConnection2.createChannel()).thenReturn(mockChannel3, mockChannel4);
when(mockConnection2.isOpen()).thenReturn(true);
when(mockChannel1.isOpen()).thenReturn(true);
when(mockChannel2.isOpen()).thenReturn(true);
when(mockChannel3.isOpen()).thenReturn(true);
when(mockChannel4.isOpen()).thenReturn(true);
CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory);
ccf.setExecutor(mock(ExecutorService.class));
ccf.setChannelCacheSize(2);
ccf.setChannelCheckoutTimeout(10);
ccf.setCacheMode(mode);
ccf.addConnectionListener(connection -> {
try {
// simulate admin
connection.createChannel(false).close();
}
catch (Exception e) {
fail(e.getMessage());
}
});
Connection con = ccf.createConnection();
Channel channel1 = con.createChannel(false);
assertEquals(1,
((Semaphore) TestUtils.getPropertyValue(ccf, "checkoutPermits", Map.class).values().iterator().next())
.availablePermits());
channel1.close();
con.close();
assertEquals(2,
((Semaphore) TestUtils.getPropertyValue(ccf, "checkoutPermits", Map.class).values().iterator().next())
.availablePermits());
when(mockConnection1.isOpen()).thenReturn(false);
when(mockChannel1.isOpen()).thenReturn(false);
when(mockChannel2.isOpen()).thenReturn(false);
con.createChannel(false).close();
con = ccf.createConnection();
con.createChannel(false).close();
con.createChannel(false).close();
con.createChannel(false).close();
con.createChannel(false).close();
con.createChannel(false).close();
verify(mockConnection1, times(1)).createChannel();
verify(mockConnection2, times(2)).createChannel();
con.close();
verify(mockConnection2, never()).close();
assertEquals(2,
((Semaphore) TestUtils.getPropertyValue(ccf, "checkoutPermits", Map.class).values().iterator().next())
.availablePermits());
ccf.destroy();
assertEquals(2,
((Semaphore) TestUtils.getPropertyValue(ccf, "checkoutPermits", Map.class).values().iterator().next())
.availablePermits());
}
@Test
public void testCheckoutLimitWithRelease() throws IOException, Exception {
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
com.rabbitmq.client.Connection mockConnection = mock(com.rabbitmq.client.Connection.class);
Channel mockChannel1 = mock(Channel.class);
when(mockConnectionFactory.newConnection(any(ExecutorService.class), anyString())).thenReturn(mockConnection);
when(mockConnection.createChannel()).thenReturn(mockChannel1);
when(mockConnection.isOpen()).thenReturn(true);
// Called during physical close
when(mockChannel1.isOpen()).thenReturn(true);
CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory);
ccf.setExecutor(mock(ExecutorService.class));
ccf.setChannelCacheSize(1);
ccf.setChannelCheckoutTimeout(10000);
final Connection con = ccf.createConnection();
final AtomicReference<Channel> channelOne = new AtomicReference<Channel>();
final CountDownLatch latch = new CountDownLatch(1);
new Thread(() -> {
Channel channel1 = con.createChannel(false);
latch.countDown();
channelOne.set(channel1);
try {
Thread.sleep(100);
channel1.close();
}
catch (InterruptedException e1) {
Thread.currentThread().interrupt();
}
catch (IOException e2) {
}
catch (TimeoutException e3) {
}
}).start();
assertTrue(latch.await(10, TimeUnit.SECONDS));
Channel channel2 = con.createChannel(false);
assertSame(channelOne.get(), channel2);
channel2.close();
verify(mockConnection, never()).close();
verify(mockChannel1, never()).close();
ccf.destroy();
}
@Test
public void testReleaseWithForcedPhysicalClose() throws Exception {
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
com.rabbitmq.client.Connection mockConnection1 = mock(com.rabbitmq.client.Connection.class);
Channel mockChannel1 = mock(Channel.class);
when(mockConnectionFactory.newConnection(any(ExecutorService.class), anyString())).thenReturn(mockConnection1);
when(mockConnection1.createChannel()).thenReturn(mockChannel1);
when(mockConnection1.isOpen()).thenReturn(true);
when(mockChannel1.isOpen()).thenReturn(true);
CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory);
ccf.setExecutor(mock(ExecutorService.class));
ccf.setChannelCacheSize(1);
ccf.setChannelCheckoutTimeout(10);
Connection con = ccf.createConnection();
Channel channel1 = con.createChannel(false);
assertEquals(0,
((Semaphore) TestUtils.getPropertyValue(ccf, "checkoutPermits", Map.class).values().iterator().next())
.availablePermits());
channel1.close();
con.close();
assertEquals(1,
((Semaphore) TestUtils.getPropertyValue(ccf, "checkoutPermits", Map.class).values().iterator().next())
.availablePermits());
channel1 = con.createChannel(false);
RabbitUtils.setPhysicalCloseRequired(true);
assertEquals(0,
((Semaphore) TestUtils.getPropertyValue(ccf, "checkoutPermits", Map.class).values().iterator().next())
.availablePermits());
channel1.close();
RabbitUtils.setPhysicalCloseRequired(false);
con.close();
verify(mockChannel1).close();
verify(mockConnection1, never()).close();
assertEquals(1,
((Semaphore) TestUtils.getPropertyValue(ccf, "checkoutPermits", Map.class).values().iterator().next())
.availablePermits());
ccf.destroy();
assertEquals(1,
((Semaphore) TestUtils.getPropertyValue(ccf, "checkoutPermits", Map.class).values().iterator().next())
.availablePermits());
}
@Test
public void testDoubleLogicalClose() throws Exception {
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
com.rabbitmq.client.Connection mockConnection1 = mock(com.rabbitmq.client.Connection.class);
Channel mockChannel1 = mock(Channel.class);
when(mockConnectionFactory.newConnection(any(ExecutorService.class), anyString())).thenReturn(mockConnection1);
when(mockConnection1.createChannel()).thenReturn(mockChannel1);
when(mockConnection1.isOpen()).thenReturn(true);
when(mockChannel1.isOpen()).thenReturn(true);
CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory);
ccf.setExecutor(mock(ExecutorService.class));
ccf.setChannelCacheSize(1);
ccf.setChannelCheckoutTimeout(10);
Connection con = ccf.createConnection();
Channel channel1 = con.createChannel(false);
assertEquals(0,
((Semaphore) TestUtils.getPropertyValue(ccf, "checkoutPermits", Map.class).values().iterator().next())
.availablePermits());
channel1.close();
assertEquals(1,
((Semaphore) TestUtils.getPropertyValue(ccf, "checkoutPermits", Map.class).values().iterator().next())
.availablePermits());
channel1.close(); // double close of proxy
assertEquals(1,
((Semaphore) TestUtils.getPropertyValue(ccf, "checkoutPermits", Map.class).values().iterator().next())
.availablePermits());
con.close();
verify(mockChannel1, never()).close();
verify(mockConnection1, never()).close();
ccf.destroy();
assertEquals(1,
((Semaphore) TestUtils.getPropertyValue(ccf, "checkoutPermits", Map.class).values().iterator().next())
.availablePermits());
}
@Test
public void testCacheSizeExceededAfterClose() throws Exception {
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
com.rabbitmq.client.Connection mockConnection = mock(com.rabbitmq.client.Connection.class);
Channel mockChannel1 = mock(Channel.class);
Channel mockChannel2 = mock(Channel.class);
when(mockConnectionFactory.newConnection(any(ExecutorService.class), anyString())).thenReturn(mockConnection);
when(mockConnection.createChannel()).thenReturn(mockChannel1).thenReturn(mockChannel2);
when(mockConnection.isOpen()).thenReturn(true);
// Called during physical close
when(mockChannel1.isOpen()).thenReturn(true);
when(mockChannel2.isOpen()).thenReturn(true);
CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory);
ccf.setExecutor(mock(ExecutorService.class));
ccf.setChannelCacheSize(1);
Connection con = ccf.createConnection();
Channel channel1 = con.createChannel(false);
channel1.close(); // should be ignored, and add last into channel cache.
Channel channel2 = con.createChannel(false);
channel2.close(); // should be ignored, and add last into channel cache.
assertSame(channel1, channel2);
Channel ch1 = con.createChannel(false); // remove first entry in cache
// (channel1)
Channel ch2 = con.createChannel(false); // create new channel
assertNotSame(ch1, ch2);
assertSame(ch1, channel1);
assertNotSame(ch2, channel2);
ch1.close();
ch2.close();
verify(mockConnection, times(2)).createChannel();
con.close(); // should be ignored
verify(mockConnection, never()).close();
verify(mockChannel1, never()).close();
verify(mockChannel2, atLeastOnce()).close();
}
@Test
public void testTransactionalAndNonTransactionalChannelsSegregated() throws Exception {
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
com.rabbitmq.client.Connection mockConnection = mock(com.rabbitmq.client.Connection.class);
Channel mockChannel1 = mock(Channel.class);
Channel mockChannel2 = mock(Channel.class);
when(mockConnectionFactory.newConnection(any(ExecutorService.class), anyString())).thenReturn(mockConnection);
when(mockConnection.createChannel()).thenReturn(mockChannel1).thenReturn(mockChannel2);
when(mockConnection.isOpen()).thenReturn(true);
// Called during physical close
when(mockChannel1.isOpen()).thenReturn(true);
when(mockChannel2.isOpen()).thenReturn(true);
CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory);
ccf.setExecutor(mock(ExecutorService.class));
ccf.setChannelCacheSize(1);
Connection con = ccf.createConnection();
Channel channel1 = con.createChannel(true);
channel1.txSelect();
channel1.close(); // should be ignored, and add last into channel cache.
/*
* When a channel is created as non-transactional we should create a new one.
*/
Channel channel2 = con.createChannel(false);
channel2.close(); // should be ignored, and add last into channel cache.
assertNotSame(channel1, channel2);
Channel ch1 = con.createChannel(true); // remove first entry in cache (channel1)
Channel ch2 = con.createChannel(false); // create new channel
assertNotSame(ch1, ch2);
assertSame(ch1, channel1); // The non-transactional one
assertSame(ch2, channel2);
ch1.close();
ch2.close();
verify(mockConnection, times(2)).createChannel();
con.close(); // should be ignored
verify(mockConnection, never()).close();
verify(mockChannel1, never()).close();
verify(mockChannel2, never()).close();
@SuppressWarnings("unchecked")
List<Channel> notxlist = (List<Channel>) ReflectionTestUtils.getField(ccf, "cachedChannelsNonTransactional");
assertEquals(1, notxlist.size());
@SuppressWarnings("unchecked")
List<Channel> txlist = (List<Channel>) ReflectionTestUtils.getField(ccf, "cachedChannelsTransactional");
assertEquals(1, txlist.size());
}
@Test
public void testWithConnectionFactoryDestroy() throws Exception {
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
com.rabbitmq.client.Connection mockConnection1 = mock(com.rabbitmq.client.Connection.class);
com.rabbitmq.client.Connection mockConnection2 = mock(com.rabbitmq.client.Connection.class);
Channel mockChannel1 = mock(Channel.class);
Channel mockChannel2 = mock(Channel.class);
Channel mockChannel3 = mock(Channel.class);
assertNotSame(mockChannel1, mockChannel2);
when(mockConnectionFactory.newConnection(any(ExecutorService.class), anyString())).thenReturn(mockConnection1, mockConnection2);
when(mockConnection1.createChannel()).thenReturn(mockChannel1, mockChannel2);
when(mockConnection1.isOpen()).thenReturn(true);
when(mockConnection2.createChannel()).thenReturn(mockChannel3);
when(mockConnection2.isOpen()).thenReturn(true);
// Called during physical close
when(mockChannel1.isOpen()).thenReturn(true);
when(mockChannel2.isOpen()).thenReturn(true);
when(mockChannel3.isOpen()).thenReturn(true);
CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory);
ccf.setExecutor(mock(ExecutorService.class));
ccf.setChannelCacheSize(2);
Connection con = ccf.createConnection();
// This will return a proxy that surpresses calls to close
Channel channel1 = con.createChannel(false);
Channel channel2 = con.createChannel(false);
// Should be ignored, and add last into channel cache.
channel1.close();
channel2.close();
// remove first entry in cache (channel1)
Channel ch1 = con.createChannel(false);
// remove first entry in cache (channel2)
Channel ch2 = con.createChannel(false);
assertSame(ch1, channel1);
assertSame(ch2, channel2);
Channel target1 = ((ChannelProxy) ch1).getTargetChannel();
Channel target2 = ((ChannelProxy) ch2).getTargetChannel();
// make sure mokito returned different mocks for the channel
assertNotSame(target1, target2);
ch1.close();
ch2.close();
con.close(); // should be ignored
com.rabbitmq.client.Connection conDelegate = targetDelegate(con);
ccf.destroy(); // should call close on connection and channels in cache
verify(mockConnection1, times(2)).createChannel();
verify(mockConnection1).close(anyInt());
// verify(mockChannel1).close();
verify(mockChannel2).close();
// After destroy we can get a new connection
Connection con1 = ccf.createConnection();
assertNotSame(conDelegate, targetDelegate(con1));
// This will return a proxy that surpresses calls to close
Channel channel3 = con.createChannel(false);
assertNotSame(channel3, channel1);
assertNotSame(channel3, channel2);
}
@Test
public void testWithChannelListener() throws Exception {
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
com.rabbitmq.client.Connection mockConnection = mock(com.rabbitmq.client.Connection.class);
Channel mockChannel = mock(Channel.class);
when(mockConnectionFactory.newConnection(any(ExecutorService.class), anyString())).thenReturn(mockConnection);
when(mockConnection.isOpen()).thenReturn(true);
when(mockChannel.isOpen()).thenReturn(true);
when(mockConnection.createChannel()).thenReturn(mockChannel);
final AtomicInteger called = new AtomicInteger(0);
AbstractConnectionFactory connectionFactory = createConnectionFactory(mockConnectionFactory);
connectionFactory.setChannelListeners(Arrays.asList(new ChannelListener() {
@Override
public void onCreate(Channel channel, boolean transactional) {
called.incrementAndGet();
}
}));
((CachingConnectionFactory) connectionFactory).setChannelCacheSize(1);
Connection con = connectionFactory.createConnection();
Channel channel = con.createChannel(false);
assertEquals(1, called.get());
channel.close();
con.close();
verify(mockConnection, never()).close();
connectionFactory.createConnection();
con.createChannel(false);
assertEquals(1, called.get());
connectionFactory.destroy();
verify(mockConnection, atLeastOnce()).close(anyInt());
verify(mockConnectionFactory).newConnection(any(ExecutorService.class), anyString());
}
@Test
public void testWithConnectionListener() throws Exception {
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
com.rabbitmq.client.Connection mockConnection1 = mock(com.rabbitmq.client.Connection.class);
when(mockConnection1.toString()).thenReturn("conn1");
com.rabbitmq.client.Connection mockConnection2 = mock(com.rabbitmq.client.Connection.class);
when(mockConnection2.toString()).thenReturn("conn2");
Channel mockChannel = mock(Channel.class);
when(mockConnectionFactory.newConnection(any(ExecutorService.class), anyString())).thenReturn(mockConnection1, mockConnection2);
when(mockConnection1.isOpen()).thenReturn(true);
when(mockChannel.isOpen()).thenReturn(true);
when(mockConnection1.createChannel()).thenReturn(mockChannel);
when(mockConnection2.createChannel()).thenReturn(mockChannel);
final AtomicReference<Connection> created = new AtomicReference<Connection>();
final AtomicReference<Connection> closed = new AtomicReference<Connection>();
final AtomicInteger timesClosed = new AtomicInteger(0);
AbstractConnectionFactory connectionFactory = createConnectionFactory(mockConnectionFactory);
connectionFactory.addConnectionListener(new ConnectionListener() {
@Override
public void onCreate(Connection connection) {
created.set(connection);
}
@Override
public void onClose(Connection connection) {
closed.set(connection);
timesClosed.getAndAdd(1);
}
});
((CachingConnectionFactory) connectionFactory).setChannelCacheSize(1);
Connection con = connectionFactory.createConnection();
Channel channel = con.createChannel(false);
assertSame(con, created.get());
channel.close();
con.close();
verify(mockConnection1, never()).close();
Connection same = connectionFactory.createConnection();
channel = con.createChannel(false);
assertSame(con, same);
channel.close();
com.rabbitmq.client.Connection conDelegate = targetDelegate(con);
when(mockConnection1.isOpen()).thenReturn(false);
when(mockChannel.isOpen()).thenReturn(false); // force a connection refresh
channel.basicCancel("foo");
channel.close();
assertEquals(1, timesClosed.get());
Connection notSame = connectionFactory.createConnection();
assertNotSame(conDelegate, targetDelegate(notSame));
assertSame(con, closed.get());
assertSame(notSame, created.get());
connectionFactory.destroy();
verify(mockConnection2, atLeastOnce()).close(anyInt());
assertSame(notSame, closed.get());
assertEquals(2, timesClosed.get());
verify(mockConnectionFactory, times(2)).newConnection(any(ExecutorService.class), anyString());
}
@Test
public void testWithConnectionFactoryCachedConnection() throws Exception {
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
final List<com.rabbitmq.client.Connection> mockConnections = new ArrayList<com.rabbitmq.client.Connection>();
final List<Channel> mockChannels = new ArrayList<Channel>();
doAnswer(new Answer<com.rabbitmq.client.Connection>() {
private int connectionNumber;
@Override
public com.rabbitmq.client.Connection answer(InvocationOnMock invocation) throws Throwable {
com.rabbitmq.client.Connection connection = mock(com.rabbitmq.client.Connection.class);
doAnswer(new Answer<Channel>() {
private int channelNumber;
@Override
public Channel answer(InvocationOnMock invocation) throws Throwable {
Channel channel = mock(Channel.class);
when(channel.isOpen()).thenReturn(true);
int channelNumnber = ++this.channelNumber;
when(channel.toString()).thenReturn("mockChannel" + connectionNumber + ":" + channelNumnber);
mockChannels.add(channel);
return channel;
}
}).when(connection).createChannel();
int connectionNumber = ++this.connectionNumber;
when(connection.toString()).thenReturn("mockConnection" + connectionNumber);
when(connection.isOpen()).thenReturn(true);
mockConnections.add(connection);
return connection;
}
}).when(mockConnectionFactory).newConnection(any(ExecutorService.class), anyString());
CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory);
ccf.setExecutor(mock(ExecutorService.class));
ccf.setCacheMode(CacheMode.CONNECTION);
ccf.afterPropertiesSet();
Set<?> allocatedConnections = TestUtils.getPropertyValue(ccf, "allocatedConnections", Set.class);
assertEquals(0, allocatedConnections.size());
BlockingQueue<?> idleConnections = TestUtils.getPropertyValue(ccf, "idleConnections", BlockingQueue.class);
assertEquals(0, idleConnections.size());
final AtomicReference<com.rabbitmq.client.Connection> createNotification =
new AtomicReference<com.rabbitmq.client.Connection>();
final AtomicReference<com.rabbitmq.client.Connection> closedNotification =
new AtomicReference<com.rabbitmq.client.Connection>();
ccf.setConnectionListeners(Collections.singletonList(new ConnectionListener() {
@Override
public void onCreate(Connection connection) {
assertNull(createNotification.get());
createNotification.set(targetDelegate(connection));
}
@Override
public void onClose(Connection connection) {
assertNull(closedNotification.get());
closedNotification.set(targetDelegate(connection));
}
}));
Connection con1 = ccf.createConnection();
verifyConnectionIs(mockConnections.get(0), con1);
assertEquals(1, allocatedConnections.size());
assertEquals(0, idleConnections.size());
assertNotNull(createNotification.get());
assertSame(mockConnections.get(0), createNotification.getAndSet(null));
Channel channel1 = con1.createChannel(false);
verifyChannelIs(mockChannels.get(0), channel1);
channel1.close();
//AMQP-358
verify(mockChannels.get(0), never()).close();
con1.close(); // should be ignored, and placed into connection cache.
verify(mockConnections.get(0), never()).close();
assertEquals(1, allocatedConnections.size());
assertEquals(1, idleConnections.size());
assertNull(closedNotification.get());
/*
* will retrieve same connection that was just put into cache, and reuse single channel from cache as well
*/
Connection con2 = ccf.createConnection();
verifyConnectionIs(mockConnections.get(0), con2);
Channel channel2 = con2.createChannel(false);
verifyChannelIs(mockChannels.get(0), channel2);
channel2.close();
verify(mockChannels.get(0), never()).close();
con2.close();
verify(mockConnections.get(0), never()).close();
assertEquals(1, allocatedConnections.size());
assertEquals(1, idleConnections.size());
assertNull(createNotification.get());
/*
* Now check for multiple connections/channels
*/
con1 = ccf.createConnection();
verifyConnectionIs(mockConnections.get(0), con1);
con2 = ccf.createConnection();
verifyConnectionIs(mockConnections.get(1), con2);
channel1 = con1.createChannel(false);
verifyChannelIs(mockChannels.get(0), channel1);
channel2 = con2.createChannel(false);
verifyChannelIs(mockChannels.get(1), channel2);
assertEquals(2, allocatedConnections.size());
assertEquals(0, idleConnections.size());
assertNotNull(createNotification.get());
assertSame(mockConnections.get(1), createNotification.getAndSet(null));
// put mock1 in cache
channel1.close();
verify(mockChannels.get(1), never()).close();
con1.close();
verify(mockConnections.get(0), never()).close();
assertEquals(2, allocatedConnections.size());
assertEquals(1, idleConnections.size());
assertNull(closedNotification.get());
Connection con3 = ccf.createConnection();
assertNull(createNotification.get());
verifyConnectionIs(mockConnections.get(0), con3);
Channel channel3 = con3.createChannel(false);
verifyChannelIs(mockChannels.get(0), channel3);
assertEquals(2, allocatedConnections.size());
assertEquals(0, idleConnections.size());
channel2.close();
con2.close();
assertEquals(2, allocatedConnections.size());
assertEquals(1, idleConnections.size());
channel3.close();
con3.close();
assertEquals(2, allocatedConnections.size());
assertEquals(2, idleConnections.size());
assertEquals("1", ccf.getCacheProperties().get("openConnections"));
/*
* Cache size is 1; con3 (mock1) should have been a real close.
* con2 (mock2) should still be in the cache.
*/
verify(mockConnections.get(0)).close(30000);
assertNotNull(closedNotification.get());
assertSame(mockConnections.get(0), closedNotification.getAndSet(null));
verify(mockChannels.get(1), never()).close();
verify(mockConnections.get(1), never()).close(30000);
verify(mockChannels.get(1), never()).close();
verifyConnectionIs(mockConnections.get(1), idleConnections.iterator().next());
/*
* Now a closed cached connection
*/
when(mockConnections.get(1).isOpen()).thenReturn(false);
when(mockChannels.get(1).isOpen()).thenReturn(false);
con3 = ccf.createConnection();
assertNotNull(closedNotification.get());
assertSame(mockConnections.get(1), closedNotification.getAndSet(null));
verifyConnectionIs(mockConnections.get(2), con3);
assertNotNull(createNotification.get());
assertSame(mockConnections.get(2), createNotification.getAndSet(null));
assertEquals(2, allocatedConnections.size());
assertEquals(1, idleConnections.size());
assertEquals("1", ccf.getCacheProperties().get("openConnections"));
channel3 = con3.createChannel(false);
verifyChannelIs(mockChannels.get(2), channel3);
channel3.close();
con3.close();
assertNull(closedNotification.get());
assertEquals(2, allocatedConnections.size());
assertEquals(2, idleConnections.size());
assertEquals("1", ccf.getCacheProperties().get("openConnections"));
/*
* Now a closed cached connection when creating a channel
*/
con3 = ccf.createConnection();
verifyConnectionIs(mockConnections.get(2), con3);
assertNull(createNotification.get());
assertEquals(2, allocatedConnections.size());
assertEquals(1, idleConnections.size());
when(mockConnections.get(2).isOpen()).thenReturn(false);
channel3 = con3.createChannel(false);
assertNotNull(closedNotification.getAndSet(null));
assertNotNull(createNotification.getAndSet(null));
verifyChannelIs(mockChannels.get(3), channel3);
channel3.close();
con3.close();
assertNull(closedNotification.get());
assertEquals(2, allocatedConnections.size());
assertEquals(2, idleConnections.size());
assertEquals("1", ccf.getCacheProperties().get("openConnections"));
// destroy
ccf.destroy();
assertNotNull(closedNotification.get());
verify(mockConnections.get(3)).close(30000);
}
@Test
public void testWithConnectionFactoryCachedConnectionAndChannels() throws Exception {
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
final List<com.rabbitmq.client.Connection> mockConnections = new ArrayList<com.rabbitmq.client.Connection>();
final List<Channel> mockChannels = new ArrayList<Channel>();
doAnswer(new Answer<com.rabbitmq.client.Connection>() {
private int connectionNumber;
@Override
public com.rabbitmq.client.Connection answer(InvocationOnMock invocation) throws Throwable {
com.rabbitmq.client.Connection connection = mock(com.rabbitmq.client.Connection.class);
doAnswer(new Answer<Channel>() {
private int channelNumber;
@Override
public Channel answer(InvocationOnMock invocation) throws Throwable {
Channel channel = mock(Channel.class);
when(channel.isOpen()).thenReturn(true);
int channelNumnber = ++this.channelNumber;
when(channel.toString()).thenReturn("mockChannel" + connectionNumber + ":" + channelNumnber);
mockChannels.add(channel);
return channel;
}
}).when(connection).createChannel();
int connectionNumber = ++this.connectionNumber;
when(connection.toString()).thenReturn("mockConnection" + connectionNumber);
when(connection.isOpen()).thenReturn(true);
mockConnections.add(connection);
return connection;
}
}).when(mockConnectionFactory).newConnection(any(ExecutorService.class), anyString());
CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory);
ccf.setExecutor(mock(ExecutorService.class));
ccf.setCacheMode(CacheMode.CONNECTION);
ccf.setConnectionCacheSize(2);
ccf.setChannelCacheSize(2);
ccf.afterPropertiesSet();
Set<?> allocatedConnections = TestUtils.getPropertyValue(ccf, "allocatedConnections", Set.class);
assertEquals(0, allocatedConnections.size());
BlockingQueue<?> idleConnections = TestUtils.getPropertyValue(ccf, "idleConnections", BlockingQueue.class);
assertEquals(0, idleConnections.size());
@SuppressWarnings("unchecked")
Map<?, List<?>> cachedChannels = TestUtils.getPropertyValue(ccf, "allocatedConnectionNonTransactionalChannels",
Map.class);
final AtomicReference<com.rabbitmq.client.Connection> createNotification =
new AtomicReference<com.rabbitmq.client.Connection>();
final AtomicReference<com.rabbitmq.client.Connection> closedNotification =
new AtomicReference<com.rabbitmq.client.Connection>();
ccf.setConnectionListeners(Collections.singletonList(new ConnectionListener() {
@Override
public void onCreate(Connection connection) {
assertNull(createNotification.get());
createNotification.set(targetDelegate(connection));
}
@Override
public void onClose(Connection connection) {
assertNull(closedNotification.get());
closedNotification.set(targetDelegate(connection));
}
}));
Connection con1 = ccf.createConnection();
verifyConnectionIs(mockConnections.get(0), con1);
assertEquals(1, allocatedConnections.size());
assertEquals(0, idleConnections.size());
assertNotNull(createNotification.get());
assertSame(mockConnections.get(0), createNotification.getAndSet(null));
Channel channel1 = con1.createChannel(false);
verifyChannelIs(mockChannels.get(0), channel1);
channel1.close();
//AMQP-358
verify(mockChannels.get(0), never()).close();
con1.close(); // should be ignored, and placed into connection cache.
verify(mockConnections.get(0), never()).close();
assertEquals(1, allocatedConnections.size());
assertEquals(1, idleConnections.size());
assertEquals(1, cachedChannels.get(con1).size());
assertNull(closedNotification.get());
/*
* will retrieve same connection that was just put into cache, and reuse single channel from cache as well
*/
Connection con2 = ccf.createConnection();
verifyConnectionIs(mockConnections.get(0), con2);
Channel channel2 = con2.createChannel(false);
verifyChannelIs(mockChannels.get(0), channel2);
channel2.close();
verify(mockChannels.get(0), never()).close();
con2.close();
verify(mockConnections.get(0), never()).close();
assertEquals(1, allocatedConnections.size());
assertEquals(1, idleConnections.size());
assertNull(createNotification.get());
/*
* Now check for multiple connections/channels
*/
con1 = ccf.createConnection();
verifyConnectionIs(mockConnections.get(0), con1);
con2 = ccf.createConnection();
verifyConnectionIs(mockConnections.get(1), con2);
channel1 = con1.createChannel(false);
verifyChannelIs(mockChannels.get(0), channel1);
channel2 = con2.createChannel(false);
verifyChannelIs(mockChannels.get(1), channel2);
assertEquals(2, allocatedConnections.size());
assertEquals(0, idleConnections.size());
assertNotNull(createNotification.get());
assertSame(mockConnections.get(1), createNotification.getAndSet(null));
// put mock1 in cache
channel1.close();
verify(mockChannels.get(1), never()).close();
con1.close();
verify(mockConnections.get(0), never()).close();
assertEquals(2, allocatedConnections.size());
assertEquals(1, idleConnections.size());
assertNull(closedNotification.get());
Connection con3 = ccf.createConnection();
assertNull(createNotification.get());
verifyConnectionIs(mockConnections.get(0), con3);
Channel channel3 = con3.createChannel(false);
verifyChannelIs(mockChannels.get(0), channel3);
assertEquals(2, allocatedConnections.size());
assertEquals(0, idleConnections.size());
channel2.close();
con2.close();
assertEquals(2, allocatedConnections.size());
assertEquals(1, idleConnections.size());
channel3.close();
con3.close();
assertEquals(2, allocatedConnections.size());
assertEquals(2, idleConnections.size());
assertEquals(1, cachedChannels.get(con1).size());
assertEquals(1, cachedChannels.get(con2).size());
/*
* Cache size is 2; neither should have been a real close.
* con2 (mock2) and con1 should still be in the cache.
*/
verify(mockConnections.get(0), never()).close(30000);
assertNull(closedNotification.get());
verify(mockChannels.get(1), never()).close();
verify(mockConnections.get(1), never()).close(30000);
verify(mockChannels.get(1), never()).close();
assertEquals(2, idleConnections.size());
Iterator<?> iterator = idleConnections.iterator();
verifyConnectionIs(mockConnections.get(1), iterator.next());
verifyConnectionIs(mockConnections.get(0), iterator.next());
/*
* Now a closed cached connection
*/
when(mockConnections.get(1).isOpen()).thenReturn(false);
con3 = ccf.createConnection();
assertNotNull(closedNotification.get());
assertSame(mockConnections.get(1), closedNotification.getAndSet(null));
verifyConnectionIs(mockConnections.get(0), con3);
assertNull(createNotification.get());
assertEquals(2, allocatedConnections.size());
assertEquals(1, idleConnections.size());
channel3 = con3.createChannel(false);
verifyChannelIs(mockChannels.get(0), channel3);
channel3.close();
con3.close();
assertNull(closedNotification.get());
assertEquals(2, allocatedConnections.size());
assertEquals(2, idleConnections.size());
/*
* Now a closed cached connection when creating a channel
*/
con3 = ccf.createConnection();
verifyConnectionIs(mockConnections.get(0), con3);
assertNull(createNotification.get());
assertEquals(2, allocatedConnections.size());
assertEquals(1, idleConnections.size());
when(mockConnections.get(0).isOpen()).thenReturn(false);
channel3 = con3.createChannel(false);
assertNotNull(closedNotification.getAndSet(null));
assertNotNull(createNotification.getAndSet(null));
verifyChannelIs(mockChannels.get(2), channel3);
channel3.close();
con3.close();
assertNull(closedNotification.get());
assertEquals(2, allocatedConnections.size());
assertEquals(2, idleConnections.size());
Connection con4 = ccf.createConnection();
assertSame(con3, con4);
assertEquals(1, idleConnections.size());
Channel channelA = con4.createChannel(false);
Channel channelB = con4.createChannel(false);
Channel channelC = con4.createChannel(false);
channelA.close();
assertEquals(1, cachedChannels.get(con4).size());
channelB.close();
assertEquals(2, cachedChannels.get(con4).size());
channelC.close();
assertEquals(2, cachedChannels.get(con4).size());
// destroy
ccf.destroy();
assertNotNull(closedNotification.get());
// physical wasn't invoked, because this mockConnection marked with 'false' for 'isOpen()'
verify(mockConnections.get(0)).close(30000);
verify(mockConnections.get(1)).close(30000);
verify(mockConnections.get(2)).close(30000);
}
@Test
public void testWithConnectionFactoryCachedConnectionIdleAreClosed() throws Exception {
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
final List<com.rabbitmq.client.Connection> mockConnections = new ArrayList<com.rabbitmq.client.Connection>();
final List<Channel> mockChannels = new ArrayList<Channel>();
doAnswer(new Answer<com.rabbitmq.client.Connection>() {
private int connectionNumber;
@Override
public com.rabbitmq.client.Connection answer(InvocationOnMock invocation) throws Throwable {
com.rabbitmq.client.Connection connection = mock(com.rabbitmq.client.Connection.class);
doAnswer(new Answer<Channel>() {
private int channelNumber;
@Override
public Channel answer(InvocationOnMock invocation) throws Throwable {
Channel channel = mock(Channel.class);
when(channel.isOpen()).thenReturn(true);
int channelNumnber = ++this.channelNumber;
when(channel.toString()).thenReturn("mockChannel" + channelNumnber);
mockChannels.add(channel);
return channel;
}
}).when(connection).createChannel();
int connectionNumber = ++this.connectionNumber;
when(connection.toString()).thenReturn("mockConnection" + connectionNumber);
when(connection.isOpen()).thenReturn(true);
mockConnections.add(connection);
return connection;
}
}).when(mockConnectionFactory).newConnection(any(ExecutorService.class), anyString());
CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory);
ccf.setExecutor(mock(ExecutorService.class));
ccf.setCacheMode(CacheMode.CONNECTION);
ccf.setConnectionCacheSize(5);
ccf.afterPropertiesSet();
Set<?> allocatedConnections = TestUtils.getPropertyValue(ccf, "allocatedConnections", Set.class);
assertEquals(0, allocatedConnections.size());
BlockingQueue<?> idleConnections = TestUtils.getPropertyValue(ccf, "idleConnections", BlockingQueue.class);
assertEquals(0, idleConnections.size());
Connection conn1 = ccf.createConnection();
Connection conn2 = ccf.createConnection();
Connection conn3 = ccf.createConnection();
assertEquals(3, allocatedConnections.size());
assertEquals(0, idleConnections.size());
conn1.close();
conn2.close();
conn3.close();
assertEquals(3, allocatedConnections.size());
assertEquals(3, idleConnections.size());
when(mockConnections.get(0).isOpen()).thenReturn(false);
when(mockConnections.get(1).isOpen()).thenReturn(false);
Connection conn4 = ccf.createConnection();
assertEquals(3, allocatedConnections.size());
assertEquals(2, idleConnections.size());
assertSame(conn3, conn4);
conn4.close();
assertEquals(3, allocatedConnections.size());
assertEquals(3, idleConnections.size());
assertEquals("1", ccf.getCacheProperties().get("openConnections"));
ccf.destroy();
assertEquals(3, allocatedConnections.size());
assertEquals(3, idleConnections.size());
assertEquals("0", ccf.getCacheProperties().get("openConnections"));
}
@Test
public void testConsumerChannelPhysicallyClosedWhenNotIsOpen() throws Exception {
testConsumerChannelPhysicallyClosedWhenNotIsOpenGuts(false);
}
@Test
public void testConsumerChannelWithPubConfPhysicallyClosedWhenNotIsOpen() throws Exception {
testConsumerChannelPhysicallyClosedWhenNotIsOpenGuts(true);
}
public void testConsumerChannelPhysicallyClosedWhenNotIsOpenGuts(boolean confirms) throws Exception {
ExecutorService executor = Executors.newSingleThreadExecutor();
try {
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
com.rabbitmq.client.Connection mockConnection = mock(com.rabbitmq.client.Connection.class);
Channel mockChannel = mock(Channel.class);
when(mockConnectionFactory.newConnection(any(ExecutorService.class), anyString())).thenReturn(mockConnection);
when(mockConnection.createChannel()).thenReturn(mockChannel);
when(mockChannel.isOpen()).thenReturn(true);
when(mockConnection.isOpen()).thenReturn(true);
CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory);
ccf.setExecutor(executor);
ccf.setPublisherConfirms(confirms);
Connection con = ccf.createConnection();
Channel channel = con.createChannel(false);
RabbitUtils.setPhysicalCloseRequired(true);
when(mockChannel.isOpen()).thenReturn(false);
final CountDownLatch physicalCloseLatch = new CountDownLatch(1);
doAnswer(i -> {
physicalCloseLatch.countDown();
return null;
}).when(mockChannel).close();
channel.close();
con.close(); // should be ignored
assertTrue(physicalCloseLatch.await(10, TimeUnit.SECONDS));
}
finally {
RabbitUtils.setPhysicalCloseRequired(false);
executor.shutdownNow();
}
}
private void verifyConnectionIs(com.rabbitmq.client.Connection mockConnection, Object con) {
assertSame(mockConnection, targetDelegate(con));
}
private com.rabbitmq.client.Connection targetDelegate(Object con) {
return TestUtils.getPropertyValue(con, "target.delegate",
com.rabbitmq.client.Connection.class);
}
private void verifyChannelIs(Channel mockChannel, Channel channel) {
ChannelProxy proxy = (ChannelProxy) channel;
assertSame(mockChannel, proxy.getTargetChannel());
}
@Test
public void setAddressesEmpty() throws Exception {
ConnectionFactory mock = mock(com.rabbitmq.client.ConnectionFactory.class);
CachingConnectionFactory ccf = new CachingConnectionFactory(mock);
ccf.setExecutor(mock(ExecutorService.class));
ccf.setHost("abc");
ccf.setAddresses("");
ccf.createConnection();
verify(mock).isAutomaticRecoveryEnabled();
verify(mock).setHost("abc");
verify(mock).newConnection(any(ExecutorService.class), anyString());
verifyNoMoreInteractions(mock);
}
@Test
public void setAddressesOneHost() throws Exception {
ConnectionFactory mock = mock(com.rabbitmq.client.ConnectionFactory.class);
CachingConnectionFactory ccf = new CachingConnectionFactory(mock);
ccf.setAddresses("mq1");
ccf.createConnection();
verify(mock).isAutomaticRecoveryEnabled();
verify(mock)
.newConnection(isNull(), aryEq(new Address[] { new Address("mq1") }), anyString());
verifyNoMoreInteractions(mock);
}
@Test
public void setAddressesTwoHosts() throws Exception {
ConnectionFactory mock = mock(com.rabbitmq.client.ConnectionFactory.class);
doReturn(true).when(mock).isAutomaticRecoveryEnabled();
CachingConnectionFactory ccf = new CachingConnectionFactory(mock);
ccf.setAddresses("mq1,mq2");
ccf.createConnection();
verify(mock).isAutomaticRecoveryEnabled();
verify(mock).newConnection(isNull(),
aryEq(new Address[] { new Address("mq1"), new Address("mq2") }), anyString());
verifyNoMoreInteractions(mock);
}
@Test
public void setUri() throws Exception {
URI uri = new URI("amqp://localhost:1234/%2f");
ConnectionFactory mock = mock(com.rabbitmq.client.ConnectionFactory.class);
CachingConnectionFactory ccf = new CachingConnectionFactory(mock);
ccf.setExecutor(mock(ExecutorService.class));
ccf.setUri(uri);
ccf.createConnection();
InOrder order = inOrder(mock);
order.verify(mock).isAutomaticRecoveryEnabled();
order.verify(mock).setUri(uri);
order.verify(mock).newConnection(any(ExecutorService.class), anyString());
verifyNoMoreInteractions(mock);
}
@Test
public void testChannelCloseIdempotency() throws Exception {
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
com.rabbitmq.client.Connection mockConnection = mock(com.rabbitmq.client.Connection.class);
Channel mockChannel = mock(Channel.class);
when(mockConnectionFactory.newConnection(any(ExecutorService.class), anyString())).thenReturn(mockConnection);
when(mockConnection.isOpen()).thenReturn(true);
when(mockConnection.createChannel()).thenReturn(mockChannel);
when(mockChannel.isOpen()).thenReturn(true).thenReturn(false);
CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory);
ccf.setExecutor(mock(ExecutorService.class));
Connection con = ccf.createConnection();
Channel channel = con.createChannel(false);
// RabbitUtils.setPhysicalCloseRequired(true);
channel.close(); // should be ignored, and placed into channel cache.
channel.close(); // physically closed, so remove from the cache.
channel.close(); // physically closed and removed from the cache before, so void "close".
Channel channel2 = con.createChannel(false);
assertNotSame(channel, channel2);
}
}