Skip to content

Commit 7805541

Browse files
NathanQingyangXusobychacko
authored andcommitted
GH-2908: MessageListenerAdapter optimizations
Fixes: gh-2908 Fix minor defect in MessagingMessageListenerAdapter#checkHeaders() Add more testings to improve robustness Centralize correlation related logic in common methods Add checkHeaders() usage to Message Iterable element and unit testing case
1 parent 8e564bf commit 7805541

File tree

2 files changed

+61
-15
lines changed

2 files changed

+61
-15
lines changed

spring-kafka/src/main/java/org/springframework/kafka/listener/adapter/MessagingMessageListenerAdapter.java

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
* @author Gary Russell
8181
* @author Artem Bilan
8282
* @author Venil Noronha
83+
* @author Nathan Xu
8384
*/
8485
public abstract class MessagingMessageListenerAdapter<K, V> implements ConsumerSeekAware {
8586

@@ -470,8 +471,8 @@ protected void sendResponse(Object result, String topic, @Nullable Object source
470471
if (!returnTypeMessage && topic == null) {
471472
this.logger.debug(() -> "No replyTopic to handle the reply: " + result);
472473
}
473-
else if (result instanceof Message) {
474-
Message<?> reply = checkHeaders(result, topic, source);
474+
else if (result instanceof Message<?> mResult) {
475+
Message<?> reply = checkHeaders(mResult, topic, source);
475476
this.replyTemplate.send(reply);
476477
}
477478
else {
@@ -483,8 +484,9 @@ else if (result instanceof Message) {
483484
}
484485
if (iterableOfMessages || this.splitIterables) {
485486
((Iterable<V>) result).forEach(v -> {
486-
if (v instanceof Message) {
487-
this.replyTemplate.send((Message<?>) v);
487+
if (v instanceof Message<?> mv) {
488+
Message<?> aReply = checkHeaders(mv, topic, source);
489+
this.replyTemplate.send(aReply);
488490
}
489491
else {
490492
this.replyTemplate.send(topic, v);
@@ -501,24 +503,23 @@ else if (result instanceof Message) {
501503
}
502504
}
503505

504-
private Message<?> checkHeaders(Object result, String topic, @Nullable Object source) { // NOSONAR (complexity)
505-
Message<?> reply = (Message<?>) result;
506+
private Message<?> checkHeaders(Message<?> reply, @Nullable String topic, @Nullable Object source) { // NOSONAR (complexity)
506507
MessageHeaders headers = reply.getHeaders();
507-
boolean needsTopic = headers.get(KafkaHeaders.TOPIC) == null;
508+
boolean needsTopic = topic != null && headers.get(KafkaHeaders.TOPIC) == null;
508509
boolean sourceIsMessage = source instanceof Message;
509-
boolean needsCorrelation = headers.get(this.correlationHeaderName) == null && sourceIsMessage;
510+
boolean needsCorrelation = headers.get(this.correlationHeaderName) == null && sourceIsMessage
511+
&& getCorrelation((Message<?>) source) != null;
510512
boolean needsPartition = headers.get(KafkaHeaders.PARTITION) == null && sourceIsMessage
511513
&& getReplyPartition((Message<?>) source) != null;
512514
if (needsTopic || needsCorrelation || needsPartition) {
513515
MessageBuilder<?> builder = MessageBuilder.fromMessage(reply);
514516
if (needsTopic) {
515517
builder.setHeader(KafkaHeaders.TOPIC, topic);
516518
}
517-
if (needsCorrelation && sourceIsMessage) {
518-
builder.setHeader(this.correlationHeaderName,
519-
((Message<?>) source).getHeaders().get(this.correlationHeaderName));
519+
if (needsCorrelation) {
520+
setCorrelation(builder, (Message<?>) source);
520521
}
521-
if (sourceIsMessage && reply.getHeaders().get(KafkaHeaders.REPLY_PARTITION) == null) {
522+
if (needsPartition) {
522523
setPartition(builder, (Message<?>) source);
523524
}
524525
reply = builder.build();
@@ -531,8 +532,8 @@ private void sendSingleResult(Object result, String topic, @Nullable Object sour
531532
byte[] correlationId = null;
532533
boolean sourceIsMessage = source instanceof Message;
533534
if (sourceIsMessage
534-
&& ((Message<?>) source).getHeaders().get(this.correlationHeaderName) != null) {
535-
correlationId = ((Message<?>) source).getHeaders().get(this.correlationHeaderName, byte[].class);
535+
&& getCorrelation((Message<?>) source) != null) {
536+
correlationId = getCorrelation((Message<?>) source);
536537
}
537538
if (sourceIsMessage) {
538539
sendReplyForMessageSource(result, topic, source, correlationId);
@@ -571,6 +572,18 @@ private void sendReplyForMessageSource(Object result, String topic, Object sourc
571572
this.replyTemplate.send(builder.build());
572573
}
573574

575+
private void setCorrelation(MessageBuilder<?> builder, Message<?> source) {
576+
byte[] correlationBytes = getCorrelation(source);
577+
if (correlationBytes != null) {
578+
builder.setHeader(this.correlationHeaderName, correlationBytes);
579+
}
580+
}
581+
582+
@Nullable
583+
private byte[] getCorrelation(Message<?> source) {
584+
return source.getHeaders().get(this.correlationHeaderName, byte[].class);
585+
}
586+
574587
private void setPartition(MessageBuilder<?> builder, Message<?> source) {
575588
byte[] partitionBytes = getReplyPartition(source);
576589
if (partitionBytes != null) {

spring-kafka/src/test/java/org/springframework/kafka/requestreply/ReplyingKafkaTemplateTests.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100

101101
/**
102102
* @author Gary Russell
103+
* @author Nathan Xu
103104
* @since 2.1.3
104105
*
105106
*/
@@ -116,7 +117,8 @@
116117
ReplyingKafkaTemplateTests.I_REPLY, ReplyingKafkaTemplateTests.I_REQUEST,
117118
ReplyingKafkaTemplateTests.J_REPLY, ReplyingKafkaTemplateTests.J_REQUEST,
118119
ReplyingKafkaTemplateTests.K_REPLY, ReplyingKafkaTemplateTests.K_REQUEST,
119-
ReplyingKafkaTemplateTests.L_REPLY, ReplyingKafkaTemplateTests.L_REQUEST })
120+
ReplyingKafkaTemplateTests.L_REPLY, ReplyingKafkaTemplateTests.L_REQUEST,
121+
ReplyingKafkaTemplateTests.M_REPLY, ReplyingKafkaTemplateTests.M_REQUEST })
120122
public class ReplyingKafkaTemplateTests {
121123

122124
public static final String A_REPLY = "aReply";
@@ -167,6 +169,10 @@ public class ReplyingKafkaTemplateTests {
167169

168170
public static final String L_REQUEST = "lRequest";
169171

172+
public static final String M_REPLY = "mReply";
173+
174+
public static final String M_REQUEST = "mRequest";
175+
170176
@Autowired
171177
private EmbeddedKafkaBroker embeddedKafka;
172178

@@ -845,6 +851,24 @@ void requestTimeoutWithMessage() throws Exception {
845851
}
846852
}
847853

854+
@Test
855+
void testMessageIterableReturn() throws Exception {
856+
ReplyingKafkaTemplate<Integer, String, String> template = createTemplate(M_REPLY);
857+
try {
858+
template.setDefaultReplyTimeout(Duration.ofSeconds(30));
859+
Headers headers = new RecordHeaders();
860+
ProducerRecord<Integer, String> record = new ProducerRecord<>(M_REQUEST, null, null, null, "foo", headers);
861+
RequestReplyFuture<Integer, String, String> future = template.sendAndReceive(record);
862+
future.getSendFuture().get(10, TimeUnit.SECONDS); // send ok
863+
ConsumerRecord<Integer, String> consumerRecord = future.get(30, TimeUnit.SECONDS);
864+
assertThat(consumerRecord.value()).isEqualTo("FOO");
865+
}
866+
finally {
867+
template.stop();
868+
template.destroy();
869+
}
870+
}
871+
848872
@Configuration
849873
@EnableKafka
850874
public static class Config {
@@ -1011,6 +1035,15 @@ public Message<String> handleL(String in) throws InterruptedException {
10111035
.build();
10121036
}
10131037

1038+
@KafkaListener(id = M_REQUEST, topics = M_REQUEST)
1039+
@SendTo // default REPLY_TOPIC header
1040+
public List<Message<String>> handleM(String in) throws InterruptedException {
1041+
Message<String> message = MessageBuilder.withPayload(in.toUpperCase())
1042+
.setHeader("serverSentAnError", "user error")
1043+
.build();
1044+
return Collections.singletonList(message);
1045+
}
1046+
10141047
}
10151048

10161049
@KafkaListener(topics = C_REQUEST, groupId = C_REQUEST)

0 commit comments

Comments
 (0)