/* * 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 gobblin.kafka; import java.io.Closeable; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.atomic.AtomicInteger; import org.I0Itec.zkclient.ZkClient; import com.google.common.collect.ImmutableMap; import kafka.admin.AdminUtils; import kafka.consumer.Consumer; import kafka.consumer.ConsumerConfig; import kafka.consumer.ConsumerIterator; import kafka.consumer.KafkaStream; import kafka.javaapi.consumer.ConsumerConnector; import kafka.server.KafkaConfig; import kafka.server.KafkaServer; import kafka.utils.MockTime; import kafka.utils.Time; import kafka.utils.ZKStringSerializer$; import kafka.utils.ZkUtils; import kafka.zk.EmbeddedZookeeper; import lombok.extern.slf4j.Slf4j; import gobblin.test.TestUtils; /** * A private class for starting a suite of servers for Kafka * Calls to start and shutdown are reference counted, so that the suite is started and shutdown in pairs. * A suite of servers (Zk, Kafka etc) will be started just once per process */ @Slf4j class KafkaServerSuite { static KafkaServerSuite _instance; static KafkaServerSuite getInstance() { if (null == _instance) { _instance = new KafkaServerSuite(); return _instance; } else { return _instance; } } private int _brokerId = 0; private EmbeddedZookeeper _zkServer; private ZkClient _zkClient; private KafkaServer _kafkaServer; private final int _kafkaServerPort; private String _zkConnectString; private final AtomicInteger _numStarted; public ZkClient getZkClient() { return _zkClient; } public KafkaServer getKafkaServer() { return _kafkaServer; } public int getKafkaServerPort() { return _kafkaServerPort; } public String getZkConnectString() { return _zkConnectString; } private KafkaServerSuite() { _kafkaServerPort = TestUtils.findFreePort(); _zkConnectString = "UNINITIALIZED_HOST_PORT"; _numStarted = new AtomicInteger(0); } void start() throws RuntimeException { if (_numStarted.incrementAndGet() == 1) { log.warn("Starting up Kafka server suite. Zk at " + _zkConnectString + "; Kafka server at " + _kafkaServerPort); _zkServer = new EmbeddedZookeeper(); _zkConnectString = "127.0.0.1:"+_zkServer.port(); _zkClient = new ZkClient(_zkConnectString, 30000, 30000, ZKStringSerializer$.MODULE$); Properties props = kafka.utils.TestUtils.createBrokerConfig( _brokerId, _zkConnectString, kafka.utils.TestUtils.createBrokerConfig$default$3(), kafka.utils.TestUtils.createBrokerConfig$default$4(), _kafkaServerPort, kafka.utils.TestUtils.createBrokerConfig$default$6(), kafka.utils.TestUtils.createBrokerConfig$default$7(), kafka.utils.TestUtils.createBrokerConfig$default$8(), kafka.utils.TestUtils.createBrokerConfig$default$9(), kafka.utils.TestUtils.createBrokerConfig$default$10(), kafka.utils.TestUtils.createBrokerConfig$default$11(), kafka.utils.TestUtils.createBrokerConfig$default$12(), kafka.utils.TestUtils.createBrokerConfig$default$13(), kafka.utils.TestUtils.createBrokerConfig$default$14() ); KafkaConfig config = new KafkaConfig(props); Time mock = new MockTime(); _kafkaServer = kafka.utils.TestUtils.createServer(config, mock); } else { log.info("Kafka server suite already started... continuing"); } } void shutdown() { if (_numStarted.decrementAndGet() == 0) { log.info("Shutting down Kafka server suite"); _kafkaServer.shutdown(); _zkClient.close(); _zkServer.shutdown(); } else { log.info("Kafka server suite still in use ... not shutting down yet"); } } } class KafkaConsumerSuite { private final ConsumerConnector _consumer; private final KafkaStream<byte[], byte[]> _stream; private final ConsumerIterator<byte[], byte[]> _iterator; private final String _topic; KafkaConsumerSuite(String zkConnectString, String topic) { _topic = topic; Properties consumeProps = new Properties(); consumeProps.put("zookeeper.connect", zkConnectString); consumeProps.put("group.id", _topic+"-"+System.nanoTime()); consumeProps.put("zookeeper.session.timeout.ms", "10000"); consumeProps.put("zookeeper.sync.time.ms", "10000"); consumeProps.put("auto.commit.interval.ms", "10000"); consumeProps.put("_consumer.timeout.ms", "10000"); _consumer = Consumer.createJavaConsumerConnector(new ConsumerConfig(consumeProps)); Map<String, List<KafkaStream<byte[], byte[]>>> consumerMap = _consumer.createMessageStreams(ImmutableMap.of(this._topic, 1)); List<KafkaStream<byte[], byte[]>> streams = consumerMap.get(this._topic); _stream = streams.get(0); _iterator = _stream.iterator(); } void shutdown() { _consumer.shutdown(); } public ConsumerIterator<byte[],byte[]> getIterator() { return _iterator; } } /** * A Helper class for testing against Kafka * A suite of servers (Zk, Kafka etc) will be started just once per process * Consumer and iterator will be created per instantiation and is one instance per topic. */ public class KafkaTestBase implements Closeable { private final KafkaServerSuite _kafkaServerSuite; private final Map<String, KafkaConsumerSuite> _topicConsumerMap; public KafkaTestBase() throws InterruptedException, RuntimeException { this._kafkaServerSuite = KafkaServerSuite.getInstance(); this._topicConsumerMap = new HashMap<>(); } public synchronized void startServers() { _kafkaServerSuite.start(); } public void stopServers() { _kafkaServerSuite.shutdown(); } public void start() { startServers(); } public void stopClients() throws IOException { for (Map.Entry<String, KafkaConsumerSuite> consumerSuiteEntry: _topicConsumerMap.entrySet()) { consumerSuiteEntry.getValue().shutdown(); AdminUtils.deleteTopic(ZkUtils.apply(_kafkaServerSuite.getZkClient(), false), consumerSuiteEntry.getKey()); } } @Override public void close() throws IOException { stopClients(); stopServers(); } public void provisionTopic(String topic) { if (_topicConsumerMap.containsKey(topic)) { // nothing to do: return } else { // provision topic AdminUtils.createTopic(ZkUtils.apply(_kafkaServerSuite.getZkClient(), false), topic, 1, 1, new Properties()); List<KafkaServer> servers = new ArrayList<>(); servers.add(_kafkaServerSuite.getKafkaServer()); kafka.utils.TestUtils.waitUntilMetadataIsPropagated(scala.collection.JavaConversions.asScalaBuffer(servers), topic, 0, 5000); KafkaConsumerSuite consumerSuite = new KafkaConsumerSuite(_kafkaServerSuite.getZkConnectString(), topic); _topicConsumerMap.put(topic, consumerSuite); } } public ConsumerIterator<byte[], byte[]> getIteratorForTopic(String topic) { if (_topicConsumerMap.containsKey(topic)) { return _topicConsumerMap.get(topic).getIterator(); } else { throw new IllegalStateException("Could not find provisioned topic" + topic + ": call provisionTopic before"); } } public int getKafkaServerPort() { return _kafkaServerSuite.getKafkaServerPort(); } }