Skip to content

Commit 19513e6

Browse files
artembilangaryrussell
authored andcommitted
GH-723: Add AmqpResourceNotAvailableException (#737)
* GH-723: Add AmqpResourceNotAvailableException Fixes #723 To avoid an `NPE` when connection returns `null` for the `createChannel()` in case of `channelMax` is reached, throw newly introduced `AmqpResourceNotAvailableException`. This exception can be used in the `RetryPolicy` to retry the original operation after some back-off - the channel permit may be released in between **Cherry-pick to 2.0.x and 1.7.x** * * Fix `AbstractConnectionFactoryTests` for proper mock * Fix `amqp.adoc` according PR comments
1 parent 9e3e5b6 commit 19513e6

File tree

5 files changed

+64
-3
lines changed

5 files changed

+64
-3
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.amqp;
18+
19+
/**
20+
* The {@link AmqpException} thrown when some resource can't be accessed.
21+
* For example when {@code channelMax} limit is reached and connect can't
22+
* create a new channel at the moment.
23+
*
24+
* @author Artem Bilan
25+
*
26+
* @since 1.7.7
27+
*/
28+
public class AmqpResourceNotAvailableException extends AmqpException {
29+
30+
public AmqpResourceNotAvailableException(String message) {
31+
super(message);
32+
}
33+
34+
public AmqpResourceNotAvailableException(Throwable cause) {
35+
super(cause);
36+
}
37+
38+
public AmqpResourceNotAvailableException(String message, Throwable cause) {
39+
super(message, cause);
40+
}
41+
42+
}

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
1919
import java.io.IOException;
2020
import java.net.InetAddress;
2121

22+
import org.springframework.amqp.AmqpResourceNotAvailableException;
2223
import org.springframework.amqp.rabbit.support.RabbitExceptionTranslator;
2324
import org.springframework.util.ObjectUtils;
2425

@@ -54,6 +55,9 @@ public SimpleConnection(com.rabbitmq.client.Connection delegate,
5455
public Channel createChannel(boolean transactional) {
5556
try {
5657
Channel channel = this.delegate.createChannel();
58+
if (channel == null) {
59+
throw new AmqpResourceNotAvailableException("The channelMax limit is reached. Try later.");
60+
}
5761
if (transactional) {
5862
// Just created so we want to start the transaction
5963
channel.txSelect();

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2010-2017 the original author or authors.
2+
* Copyright 2010-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -46,6 +46,7 @@
4646
import org.springframework.beans.DirectFieldAccessor;
4747
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
4848

49+
import com.rabbitmq.client.Channel;
4950
import com.rabbitmq.client.ConnectionFactory;
5051

5152
/**
@@ -176,6 +177,7 @@ public void testCloseInvalidConnection() throws Exception {
176177
.thenReturn(mockConnection1, mockConnection2);
177178
// simulate a dead connection
178179
when(mockConnection1.isOpen()).thenReturn(false);
180+
when(mockConnection2.createChannel()).thenReturn(mock(Channel.class));
179181

180182
AbstractConnectionFactory connectionFactory = createConnectionFactory(mockConnectionFactory);
181183

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -64,6 +64,7 @@
6464
import org.springframework.amqp.AmqpAuthenticationException;
6565
import org.springframework.amqp.AmqpException;
6666
import org.springframework.amqp.AmqpIOException;
67+
import org.springframework.amqp.AmqpResourceNotAvailableException;
6768
import org.springframework.amqp.AmqpTimeoutException;
6869
import org.springframework.amqp.core.Queue;
6970
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory.CacheMode;
@@ -89,6 +90,7 @@
8990
* @author Gunnar Hillert
9091
* @author Gary Russell
9192
* @author Artem Bilan
93+
*
9294
* @since 1.0
9395
*
9496
*/
@@ -571,6 +573,14 @@ public void hangOnClose() throws Exception {
571573
factory.destroy();
572574
}
573575

576+
@Test(expected = AmqpResourceNotAvailableException.class)
577+
public void testChannelMax() {
578+
this.connectionFactory.getRabbitConnectionFactory().setRequestedChannelMax(1);
579+
Connection connection = this.connectionFactory.createConnection();
580+
connection.createChannel(true);
581+
connection.createChannel(false);
582+
}
583+
574584
private Log spyOnLogger(CachingConnectionFactory connectionFactory2) {
575585
DirectFieldAccessor dfa = new DirectFieldAccessor(connectionFactory2);
576586
Log logger = spy((Log) dfa.getPropertyValue("logger"));

src/reference/asciidoc/amqp.adoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,9 @@ Starting with _version 2.0.2_, the `RabbitTemplate` has a configuration option t
377377
See <<separate-connection>> for more information.
378378
The `ConnectionNameStrategy` for the publisher connection is the same as the primary strategy with `.publisher` appended to the result of calling the method.
379379

380+
Starting with _version 1.7.7_, an `AmqpResourceNotAvailableException` is provided, which is thrown now when `SimpleConnection.createChannel()` can't create a `Channel`, for example, because the `channelMax` limit is reached and there are no available channels in the cache.
381+
This exception can be used in the `RetryPolicy` to recover the operation after some back-off.
382+
380383
[[connection-factory]]
381384
===== Configuring the Underlying Client Connection Factory
382385

0 commit comments

Comments
 (0)