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