|
102 | 102 | * @author Gary Russell
|
103 | 103 | * @author Nathan Xu
|
104 | 104 | * @author Soby Chacko
|
| 105 | + * @author Mikhail Polivakha |
105 | 106 | * @since 2.1.3
|
106 | 107 | *
|
107 | 108 | */
|
108 | 109 | @SpringJUnitConfig
|
109 | 110 | @DirtiesContext
|
110 |
| -@EmbeddedKafka(partitions = 5, topics = { ReplyingKafkaTemplateTests.A_REPLY, ReplyingKafkaTemplateTests.A_REQUEST, |
| 111 | +@EmbeddedKafka(partitions = 5, topics = { |
| 112 | + ReplyingKafkaTemplateTests.A_REPLY, ReplyingKafkaTemplateTests.A_REQUEST, |
111 | 113 | ReplyingKafkaTemplateTests.B_REPLY, ReplyingKafkaTemplateTests.B_REQUEST,
|
112 | 114 | ReplyingKafkaTemplateTests.C_REPLY, ReplyingKafkaTemplateTests.C_REQUEST,
|
113 | 115 | ReplyingKafkaTemplateTests.D_REPLY, ReplyingKafkaTemplateTests.D_REQUEST,
|
|
119 | 121 | ReplyingKafkaTemplateTests.J_REPLY, ReplyingKafkaTemplateTests.J_REQUEST,
|
120 | 122 | ReplyingKafkaTemplateTests.K_REPLY, ReplyingKafkaTemplateTests.K_REQUEST,
|
121 | 123 | ReplyingKafkaTemplateTests.L_REPLY, ReplyingKafkaTemplateTests.L_REQUEST,
|
122 |
| - ReplyingKafkaTemplateTests.M_REPLY, ReplyingKafkaTemplateTests.M_REQUEST }) |
| 124 | + ReplyingKafkaTemplateTests.M_REPLY, ReplyingKafkaTemplateTests.M_REQUEST, |
| 125 | + ReplyingKafkaTemplateTests.CUSTOM_REPLY_HEADER_REPLY, ReplyingKafkaTemplateTests.CUSTOM_REPLY_HEADER_REQUEST, |
| 126 | + ReplyingKafkaTemplateTests.CUSTOM_REPLY_HEADER_WITH_PARTITION_REPLY, ReplyingKafkaTemplateTests.CUSTOM_REPLY_HEADER_WITH_PARTITION_REQUEST |
| 127 | +}) |
123 | 128 | public class ReplyingKafkaTemplateTests {
|
124 | 129 |
|
125 | 130 | public static final String A_REPLY = "aReply";
|
@@ -174,6 +179,14 @@ public class ReplyingKafkaTemplateTests {
|
174 | 179 |
|
175 | 180 | public static final String M_REQUEST = "mRequest";
|
176 | 181 |
|
| 182 | + public static final String CUSTOM_REPLY_HEADER_REPLY = "CUSTOM_REPLY_HEADER_REPLY"; |
| 183 | + |
| 184 | + public static final String CUSTOM_REPLY_HEADER_REQUEST = "CUSTOM_REPLY_HEADER_REQUEST"; |
| 185 | + |
| 186 | + public static final String CUSTOM_REPLY_HEADER_WITH_PARTITION_REPLY = "CUSTOM_REPLY_HEADER_WITH_PARTITION_REPLY"; |
| 187 | + |
| 188 | + public static final String CUSTOM_REPLY_HEADER_WITH_PARTITION_REQUEST = "CUSTOM_REPLY_HEADER_WITH_PARTITION_REQUEST"; |
| 189 | + |
177 | 190 | @Autowired
|
178 | 191 | private EmbeddedKafkaBroker embeddedKafka;
|
179 | 192 |
|
@@ -365,6 +378,54 @@ public void testHandlerReturn() throws Exception {
|
365 | 378 | }
|
366 | 379 | }
|
367 | 380 |
|
| 381 | + @Test |
| 382 | + public void testCustomReplyTopicHeaderIsNotDuplicated() throws Exception { |
| 383 | + String customReplyHeaderName = "X-Custom-Reply-Header"; |
| 384 | + ReplyingKafkaTemplate<Integer, String, String> template = createTemplate(CUSTOM_REPLY_HEADER_REPLY); |
| 385 | + template.setReplyTopicHeaderName(customReplyHeaderName); |
| 386 | + try { |
| 387 | + Message<String> message = MessageBuilder.withPayload("expected_message") |
| 388 | + .setHeader(customReplyHeaderName, CUSTOM_REPLY_HEADER_REPLY) |
| 389 | + .setHeader(KafkaHeaders.TOPIC, CUSTOM_REPLY_HEADER_REQUEST) |
| 390 | + .build(); |
| 391 | + |
| 392 | + RequestReplyMessageFuture<Integer, String> future = template.sendAndReceive(message, Duration.ofSeconds(30)); |
| 393 | + future.getSendFuture().get(10, TimeUnit.SECONDS); // send ok |
| 394 | + Message<?> resultingMessage = future.get(30, TimeUnit.SECONDS); |
| 395 | + assertThat(resultingMessage.getPayload()).isEqualTo("OK"); |
| 396 | + } |
| 397 | + finally { |
| 398 | + template.stop(); |
| 399 | + template.destroy(); |
| 400 | + } |
| 401 | + } |
| 402 | + |
| 403 | + @Test |
| 404 | + public void testCustomReplyHeadersAreNotDuplicated() throws Exception { |
| 405 | + String customReplyTopicHeaderName = "X-Custom-Reply-Header"; |
| 406 | + String customReplyPartitionHeaderName = "X-Custom-Reply-Partition"; |
| 407 | + ReplyingKafkaTemplate<Integer, String, String> template = createTemplate(CUSTOM_REPLY_HEADER_WITH_PARTITION_REPLY); |
| 408 | + template.setReplyTopicHeaderName(customReplyTopicHeaderName); |
| 409 | + template.setReplyPartitionHeaderName(customReplyPartitionHeaderName); |
| 410 | + |
| 411 | + try { |
| 412 | + Message<String> message = MessageBuilder.withPayload("expected_message") |
| 413 | + .setHeader(customReplyTopicHeaderName, CUSTOM_REPLY_HEADER_REPLY) |
| 414 | + .setHeader(customReplyPartitionHeaderName, "test-partition") |
| 415 | + .setHeader(KafkaHeaders.TOPIC, CUSTOM_REPLY_HEADER_WITH_PARTITION_REQUEST) |
| 416 | + .build(); |
| 417 | + |
| 418 | + RequestReplyMessageFuture<Integer, String> future = template.sendAndReceive(message, Duration.ofSeconds(30)); |
| 419 | + future.getSendFuture().get(10, TimeUnit.SECONDS); // send ok |
| 420 | + Message<?> resultingMessage = future.get(30, TimeUnit.SECONDS); |
| 421 | + assertThat(resultingMessage.getPayload()).isEqualTo("OK"); |
| 422 | + } |
| 423 | + finally { |
| 424 | + template.stop(); |
| 425 | + template.destroy(); |
| 426 | + } |
| 427 | + } |
| 428 | + |
368 | 429 | @Test
|
369 | 430 | public void testMessageReturnNoHeadersProvidedByListener() throws Exception {
|
370 | 431 | ReplyingKafkaTemplate<Integer, String, String> template = createTemplate(H_REPLY);
|
@@ -1046,6 +1107,45 @@ public List<Message<String>> handleM(String in) throws InterruptedException {
|
1046 | 1107 | return Collections.singletonList(message);
|
1047 | 1108 | }
|
1048 | 1109 |
|
| 1110 | + @KafkaListener(id = CUSTOM_REPLY_HEADER_REQUEST, topics = CUSTOM_REPLY_HEADER_REQUEST) |
| 1111 | + @SendTo(CUSTOM_REPLY_HEADER_REPLY) // send to custom topic back |
| 1112 | + public String handleCustomReplyHeaderNoReplyPartition(ConsumerRecord<?, String> inputMessage) { |
| 1113 | + Headers headers = inputMessage.headers(); |
| 1114 | + |
| 1115 | + if (length(headers.headers("X-Custom-Reply-Header")) != 1) { |
| 1116 | + return "The X-Custom-Reply-Header header that signify the custom reply topic header name is duplicated. It is supposed to present only once"; |
| 1117 | + } |
| 1118 | + |
| 1119 | + if (length(headers.headers(KafkaHeaders.REPLY_PARTITION)) != 0) { |
| 1120 | + return "It is expected that the user does NOT specify the reply partition in this test case"; |
| 1121 | + } |
| 1122 | + |
| 1123 | + if (!"expected_message".equals(inputMessage.value())) { |
| 1124 | + return "Expected message is 'expected_message', but got %s".formatted(inputMessage.value()); |
| 1125 | + } |
| 1126 | + |
| 1127 | + return "OK"; |
| 1128 | + } |
| 1129 | + |
| 1130 | + @KafkaListener(id = CUSTOM_REPLY_HEADER_WITH_PARTITION_REQUEST, topics = CUSTOM_REPLY_HEADER_WITH_PARTITION_REQUEST) |
| 1131 | + @SendTo(CUSTOM_REPLY_HEADER_WITH_PARTITION_REPLY) // send to custom topic back |
| 1132 | + public String handleCustomReplyHeaderDefaultPartitionHeader(ConsumerRecord<?, String> inputMessage) { |
| 1133 | + Headers headers = inputMessage.headers(); |
| 1134 | + |
| 1135 | + if (length(headers.headers("X-Custom-Reply-Header")) != 1) { |
| 1136 | + return "The X-Custom-Reply-Header header that signify the custom reply topic header name is duplicated. It is supposed to present only once"; |
| 1137 | + } |
| 1138 | + |
| 1139 | + if (length(headers.headers("X-Custom-Reply-Partition")) != 1) { |
| 1140 | + return "Executed a single reply partition header '%s' in the incoming message".formatted(KafkaHeaders.REPLY_PARTITION); |
| 1141 | + } |
| 1142 | + |
| 1143 | + if (!"expected_message".equals(inputMessage.value())) { |
| 1144 | + return "Expected message is 'expected_message', but got %s".formatted(inputMessage.value()); |
| 1145 | + } |
| 1146 | + |
| 1147 | + return "OK"; |
| 1148 | + } |
1049 | 1149 | }
|
1050 | 1150 |
|
1051 | 1151 | @KafkaListener(topics = C_REQUEST, groupId = C_REQUEST)
|
@@ -1090,6 +1190,12 @@ public Object deserialize(String topic, Headers headers, byte[] data) {
|
1090 | 1190 |
|
1091 | 1191 | }
|
1092 | 1192 |
|
| 1193 | + private static int length(Iterable<?> iterable) { |
| 1194 | + int counter = 0; |
| 1195 | + for (; iterable.iterator().hasNext(); counter++) {} |
| 1196 | + return counter; |
| 1197 | + } |
| 1198 | + |
1093 | 1199 | public static class Foo {
|
1094 | 1200 |
|
1095 | 1201 | private String bar;
|
|
0 commit comments