|
18 | 18 | import static org.assertj.core.api.Assertions.*; |
19 | 19 | import static org.awaitility.Awaitility.*; |
20 | 20 |
|
| 21 | +import java.time.Duration; |
21 | 22 | import java.util.Arrays; |
22 | 23 | import java.util.Collection; |
| 24 | +import java.util.HashMap; |
| 25 | +import java.util.Map; |
| 26 | +import java.util.Set; |
23 | 27 | import java.util.concurrent.CompletableFuture; |
24 | 28 | import java.util.concurrent.CountDownLatch; |
25 | 29 | import java.util.concurrent.TimeUnit; |
@@ -354,6 +358,102 @@ public void onChannelSubscribed(byte[] channel, long count) { |
354 | 358 | container.destroy(); |
355 | 359 | } |
356 | 360 |
|
| 361 | + /** |
| 362 | + * Tests the variants of {@link RedisMessageListenerContainer#removeMessageListener} to ensure the removal stops |
| 363 | + * message delivery and that the pattern and channel listeners do not interfere with one another upon removal. |
| 364 | + */ |
| 365 | + @Test // GH-3237 |
| 366 | + void shouldRemoveListenerFromChannelsAndTopics() throws Exception { |
| 367 | + |
| 368 | + AtomicInteger subscriptions = new AtomicInteger(); |
| 369 | + Map<String, String> patternsMessages = new HashMap<>(); |
| 370 | + Map<String, String> channelsMessages = new HashMap<>(); |
| 371 | + |
| 372 | + MessageListener patternListener = new CompositeListener() { |
| 373 | + @Override |
| 374 | + public void onMessage(Message message, byte @Nullable [] pattern) { |
| 375 | + patternsMessages.put(new String(pattern), message.toString()); |
| 376 | + } |
| 377 | + |
| 378 | + @Override |
| 379 | + public void onPatternSubscribed(byte[] pattern, long count) { |
| 380 | + subscriptions.incrementAndGet(); |
| 381 | + } |
| 382 | + |
| 383 | + }; |
| 384 | + |
| 385 | + CompositeListener channelListener = new CompositeListener() { |
| 386 | + @Override |
| 387 | + public void onMessage(Message message, byte @Nullable [] pattern) { |
| 388 | + channelsMessages.put(new String(pattern), message.toString()); |
| 389 | + } |
| 390 | + |
| 391 | + @Override |
| 392 | + public void onChannelSubscribed(byte[] channel, long count) { |
| 393 | + subscriptions.incrementAndGet(); |
| 394 | + } |
| 395 | + |
| 396 | + }; |
| 397 | + |
| 398 | + try { |
| 399 | + container.start(); |
| 400 | + container.addMessageListener(patternListener, new PatternTopic("a-pattern-0")); |
| 401 | + container.addMessageListener(patternListener, new PatternTopic("a-pattern-1")); |
| 402 | + container.addMessageListener(patternListener, new PatternTopic("a-pattern-2")); |
| 403 | + container.addMessageListener(channelListener, new ChannelTopic("a-channel-0")); |
| 404 | + container.addMessageListener(channelListener, new ChannelTopic("a-channel-1")); |
| 405 | + |
| 406 | + // Wait for the subscriptions to register |
| 407 | + await().untilAtomic(subscriptions, Matchers.is(5)); |
| 408 | + |
| 409 | + try (RedisConnection connection = connectionFactory.getConnection()) { |
| 410 | + |
| 411 | + // remove patternListener from single topic |
| 412 | + container.removeMessageListener(patternListener, new PatternTopic("a-pattern-0")); |
| 413 | + |
| 414 | + // send each topic a msg - removed listener should not get msg |
| 415 | + connection.publish("a-pattern-0".getBytes(), "pattern100".getBytes()); |
| 416 | + connection.publish("a-pattern-1".getBytes(), "pattern101".getBytes()); |
| 417 | + connection.publish("a-pattern-2".getBytes(), "pattern102".getBytes()); |
| 418 | + await().atMost(Duration.ofSeconds(15)).untilAsserted(() -> { |
| 419 | + assertThat(patternsMessages).doesNotContain(entry("a-pattern-0", "pattern100")) |
| 420 | + .contains(entry("a-pattern-1", "pattern101"), entry("a-pattern-2", "pattern102")); |
| 421 | + }); |
| 422 | + patternsMessages.clear(); |
| 423 | + |
| 424 | + // remove pattern listener from multiple (2 remaining topics) |
| 425 | + container.removeMessageListener(patternListener, |
| 426 | + Set.of(new PatternTopic("a-pattern-1"), new PatternTopic("a-pattern-2"))); |
| 427 | + |
| 428 | + // no listener should receive msgs |
| 429 | + connection.publish("a-pattern-0".getBytes(), "pattern100".getBytes()); |
| 430 | + connection.publish("a-pattern-1".getBytes(), "pattern101".getBytes()); |
| 431 | + connection.publish("a-pattern-2".getBytes(), "pattern102".getBytes()); |
| 432 | + await().atMost(Duration.ofSeconds(15)).untilAsserted(() -> assertThat(patternsMessages).isEmpty()); |
| 433 | + |
| 434 | + // sanity check sending msgs to channels (removal of pattern listeners did not disrupt) |
| 435 | + connection.publish("a-channel-0".getBytes(), "channel100".getBytes()); |
| 436 | + connection.publish("a-channel-1".getBytes(), "channel101".getBytes()); |
| 437 | + await().atMost(Duration.ofSeconds(15)).untilAsserted(() -> { |
| 438 | + assertThat(channelsMessages).containsExactly(entry("a-channel-0", "channel100"), |
| 439 | + entry("a-channel-1", "channel101")); |
| 440 | + }); |
| 441 | + channelsMessages.clear(); |
| 442 | + |
| 443 | + // remove channel listener from all channel topics |
| 444 | + container.removeMessageListener(channelListener); |
| 445 | + |
| 446 | + // should receive no msgs |
| 447 | + connection.publish("a-channel-0".getBytes(), "channel100".getBytes()); |
| 448 | + connection.publish("a-channel-1".getBytes(), "channel101".getBytes()); |
| 449 | + await().atMost(Duration.ofSeconds(15)).untilAsserted(() -> assertThat(channelsMessages).isEmpty()); |
| 450 | + } |
| 451 | + } |
| 452 | + finally { |
| 453 | + container.destroy(); |
| 454 | + } |
| 455 | + } |
| 456 | + |
357 | 457 | interface CompositeListener extends MessageListener, SubscriptionListener { |
358 | 458 |
|
359 | 459 | } |
|
0 commit comments