|
88 | 88 | import org.junit.jupiter.params.provider.Arguments;
|
89 | 89 | import org.junit.jupiter.params.provider.EnumSource;
|
90 | 90 | import org.junit.jupiter.params.provider.MethodSource;
|
| 91 | +import org.junit.jupiter.params.provider.ValueSource; |
91 | 92 | import org.mockito.ArgumentCaptor;
|
92 | 93 | import org.mockito.InOrder;
|
93 | 94 |
|
|
126 | 127 | import org.springframework.kafka.test.utils.KafkaTestUtils;
|
127 | 128 | import org.springframework.lang.Nullable;
|
128 | 129 | import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
| 130 | +import org.springframework.transaction.PlatformTransactionManager; |
129 | 131 | import org.springframework.util.backoff.FixedBackOff;
|
130 | 132 |
|
131 | 133 | /**
|
@@ -3735,6 +3737,148 @@ public void clearThreadState(Consumer<?, ?> consumer) {
|
3735 | 3737 | container.stop();
|
3736 | 3738 | }
|
3737 | 3739 |
|
| 3740 | + private static Stream<Arguments> paramsForRecordAllSkipped() { |
| 3741 | + return Stream.of( |
| 3742 | + Arguments.of(AckMode.RECORD, false), |
| 3743 | + Arguments.of(AckMode.RECORD, true), |
| 3744 | + Arguments.of(AckMode.BATCH, false), |
| 3745 | + Arguments.of(AckMode.BATCH, true)); |
| 3746 | + } |
| 3747 | + |
| 3748 | + @ParameterizedTest(name = "{index} AckMode.{0} early intercept {1}") |
| 3749 | + @MethodSource("paramsForRecordAllSkipped") |
| 3750 | + @SuppressWarnings({"unchecked", "deprecation"}) |
| 3751 | + public void testInvokeRecordInterceptorAllSkipped(AckMode ackMode, boolean early) throws Exception { |
| 3752 | + ConsumerFactory<Integer, String> cf = mock(ConsumerFactory.class); |
| 3753 | + Consumer<Integer, String> consumer = mock(Consumer.class); |
| 3754 | + given(cf.createConsumer(eq("grp"), eq("clientId"), isNull(), any())).willReturn(consumer); |
| 3755 | + ConsumerRecord<Integer, String> firstRecord = new ConsumerRecord<>("foo", 0, 0L, 1, "foo"); |
| 3756 | + ConsumerRecord<Integer, String> secondRecord = new ConsumerRecord<>("foo", 0, 1L, 1, "bar"); |
| 3757 | + Map<TopicPartition, List<ConsumerRecord<Integer, String>>> records = new HashMap<>(); |
| 3758 | + records.put(new TopicPartition("foo", 0), List.of(firstRecord, secondRecord)); |
| 3759 | + ConsumerRecords<Integer, String> consumerRecords = new ConsumerRecords<>(records); |
| 3760 | + AtomicBoolean first = new AtomicBoolean(true); |
| 3761 | + given(consumer.poll(any(Duration.class))).willAnswer(i -> { |
| 3762 | + Thread.sleep(50); |
| 3763 | + return first.getAndSet(false) ? consumerRecords : ConsumerRecords.empty(); |
| 3764 | + }); |
| 3765 | + CountDownLatch latch = new CountDownLatch(1); |
| 3766 | + willAnswer(inv -> { |
| 3767 | + latch.countDown(); |
| 3768 | + return null; |
| 3769 | + }).given(consumer).commitSync(any(), any()); |
| 3770 | + TopicPartitionOffset[] topicPartition = new TopicPartitionOffset[] { |
| 3771 | + new TopicPartitionOffset("foo", 0) }; |
| 3772 | + |
| 3773 | + ContainerProperties containerProps = new ContainerProperties(topicPartition); |
| 3774 | + containerProps.setGroupId("grp"); |
| 3775 | + containerProps.setAckMode(ackMode); |
| 3776 | + |
| 3777 | + containerProps.setMessageListener((MessageListener) msg -> { |
| 3778 | + }); |
| 3779 | + containerProps.setClientId("clientId"); |
| 3780 | + |
| 3781 | + RecordInterceptor<Integer, String> recordInterceptor = spy(new RecordInterceptor<Integer, String>() { |
| 3782 | + |
| 3783 | + @Override |
| 3784 | + @Nullable |
| 3785 | + public ConsumerRecord<Integer, String> intercept(ConsumerRecord<Integer, String> record, |
| 3786 | + Consumer<Integer, String> consumer) { |
| 3787 | + |
| 3788 | + return null; |
| 3789 | + } |
| 3790 | + |
| 3791 | + }); |
| 3792 | + |
| 3793 | + KafkaMessageListenerContainer<Integer, String> container = |
| 3794 | + new KafkaMessageListenerContainer<>(cf, containerProps); |
| 3795 | + container.setRecordInterceptor(recordInterceptor); |
| 3796 | + container.setInterceptBeforeTx(early); |
| 3797 | + container.start(); |
| 3798 | + assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue(); |
| 3799 | + |
| 3800 | + InOrder inOrder = inOrder(recordInterceptor, consumer); |
| 3801 | + inOrder.verify(recordInterceptor).setupThreadState(eq(consumer)); |
| 3802 | + inOrder.verify(consumer).poll(Duration.ofMillis(ContainerProperties.DEFAULT_POLL_TIMEOUT)); |
| 3803 | + inOrder.verify(recordInterceptor).intercept(eq(firstRecord), eq(consumer)); |
| 3804 | + if (ackMode.equals(AckMode.RECORD)) { |
| 3805 | + inOrder.verify(consumer).commitSync(eq(Map.of(new TopicPartition("foo", 0), new OffsetAndMetadata(1L))), |
| 3806 | + any(Duration.class)); |
| 3807 | + } |
| 3808 | + else { |
| 3809 | + verify(consumer, never()).commitSync(eq(Map.of(new TopicPartition("foo", 0), new OffsetAndMetadata(1L))), |
| 3810 | + any(Duration.class)); |
| 3811 | + } |
| 3812 | + inOrder.verify(recordInterceptor).intercept(eq(secondRecord), eq(consumer)); |
| 3813 | + inOrder.verify(consumer).commitSync(eq(Map.of(new TopicPartition("foo", 0), new OffsetAndMetadata(2L))), |
| 3814 | + any(Duration.class)); |
| 3815 | + container.stop(); |
| 3816 | + } |
| 3817 | + |
| 3818 | + @ParameterizedTest(name = "{index} early intercept {0}") |
| 3819 | + @ValueSource(booleans = { true, false }) |
| 3820 | + @SuppressWarnings({"unchecked", "deprecation"}) |
| 3821 | + public void testInvokeBatchInterceptorAllSkipped(boolean early) throws Exception { |
| 3822 | + ConsumerFactory<Integer, String> cf = mock(ConsumerFactory.class); |
| 3823 | + Consumer<Integer, String> consumer = mock(Consumer.class); |
| 3824 | + given(cf.createConsumer(eq("grp"), eq("clientId"), isNull(), any())).willReturn(consumer); |
| 3825 | + ConsumerRecord<Integer, String> firstRecord = new ConsumerRecord<>("foo", 0, 0L, 1, "foo"); |
| 3826 | + ConsumerRecord<Integer, String> secondRecord = new ConsumerRecord<>("foo", 0, 1L, 1, "bar"); |
| 3827 | + Map<TopicPartition, List<ConsumerRecord<Integer, String>>> records = new HashMap<>(); |
| 3828 | + records.put(new TopicPartition("foo", 0), List.of(firstRecord, secondRecord)); |
| 3829 | + ConsumerRecords<Integer, String> consumerRecords = new ConsumerRecords<>(records); |
| 3830 | + AtomicBoolean first = new AtomicBoolean(true); |
| 3831 | + given(consumer.poll(any(Duration.class))).willAnswer(i -> { |
| 3832 | + Thread.sleep(50); |
| 3833 | + return first.getAndSet(false) ? consumerRecords : ConsumerRecords.empty(); |
| 3834 | + }); |
| 3835 | + CountDownLatch latch = new CountDownLatch(1); |
| 3836 | + willAnswer(inv -> { |
| 3837 | + latch.countDown(); |
| 3838 | + return null; |
| 3839 | + }).given(consumer).commitSync(any(), any()); |
| 3840 | + TopicPartitionOffset[] topicPartition = new TopicPartitionOffset[] { |
| 3841 | + new TopicPartitionOffset("foo", 0) }; |
| 3842 | + |
| 3843 | + ContainerProperties containerProps = new ContainerProperties(topicPartition); |
| 3844 | + containerProps.setGroupId("grp"); |
| 3845 | + containerProps.setAckMode(AckMode.BATCH); |
| 3846 | + |
| 3847 | + containerProps.setMessageListener((BatchMessageListener) msgs -> { |
| 3848 | + }); |
| 3849 | + containerProps.setClientId("clientId"); |
| 3850 | + if (!early) { |
| 3851 | + containerProps.setTransactionManager(mock(PlatformTransactionManager.class)); |
| 3852 | + } |
| 3853 | + |
| 3854 | + BatchInterceptor<Integer, String> interceptor = spy(new BatchInterceptor<Integer, String>() { |
| 3855 | + |
| 3856 | + @Override |
| 3857 | + @Nullable |
| 3858 | + public ConsumerRecords<Integer, String> intercept(ConsumerRecords<Integer, String> records, |
| 3859 | + Consumer<Integer, String> consumer) { |
| 3860 | + |
| 3861 | + return null; |
| 3862 | + } |
| 3863 | + |
| 3864 | + }); |
| 3865 | + |
| 3866 | + KafkaMessageListenerContainer<Integer, String> container = |
| 3867 | + new KafkaMessageListenerContainer<>(cf, containerProps); |
| 3868 | + container.setBatchInterceptor(interceptor); |
| 3869 | + container.setInterceptBeforeTx(early); |
| 3870 | + container.start(); |
| 3871 | + assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue(); |
| 3872 | + |
| 3873 | + InOrder inOrder = inOrder(interceptor, consumer); |
| 3874 | + inOrder.verify(interceptor).setupThreadState(eq(consumer)); |
| 3875 | + inOrder.verify(consumer).poll(Duration.ofMillis(ContainerProperties.DEFAULT_POLL_TIMEOUT)); |
| 3876 | + inOrder.verify(interceptor).intercept(any(), eq(consumer)); |
| 3877 | + inOrder.verify(consumer).commitSync(eq(Map.of(new TopicPartition("foo", 0), new OffsetAndMetadata(2L))), |
| 3878 | + any(Duration.class)); |
| 3879 | + container.stop(); |
| 3880 | + } |
| 3881 | + |
3738 | 3882 | @Test
|
3739 | 3883 | @SuppressWarnings({"unchecked", "deprecation"})
|
3740 | 3884 | public void testInvokeRecordInterceptorFailure() throws Exception {
|
|
0 commit comments