diff --git a/apache-kafka-2/log4j.properties b/apache-kafka-2/log4j.properties deleted file mode 100644 index 2173c5d96f..0000000000 --- a/apache-kafka-2/log4j.properties +++ /dev/null @@ -1 +0,0 @@ -log4j.rootLogger=INFO, stdout diff --git a/apache-kafka-2/pom.xml b/apache-kafka-2/pom.xml index 067dedef8a..d1f74e8aae 100644 --- a/apache-kafka-2/pom.xml +++ b/apache-kafka-2/pom.xml @@ -23,11 +23,6 @@ slf4j-api ${org.slf4j.version} - - org.slf4j - slf4j-log4j12 - ${org.slf4j.version} - com.google.guava guava @@ -57,6 +52,11 @@ ${lombok.version} provided + + com.fasterxml.jackson.core + jackson-databind + ${jackson.databind.version} + @@ -64,6 +64,7 @@ 2.8.0 1.15.3 1.15.3 + 2.15.2 \ No newline at end of file diff --git a/apache-kafka-2/src/main/java/com/baeldung/kafka/message/ordering/Config.java b/apache-kafka-2/src/main/java/com/baeldung/kafka/message/ordering/Config.java new file mode 100644 index 0000000000..7fae8403b5 --- /dev/null +++ b/apache-kafka-2/src/main/java/com/baeldung/kafka/message/ordering/Config.java @@ -0,0 +1,11 @@ +package com.baeldung.kafka.message.ordering; + +public class Config { + public static final String CONSUMER_VALUE_DESERIALIZER_SERIALIZED_CLASS = "value.deserializer.serializedClass"; + public static final String MULTI_PARTITION_TOPIC = "multi_partition_topic"; + public static final String SINGLE_PARTITION_TOPIC = "single_partition_topic"; + + public static final int MULTIPLE_PARTITIONS = 5; + public static final int SINGLE_PARTITION = 1; + public static final short REPLICATION_FACTOR = 1; +} diff --git a/apache-kafka-2/src/main/java/com/baeldung/kafka/message/ordering/payload/UserEvent.java b/apache-kafka-2/src/main/java/com/baeldung/kafka/message/ordering/payload/UserEvent.java new file mode 100644 index 0000000000..99e0cc6c7e --- /dev/null +++ b/apache-kafka-2/src/main/java/com/baeldung/kafka/message/ordering/payload/UserEvent.java @@ -0,0 +1,61 @@ +package com.baeldung.kafka.message.ordering.payload; + +import java.util.Objects; + +public class UserEvent implements Comparable { + private String userEventId; + private long eventNanoTime; + private long globalSequenceNumber; + + @SuppressWarnings("unused") + public UserEvent() { + // Required for Jackson Serialization and Deserialization + } + + public UserEvent(String userEventId) { + this.userEventId = userEventId; + } + + public String getUserEventId() { + return userEventId; + } + + public long getEventNanoTime() { + return eventNanoTime; + } + + public void setEventNanoTime(long eventNanoTime) { + this.eventNanoTime = eventNanoTime; + } + + public long getGlobalSequenceNumber() { + return globalSequenceNumber; + } + + public void setGlobalSequenceNumber(long globalSequenceNumber) { + this.globalSequenceNumber = globalSequenceNumber; + } + + @Override + public int compareTo(UserEvent other) { + return Long.compare(this.globalSequenceNumber, other.globalSequenceNumber); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof UserEvent)) { + return false; + } + UserEvent userEvent = (UserEvent) obj; + return this.globalSequenceNumber == userEvent.globalSequenceNumber; + } + + @Override + public int hashCode() { + return Objects.hash(globalSequenceNumber); + } +} + diff --git a/apache-kafka-2/src/main/java/com/baeldung/kafka/message/ordering/serialization/JacksonDeserializer.java b/apache-kafka-2/src/main/java/com/baeldung/kafka/message/ordering/serialization/JacksonDeserializer.java new file mode 100644 index 0000000000..cf72ab12df --- /dev/null +++ b/apache-kafka-2/src/main/java/com/baeldung/kafka/message/ordering/serialization/JacksonDeserializer.java @@ -0,0 +1,34 @@ +package com.baeldung.kafka.message.ordering.serialization; + +import com.baeldung.kafka.message.ordering.Config; +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.apache.kafka.common.serialization.Deserializer; + +import java.util.Map; + +/** + * Configured via {@link org.apache.kafka.clients.consumer.ConsumerConfig#VALUE_DESERIALIZER_CLASS_CONFIG} + */ +public class JacksonDeserializer implements Deserializer { + private final ObjectMapper objectMapper = new ObjectMapper(); + private Class type; + + @Override + public void configure(Map configs, boolean isKey) { + this.type = (Class) configs.get(Config.CONSUMER_VALUE_DESERIALIZER_SERIALIZED_CLASS); + } + + @Override + public T deserialize(String topic, byte[] bytes) { + if (bytes == null) { + return null; + } + try { + return objectMapper.readValue(bytes, type); + } catch (Exception e) { + throw new RuntimeException("Error deserializing value", e); + } + } +} + diff --git a/apache-kafka-2/src/main/java/com/baeldung/kafka/message/ordering/serialization/JacksonSerializer.java b/apache-kafka-2/src/main/java/com/baeldung/kafka/message/ordering/serialization/JacksonSerializer.java new file mode 100644 index 0000000000..b2ace3b8ed --- /dev/null +++ b/apache-kafka-2/src/main/java/com/baeldung/kafka/message/ordering/serialization/JacksonSerializer.java @@ -0,0 +1,24 @@ +package com.baeldung.kafka.message.ordering.serialization; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.apache.kafka.common.serialization.Serializer; + +/** + * Configured via {@link org.apache.kafka.clients.producer.ProducerConfig#VALUE_SERIALIZER_CLASS_CONFIG} + */ +public class JacksonSerializer implements Serializer { + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public byte[] serialize(String topic, T data) { + if (data == null) { + return null; + } + try { + return objectMapper.writeValueAsBytes(data); + } catch (Exception e) { + throw new RuntimeException("Error serializing value", e); + } + } +} diff --git a/apache-kafka-2/src/test/java/com/baeldung/kafka/message/ordering/ExternalSequenceWithTimeWindowLiveTest.java b/apache-kafka-2/src/test/java/com/baeldung/kafka/message/ordering/ExternalSequenceWithTimeWindowLiveTest.java new file mode 100644 index 0000000000..f36c6ebd63 --- /dev/null +++ b/apache-kafka-2/src/test/java/com/baeldung/kafka/message/ordering/ExternalSequenceWithTimeWindowLiveTest.java @@ -0,0 +1,126 @@ +package com.baeldung.kafka.message.ordering; + +import com.baeldung.kafka.message.ordering.payload.UserEvent; +import com.baeldung.kafka.message.ordering.serialization.JacksonDeserializer; +import com.baeldung.kafka.message.ordering.serialization.JacksonSerializer; + +import org.apache.kafka.clients.admin.*; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.clients.producer.RecordMetadata; +import org.apache.kafka.common.serialization.LongDeserializer; +import org.apache.kafka.common.serialization.LongSerializer; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.KafkaContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + +import java.time.Duration; +import java.util.*; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import com.google.common.collect.ImmutableList; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +@Testcontainers +public class ExternalSequenceWithTimeWindowLiveTest { + + private static Admin admin; + private static KafkaProducer producer; + private static KafkaConsumer consumer; + private static final Duration TIMEOUT_WAIT_FOR_MESSAGES = Duration.ofSeconds(5); + private static final long BUFFER_PERIOD_NS = Duration.ofSeconds(5) + .toNanos(); + private static Logger logger = LoggerFactory.getLogger(ExternalSequenceWithTimeWindowLiveTest.class); + + @Container + private static final KafkaContainer KAFKA_CONTAINER = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:latest")); + + @BeforeAll + static void setup() throws ExecutionException, InterruptedException { + KAFKA_CONTAINER.addExposedPort(9092); + + Properties adminProperties = new Properties(); + adminProperties.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_CONTAINER.getBootstrapServers()); + + Properties producerProperties = new Properties(); + producerProperties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_CONTAINER.getBootstrapServers()); + producerProperties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, LongSerializer.class.getName()); + producerProperties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JacksonSerializer.class.getName()); + + Properties consumerProperties = new Properties(); + consumerProperties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_CONTAINER.getBootstrapServers()); + consumerProperties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, LongDeserializer.class.getName()); + consumerProperties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JacksonDeserializer.class.getName()); + consumerProperties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); + consumerProperties.put(Config.CONSUMER_VALUE_DESERIALIZER_SERIALIZED_CLASS, UserEvent.class); + consumerProperties.put(ConsumerConfig.GROUP_ID_CONFIG, "test-group"); + admin = Admin.create(adminProperties); + producer = new KafkaProducer<>(producerProperties); + consumer = new KafkaConsumer<>(consumerProperties); + admin.createTopics(ImmutableList.of(new NewTopic(Config.MULTI_PARTITION_TOPIC, Config.MULTIPLE_PARTITIONS, Config.REPLICATION_FACTOR))) + .all() + .get(); + } + + @AfterAll + static void destroy() { + KAFKA_CONTAINER.stop(); + } + + @Test + void givenMultiplePartitions_whenPublishedToKafkaAndConsumedWithExtSeqNumberAndTimeWindow_thenCheckForMessageOrder() throws ExecutionException, InterruptedException { + List sentUserEventList = new ArrayList<>(); + List receivedUserEventList = new ArrayList<>(); + for (long sequenceNumber = 1; sequenceNumber <= 10; sequenceNumber++) { + UserEvent userEvent = new UserEvent(UUID.randomUUID() + .toString()); + userEvent.setEventNanoTime(System.nanoTime()); + userEvent.setGlobalSequenceNumber(sequenceNumber); + Future future = producer.send(new ProducerRecord<>(Config.MULTI_PARTITION_TOPIC, sequenceNumber, userEvent)); + sentUserEventList.add(userEvent); + RecordMetadata metadata = future.get(); + logger.info("User Event ID: " + userEvent.getUserEventId() + ", Partition : " + metadata.partition()); + } + + consumer.subscribe(Collections.singletonList(Config.MULTI_PARTITION_TOPIC)); + List buffer = new ArrayList<>(); + long lastProcessedTime = System.nanoTime(); + ConsumerRecords records = consumer.poll(TIMEOUT_WAIT_FOR_MESSAGES); + records.forEach(record -> { + buffer.add(record.value()); + }); + while (!buffer.isEmpty()) { + if (System.nanoTime() - lastProcessedTime > BUFFER_PERIOD_NS) { + processBuffer(buffer, receivedUserEventList); + lastProcessedTime = System.nanoTime(); + } + records = consumer.poll(TIMEOUT_WAIT_FOR_MESSAGES); + records.forEach(record -> { + buffer.add(record.value()); + }); + } + assertThat(receivedUserEventList).isEqualTo(sentUserEventList) + .containsExactlyElementsOf(sentUserEventList); + } + + private static void processBuffer(List buffer, List receivedUserEventList) { + Collections.sort(buffer); + buffer.forEach(userEvent -> { + receivedUserEventList.add(userEvent); + logger.info("Processing message with Global Sequence number: " + userEvent.getGlobalSequenceNumber() + ", User Event Id: " + userEvent.getUserEventId()); + }); + buffer.clear(); + } +} diff --git a/apache-kafka-2/src/test/java/com/baeldung/kafka/message/ordering/MultiplePartitionLiveTest.java b/apache-kafka-2/src/test/java/com/baeldung/kafka/message/ordering/MultiplePartitionLiveTest.java new file mode 100644 index 0000000000..407b4d52a9 --- /dev/null +++ b/apache-kafka-2/src/test/java/com/baeldung/kafka/message/ordering/MultiplePartitionLiveTest.java @@ -0,0 +1,105 @@ +package com.baeldung.kafka.message.ordering; + +import com.baeldung.kafka.message.ordering.payload.UserEvent; +import com.baeldung.kafka.message.ordering.serialization.JacksonDeserializer; +import com.baeldung.kafka.message.ordering.serialization.JacksonSerializer; + +import org.apache.kafka.clients.admin.*; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.clients.producer.RecordMetadata; +import org.apache.kafka.common.serialization.LongDeserializer; +import org.apache.kafka.common.serialization.LongSerializer; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.KafkaContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + +import java.time.Duration; +import java.util.*; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import com.google.common.collect.ImmutableList; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +@Testcontainers +public class MultiplePartitionLiveTest { + + private static Admin admin; + private static KafkaProducer producer; + private static KafkaConsumer consumer; + private static final Duration TIMEOUT_WAIT_FOR_MESSAGES = Duration.ofSeconds(5); + + private static Logger logger = LoggerFactory.getLogger(MultiplePartitionLiveTest.class); + @Container + private static final KafkaContainer KAFKA_CONTAINER = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:latest")); + + @BeforeAll + static void setup() throws ExecutionException, InterruptedException { + KAFKA_CONTAINER.addExposedPort(9092); + + Properties adminProperties = new Properties(); + adminProperties.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_CONTAINER.getBootstrapServers()); + + Properties producerProperties = new Properties(); + producerProperties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_CONTAINER.getBootstrapServers()); + producerProperties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, LongSerializer.class.getName()); + producerProperties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JacksonSerializer.class.getName()); + + Properties consumerProperties = new Properties(); + consumerProperties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_CONTAINER.getBootstrapServers()); + consumerProperties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, LongDeserializer.class.getName()); + consumerProperties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JacksonDeserializer.class.getName()); + consumerProperties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); + consumerProperties.put(Config.CONSUMER_VALUE_DESERIALIZER_SERIALIZED_CLASS, UserEvent.class); + consumerProperties.put(ConsumerConfig.GROUP_ID_CONFIG, "test-group"); + admin = Admin.create(adminProperties); + producer = new KafkaProducer<>(producerProperties); + consumer = new KafkaConsumer<>(consumerProperties); + admin.createTopics(ImmutableList.of(new NewTopic(Config.MULTI_PARTITION_TOPIC, Config.MULTIPLE_PARTITIONS, Config.REPLICATION_FACTOR))) + .all() + .get(); + } + + @AfterAll + static void destroy() { + KAFKA_CONTAINER.stop(); + } + + @Test + void givenMultiplePartitions_whenPublishedToKafkaAndConsumed_thenCheckForMessageOrder() throws ExecutionException, InterruptedException { + List sentUserEventList = new ArrayList<>(); + List receivedUserEventList = new ArrayList<>(); + for (long sequenceNumber = 1; sequenceNumber <= 10; sequenceNumber++) { + UserEvent userEvent = new UserEvent(UUID.randomUUID() + .toString()); + userEvent.setGlobalSequenceNumber(sequenceNumber); + userEvent.setEventNanoTime(System.nanoTime()); + Future future = producer.send(new ProducerRecord<>(Config.MULTI_PARTITION_TOPIC, sequenceNumber, userEvent)); + sentUserEventList.add(userEvent); + RecordMetadata metadata = future.get(); + logger.info("User Event ID: " + userEvent.getUserEventId() + ", Partition : " + metadata.partition()); + } + + consumer.subscribe(Collections.singletonList(Config.MULTI_PARTITION_TOPIC)); + ConsumerRecords records = consumer.poll(TIMEOUT_WAIT_FOR_MESSAGES); + records.forEach(record -> { + UserEvent userEvent = record.value(); + receivedUserEventList.add(userEvent); + logger.info("User Event ID: " + userEvent.getUserEventId()); + }); + assertThat(receivedUserEventList).isNotEqualTo(sentUserEventList) + .containsExactlyInAnyOrderElementsOf(sentUserEventList); + } +} diff --git a/apache-kafka-2/src/test/java/com/baeldung/kafka/message/ordering/SinglePartitionLiveTest.java b/apache-kafka-2/src/test/java/com/baeldung/kafka/message/ordering/SinglePartitionLiveTest.java new file mode 100644 index 0000000000..9c6a15ebeb --- /dev/null +++ b/apache-kafka-2/src/test/java/com/baeldung/kafka/message/ordering/SinglePartitionLiveTest.java @@ -0,0 +1,109 @@ +package com.baeldung.kafka.message.ordering; + +import com.baeldung.kafka.message.ordering.payload.UserEvent; +import com.baeldung.kafka.message.ordering.serialization.JacksonDeserializer; +import com.baeldung.kafka.message.ordering.serialization.JacksonSerializer; + +import org.apache.kafka.clients.admin.Admin; +import org.apache.kafka.clients.admin.AdminClientConfig; +import org.apache.kafka.clients.admin.NewTopic; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.clients.producer.RecordMetadata; +import org.apache.kafka.common.serialization.LongDeserializer; +import org.apache.kafka.common.serialization.LongSerializer; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.KafkaContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + +import java.time.Duration; +import java.util.*; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import com.google.common.collect.ImmutableList; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +@Testcontainers +public class SinglePartitionLiveTest { + + private static Admin admin; + private static KafkaProducer producer; + private static KafkaConsumer consumer; + + private static final Duration TIMEOUT_WAIT_FOR_MESSAGES = Duration.ofSeconds(5); + + private static Logger logger = LoggerFactory.getLogger(SinglePartitionLiveTest.class); + @Container + private static final KafkaContainer KAFKA_CONTAINER = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:latest")); + + @BeforeAll + static void setup() throws ExecutionException, InterruptedException { + KAFKA_CONTAINER.addExposedPort(9092); + + Properties adminProperties = new Properties(); + adminProperties.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_CONTAINER.getBootstrapServers()); + + Properties producerProperties = new Properties(); + producerProperties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_CONTAINER.getBootstrapServers()); + producerProperties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, LongSerializer.class.getName()); + producerProperties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JacksonSerializer.class.getName()); + + Properties consumerProperties = new Properties(); + consumerProperties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_CONTAINER.getBootstrapServers()); + consumerProperties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, LongDeserializer.class.getName()); + consumerProperties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JacksonDeserializer.class.getName()); + consumerProperties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); + consumerProperties.put(Config.CONSUMER_VALUE_DESERIALIZER_SERIALIZED_CLASS, UserEvent.class); + consumerProperties.put(ConsumerConfig.GROUP_ID_CONFIG, "test-group"); + admin = Admin.create(adminProperties); + producer = new KafkaProducer<>(producerProperties); + consumer = new KafkaConsumer<>(consumerProperties); + admin.createTopics(ImmutableList.of(new NewTopic(Config.SINGLE_PARTITION_TOPIC, Config.SINGLE_PARTITION, Config.REPLICATION_FACTOR))) + .all() + .get(); + } + + @AfterAll + static void destroy() { + KAFKA_CONTAINER.stop(); + } + + @Test + void givenASinglePartition_whenPublishedToKafkaAndConsumed_thenCheckForMessageOrder() throws ExecutionException, InterruptedException { + List sentUserEventList = new ArrayList<>(); + List receivedUserEventList = new ArrayList<>(); + for (long sequenceNumber = 1; sequenceNumber <= 10; sequenceNumber++) { + UserEvent userEvent = new UserEvent(UUID.randomUUID() + .toString()); + userEvent.setGlobalSequenceNumber(sequenceNumber); + userEvent.setEventNanoTime(System.nanoTime()); + ProducerRecord producerRecord = new ProducerRecord<>(Config.SINGLE_PARTITION_TOPIC, userEvent); + Future future = producer.send(producerRecord); + sentUserEventList.add(userEvent); + RecordMetadata metadata = future.get(); + logger.info("User Event ID: " + userEvent.getUserEventId() + ", Partition : " + metadata.partition()); + } + + consumer.subscribe(Collections.singletonList(Config.SINGLE_PARTITION_TOPIC)); + ConsumerRecords records = consumer.poll(TIMEOUT_WAIT_FOR_MESSAGES); + records.forEach(record -> { + UserEvent userEvent = record.value(); + receivedUserEventList.add(userEvent); + logger.info("User Event ID: " + userEvent.getUserEventId()); + }); + assertThat(receivedUserEventList).isEqualTo(sentUserEventList) + .containsExactlyElementsOf(sentUserEventList); + } +} diff --git a/apache-kafka-2/src/test/resources/logback.xml b/apache-kafka-2/src/test/resources/logback.xml new file mode 100644 index 0000000000..6156c2188e --- /dev/null +++ b/apache-kafka-2/src/test/resources/logback.xml @@ -0,0 +1,11 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + \ No newline at end of file