Skip to content

Commit f69f54e

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 6498219 commit f69f54e

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

@@ -53,6 +54,9 @@ public SimpleConnection(com.rabbitmq.client.Connection delegate,
5354
public Channel createChannel(boolean transactional) {
5455
try {
5556
Channel channel = this.delegate.createChannel();
57+
if (channel == null) {
58+
throw new AmqpResourceNotAvailableException("The channelMax limit is reached. Try later.");
59+
}
5660
if (transactional) {
5761
// Just created so we want to start the transaction
5862
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-2016 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.
@@ -60,6 +60,7 @@
6060
import org.springframework.amqp.AmqpAuthenticationException;
6161
import org.springframework.amqp.AmqpException;
6262
import org.springframework.amqp.AmqpIOException;
63+
import org.springframework.amqp.AmqpResourceNotAvailableException;
6364
import org.springframework.amqp.AmqpTimeoutException;
6465
import org.springframework.amqp.core.Queue;
6566
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory.CacheMode;
@@ -87,6 +88,7 @@
8788
* @author Gunnar Hillert
8889
* @author Gary Russell
8990
* @author Artem Bilan
91+
*
9092
* @since 1.0
9193
*
9294
*/
@@ -568,6 +570,14 @@ public void hangOnClose() throws Exception {
568570
factory.destroy();
569571
}
570572

573+
@Test(expected = AmqpResourceNotAvailableException.class)
574+
public void testChannelMax() {
575+
this.connectionFactory.getRabbitConnectionFactory().setRequestedChannelMax(1);
576+
Connection connection = this.connectionFactory.createConnection();
577+
connection.createChannel(true);
578+
connection.createChannel(false);
579+
}
580+
571581
private Log spyOnLogger(CachingConnectionFactory connectionFactory2) {
572582
DirectFieldAccessor dfa = new DirectFieldAccessor(connectionFactory2);
573583
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
@@ -361,6 +361,9 @@ The `ConnectionFactory` argument can be used to distinguish target connection na
361361
By default a `beanName` of the `AbstractConnectionFactory` and an internal counter are used to generate `connection_name`.
362362
The `<rabbit:connection-factory>` namespace component is also supplied with the `connection-name-strategy` attribute.
363363

364+
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.
365+
This exception can be used in the `RetryPolicy` to recover the operation after some back-off.
366+
364367
[[connection-factory]]
365368
===== Configuring the Underlying Client Connection Factory
366369

0 commit comments

Comments
 (0)