/* * Copyright 2015 Confluent Inc. * * Licensed 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 io.confluent.kafkarest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Properties; import java.util.Vector; import javax.ws.rs.InternalServerErrorException; import io.confluent.kafkarest.entities.Partition; import io.confluent.kafkarest.entities.PartitionReplica; import io.confluent.kafkarest.entities.Topic; import io.confluent.rest.exceptions.RestNotFoundException; import kafka.admin.AdminUtils; import kafka.api.LeaderAndIsr; import kafka.cluster.Broker; import kafka.utils.ZkUtils; import scala.Option; import scala.collection.JavaConversions; import scala.collection.Map; import scala.collection.Seq; import scala.math.Ordering; /** * Observes metadata about the Kafka cluster. */ public class MetadataObserver { private static final Logger log = LoggerFactory.getLogger(MetadataObserver.class); private ZkUtils zkUtils; public MetadataObserver(KafkaRestConfig config, ZkUtils zkUtils) { this.zkUtils = zkUtils; } public List<Integer> getBrokerIds() { Seq<Broker> brokers = zkUtils.getAllBrokersInCluster(); List<Integer> brokerIds = new Vector<Integer>(brokers.size()); for (Broker broker : JavaConversions.asJavaCollection(brokers)) { brokerIds.add(broker.id()); } return brokerIds; } private Broker getBrokerById(final int brokerId) { Option<Broker> broker = zkUtils.getBrokerInfo(brokerId); if (broker.isDefined()) { return broker.get(); } else { throw Errors.leaderNotAvailableException(); } } public Broker getLeader(final String topicName, final int partitionId) { return getBrokerById(getLeaderId(topicName, partitionId)); } public Collection<String> getTopicNames() { Seq<String> topicNames = zkUtils.getAllTopics().sorted(Ordering.String$.MODULE$); return JavaConversions.asJavaCollection(topicNames); } public List<Topic> getTopics() { try { Seq<String> topicNames = zkUtils.getAllTopics().sorted(Ordering.String$.MODULE$); return getTopicsData(topicNames); } catch (RestNotFoundException e) { throw new InternalServerErrorException(e); } } public boolean topicExists(String topicName) { Collection<String> topicNames = getTopicNames(); for (String topic : topicNames) { if (topic.equals(topicName)) { return true; } } return false; } public Topic getTopic(String topicName) { List<Topic> topics = getTopicsData(JavaConversions.asScalaBuffer(Arrays.asList(topicName))); return (topics.isEmpty() ? null : topics.get(0)); } private List<Topic> getTopicsData(Seq<String> topicNames) { Map<String, Map<Object, Seq<Object>>> topicPartitions = zkUtils.getPartitionAssignmentForTopics(topicNames); List<Topic> topics = new Vector<Topic>(topicNames.size()); // Admin utils only supports getting either 1 or all topic configs. These per-topic overrides // shouldn't be common, so we just grab all of them to keep this simple Map<String, Properties> configs = AdminUtils.fetchAllTopicConfigs(zkUtils); for (String topicName : JavaConversions.asJavaCollection(topicNames)) { if (!topicPartitions.get(topicName).isEmpty()) { Map<Object, Seq<Object>> partitionMap = topicPartitions.get(topicName).get(); List<Partition> partitions = extractPartitionsFromZkData(partitionMap, topicName, null); if (partitions.size() == 0) { continue; } Option<Properties> topicConfigOpt = configs.get(topicName); Properties topicConfigs = topicConfigOpt.isEmpty() ? new Properties() : topicConfigOpt.get(); Topic topic = new Topic(topicName, topicConfigs, partitions); topics.add(topic); } } return topics; } public List<Partition> getTopicPartitions(String topic) { return getTopicPartitions(topic, null); } private List<Partition> getTopicPartitions(String topic, Integer partitionsFilter) { Map<String, Map<Object, Seq<Object>>> topicPartitions = zkUtils.getPartitionAssignmentForTopics( JavaConversions.asScalaBuffer(Arrays.asList(topic))); if (!topicPartitions.get(topic).isEmpty()) { Map<Object, Seq<Object>> parts = topicPartitions.get(topic).get(); return extractPartitionsFromZkData(parts, topic, partitionsFilter); } return null; } public Partition getTopicPartition(String topic, int partition) { List<Partition> partitions = getTopicPartitions(topic, partition); if (partitions.isEmpty()) { return null; } return partitions.get(0); } public boolean partitionExists(String topicName, int partition) { Topic topic = getTopic(topicName); return (partition >= 0 && partition < topic.getPartitions().size()); } public int getLeaderId(final String topicName, final int partitionId) { final List<Partition> partitions = getTopicPartitions(topicName); if (partitions.size() == 0) { throw Errors.topicNotFoundException(); } for (final Partition partition : partitions) { if (partition.getPartition() == partitionId) { return partition.getLeader(); } } throw Errors.partitionNotFoundException(); } private List<Partition> extractPartitionsFromZkData( Map<Object, Seq<Object>> parts, String topic, Integer partitionsFilter ) { List<Partition> partitions = new Vector<Partition>(); java.util.Map<Object, Seq<Object>> partsJava = JavaConversions.mapAsJavaMap(parts); for (java.util.Map.Entry<Object, Seq<Object>> part : partsJava.entrySet()) { int partId = (Integer) part.getKey(); if (partitionsFilter != null && partitionsFilter != partId) { continue; } Partition p = new Partition(); p.setPartition(partId); Option<LeaderAndIsr> leaderAndIsrOpt = zkUtils.getLeaderAndIsrForPartition(topic, partId); if (!leaderAndIsrOpt.isEmpty()) { LeaderAndIsr leaderAndIsr = leaderAndIsrOpt.get(); p.setLeader(leaderAndIsr.leader()); scala.collection.immutable.Set<Integer> isr = leaderAndIsr.isr().toSet(); List<PartitionReplica> partReplicas = new Vector<PartitionReplica>(); for (Object brokerObj : JavaConversions.asJavaCollection(part.getValue())) { int broker = (Integer) brokerObj; PartitionReplica r = new PartitionReplica(broker, (leaderAndIsr.leader() == broker), isr.contains(broker)); partReplicas.add(r); } p.setReplicas(partReplicas); partitions.add(p); } } return partitions; } public void shutdown() { log.debug("Shutting down MetadataObserver"); } }