0

I produce a Message (org.springframework.messaging) in application X. I've implemented a custom MessageConverter that implements SmartMessageConverter. The message is sent to a topic with custom headers and with a CLI consumer the custom headers are shown.

When using a Kstream in application Y (Java), the custom headers are not available.

@Bean
public Consumer<KStream<String, User>> streamUser() {
    return user -> log.info("Received: " + user);
}

I use the configuration:

spring:
  cloud:
    stream:
      bindings:
        streamUser-in-0:
          destination: user.0
          consumer:
            use-native-decoding: false

With this, i only receive these three headers in the log:

contentType=application/json
id=31f9bed4-5750-fcf8-f5bf-e135e262fc43
timestamp=1652795431177

My custom MessageConvertor is called, but fails because the custom headers are missing.

public Object fromMessage(@NonNull Message<?> message, @NonNull Class<?> targetClass{ ... }

When using a different consumer like:

@Bean
public Consumer<Message<User>> consumeUser() { ... }

I can see all the headers (custom_ and kafka_ ):

deliveryAttempt: 1
custom_header_name: Foo
custom_header_version: Bar
kafka_receivedTopic: user.0
kafka_timestampType: LOG_APPEND_TIME
kafka_offset: 2
kafka_consumer: org.apache.kafka.clients.consumer.KafkaConsumer@12178ee3
kafka_receivedPartitionId: 0
kafka_receivedTimestamp: 1652796124185
kafka_groupId: anonymous.cc23b653-b6d2-4e00-b8aa-a01f2e07bb34
scst_nativeHeadersPresent: true
id: 1200be22-bf11-adef-d14f-9262539d66cc
contentType: application/json
timestamp: 1652796124435

I have also tried setting the spring.cloud.stream.bindings.streamuser-in-0.consumer.header-mode but this didn't fix it.

Anyone idea's how to get the headers?

Edit (comment was too long)

The message conversion is used to encrypt the byte[] after serialisation and decrypt the byte[] before deserialisation. A simplified example of the SmartMessageConverter.

@Component
@RequiredArgsConstructor
public class CustomMessageConverter implements SmartMessageConverter {

private final ObjectMapper mapper;

// Decrypting
@Override
@SneakyThrows
public Object fromMessage(@NonNull Message<?> message, @NonNull Class<?> targetClass) {
    if (message.getHeaders().containsKey("custom_header_foo")) {
        byte[] bytes = decrypt(message.getPayload(), message.getHeaders());
        return mapper.readValue(bytes, targetClass);
    } else {
        throw new CustomException("Something failed");
    }
}

// Encrypting
@Override
@SneakyThrows
public Message<?> toMessage(@NonNull Object payload, MessageHeaders headers) {
    EncryptedPoJo ep = encrypt(payload);
    MessageHeaderAccessor accessor = new MessageHeaderAccessor();
    accessor.copyHeaders(headers);
    ep.getMetadata().forEach(accessor::setHeader);
    return MessageBuilder.createMessage(ep.getPayload(), accessor.getMessageHeaders()));
}

@Builder
@Getter
private class EncryptedPoJo {
    private byte[] payload;
    private HashMap<String, Object> metadata;
}

Sending the Message is done with a custom KafkaTemplate with the CustomMessageConverter set and adapted the method:

public ListenableFuture<SendResult<K, V>> send(Message<?> message) { ... }

When debugging, it seems that in the class:

class KStreamMapValues<KIn, VIn, VOut> implements ProcessorSupplier<KIn, VIn, KIn, VOut> {

  public void process(Record<KIn, VIn> record) {
      VOut newValue = KStreamMapValues.this.mapper.apply(record.key(), record.value());
      this.context().forward(record.withValue(newValue));
  }

}

The headers from the Record are unused when the GenericMessage (newValue) is created. Only record.key and record.value are processed.

debug information

Paulofski
  • 1
  • 4
  • Any particular reason you are using message conversion in your Kafka Streams processor? If you are using native Serde's, I think it properly transports all the headers. You can try that by not setting `use-native-decoding`. If you have a valid use case for Spring's message conversion, please provide us a minimal sample and we can triage it further. – sobychacko May 17 '22 at 21:04
  • In Kafka Streams binder for Spring Cloud Stream, using native Serde's is the default, but Spring's message conversion is used if you disable that. – sobychacko May 17 '22 at 21:04
  • Edited my post, was too long for comment. – Paulofski May 18 '22 at 08:02
  • This could be a bug. We need to verify. Could you create an issue in the Spring Cloud Stream repository and link to this SO thread? – sobychacko May 19 '22 at 21:31
  • I've created the issue. https://github.com/spring-cloud/spring-cloud-stream/issues/2411 – Paulofski Jun 03 '22 at 09:52
  • This issue is now addressed. Please look at the latest snapshots and try them on an application. – sobychacko Jun 09 '22 at 14:20

0 Answers0