|
19 | 19 | import static org.assertj.core.api.Assertions.assertThat;
|
20 | 20 |
|
21 | 21 | import java.time.Duration;
|
| 22 | +import java.util.Collections; |
22 | 23 | import java.util.HashMap;
|
23 | 24 | import java.util.Iterator;
|
24 | 25 | import java.util.Map;
|
25 | 26 | import java.util.Properties;
|
26 | 27 | import java.util.stream.Collectors;
|
27 | 28 |
|
28 | 29 | import org.apache.commons.logging.LogFactory;
|
| 30 | +import org.apache.kafka.clients.admin.AdminClient; |
| 31 | +import org.apache.kafka.clients.admin.AdminClientConfig; |
29 | 32 | import org.apache.kafka.clients.consumer.Consumer;
|
30 | 33 | import org.apache.kafka.clients.consumer.ConsumerConfig;
|
31 | 34 | import org.apache.kafka.clients.consumer.ConsumerRecord;
|
32 | 35 | import org.apache.kafka.clients.consumer.ConsumerRecords;
|
| 36 | +import org.apache.kafka.clients.consumer.KafkaConsumer; |
| 37 | +import org.apache.kafka.clients.consumer.OffsetAndMetadata; |
33 | 38 | import org.apache.kafka.clients.producer.ProducerConfig;
|
34 | 39 | import org.apache.kafka.common.TopicPartition;
|
35 | 40 | import org.apache.kafka.common.serialization.IntegerDeserializer;
|
|
40 | 45 | import org.springframework.beans.DirectFieldAccessor;
|
41 | 46 | import org.springframework.core.log.LogAccessor;
|
42 | 47 | import org.springframework.kafka.test.EmbeddedKafkaBroker;
|
| 48 | +import org.springframework.lang.Nullable; |
43 | 49 | import org.springframework.util.Assert;
|
44 | 50 |
|
45 | 51 | /**
|
@@ -159,6 +165,63 @@ public static <K, V> ConsumerRecord<K, V> getSingleRecord(Consumer<K, V> consume
|
159 | 165 | return received.records(topic).iterator().next();
|
160 | 166 | }
|
161 | 167 |
|
| 168 | + /** |
| 169 | + * Get a single record for the group from the topic/partition. Optionally, seeking to the current last record. |
| 170 | + * @param brokerAddresses the broker address(es). |
| 171 | + * @param group the group. |
| 172 | + * @param topic the topic. |
| 173 | + * @param partition the partition. |
| 174 | + * @param seekToLast true to fetch an existing last record, if present. |
| 175 | + * @param timeout the timeout. |
| 176 | + * @return the record or null if no record received. |
| 177 | + * @since 2.3 |
| 178 | + */ |
| 179 | + @Nullable |
| 180 | + @SuppressWarnings({ "rawtypes", "unchecked" }) |
| 181 | + public static ConsumerRecord<?, ?> getOneRecord(String brokerAddresses, String group, String topic, int partition, |
| 182 | + boolean seekToLast, boolean commit, long timeout) { |
| 183 | + |
| 184 | + Map<String, Object> consumerConfig = consumerProps(brokerAddresses, group, "false"); |
| 185 | + consumerConfig.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 1); |
| 186 | + consumerConfig.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); |
| 187 | + try (KafkaConsumer consumer = new KafkaConsumer(consumerConfig)) { |
| 188 | + TopicPartition topicPart = new TopicPartition(topic, partition); |
| 189 | + consumer.assign(Collections.singletonList(topicPart)); |
| 190 | + if (seekToLast) { |
| 191 | + consumer.seekToEnd(Collections.singletonList(topicPart)); |
| 192 | + if (consumer.position(topicPart) > 0) { |
| 193 | + consumer.seek(topicPart, consumer.position(topicPart) - 1); |
| 194 | + } |
| 195 | + } |
| 196 | + ConsumerRecords<?, ?> records = consumer.poll(Duration.ofMillis(timeout)); |
| 197 | + ConsumerRecord<?, ?> record = records.count() == 1 ? records.iterator().next() : null; |
| 198 | + if (record != null && commit) { |
| 199 | + consumer.commitSync(); |
| 200 | + } |
| 201 | + return record; |
| 202 | + } |
| 203 | + } |
| 204 | + |
| 205 | + /** |
| 206 | + * Get the current offset and metadata for the provided group/topic/partition. |
| 207 | + * @param brokerAddresses the broker address(es). |
| 208 | + * @param group the group. |
| 209 | + * @param topic the topic. |
| 210 | + * @param partition the partition. |
| 211 | + * @return the offset and metadata. |
| 212 | + * @throws Exception if an exception occurs. |
| 213 | + * @since 2.3 |
| 214 | + */ |
| 215 | + public static OffsetAndMetadata getCurrentOffset(String brokerAddresses, String group, String topic, int partition) |
| 216 | + throws Exception { |
| 217 | + |
| 218 | + try (AdminClient client = AdminClient |
| 219 | + .create(Collections.singletonMap(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, brokerAddresses))) { |
| 220 | + return client.listConsumerGroupOffsets(group).partitionsToOffsetAndMetadata().get() |
| 221 | + .get(new TopicPartition(topic, partition)); |
| 222 | + } |
| 223 | + } |
| 224 | + |
162 | 225 | /**
|
163 | 226 | * Poll the consumer for records.
|
164 | 227 | * @param consumer the consumer.
|
|
0 commit comments