Skip to content

Conversation

@Jujuwryy
Copy link

Fixes #4097

Problem: SmartMessageConverter was ignored in batch listeners, causing ClassCastException
Root Cause: BatchMessagingMessageConverter lacked setMessagingConverter() method
Solution:

  • Add setMessagingConverter() to BatchMessagingMessageConverter
  • Modify convert() method to use SmartMessageConverter in batch conversion
  • Add setMessagingConverter() override to BatchMessagingMessageListenerAdapter
  • Add comprehensive test coverage

Testing: All existing tests pass, new tests verify the fix works

Note: This is my first contribution to Spring Kafka. Happy to make any requested changes!

Copy link
Member

@artembilan artembilan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, take a look into this article: https://cbea.ms/git-commit/.

The point is to say as much as possible about why? in the commit message, rather then what?.
Your comment on the PR and in the issue is a great foundation about what to put into commit message.

Side note: the first commit message becomes PR title and description.
Therefore, no need in extra work editing it.
And in the long run no one would think about PRs and issue. We would just look into commit history to determine what is going on.
This way I believe just with a single comprehensive commit message we shot a lot of birds and save some time from typing and reviewing.

With the current state I have to read everything to understand what is going.

Please, think about this while we are reviewing your contribution.
Thank you!

@Jujuwryy
Copy link
Author

Jujuwryy commented Oct 23, 2025 via email

Copy link
Member

@artembilan artembilan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for contribution!

I think we need to dedicate more time for reverse engineering to see what is going on and how it can be fixed.

@Jujuwryy
Copy link
Author

Jujuwryy commented Oct 23, 2025 via email

@Jujuwryy Jujuwryy force-pushed the GH-4097 branch 2 times, most recently from a41485a to 5a43d31 Compare October 23, 2025 20:28
@Jujuwryy
Copy link
Author

Hi @artembilan, I've addressed all your feedback in the latest commit. The changes include:

  • Fixed Javadoc formatting (no blank lines)
  • Corrected @SInCE version to 3.3.11
  • Removed @SInCE from override method
  • Fixed the core logic - SmartMessageConverter now properly propagates to MessagingMessageConverter instead of trying to implement conversion logic directly
  • Replaced tests with meaningful ones that actually test the problem scenario
  • Added proper DCO signoff
  • Single clean commit with better commit message explaining the WHY

All tests pass and the fix is ready for review. Thanks for the detailed feedback!

@artembilan
Copy link
Member

Thank you for an update!
I will have a chance to review already tomorrow.

One note though: no need to squash commits all the time.
The first one indeed is crucial to understand what is going on.
All others would be easier to review as follow up.
We squash them anyway on merge.

@Jujuwryy
Copy link
Author

Alright, thank you for letting me know.

Copy link
Member

@artembilan artembilan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


Error: eckstyle] [ERROR] /home/runner/work/spring-kafka/spring-kafka/spring-kafka/src/main/java/org/springframework/kafka/listener/adapter/BatchMessagingMessageListenerAdapter.java:146: Line has leading space characters; indentation should be performed with tabs only. [RegexpSinglelineJava]
> Task :spring-kafka:checkstyleMain
Error: eckstyle] [ERROR] /home/runner/work/spring-kafka/spring-kafka/spring-kafka/src/main/java/org/springframework/kafka/listener/adapter/BatchMessagingMessageListenerAdapter.java:161: Line has leading space characters; indentation should be performed with tabs only. [RegexpSinglelineJava]
Error: eckstyle] [ERROR] /home/runner/work/spring-kafka/spring-kafka/spring-kafka/src/main/java/org/springframework/kafka/support/converter/BatchMessagingMessageConverter.java:228: Line has leading space characters; indentation should be performed with tabs only. [RegexpSinglelineJava]
Error: eckstyle] [ERROR] /home/runner/work/spring-kafka/spring-kafka/spring-kafka/src/main/java/org/springframework/kafka/support/converter/BatchMessagingMessageConverter.java:243: Line has leading space characters; indentation should be performed with tabs only. [RegexpSinglelineJava]
Error: eckstyle] [ERROR] /home/runner/work/spring-kafka/spring-kafka/spring-kafka/src/main/java/org/springframework/kafka/support/converter/BatchMessagingMessageConverter.java:244: Line has leading space characters; indentation should be performed with tabs only. [RegexpSinglelineJava]

Please, run gradlew check locally before pushing to PR.

Thanks

BatchMessagingMessageConverter was missing setMessagingConverter() method that exists in MessagingMessageConverter, causing SmartMessageConverter configured via @KafkaListener(contentTypeConverter) to be ignored in batch listeners.

This inconsistency between regular and batch listeners leads to ClassCastException when byte[] values aren't converted to the expected String type, breaking the contract that SmartMessageConverter should work the same way regardless of listener type.

The fix ensures SmartMessageConverter propagation works consistently by:
- Adding setMessagingConverter() to BatchMessagingMessageConverter that delegates to underlying MessagingMessageConverter
- Overriding setMessagingConverter() in BatchMessagingMessageListenerAdapter to propagate the converter to batch converter
- Maintaining the same SmartMessageConverter behavior between regular and batch listeners

