/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.kafka.common.requests; import org.apache.kafka.common.TopicPartition; import org.apache.kafka.common.protocol.ApiKeys; import org.apache.kafka.common.protocol.Errors; import org.apache.kafka.common.protocol.types.Struct; import org.apache.kafka.common.utils.CollectionUtils; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class OffsetsForLeaderEpochRequest extends AbstractRequest { public static final String TOPICS = "topics"; public static final String TOPIC = "topic"; public static final String PARTITIONS = "partitions"; public static final String PARTITION_ID = "partition_id"; public static final String LEADER_EPOCH = "leader_epoch"; private Map<TopicPartition, Integer> epochsByPartition; public Map<TopicPartition, Integer> epochsByTopicPartition() { return epochsByPartition; } public static class Builder extends AbstractRequest.Builder<OffsetsForLeaderEpochRequest> { private Map<TopicPartition, Integer> epochsByPartition = new HashMap(); public Builder() { super(ApiKeys.OFFSET_FOR_LEADER_EPOCH); } public Builder(Map<TopicPartition, Integer> epochsByPartition) { super(ApiKeys.OFFSET_FOR_LEADER_EPOCH); this.epochsByPartition = epochsByPartition; } public Builder add(TopicPartition topicPartition, Integer epoch) { epochsByPartition.put(topicPartition, epoch); return this; } @Override public OffsetsForLeaderEpochRequest build(short version) { return new OffsetsForLeaderEpochRequest(epochsByPartition, version); } public static OffsetsForLeaderEpochRequest parse(ByteBuffer buffer, short version) { return new OffsetsForLeaderEpochRequest(ApiKeys.OFFSET_FOR_LEADER_EPOCH.parseRequest(version, buffer), version); } @Override public String toString() { StringBuilder bld = new StringBuilder(); bld.append("(type=OffsetForLeaderEpochRequest, "). append("epochsByTopic=").append(epochsByPartition). append(")"); return bld.toString(); } } public OffsetsForLeaderEpochRequest(Map<TopicPartition, Integer> epochsByPartition, short version) { super(version); this.epochsByPartition = epochsByPartition; } public OffsetsForLeaderEpochRequest(Struct struct, short version) { super(version); epochsByPartition = new HashMap<>(); for (Object topicAndEpochsObj : struct.getArray(TOPICS)) { Struct topicAndEpochs = (Struct) topicAndEpochsObj; String topic = topicAndEpochs.getString(TOPIC); for (Object partitionAndEpochObj : topicAndEpochs.getArray(PARTITIONS)) { Struct partitionAndEpoch = (Struct) partitionAndEpochObj; int partitionId = partitionAndEpoch.getInt(PARTITION_ID); int epoch = partitionAndEpoch.getInt(LEADER_EPOCH); TopicPartition tp = new TopicPartition(topic, partitionId); epochsByPartition.put(tp, epoch); } } } public static OffsetsForLeaderEpochRequest parse(ByteBuffer buffer, short versionId) { return new OffsetsForLeaderEpochRequest(ApiKeys.OFFSET_FOR_LEADER_EPOCH.parseRequest(versionId, buffer), versionId); } @Override protected Struct toStruct() { Struct requestStruct = new Struct(ApiKeys.OFFSET_FOR_LEADER_EPOCH.requestSchema(version())); Map<String, Map<Integer, Integer>> topicsToPartitionEpochs = CollectionUtils.groupDataByTopic(epochsByPartition); List<Struct> topics = new ArrayList<>(); for (Map.Entry<String, Map<Integer, Integer>> topicToEpochs : topicsToPartitionEpochs.entrySet()) { Struct topicsStruct = requestStruct.instance(TOPICS); topicsStruct.set(TOPIC, topicToEpochs.getKey()); List<Struct> partitions = new ArrayList<>(); for (Map.Entry<Integer, Integer> partitionEpoch : topicToEpochs.getValue().entrySet()) { Struct partitionStruct = topicsStruct.instance(PARTITIONS); partitionStruct.set(PARTITION_ID, partitionEpoch.getKey()); partitionStruct.set(LEADER_EPOCH, partitionEpoch.getValue()); partitions.add(partitionStruct); } topicsStruct.set(PARTITIONS, partitions.toArray()); topics.add(topicsStruct); } requestStruct.set(TOPICS, topics.toArray()); return requestStruct; } @Override public AbstractResponse getErrorResponse(int throttleTimeMs, Throwable e) { Errors error = Errors.forException(e); Map<TopicPartition, EpochEndOffset> errorResponse = new HashMap(); for (TopicPartition tp : epochsByPartition.keySet()) { errorResponse.put(tp, new EpochEndOffset(error, EpochEndOffset.UNDEFINED_EPOCH_OFFSET)); } return new OffsetsForLeaderEpochResponse(errorResponse); } }