package net.scharrenbach.kafka;
/*
* #%L
* Kafka Run
* %%
* Copyright (C) 2013 Thomas Scharrenbach
* %%
* 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.
* #L%
*/
import kafka.server.KafkaConfig;
import kafka.utils.ZkUtils;
import org.I0Itec.zkclient.ZkClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import scala.collection.Iterator;
import scala.collection.Seq;
/**
*
* @author Thomas Scharrenbach
* @version 0.8.0
* @since 0.8.0
*
*/
public class KafkaServerRunnable implements Runnable {
private static final Logger _log = LoggerFactory
.getLogger(KafkaServerRunnable.class);
/**
* <p>
* Creates a new runnable with a configuration read from the specified file.
* </p>
*
* @author Thomas Scharrenbach
* @version 0.8.0
* @since 0.8.0
*
* @param kafkaConfig
* @param addShutdownHook
* @return
*/
public static final KafkaServerRunnable newInstance(String kafkaConfigFile,
boolean addShutdownHook) {
// Parse the config from file.
final KafkaConfig kafkaConfig = new KafkaConfig(
kafka.utils.Utils.loadProps(kafkaConfigFile));
return newInstance(kafkaConfig, addShutdownHook);
}
/**
* <p>
* Creates a new runnable with the specified configuration.
* </p>
*
* @author Thomas Scharrenbach
* @version 0.8.0
* @since 0.8.0
*
* @param kafkaConfig
* @param addShutdownHook
* @return
*/
public static final KafkaServerRunnable newInstance(
KafkaConfig kafkaConfig, boolean addShutdownHook) {
// Create the actual server.
final kafka.server.KafkaServerStartable kafkaServer = new kafka.server.KafkaServerStartable(
kafkaConfig);
final KafkaServerRunnable runnable = new KafkaServerRunnable(
kafkaServer, kafkaConfig);
// Add shutdown hook if desired.
if (addShutdownHook) {
addShutdownHook(runnable);
}
return runnable;
}
/**
* Adds a shutdown hook that shuts down the Kafka server.
*
* @author Thomas Scharrenbach
* @version 0.8.0
* @since 0.8.0
*
* @param kafkaServer
*/
private static final void addShutdownHook(
final KafkaServerRunnable kafkaServerRunnable) {
kafkaServerRunnable.shutdown();
}
//
// Fields
//
private final kafka.server.KafkaServerStartable _kafkaServer;
private final KafkaConfig _kafkaConfig;
private boolean _kafkaServerStarted;
//
// Constructors
//
/**
* Simple copy constructor.
*
* @author Thomas Scharrenbach
* @version 0.8.0
* @since 0.8.0
*
* @param kafkaServer
* @param kafkaConfig
*/
private KafkaServerRunnable(kafka.server.KafkaServerStartable kafkaServer,
KafkaConfig kafkaConfig) {
_kafkaServer = kafkaServer;
_kafkaConfig = kafkaConfig;
}
/**
* Starts the Kafka server.
*
* @author Thomas Scharrenbach
* @version 0.8.0
* @since 0.8.0
*/
public void run() {
_log.info("Started starting Kafka...");
try {
_kafkaServer.startup();
Thread.sleep(10);
_kafkaServerStarted = true;
} catch (Exception e) {
_log.error("Error starting Kafka!", e);
throw new RuntimeException(e);
}
_log.info("Finished starting Kafka.");
}
/**
* Add a new topic to the running Kafka server.
*
* @param topic
* the topic to add.
* @throws IllegalStateException
* if Kafka server has not yet been started.
*
* @author Thomas Scharrenbach
* @version 0.8.0
* @since 0.8.0
*
*/
public void addTopic(KafkaTopicBean topic) {
int waitForStartedCounter = 0;
int maxTriesStarted = 5;
while (waitForStartedCounter < maxTriesStarted && !_kafkaServerStarted) {
++waitForStartedCounter;
_log.debug("Waiting for Kafka server to be started round "
+ waitForStartedCounter + " of " + maxTriesStarted);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
if (!_kafkaServerStarted) {
throw new IllegalStateException(String.format(
"Kafka server not started!" + " Could not add topic %s!",
topic));
}
try {
_log.debug("Checking whether topic is already defined...");
String zkConnect = _kafkaConfig.zkConnect();
final ZkClient zkClient = new ZkClient(zkConnect, 30000, 30000,
kafka.utils.ZKStringSerializer$.MODULE$);
final Seq<String> topicList = ZkUtils.getChildrenParentMayNotExist(
zkClient, ZkUtils.BrokerTopicsPath());
boolean topicExists = false;
final Iterator<String> topicListIt = topicList.iterator();
final String topicName = topic.getTopic();
while (topicListIt.hasNext() && !topicExists) {
final String existingTopic = topicListIt.next();
_log.debug("Checking existing topic {} with new topic {}",
existingTopic, topicName);
if (topicName.equals(existingTopic)) {
topicExists = true;
_log.info("Topic {} already exists doing nothing...", topicName);
}
}
if (!topicExists) {
_log.info("Topic {} does not yet exist, creating it...", topicName);
_log.info("Started starting Kafka topic {}...", topicName);
kafka.admin.CreateTopicCommand.createTopic(zkClient,
topic.getTopic(), topic.getNPartitions(),
topic.getReplicationFactor(),
topic.getReplicaAssignmentStr());
Thread.sleep(10);
_log.info("Finished starting Kafka topic {}.", topicName);
}
} catch (Exception e) {
_log.error("Error starting Kafka topic {}!", topic);
throw new RuntimeException(e);
}
}
/**
*
*/
public void shutdown() {
try {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
_log.info("Started stopping Kafka...");
_kafkaServer.shutdown();
_log.info("Finished stopping Kafka.");
}
});
} catch (Exception e) {
_log.error("Error stopping Kafka!", e);
assert false;
}
}
}