Fixes spring-projectsGH-4097

Signed-off-by: Jujuwryy <georgemahfoud2@gmail.com>
@Jujuwryy
Copy link
Author

All done. Builds were successful.

Thank you.

Copy link
Member

@artembilan artembilan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, add your real name to the @author tag of all the affected classes.

@artembilan
Copy link
Member

Please, don't squash commits.
It is much easier to review new changes, rather than go over the whole set again.

Copy link
Member

@artembilan artembilan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The annotations in commit messages have to be back-ticked like code snippets to avoid GH user invitation.
You can use -s Git command option to add Singed-of-by automatically into each commit.

…ntation

Integration testing revealed the root cause of spring-projectsGH-4097. When Spring processes
a `@KafkaListener` with `contentTypeConverter` and `batch="true"`, the framework:

1. Calls `setBatchMessageConverter()` on the adapter
2. This internally calls `setMessageConverter()` which sets `converterSet=true`
3. Spring then tries to apply `contentTypeConverter` by calling `setMessagingConverter()`
4. The parent's validation `Assert.isTrue(!this.converterSet, ...)` blocks this

The unit test didn't catch this because it bypassed the adapter and Spring
framework integration entirely.

Changes:
- `BatchMessagingMessageListenerAdapter.setMessagingConverter()`: Override now
  directly applies `SmartMessageConverter` to batch converter (which propagates
  to record converter) without calling super, bypassing the validation that
  doesn't apply to the batch listener workflow

- `BatchSmartMessageConverterTests`: Replaced unit test with full integration
  test using `@SpringJUnitConfig`, `@EmbeddedKafka`, `ConcurrentKafkaListenerContainerFactory`,
  and `@KafkaListener` to verify the complete framework flow

- Added minimal `ByteArrayToStringConverter` (24 lines) for testing as no
  existing Spring Framework converter provides simple byte[] to String
  conversion needed for this test scenario

All tests pass and checkstyle validation successful.

Signed-off-by: Jujuwryy <georgemahfoud2@gmail.com>
@Jujuwryy
Copy link
Author

Jujuwryy commented Oct 24, 2025

Apologies for the force push - I needed to amend the commit to add the DCO sign-off and use backticks around annotations to avoid GitHub mentions. The latest commit does include a small change to the "setMessagingConverter" method in the BatchMessagingMessageListenerAdapter.java.

Thank you.

@Jujuwryy
Copy link
Author

Jujuwryy commented Oct 24, 2025

Hello @artembilan,

I tested the setMessagingConverter() implementation both with and without calling super.setMessagingConverter(messageConverter).

The Issue with Calling Super:
When calling super.setMessagingConverter(messageConverter) first, the test fails with:

"text
java.lang.IllegalArgumentException: Cannot set the SmartMessageConverter when setting the messageConverter, add the SmartConverter to the message converter instead
at MessagingMessageListenerAdapter.setMessagingConverter(MessagingMessageListenerAdapter.java:228)."

Root Cause:
This reproduces the exact GH-4097 bug. The problem occurs because:

setBatchMessageConverter() internally calls setMessageConverter(), which sets converterSet = true in the parent class

When Spring later processes @KafkaListener(contentTypeConverter = "...") and calls setMessagingConverter(), the parent's validation Assert.isTrue(!this.converterSet) blocks it

This creates a timing conflict specific to batch listeners due to their different initialization flow

Current Solution:
The current implementation (without calling super) passes all tests because:

We bypass the parent's restrictive validation

We directly propagate the converter to the batch converter

The batch converter then properly delegates to the record converter via its own setMessagingConverter() method

This approach respects the different initialization sequence required for batch listeners

Regarding the Test Converter:
The conversionHint parameter is unused in the test SmartMessageConverter because this is a minimal byte[] → String converter for testing purposes. I couldn't find an existing Spring converter for simple byte-to-string conversion without introducing Jackson/JSON dependencies, and similar minimal test converters exist in other test classes within the codebase.

I've also incorporated your previous feedback by:

Using pattern matching for instanceof where applicable.

The current implementation ensures that @KafkaListener(contentTypeConverter = "...") works consistently for both record and batch listeners without triggering the framework's validation conflicts.

Kindly let me know if i should re - commit the code.

- Use pattern matching for instanceof in setMessagingConverter() to avoid explicit casting
- Fix constructor parameter indentation to use tabs only (not mixed spaces)
- Address checkstyle violations per reviewer feedback

These changes improve code readability without affecting functionality.

Signed-off-by: Jujuwryy <georgemahfoud2@gmail.com>
- Fix toMessagingMessage() parameter indentation in BatchMessagingMessageListenerAdapter
- Fix toMessage() parameter indentation in BatchMessagingMessageConverter
- Use single tab indentation consistently per Spring code style

Signed-off-by: Jujuwryy <georgemahfoud2@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Conversion using a SmartMessageConverter does not work with batch listener

2 participants