io.streamthoughts.jikkou.kafka.internals.KafkaRecord Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jikkou-provider-kafka Show documentation
Show all versions of jikkou-provider-kafka Show documentation
Integration between Apache Kafka and Jikkou
The newest version!
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright (c) The original authors
*
* Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package io.streamthoughts.jikkou.kafka.internals;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.function.Function;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.header.Headers;
import org.apache.kafka.common.header.internals.RecordHeaders;
import org.apache.kafka.common.record.TimestampType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Generic Kafka record to be used in place of {@link ProducerRecord} and {@link ConsumerRecord}/
*
* @param the key type.
* @param the value type.
*/
public interface KafkaRecord {
/**
* @return The topic this record should be sent to or is received from.
*/
String topic();
/**
* @return The key (or {@code null} if no key is specified).
*/
K key();
/**
* @return The value (or {@code null} if no key is specified).
*/
V value();
/**
* @return The timestamp of the record, in milliseconds since epoch (or {@code -1} if no
* timestamp is specified).
*/
Long timestamp();
/**
* @return The type of returned {@link #timestamp()} (or {@code null} if timestamp-type is
* unknown).
*/
TimestampType timestampType();
/**
* @return The partition to which the record should be sent or is received from (or {@code null}
* if partition is unknown).
*/
Integer partition();
/**
* @return The offset of this record in the corresponding Kafka partition
*/
Long offset();
/**
* @return The headers included in the record.
*/
Headers headers();
/**
* @return The corresponding {@link ProducerRecord} for this record.
*/
default ProducerRecord toProducerRecord() {
return new ProducerRecord<>(
topic(),
partition(),
timestamp(),
key(),
value(),
headers()
);
}
/**
* @return a builder from this record *
*/
default Builder toBuilder() {
return new Builder<>(this);
}
/**
* @return an empty kafka record *
*/
static KafkaRecord empty() {
return new ClientKafkaRecord<>();
}
default KafkaRecord mapKey(final Function fn) {
return KafkaRecord.builder()
.key(fn.apply(key()))
.value(value())
.headers(headers())
.topic(topic())
.timestamp(timestamp())
.offset(offset())
.partition(partition())
.timestampType(timestampType())
.build();
}
default KafkaRecord mapValue(final Function fn) {
return KafkaRecord.builder()
.key(key())
.value(fn.apply(value()))
.headers(headers())
.topic(topic())
.timestamp(timestamp())
.offset(offset())
.partition(partition())
.timestampType(timestampType())
.build();
}
/**
* Creates a new {@link KafkaRecord} from the specified {@link ProducerRecord}.
*
* @param record the {@link ProducerRecord} from which to create the new {@link KafkaRecord}.
* @param the record-key type.
* @param the record-value type.
* @return a new {@link ProducerRecord}.
*/
static KafkaRecord of(final ProducerRecord record) {
return new ClientKafkaRecord<>(
record.topic(),
record.partition(),
record.timestamp(),
record.key(),
record.value(),
new RecordHeaders(record.headers()));
}
/**
* Creates a new {@link KafkaRecord} from the specified {@link ConsumerRecord}.
*
* @param record the {@link ConsumerRecord} from which to create the new {@link KafkaRecord}.
* @param the record-key type.
* @param the record-value type.
* @return a new {@link ConsumerRecord}.
*/
static KafkaRecord of(final ConsumerRecord record) {
return new ClientKafkaRecord<>(
record.topic(),
record.partition(),
record.offset(),
record.timestamp(),
record.timestampType(),
record.key(),
record.value(),
new RecordHeaders(record.headers()));
}
static Builder builder() {
return new Builder<>();
}
class Builder {
private KafkaRecord internal;
public Builder() {
this(new ClientKafkaRecord<>());
}
public Builder(final KafkaRecord base) {
this.internal = base;
}
public Builder topic(@NotNull final String topic) {
this.internal =
new ClientKafkaRecord<>(
topic,
internal.partition(),
internal.offset(),
internal.timestamp(),
internal.timestampType(),
internal.key(),
internal.value(),
internal.headers());
return this;
}
public Builder key(@Nullable final K key) {
this.internal =
new ClientKafkaRecord<>(
internal.topic(),
internal.partition(),
internal.offset(),
internal.timestamp(),
internal.timestampType(),
key,
internal.value(),
internal.headers());
return this;
}
public Builder value(@Nullable final V value) {
this.internal =
new ClientKafkaRecord<>(
internal.topic(),
internal.partition(),
internal.offset(),
internal.timestamp(),
internal.timestampType(),
internal.key(),
value,
internal.headers());
return this;
}
public Builder partition(final Integer partition) {
this.internal =
new ClientKafkaRecord<>(
internal.topic(),
partition,
internal.offset(),
internal.timestamp(),
internal.timestampType(),
internal.key(),
internal.value(),
internal.headers());
return this;
}
public Builder offset(final Long offset) {
this.internal =
new ClientKafkaRecord<>(
internal.topic(),
internal.partition(),
offset,
internal.timestamp(),
internal.timestampType(),
internal.key(),
internal.value(),
internal.headers());
return this;
}
public Builder timestamp(final Long timestamp) {
this.internal =
new ClientKafkaRecord<>(
internal.topic(),
internal.partition(),
internal.offset(),
timestamp,
internal.timestampType(),
internal.key(),
internal.value(),
internal.headers());
return this;
}
public Builder timestampType(final TimestampType timestampType) {
this.internal =
new ClientKafkaRecord<>(
internal.topic(),
internal.partition(),
internal.offset(),
internal.timestamp(),
timestampType,
internal.key(),
internal.value(),
internal.headers());
return this;
}
public Builder header(final String key, final String value) {
return header(key, value == null ? null : value.getBytes(StandardCharsets.UTF_8));
}
public Builder header(final String key, final byte[] value) {
RecordHeaders headers = new RecordHeaders(internal.headers());
headers.add(key, value);
this.internal =
new ClientKafkaRecord<>(
internal.topic(),
internal.partition(),
internal.offset(),
internal.timestamp(),
internal.timestampType(),
internal.key(),
internal.value(),
headers);
return this;
}
public Builder headers(final Headers headers) {
this.internal =
new ClientKafkaRecord<>(
internal.topic(),
internal.partition(),
internal.offset(),
internal.timestamp(),
internal.timestampType(),
internal.key(),
internal.value(),
headers);
return this;
}
public KafkaRecord build() {
return internal;
}
}
/**
* Represents a {@link KafkaRecord}.
*/
final class ClientKafkaRecord implements KafkaRecord {
public static final Long NO_TIMESTAMP = null;
public static final TimestampType NO_TIMESTAMP_TYPE = null;
public static final long NO_OFFSET = -1L;
private final String topic;
private final Integer partition;
private final Long offset;
private final Long timestamp;
private final TimestampType timestampType;
private final K key;
private final V value;
private final Headers headers;
/**
* Creates a new {@link ClientKafkaRecord} instance.
*
* @param topic The topic this record should be sent to or is received from.
* @param partition The partition to which the record should be sent or is received from (or
* {@code null} if partition is unknown).
* @param offset The offset of this record in the corresponding Kafka partition.
* @param timestamp The timestamp of the record, in milliseconds since epoch (or {@code -1}
* if no timestamp is specified).
* @param timestampType The type of returned {@link #timestamp()} (or {@code null} if
* timestamp-type is unknown).
* @param key The key (or {@code null} if no key is specified).
* @param value The value (or {@code null} if no value is specified).
* @param headers The headers included in the record.
*/
public ClientKafkaRecord(
String topic,
Integer partition,
Long offset,
Long timestamp,
TimestampType timestampType,
K key,
V value,
Headers headers) {
this.topic = topic;
this.partition = partition;
this.offset = offset;
this.timestamp = timestamp;
this.timestampType = timestampType;
this.key = key;
this.value = value;
this.headers = headers;
}
private ClientKafkaRecord() {
this(null, null, null, null);
}
public ClientKafkaRecord(
@Nullable final String topic, @Nullable final K key, @Nullable final V value) {
this(
topic,
null,
NO_OFFSET,
NO_TIMESTAMP,
NO_TIMESTAMP_TYPE,
key,
value,
new RecordHeaders());
}
public ClientKafkaRecord(
@Nullable final String topic,
@Nullable final Integer partition,
@Nullable final K key,
@Nullable final V value) {
this(
topic,
partition,
NO_OFFSET,
NO_TIMESTAMP,
NO_TIMESTAMP_TYPE,
key,
value,
new RecordHeaders());
}
public ClientKafkaRecord(
@Nullable final String topic,
@Nullable final Integer partition,
@Nullable final K key,
@Nullable final V value,
@Nullable final Headers headers) {
this(topic, partition, NO_OFFSET, NO_TIMESTAMP, NO_TIMESTAMP_TYPE, key, value, headers);
}
public ClientKafkaRecord(
@Nullable final String topic,
@Nullable final Integer partition,
@Nullable final Long timestamp,
@Nullable final K key,
@Nullable final V value,
@Nullable final Headers headers) {
this(topic, partition, NO_OFFSET, timestamp, NO_TIMESTAMP_TYPE, key, value, headers);
}
public String topic() {
return topic;
}
public Integer partition() {
return partition;
}
public Long offset() {
return offset;
}
public Long timestamp() {
return timestamp;
}
public TimestampType timestampType() {
return timestampType;
}
public K key() {
return key;
}
public V value() {
return value;
}
public Headers headers() {
return headers;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (ClientKafkaRecord) obj;
return Objects.equals(this.topic, that.topic)
&& Objects.equals(this.partition, that.partition)
&& Objects.equals(this.offset, that.offset)
&& Objects.equals(this.timestamp, that.timestamp)
&& Objects.equals(this.timestampType, that.timestampType)
&& Objects.equals(this.key, that.key)
&& Objects.equals(this.value, that.value)
&& Objects.equals(this.headers, that.headers);
}
@Override
public int hashCode() {
return Objects.hash(
topic, partition, offset, timestamp, timestampType, key, value, headers);
}
@Override
public String toString() {
return "ClientKafkaRecord["
+ "topic="
+ topic
+ ", "
+ "partition="
+ partition
+ ", "
+ "offset="
+ offset
+ ", "
+ "timestamp="
+ timestamp
+ ", "
+ "timestampType="
+ timestampType
+ ", "
+ "key="
+ key
+ ", "
+ "value="
+ value
+ ", "
+ "headers="
+ headers
+ ']';
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy