Skip to content

Commit 5471679

Browse files
committed
GH-2891: Add rabbitConnection.addShutdownListener(this)
Fixes: #2891 Issue link: #2891 The `ShutdownListener` is not registered into connections created by the `AbstractConnectionFactory` * Fix `AbstractConnectionFactory.createBareConnection()` add itself into just created connection as a `ShutdownListener` * Fix tests with mocks where `mockConnectionFactory.newConnection()` did not return an instance of `Connection`
1 parent a2ac767 commit 5471679

File tree

4 files changed

+48
-94
lines changed

4 files changed

+48
-94
lines changed

spring-rabbit/src/main/java/org/springframework/amqp/rabbit/connection/AbstractConnectionFactory.java

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ public void handleRecovery(Recoverable recoverable) {
166166

167167
@Nullable
168168
private BackOff connectionCreatingBackOff;
169+
169170
/**
170171
* Create a new AbstractConnectionFactory for the given target ConnectionFactory, with no publisher connection
171172
* factory.
@@ -580,8 +581,8 @@ public ConnectionFactory getPublisherConnectionFactory() {
580581
protected final Connection createBareConnection() {
581582
try {
582583
String connectionName = this.connectionNameStrategy.obtainNewConnectionName(this);
583-
584584
com.rabbitmq.client.Connection rabbitConnection = connect(connectionName);
585+
rabbitConnection.addShutdownListener(this);
585586
Connection connection = new SimpleConnection(rabbitConnection, this.closeTimeout,
586587
this.connectionCreatingBackOff == null ? null : this.connectionCreatingBackOff.start());
587588
if (rabbitConnection instanceof AutorecoveringConnection auto) {
@@ -732,16 +733,8 @@ public String toString() {
732733
}
733734
}
734735

735-
private static final class ConnectionBlockedListener implements BlockedListener {
736-
737-
private final Connection connection;
738-
739-
private final ApplicationEventPublisher applicationEventPublisher;
740-
741-
ConnectionBlockedListener(Connection connection, ApplicationEventPublisher applicationEventPublisher) {
742-
this.connection = connection;
743-
this.applicationEventPublisher = applicationEventPublisher;
744-
}
736+
private record ConnectionBlockedListener(Connection connection, ApplicationEventPublisher applicationEventPublisher)
737+
implements BlockedListener {
745738

746739
@Override
747740
public void handleBlocked(String reason) {

spring-rabbit/src/test/java/org/springframework/amqp/rabbit/connection/AbstractConnectionFactoryTests.java

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
2121
import static org.mockito.ArgumentMatchers.any;
2222
import static org.mockito.ArgumentMatchers.anyInt;
23+
import static org.mockito.ArgumentMatchers.anyList;
2324
import static org.mockito.ArgumentMatchers.anyString;
2425
import static org.mockito.BDDMockito.given;
2526
import static org.mockito.BDDMockito.willCallRealMethod;
@@ -64,11 +65,11 @@ public abstract class AbstractConnectionFactoryTests {
6465

6566
@Test
6667
public void testWithListener() throws Exception {
67-
68-
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
69-
com.rabbitmq.client.Connection mockConnection = mock(com.rabbitmq.client.Connection.class);
68+
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock();
69+
com.rabbitmq.client.Connection mockConnection = mock();
7070

7171
given(mockConnectionFactory.newConnection(any(ExecutorService.class), anyString())).willReturn(mockConnection);
72+
given(mockConnectionFactory.newConnection(any(), anyList(), anyString())).willReturn(mockConnection);
7273

7374
final AtomicInteger called = new AtomicInteger(0);
7475
AbstractConnectionFactory connectionFactory = createConnectionFactory(mockConnectionFactory);
@@ -125,9 +126,8 @@ public void onClose(Connection connection) {
125126

126127
@Test
127128
public void testWithListenerRegisteredAfterOpen() throws Exception {
128-
129-
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
130-
com.rabbitmq.client.Connection mockConnection = mock(com.rabbitmq.client.Connection.class);
129+
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock();
130+
com.rabbitmq.client.Connection mockConnection = mock();
131131

132132
given(mockConnectionFactory.newConnection(any(ExecutorService.class), anyString())).willReturn(mockConnection);
133133

@@ -168,10 +168,9 @@ public void onClose(Connection connection) {
168168

169169
@Test
170170
public void testCloseInvalidConnection() throws Exception {
171-
172-
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
173-
com.rabbitmq.client.Connection mockConnection1 = mock(com.rabbitmq.client.Connection.class);
174-
com.rabbitmq.client.Connection mockConnection2 = mock(com.rabbitmq.client.Connection.class);
171+
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock();
172+
com.rabbitmq.client.Connection mockConnection1 = mock();
173+
com.rabbitmq.client.Connection mockConnection2 = mock();
175174

176175
given(mockConnectionFactory.newConnection(any(ExecutorService.class), anyString()))
177176
.willReturn(mockConnection1, mockConnection2);
@@ -194,8 +193,7 @@ public void testCloseInvalidConnection() throws Exception {
194193

195194
@Test
196195
public void testDestroyBeforeUsed() throws Exception {
197-
198-
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
196+
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock();
199197

200198
AbstractConnectionFactory connectionFactory = createConnectionFactory(mockConnectionFactory);
201199
connectionFactory.destroy();
@@ -205,7 +203,7 @@ public void testDestroyBeforeUsed() throws Exception {
205203

206204
@Test
207205
public void testCreatesConnectionWithGivenFactory() {
208-
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
206+
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock();
209207
willCallRealMethod().given(mockConnectionFactory).params(any(ExecutorService.class));
210208
willCallRealMethod().given(mockConnectionFactory).setThreadFactory(any(ThreadFactory.class));
211209
willCallRealMethod().given(mockConnectionFactory).getThreadFactory();

spring-rabbit/src/test/java/org/springframework/amqp/rabbit/connection/CachingConnectionFactoryTests.java

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import static org.mockito.ArgumentMatchers.any;
2323
import static org.mockito.ArgumentMatchers.anyBoolean;
2424
import static org.mockito.ArgumentMatchers.anyInt;
25+
import static org.mockito.ArgumentMatchers.anyList;
2526
import static org.mockito.ArgumentMatchers.anyLong;
2627
import static org.mockito.ArgumentMatchers.anyString;
2728
import static org.mockito.ArgumentMatchers.argThat;
@@ -117,7 +118,7 @@ void stringRepresentation() {
117118
assertThat(ccf.toString()).contains(", addresses=[h3:1236, h4:1237]")
118119
.doesNotContain("host")
119120
.doesNotContain("port");
120-
ccf.setAddressResolver(() -> {
121+
ccf.setAddressResolver(() -> {
121122
throw new IOException("test");
122123
});
123124
ccf.setPort(0);
@@ -710,7 +711,7 @@ public void testCheckoutLimitWithPublisherConfirmsLogicalAlreadyCloses() throws
710711
willAnswer(invoc -> {
711712
open.set(false); // so the logical close detects a closed delegate
712713
return null;
713-
}).given(mockChannel).basicPublish(any(), any(), anyBoolean(), any(), any());
714+
}).given(mockChannel).basicPublish(any(), any(), anyBoolean(), any(), any());
714715

715716
CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory);
716717
ccf.setExecutor(mock(ExecutorService.class));
@@ -722,7 +723,7 @@ public void testCheckoutLimitWithPublisherConfirmsLogicalAlreadyCloses() throws
722723
rabbitTemplate.convertAndSend("foo", "bar");
723724
open.set(true);
724725
rabbitTemplate.convertAndSend("foo", "bar");
725-
verify(mockChannel, times(2)).basicPublish(any(), any(), anyBoolean(), any(), any());
726+
verify(mockChannel, times(2)).basicPublish(any(), any(), anyBoolean(), any(), any());
726727
}
727728

728729
@Test
@@ -1300,7 +1301,6 @@ public void onClose(Connection connection) {
13001301
verify(mockConnections.get(3)).close(30000);
13011302
}
13021303

1303-
13041304
@Test
13051305
public void testWithConnectionFactoryCachedConnectionAndChannels() throws Exception {
13061306
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
@@ -1644,6 +1644,8 @@ private void verifyChannelIs(Channel mockChannel, Channel channel) {
16441644
@Test
16451645
public void setAddressesEmpty() throws Exception {
16461646
ConnectionFactory mock = mock(com.rabbitmq.client.ConnectionFactory.class);
1647+
given(mock.newConnection(any(ExecutorService.class), anyString()))
1648+
.willReturn(mock(com.rabbitmq.client.Connection.class));
16471649
CachingConnectionFactory ccf = new CachingConnectionFactory(mock);
16481650
ccf.setExecutor(mock(ExecutorService.class));
16491651
ccf.setHost("abc");
@@ -1663,6 +1665,8 @@ public void setAddressesEmpty() throws Exception {
16631665
@Test
16641666
public void setAddressesOneHost() throws Exception {
16651667
ConnectionFactory mock = mock(com.rabbitmq.client.ConnectionFactory.class);
1668+
given(mock.newConnection(any(), anyList(), anyString()))
1669+
.willReturn(mock(com.rabbitmq.client.Connection.class));
16661670
CachingConnectionFactory ccf = new CachingConnectionFactory(mock);
16671671
ccf.setAddresses("mq1");
16681672
ccf.createConnection();
@@ -1674,16 +1678,18 @@ public void setAddressesOneHost() throws Exception {
16741678

16751679
@Test
16761680
public void setAddressesTwoHosts() throws Exception {
1677-
ConnectionFactory mock = mock(com.rabbitmq.client.ConnectionFactory.class);
1681+
ConnectionFactory mock = mock();
16781682
willReturn(true).given(mock).isAutomaticRecoveryEnabled();
1683+
willReturn(mock(com.rabbitmq.client.Connection.class)).given(mock).newConnection(any(), anyList(), anyString());
16791684
CachingConnectionFactory ccf = new CachingConnectionFactory(mock);
16801685
ccf.setAddresses("mq1,mq2");
16811686
ccf.createConnection();
16821687
verify(mock).isAutomaticRecoveryEnabled();
16831688
verify(mock).setAutomaticRecoveryEnabled(false);
16841689
verify(mock).newConnection(
16851690
isNull(),
1686-
argThat((ArgumentMatcher<List<Address>>) a -> a.size() == 2 && a.contains(new Address("mq1")) && a.contains(new Address("mq2"))),
1691+
argThat((ArgumentMatcher<List<Address>>) a -> a.size() == 2
1692+
&& a.contains(new Address("mq1")) && a.contains(new Address("mq2"))),
16871693
anyString());
16881694
verifyNoMoreInteractions(mock);
16891695
}
@@ -1692,7 +1698,9 @@ public void setAddressesTwoHosts() throws Exception {
16921698
public void setUri() throws Exception {
16931699
URI uri = new URI("amqp://localhost:1234/%2f");
16941700

1695-
ConnectionFactory mock = mock(com.rabbitmq.client.ConnectionFactory.class);
1701+
ConnectionFactory mock = mock();
1702+
given(mock.newConnection(any(ExecutorService.class), anyString()))
1703+
.willReturn(mock(com.rabbitmq.client.Connection.class));
16961704
CachingConnectionFactory ccf = new CachingConnectionFactory(mock);
16971705
ccf.setExecutor(mock(ExecutorService.class));
16981706

@@ -1854,12 +1862,12 @@ public void testFirstConnectionDoesntWait() throws IOException, TimeoutException
18541862
@SuppressWarnings("unchecked")
18551863
@Test
18561864
public void testShuffleRandom() throws IOException, TimeoutException {
1857-
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
1858-
com.rabbitmq.client.Connection mockConnection = mock(com.rabbitmq.client.Connection.class);
1865+
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock();
1866+
com.rabbitmq.client.Connection mockConnection = mock();
18591867
Channel mockChannel = mock(Channel.class);
18601868

1861-
given(mockConnectionFactory.newConnection((ExecutorService) isNull(), any(List.class), anyString()))
1862-
.willReturn(mockConnection);
1869+
given(mockConnectionFactory.newConnection(any(), anyList(), anyString()))
1870+
.willReturn(mockConnection);
18631871
given(mockConnection.createChannel()).willReturn(mockChannel);
18641872
given(mockChannel.isOpen()).willReturn(true);
18651873
given(mockConnection.isOpen()).willReturn(true);
@@ -1873,11 +1881,11 @@ public void testShuffleRandom() throws IOException, TimeoutException {
18731881
ArgumentCaptor<List<Address>> captor = ArgumentCaptor.forClass(List.class);
18741882
verify(mockConnectionFactory, times(100)).newConnection(isNull(), captor.capture(), anyString());
18751883
List<String> firstAddress = captor.getAllValues()
1876-
.stream()
1877-
.map(addresses -> addresses.get(0).getHost())
1878-
.distinct()
1879-
.sorted()
1880-
.collect(Collectors.toList());
1884+
.stream()
1885+
.map(addresses -> addresses.get(0).getHost())
1886+
.distinct()
1887+
.sorted()
1888+
.collect(Collectors.toList());
18811889
assertThat(firstAddress).containsExactly("host1", "host2", "host3");
18821890
}
18831891

@@ -1888,8 +1896,8 @@ public void testShuffleInOrder() throws IOException, TimeoutException {
18881896
com.rabbitmq.client.Connection mockConnection = mock(com.rabbitmq.client.Connection.class);
18891897
Channel mockChannel = mock(Channel.class);
18901898

1891-
given(mockConnectionFactory.newConnection((ExecutorService) isNull(), any(List.class), anyString()))
1892-
.willReturn(mockConnection);
1899+
given(mockConnectionFactory.newConnection(isNull(), anyList(), anyString()))
1900+
.willReturn(mockConnection);
18931901
given(mockConnection.createChannel()).willReturn(mockChannel);
18941902
given(mockChannel.isOpen()).willReturn(true);
18951903
given(mockConnection.isOpen()).willReturn(true);
@@ -1903,17 +1911,17 @@ public void testShuffleInOrder() throws IOException, TimeoutException {
19031911
ArgumentCaptor<List<Address>> captor = ArgumentCaptor.forClass(List.class);
19041912
verify(mockConnectionFactory, times(3)).newConnection(isNull(), captor.capture(), anyString());
19051913
List<String> connectAddresses = captor.getAllValues()
1906-
.stream()
1907-
.map(addresses -> addresses.get(0).getHost())
1908-
.collect(Collectors.toList());
1914+
.stream()
1915+
.map(addresses -> addresses.get(0).getHost())
1916+
.collect(Collectors.toList());
19091917
assertThat(connectAddresses).containsExactly("host1", "host2", "host3");
19101918
}
19111919

19121920
@Test
19131921
void testResolver() throws Exception {
1914-
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
1915-
com.rabbitmq.client.Connection mockConnection = mock(com.rabbitmq.client.Connection.class);
1916-
Channel mockChannel = mock(Channel.class);
1922+
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock();
1923+
com.rabbitmq.client.Connection mockConnection = mock();
1924+
Channel mockChannel = mock();
19171925

19181926
AddressResolver resolver = () -> Collections.singletonList(Address.parseAddress("foo:5672"));
19191927
given(mockConnectionFactory.newConnection(any(ExecutorService.class), eq(resolver), anyString()))
@@ -1934,7 +1942,7 @@ void testResolver() throws Exception {
19341942

19351943
@Test
19361944
void nullShutdownCause() {
1937-
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
1945+
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock();
19381946
AbstractConnectionFactory cf = createConnectionFactory(mockConnectionFactory);
19391947
AtomicBoolean connShutDown = new AtomicBoolean();
19401948
cf.addConnectionListener(new ConnectionListener() {

spring-rabbit/src/test/java/org/springframework/amqp/rabbit/core/RabbitAdminDeclarationTests.java

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,9 @@
3232
import static org.mockito.Mockito.verify;
3333

3434
import java.io.IOException;
35-
import java.util.ArrayList;
3635
import java.util.HashMap;
37-
import java.util.List;
3836
import java.util.Map;
3937
import java.util.concurrent.ExecutorService;
40-
import java.util.concurrent.atomic.AtomicInteger;
4138
import java.util.concurrent.atomic.AtomicReference;
4239

4340
import org.junit.jupiter.api.Test;
@@ -50,7 +47,6 @@
5047
import org.springframework.amqp.core.Exchange;
5148
import org.springframework.amqp.core.Queue;
5249
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
53-
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory.CacheMode;
5450
import org.springframework.amqp.rabbit.connection.Connection;
5551
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
5652
import org.springframework.amqp.rabbit.connection.ConnectionListener;
@@ -104,47 +100,6 @@ public void testUnconditional() throws Exception {
104100
verify(channel).queueBind("foo", "bar", "foo", new HashMap<>());
105101
}
106102

107-
@Test
108-
public void testNoDeclareWithCachedConnections() throws Exception {
109-
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
110-
111-
List<Channel> mockChannels = new ArrayList<>();
112-
113-
AtomicInteger connectionNumber = new AtomicInteger();
114-
willAnswer(invocation -> {
115-
com.rabbitmq.client.Connection connection = mock(com.rabbitmq.client.Connection.class);
116-
AtomicInteger channelNumber = new AtomicInteger();
117-
willAnswer(invocation1 -> {
118-
Channel channel = mock(Channel.class);
119-
given(channel.isOpen()).willReturn(true);
120-
int channelNum = channelNumber.incrementAndGet();
121-
given(channel.toString()).willReturn("mockChannel" + channelNum);
122-
mockChannels.add(channel);
123-
return channel;
124-
}).given(connection).createChannel();
125-
int connectionNum = connectionNumber.incrementAndGet();
126-
given(connection.toString()).willReturn("mockConnection" + connectionNum);
127-
given(connection.isOpen()).willReturn(true);
128-
return connection;
129-
}).given(mockConnectionFactory).newConnection((ExecutorService) null);
130-
131-
CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory);
132-
ccf.setCacheMode(CacheMode.CONNECTION);
133-
ccf.afterPropertiesSet();
134-
135-
RabbitAdmin admin = new RabbitAdmin(ccf);
136-
GenericApplicationContext context = new GenericApplicationContext();
137-
Queue queue = new Queue("foo");
138-
context.getBeanFactory().registerSingleton("foo", queue);
139-
context.refresh();
140-
admin.setApplicationContext(context);
141-
admin.afterPropertiesSet();
142-
ccf.createConnection().close();
143-
ccf.destroy();
144-
145-
assertThat(mockChannels.size()).as("Admin should not have created a channel").isEqualTo(0);
146-
}
147-
148103
@Test
149104
public void testUnconditionalWithExplicitFactory() throws Exception {
150105
ConnectionFactory cf = mock(ConnectionFactory.class);

0 commit comments

Comments
 (0)