I have created a rest endpoint to push message to kafka, the details as follows
Data or message payload, used for example
package com.learn.kafka.model;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.Data;
import java.util.Map;
#Data
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "type")
public class SpecialData {
Map<String, Object> messageInfo;
}
consumer service with kafka listener
package com.learn.kafka.service;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
#Component
#Slf4j
public class ConsumerService {
#KafkaListener(topics={"#{'${spring.kafka.topic}'}"},groupId="#{'${spring.kafka.consumer.group-id}'}")
public void consumeMessage(String message){
log.info("Consumed message - {}",message);
}
}
producer service, that contains kafka template
package com.learn.kafka.service;
import java.text.MessageFormat;
import com.learn.kafka.model.SpecialData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.kafka.support.KafkaHeaders;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
#Service
#Slf4j
public class ProducerService{
#Value("${spring.kafka.topic:demo-topic}")
String topicName;
#Autowired
KafkaTemplate<String,Object> kafkaTemplate;
public String sendMessage(SpecialData messageModel){
log.info("Sending message from producer - {}",messageModel);
Message message = constructMessage(messageModel);
kafkaTemplate.send(message);
return MessageFormat.format("Message Sent from Producer - {0}",message);
}
private Message constructMessage(SpecialData messageModel) {
return MessageBuilder.withPayload(messageModel)
.setHeader(KafkaHeaders.TOPIC,topicName)
.setHeader("reason","for-Local-validation")
.build();
}
}
sample controller to send same message
package com.learn.kafka.controller;
import com.learn.kafka.model.SpecialData;
import com.learn.kafka.service.ProducerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.Map;
#RestController
#RequestMapping("/api")
#Slf4j
public class MessageController {
#Autowired
private ProducerService producerService;
#GetMapping("/send")
public void sendMessage(){
SpecialData messageData = new SpecialData();
Map<String,Object> input = new HashMap<>();
input.put(null,"the key is null explicitly");
input.put("1","the key is one non-null");
messageData.setMessageInfo(input);
producerService.sendMessage(messageData);
}
}
custom serializer
package com.learn.kafka;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.fasterxml.jackson.databind.util.StdDateFormat;
import org.apache.kafka.common.errors.SerializationException;
import org.apache.kafka.common.serialization.Serializer;
import java.io.IOException;
import java.util.Map;
public class CustomSerializer implements Serializer<Object> {
private static final ObjectMapper MAPPER = new ObjectMapper();
static {
MAPPER.findAndRegisterModules();
MAPPER.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
MAPPER.setDateFormat(new StdDateFormat().withColonInTimeZone(true));
MAPPER.getSerializerProvider().setNullKeySerializer(new NullKeyKeySerializer());
}
#Override
public void configure(Map<String, ?> configs, boolean isKey) {
}
#Override
public byte[] serialize(String topic, Object data) {
try {
if (data == null){
System.out.println("Null received at serializing");
return null;
}
System.out.println("Serializing...");
return MAPPER.writeValueAsBytes(data);
} catch (Exception e) {
e.printStackTrace();
throw new SerializationException("Error when serializing MessageDto to byte[]");
}
}
#Override
public void close() {
}
static class NullKeyKeySerializer extends StdSerializer<Object> {
public NullKeyKeySerializer() {
this(null);
}
public NullKeyKeySerializer(Class<Object> t) {
super(t);
}
#Override
public void serialize(Object obj, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeFieldName("null");
}
}
}
application.yaml
spring:
kafka:
topic: input-topic
consumer:
bootstrap-servers: localhost:9092
group-id: input-group-id
auto-offset-reset: earliest
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
producer:
bootstrap-servers: localhost:9092
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: com.learn.kafka.CustomSerializer
properties:
spring.json.add.type.headers: false
Above code works. I was able to serialize the SpecialData with null key in map and send to broker and receive the message. The consumer uses the String Deserializer, so it printed is as expected. But I think there will be issue when using simply the JsonDeSerializer.
Is there different approaches like,
Extend the existing spring JsonSerializer, just to add the NullKeySerializer to the ObjectMapper?
Simple configuration in the application.yaml?
Reference for null key serializer implementation
You don't need to extend the deserializer, it already has a constructor that takes a custom ObjectMapper; simply create one in Java and add it to the consumer factory using setValueDeserializer(). (There are similar setters for serializers on the producer factory).
However, extending the class will allow you to configure it in the yaml, if that is what you prefer.
Adding the code details to #Gary Russell answer above.
In my scenario I am ok with only serialization part of it. No issues since I don't want to de-serialize the payload and use specific data.
package com.learn.kafka;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.fasterxml.jackson.databind.util.StdDateFormat;
import org.springframework.kafka.support.serializer.JsonSerializer;
import java.io.IOException;
public class CustomJsonSerializer extends JsonSerializer<Object> {
public CustomJsonSerializer() {
super(customizedObjectMapper());
}
private static ObjectMapper customizedObjectMapper() {
ObjectMapper objMapper= new ObjectMapper();
//if the pay load include timestamps we need to use modules
objMapper.findAndRegisterModules();
objMapper.getSerializerProvider().setNullKeySerializer(new NullKeySerializer());
return objMapper;
}
static class NullKeySerializer extends StdSerializer<Object> {
public NullKeySerializer() {
this(null);
}
public NullKeySerializer(Class<Object> t) {
super(t);
}
#Override
public void serialize(Object nullKey, JsonGenerator generator, SerializerProvider unused)
throws IOException {
generator.writeFieldName("null");
}
}
}
Use the class in application.yaml in value-serializer
spring:
kafka:
bootstrap-servers: domain:9092
producer:
value-serializer: com.learn.kafka.CustomJsonSerialier
Related
as explained in the title, I am using spring boot to send a message and receive it, apparently, it is working perfectly since I can see what I send in the console:
Sending a message:
sending
Receiving a message:
receiving
The problem here is that I can't see my message in RabbitMq interface and the queue is not even created :
RabbitMq Interface
This is my RabbitMqConfig
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class RabbitMqConfig {
#Value("MessageQueue")
private String qName;
#Value("exchange")
private String exchange;
#Value("routekey")
private String routingKey;
#Bean
Queue qu() {
return new Queue(qName, Boolean.FALSE);
}
#Bean
TopicExchange topicExchange() {
return new TopicExchange(exchange);
}
#Bean
Binding binding(final Queue q, final TopicExchange topicExchange) {
return BindingBuilder.bind(q).to(topicExchange).with(routingKey);
}
#Bean(name = "pimAmqpAdmin")
public AmqpAdmin pimAmqpAdmin( ConnectionFactory connectionFactory) {
return new RabbitAdmin(connectionFactory);
}
}
This is my sender
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class Sender {
private static final Logger LOGGER = LoggerFactory.getLogger(Sender.class);
#Autowired
RabbitTemplate rabbitTemplate;
#Autowired
Binding binding;
#GetMapping(value = "/send/{msg}")
#ResponseStatus(code = HttpStatus.OK)
public String send(#PathVariable("msg") final String message) {
LOGGER.info("Sending message to the queue.");
rabbitTemplate.convertAndSend(binding.getExchange(), binding.getRoutingKey(), message);
LOGGER.info("Message sent successfully to the queue!!!");
return "Great!! your message is sent";
}
}
and this is my Receiver
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class Receiver {
private static final Logger LOGGER = LoggerFactory.getLogger(Receiver.class);
#Autowired
Queue qu;
#RabbitListener(queues = "#{qu.getName()}")
public void getMsg(final String message) {
LOGGER.info("Getting messages.....");
LOGGER.info("Finally Receiver received the message and the message is..\n" + message);
}
}
Any help here would be much appreciated, thank you.
Are you using the default guest user? if yes, try to create a new user with a username and password and use it instead of a guest.
I have an spring boot kafka consumer application. So as per the documentation added factory.getContainerProperties().setDeliveryAttemptHeader(true); in below and still not able to receive the deliveryAttempt header in consumer record at the kafka listener. What am I missing? What changes do I need to get DeliveryAttempt Header at the consumer side?
#Bean
public ConcurrentKafkaListenerContainerFactory<Integer, String> kafkaListenerContainerFactory(
ConsumerFactory<Integer, String> consumerFactory) {
final ConcurrentKafkaListenerContainerFactory<Integer, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory);
factory.setCommonErrorHandler(testErrorHandler);
factory.setConcurrency(10);
factory.getContainerProperties().setDeliveryAttemptHeader(true);
return factory;
}
ErrorHandler:
import org.apache.commons.logging.LogFactory;
import org.apache.kafka.clients.consumer.Consumer;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.listener.CommonErrorHandler;
import org.springframework.kafka.listener.MessageListenerContainer;
import org.springframework.stereotype.Component;
#Component
public class TestErrorHandler implements CommonErrorHandler {
#Override
public void handleRecord(Exception thrownException, ConsumerRecord<?, ?> record, Consumer<?, ?> consumer,
MessageListenerContainer container) {
LogFactory.getLog(getClass()).error("'handleRecord' is not implemented by this handler", thrownException);
}
}
In the above class, I have implemented CommonErrorHander(which extends DeliveryAttemptAware class internally).
Consumer:
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
#Component
#Slf4j
public class DataConsumer {
#KafkaListener(topics = { "topic1" }, groupId = "test-group-1", containerFactory = "kafkaListenerContainerFactory")
public void listenTestEvents(final ConsumerRecord<Integer, String> message) {
log.info("Received TestEvent message :{}, headers: {}", message, message.headers());
if (true) {
throw new RuntimeException();
}
}
}
application.yml
spring:
kafka:
consumer:
bootstrap-servers: localhost:9092
group-id: test-group-1
auto-offset-reset: earliest
key-deserializer: org.apache.kafka.common.serialization.IntegerDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
AppConfig:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
import org.springframework.kafka.core.ConsumerFactory;
import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
import com.tmobile.retrykafkaconsume.kafka.TestErrorHandler;
#Configuration
public class AppConfig {
#Autowired
private TestErrorHandler testErrorHandler;
#Bean
public ConsumerFactory<Integer, String> consumerFactory(KafkaProperties kafkaProperties) {
return new DefaultKafkaConsumerFactory<Integer, String>(kafkaProperties.buildConsumerProperties());
}
#Bean
public ConcurrentKafkaListenerContainerFactory<Integer, String> kafkaListenerContainerFactory(
ConsumerFactory<Integer, String> consumerFactory) {
final ConcurrentKafkaListenerContainerFactory<Integer, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory);
factory.setCommonErrorHandler(testErrorHandler);
factory.setConcurrency(10);
factory.getContainerProperties().setDeliveryAttemptHeader(true);
return factory;
}
}
The error handler must implement DeliveryAttemptAware; see FailedRecordProcessor which is a super class of DefaultErrorHandler.
Show your testErrorHandler code.
I need to document my SpringBoot APIs and their possible exceptions with OpenAPI,
and I am using SpringDoc-OpenAPI https://springdoc.org/.
To handle the NotFound cases I created this exception class:
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ResponseStatusException;
public class NotFoundException extends ResponseStatusException {
public NotFoundException() {
super(HttpStatus.NOT_FOUND);
}
}
and this #RestControllerAdvice
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
#RestControllerAdvice
public class GlobalControllerExceptionHandler {
#ExceptionHandler(NotFoundException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
public ResponseEntity<String> handleNotFoundException(RuntimeException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
}
}
The problem I am facing is that the generated OpenAPI yaml file has
responses:
"404":
description: Not Found
content:
'*/*':
schema:
type: string
for all #RestController endpoints, instead of only for the methods with throws NotFoundException.
How can I limit the #ControllerAdvice (or the OpenAPI), to generate the 404 Response documentation only for methods with the throwing signature?
Do I need to use something else other than the #RestControllerAdvice?
I would like to avoid having to annotate every single method.
A possible solution is to:
Make the #RestControllerAdvice #Hidden
Provide an OperationCustomizer #Bean
import io.swagger.v3.oas.annotations.Hidden;
import it.eng.cysec.ot.risk.assessment.api.exceptions.NotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
#Hidden
#RestControllerAdvice
public class GlobalControllerExceptionHandler {
#ExceptionHandler(NotFoundException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
public ResponseEntity<String> handleNotFoundException(NotFoundException exception) {
return new ResponseEntity<>(exception.getMessage(), HttpStatus.NOT_FOUND);
}
}
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.media.Content;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.StringSchema;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.responses.ApiResponses;
import it.eng.cysec.ot.risk.assessment.api.exceptions.NotFoundException;
import org.springdoc.core.customizers.OperationCustomizer;
import org.springframework.web.method.HandlerMethod;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
public class OperationResponseCustomizer implements OperationCustomizer {
public static final ApiResponse NOT_FOUND_API_RESPONSE;
static {
MediaType mediaType = new MediaType();
mediaType.setSchema(new StringSchema());
Content content = new Content();
content.addMediaType("*/*", mediaType);
NOT_FOUND_API_RESPONSE = new ApiResponse()
.description("Not Found")
.content(content);
}
/**
* Customize operation.
*
* #param operation input operation
* #param handlerMethod original handler method
* #return customized operation
*/
#Override
public Operation customize(Operation operation, HandlerMethod handlerMethod) {
Method method = handlerMethod.getMethod();
List<Class<?>> exceptions = Arrays.asList(method.getExceptionTypes());
if(exceptions.contains(NotFoundException.class)){
ApiResponses apiResponses = operation.getResponses();
apiResponses.addApiResponse("404", NOT_FOUND_API_RESPONSE);
}
return operation;
}
}
For the following test I am always getting the error:
org.opentest4j.AssertionFailedError: expected: 10 but was : 0
What exactly I am trying to verify in scope of the test:
I am trying to send 10 messages to Kafka and after that immediately I am trying to read those messages from Kafka, but for some unknown reason KafkaConsumer returns 0 records, and I am struggling to understand why Consumer can't read messages that were sent earlier?
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.stream.IntStream;
#SpringBootTest
#ActiveProfiles("test")
#ContextConfiguration(initializers = TestKafkaContextInitializer.class)
#Slf4j
public class KafkaFlowVerificationITest {
#Autowired
private KafkaTemplate<String, String> kafkaTemplate;
#Autowired
private KafkaProperties kafkaProperties;
private final String kafkaTopic = "test.topic";
#Test
void testKafkaFlow() {
IntStream.range(0, 10)
.forEach(e -> {
try {
kafkaTemplate.send(kafkaTopic, UUID.randomUUID().toString()).get();
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
}
});
checkKafkaForMessage();
}
private void checkKafkaForMessage() {
Map<String, Object> properties = new HashMap<>();
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getBootstrapServers());
properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, org.apache.kafka.common.serialization.StringDeserializer.class);
properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, org.apache.kafka.common.serialization.StringDeserializer.class);
properties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
properties.put(ConsumerConfig.GROUP_ID_CONFIG, "acme");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(properties);
consumer.subscribe(List.of(kafkaTopic));
ConsumerRecords<String, String> records = consumer.poll(Duration.ZERO);
Assertions.assertThat(records.count()).isEqualTo(10);
}
}
and TestKafkaContextInitializer:
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.testcontainers.containers.KafkaContainer;
import org.testcontainers.utility.DockerImageName;
#Slf4j
public class TestKafkaContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private final KafkaContainer kafkaContainer =
new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:5.4.3"));
#Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
kafkaContainer.start();
var values = TestPropertyValues.of(
"spring.kafka.producer.bootstrap-servers=" + kafkaContainer.getBootstrapServers(),
"spring.kafka.consumer.bootstrap-servers=" + kafkaContainer.getBootstrapServers()
);
values.applyTo(configurableApplicationContext);
}
}
The root cause of the issue is:
I set TestContainer Kafka bootstrap server url for the following properties:
spring.kafka.producer.bootstrap-servers
spring.kafka.consumer.bootstrap-servers
but in the test I use:
spring.kafka.bootstrap-servers property, that why Consumer made attempt connect to localhost:9092 default URL instead of URL provided by TestContainer.
I am trying to run a test case with Kafka API with below test case code -
Taken from https://www.codenotfound.com/spring-kafka-consumer-producer-example.html
//SpringKafkaApplicationTest .java
import static org.assertj.core.api.Assertions.assertThat;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.kafka.config.KafkaListenerContainerFactory;
import org.springframework.kafka.config.KafkaListenerEndpoint;
import org.springframework.kafka.config.KafkaListenerEndpointRegistry;
import org.springframework.kafka.listener.MessageListenerContainer;
import org.springframework.kafka.test.rule.KafkaEmbedded;
import org.springframework.kafka.test.utils.ContainerTestUtils;
import org.springframework.test.context.junit4.SpringRunner;
//#TestExecutionListeners( { DependencyInjectionTestExecutionListener.class })
#RunWith(SpringRunner.class)
#SpringBootTest(classes= {SpringKafkaIntegrationApplication.class },webEnvironment = WebEnvironment.RANDOM_PORT)
public class SpringKafkaApplicationTest {
private static String BOOT_TOPIC = "boot.t";
#Autowired
private Sender sender;
#Autowired
private Receiver receiver;
#Autowired
private KafkaListenerEndpointRegistry kafkaListenerEndpointRegistry;
#ClassRule
public static KafkaEmbedded embeddedKafka = new KafkaEmbedded(1, true, BOOT_TOPIC);
#Before
public void setUp() throws Exception {
// wait until the partitions are assigned
// kafkaListenerEndpointRegistry.registerListenerContainer(endpoint, factory);
for (MessageListenerContainer messageListenerContainer : kafkaListenerEndpointRegistry
.getListenerContainers()) {
**Below line gives issue**
ContainerTestUtils.waitForAssignment(messageListenerContainer,
embeddedKafka.getPartitionsPerTopic());
}
}
#Test
public void testReceive() throws Exception {
sender.send(BOOT_TOPIC, "Hello");
System.out.println("check");
receiver.getLatch().await(10000, TimeUnit.MILLISECONDS);
// receiver.receive(BOOT_TOPIC);
assertThat(receiver.getLatch().getCount()).isEqualTo(0);
}
}
I have defined the topics inside the application.yml as below-
spring:
kafka:
consumer:
auto-offset-reset: earliest
group-id: boot
kafka:
topic:
boot: boot.t
Inside ContainerTestUtils class inside waitForAssignment method with below call returns assignedPartitions = null
int count = 0;
....
Collection<?> assignedPartitions = (Collection<?>) getAssignedPartitions.invoke(aContainer);
if (assignedPartitions != null) {
count += assignedPartitions.size();
}
...
So the count above remains 0. But I get the assertion failure as below:
org.junit.ComparisonFailure: expected:<[2]> but was:<[0]>