Skip to content

Commit 8ca2cbb

Browse files
garyrussellartembilan
authored andcommitted
GH-1108: @QueueBinding.key recursive resolution
Fixes #1108 `@RabbitListener.queues()` resolves `String[]` recursively. AMQP-722 added support for multiple routing keys to `@queueBinding` but did not add support for recursive resolution. This is required to support constructs like `key = "#{'${my-app.amqp.routing-key}'.split(',')}"`. **cherry-pick to 2.1.x**
1 parent c939c58 commit 8ca2cbb

File tree

2 files changed

+28
-17
lines changed

2 files changed

+28
-17
lines changed

spring-rabbit/src/main/java/org/springframework/amqp/rabbit/annotation/RabbitListenerAnnotationBeanPostProcessor.java

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,7 @@ private String[] resolveQueues(RabbitListener rabbitListener) {
562562
List<String> result = new ArrayList<String>();
563563
if (queues.length > 0) {
564564
for (int i = 0; i < queues.length; i++) {
565-
resolveAsString(resolveExpression(queues[i]), result);
565+
resolveAsString(resolveExpression(queues[i]), result, true, "queues");
566566
}
567567
}
568568
if (queuesToDeclare.length > 0) {
@@ -585,25 +585,28 @@ private String[] resolveQueues(RabbitListener rabbitListener) {
585585
}
586586

587587
@SuppressWarnings("unchecked")
588-
private void resolveAsString(Object resolvedValue, List<String> result) {
588+
private void resolveAsString(Object resolvedValue, List<String> result, boolean canBeQueue, String what) {
589589
Object resolvedValueToUse = resolvedValue;
590590
if (resolvedValue instanceof String[]) {
591591
resolvedValueToUse = Arrays.asList((String[]) resolvedValue);
592592
}
593-
if (resolvedValueToUse instanceof Queue) {
593+
if (canBeQueue && resolvedValueToUse instanceof Queue) {
594594
result.add(((Queue) resolvedValueToUse).getName());
595595
}
596596
else if (resolvedValueToUse instanceof String) {
597597
result.add((String) resolvedValueToUse);
598598
}
599599
else if (resolvedValueToUse instanceof Iterable) {
600600
for (Object object : (Iterable<Object>) resolvedValueToUse) {
601-
resolveAsString(object, result);
601+
resolveAsString(object, result, canBeQueue, what);
602602
}
603603
}
604604
else {
605605
throw new IllegalArgumentException(String.format(
606-
"@RabbitListener can't resolve '%s' as either a String or a Queue",
606+
"@RabbitListener."
607+
+ what
608+
+ " can't resolve '%s' as a String[] or a String "
609+
+ (canBeQueue ? "or a Queue" : ""),
607610
resolvedValue));
608611
}
609612
}
@@ -691,15 +694,15 @@ private void declareExchangeAndBinding(QueueBinding binding, String queueName) {
691694
}
692695

693696
private void registerBindings(QueueBinding binding, String queueName, String exchangeName, String exchangeType) {
694-
final String[] routingKeys;
697+
final List<String> routingKeys;
695698
if (exchangeType.equals(ExchangeTypes.FANOUT) || binding.key().length == 0) {
696-
routingKeys = new String[] { "" };
699+
routingKeys = Collections.singletonList("");
697700
}
698701
else {
699702
final int length = binding.key().length;
700-
routingKeys = new String[length];
703+
routingKeys = new ArrayList<>();
701704
for (int i = 0; i < length; ++i) {
702-
routingKeys[i] = resolveExpressionAsString(binding.key()[i], "@QueueBinding.key");
705+
resolveAsString(resolveExpression(binding.key()[i]), routingKeys, false, "@QueueBinding.key");
703706
}
704707
}
705708
final Map<String, Object> bindingArguments = resolveArguments(binding.arguments());

spring-rabbit/src/test/java/org/springframework/amqp/rabbit/annotation/RabbitListenerAnnotationBeanPostProcessorTests.java

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,8 @@ public void invalidValueInAnnotationTestBean() {
197197
}
198198
catch (BeanCreationException e) {
199199
assertThat(e.getCause()).isInstanceOf(IllegalArgumentException.class);
200-
assertThat(e.getMessage()).contains("@RabbitListener can't resolve").contains("as either a String or a Queue");
200+
assertThat(e.getMessage()).contains("@RabbitListener.queuesToDeclare can't resolve")
201+
.contains("as a String[] or a String or a Queue");
201202
}
202203
}
203204

@@ -209,19 +210,25 @@ public void multipleRoutingKeysTestBean() {
209210
RabbitListenerContainerTestFactory factory = context.getBean(RabbitListenerContainerTestFactory.class);
210211
assertThat(factory.getListenerContainers()).as("one container should have been registered").hasSize(1);
211212
RabbitListenerEndpoint endpoint = factory.getListenerContainers().get(0).getEndpoint();
212-
assertThat(((AbstractRabbitListenerEndpoint) endpoint).getQueueNames()).isEqualTo(Collections.singletonList("my_queue"));
213+
assertThat(((AbstractRabbitListenerEndpoint) endpoint).getQueueNames())
214+
.isEqualTo(Collections.singletonList("my_queue"));
213215
final List<Queue> queues = new ArrayList<>(context.getBeansOfType(Queue.class).values());
214216
queues.sort(Comparator.comparing(Queue::getName));
215-
assertThat(queues.stream().map(Queue::getName).collect(Collectors.toList())).containsExactly("my_queue", "secondQueue", "testQueue");
217+
assertThat(queues.stream().map(Queue::getName).collect(Collectors.toList())).containsExactly("my_queue",
218+
"secondQueue", "testQueue");
216219
assertThat(queues.get(0).getArguments()).isEqualTo(Collections.singletonMap("foo", "bar"));
217220

218221
assertThat(context.getBeansOfType(org.springframework.amqp.core.Exchange.class).values()).hasSize(1);
219222

220223
final List<Binding> bindings = new ArrayList<>(context.getBeansOfType(Binding.class).values());
221-
assertThat(bindings).hasSize(2);
224+
assertThat(bindings).hasSize(3);
222225
bindings.sort(Comparator.comparing(Binding::getRoutingKey));
223-
assertThat(bindings.get(0).toString()).isEqualTo("Binding [destination=my_queue, exchange=my_exchange, routingKey=red]");
224-
assertThat(bindings.get(1).toString()).isEqualTo("Binding [destination=my_queue, exchange=my_exchange, routingKey=yellow]");
226+
assertThat(bindings.get(0).toString())
227+
.isEqualTo("Binding [destination=my_queue, exchange=my_exchange, routingKey=green]");
228+
assertThat(bindings.get(1).toString())
229+
.isEqualTo("Binding [destination=my_queue, exchange=my_exchange, routingKey=red]");
230+
assertThat(bindings.get(2).toString())
231+
.isEqualTo("Binding [destination=my_queue, exchange=my_exchange, routingKey=yellow]");
225232

226233
context.close();
227234
}
@@ -379,8 +386,9 @@ public void handleIt(String body) {
379386
static class MultipleRoutingKeysTestBean {
380387

381388
@RabbitListener(bindings = @QueueBinding(exchange = @Exchange("my_exchange"),
382-
value = @org.springframework.amqp.rabbit.annotation.Queue(value = "my_queue", arguments = @Argument(name = "foo", value = "bar")),
383-
key = {"${xxxxxxx:red}", "yellow"}))
389+
value = @org.springframework.amqp.rabbit.annotation.Queue(value = "my_queue",
390+
arguments = @Argument(name = "foo", value = "bar")),
391+
key = {"${xxxxxxx:red}", "#{'yellow,green'.split(',')}"}))
384392
public void handleIt(String body) {
385393
}
386394
}

0 commit comments

Comments
 (0)