package io.confluent.examples.streams.kafka; import org.apache.curator.test.InstanceSpec; import org.junit.rules.ExternalResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.Properties; import io.confluent.kafka.schemaregistry.RestApp; import io.confluent.examples.streams.zookeeper.ZooKeeperEmbedded; import io.confluent.kafka.schemaregistry.avro.AvroCompatibilityLevel; import kafka.server.KafkaConfig$; /** * Runs an in-memory, "embedded" Kafka cluster with 1 ZooKeeper instance, 1 Kafka broker, and 1 * Confluent Schema Registry instance. */ public class EmbeddedSingleNodeKafkaCluster extends ExternalResource { private static final Logger log = LoggerFactory.getLogger(EmbeddedSingleNodeKafkaCluster.class); private static final int DEFAULT_BROKER_PORT = 0; // 0 results in a random port being selected private static final String KAFKA_SCHEMAS_TOPIC = "_schemas"; private static final String AVRO_COMPATIBILITY_TYPE = AvroCompatibilityLevel.NONE.name; private ZooKeeperEmbedded zookeeper; private KafkaEmbedded broker; private RestApp schemaRegistry; private final Properties brokerConfig; /** * Creates and starts the cluster. */ public EmbeddedSingleNodeKafkaCluster() { this(new Properties()); } /** * Creates and starts the cluster. * * @param brokerConfig Additional broker configuration settings. */ public EmbeddedSingleNodeKafkaCluster(Properties brokerConfig) { this.brokerConfig = new Properties(); this.brokerConfig.putAll(brokerConfig); } /** * Creates and starts the cluster. */ public void start() throws Exception { log.debug("Initiating embedded Kafka cluster startup"); log.debug("Starting a ZooKeeper instance..."); zookeeper = new ZooKeeperEmbedded(); log.debug("ZooKeeper instance is running at {}", zookeeper.connectString()); Properties effectiveBrokerConfig = effectiveBrokerConfigFrom(brokerConfig, zookeeper); log.debug("Starting a Kafka instance on port {} ...", effectiveBrokerConfig.getProperty(KafkaConfig$.MODULE$.PortProp())); broker = new KafkaEmbedded(effectiveBrokerConfig); log.debug("Kafka instance is running at {}, connected to ZooKeeper at {}", broker.brokerList(), broker.zookeeperConnect()); schemaRegistry = new RestApp( InstanceSpec.getRandomPort(), zookeeperConnect(), KAFKA_SCHEMAS_TOPIC, AVRO_COMPATIBILITY_TYPE); schemaRegistry.start(); } private Properties effectiveBrokerConfigFrom(Properties brokerConfig, ZooKeeperEmbedded zookeeper) { Properties effectiveConfig = new Properties(); effectiveConfig.putAll(brokerConfig); effectiveConfig.put(KafkaConfig$.MODULE$.ZkConnectProp(), zookeeper.connectString()); effectiveConfig.put(KafkaConfig$.MODULE$.PortProp(), DEFAULT_BROKER_PORT); effectiveConfig.put(KafkaConfig$.MODULE$.DeleteTopicEnableProp(), true); effectiveConfig.put(KafkaConfig$.MODULE$.LogCleanerDedupeBufferSizeProp(), 2 * 1024 * 1024L); effectiveConfig.put(KafkaConfig$.MODULE$.GroupMinSessionTimeoutMsProp(), 0); effectiveConfig.put(KafkaConfig$.MODULE$.OffsetsTopicReplicationFactorProp(), (short) 1); effectiveConfig.put(KafkaConfig$.MODULE$.AutoCreateTopicsEnableProp(), true); return effectiveConfig; } @Override protected void before() throws Exception { start(); } @Override protected void after() { stop(); } /** * Stops the cluster. */ public void stop() { try { if (schemaRegistry != null) { schemaRegistry.stop(); } } catch (Exception e) { throw new RuntimeException(e); } if (broker != null) { broker.stop(); } try { if (zookeeper != null) { zookeeper.stop(); } } catch (IOException e) { throw new RuntimeException(e); } } /** * This cluster's `bootstrap.servers` value. Example: `127.0.0.1:9092`. * * You can use this to tell Kafka Streams applications, Kafka producers, and Kafka consumers (new * consumer API) how to connect to this cluster. */ public String bootstrapServers() { return broker.brokerList(); } /** * This cluster's ZK connection string aka `zookeeper.connect` in `hostnameOrIp:port` format. * Example: `127.0.0.1:2181`. * * You can use this to e.g. tell Kafka consumers (old consumer API) how to connect to this * cluster. */ public String zookeeperConnect() { return zookeeper.connectString(); } /** * The "schema.registry.url" setting of the schema registry instance. */ public String schemaRegistryUrl() { return schemaRegistry.restConnect; } /** * Creates a Kafka topic with 1 partition and a replication factor of 1. * * @param topic The name of the topic. */ public void createTopic(String topic) { createTopic(topic, 1, 1, new Properties()); } /** * Creates a Kafka topic with the given parameters. * * @param topic The name of the topic. * @param partitions The number of partitions for this topic. * @param replication The replication factor for (the partitions of) this topic. */ public void createTopic(String topic, int partitions, int replication) { createTopic(topic, partitions, replication, new Properties()); } /** * Creates a Kafka topic with the given parameters. * * @param topic The name of the topic. * @param partitions The number of partitions for this topic. * @param replication The replication factor for (partitions of) this topic. * @param topicConfig Additional topic-level configuration settings. */ public void createTopic(String topic, int partitions, int replication, Properties topicConfig) { broker.createTopic(topic, partitions, replication, topicConfig); } }