package storm.applications.spout; import backtype.storm.spout.SchemeAsMultiScheme; import backtype.storm.tuple.Fields; import backtype.storm.utils.Utils; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import storm.applications.constants.BaseConstants.BaseConf; import storm.applications.spout.parser.Parser; import storm.applications.util.config.ClassLoaderUtils; import storm.applications.util.stream.StreamValues; import storm.kafka.BrokerHosts; import storm.kafka.SpoutConfig; import storm.kafka.StringScheme; import storm.kafka.ZkHosts; /** * This is an encapsulation of the storm-kafka-0.8-plus spout implementation. * It only emits tuples to the default stream, also only the first value of the parser * is going to be emitted, this is a limitation of the implementation, most applications * won't be affected. * * @author Maycon Viana Bordin <mayconbordin@gmail.com> */ public class KafkaSpout extends AbstractSpout { private static final Logger LOG = LoggerFactory.getLogger(KafkaSpout.class); private BrokerHosts brokerHosts; private static storm.kafka.KafkaSpout spout; @Override protected void initialize() { String parserClass = config.getString(getConfigKey(BaseConf.SPOUT_PARSER)); String host = config.getString(getConfigKey(BaseConf.KAFKA_HOST)); String topic = config.getString(getConfigKey(BaseConf.KAFKA_SPOUT_TOPIC)); String consumerId = config.getString(getConfigKey(BaseConf.KAFKA_CONSUMER_ID)); String path = config.getString(getConfigKey(BaseConf.KAFKA_ZOOKEEPER_PATH)); Parser parser = (Parser) ClassLoaderUtils.newInstance(parserClass, "parser", LOG); parser.initialize(config); Fields defaultFields = fields.get(Utils.DEFAULT_STREAM_ID); if (defaultFields == null) { throw new RuntimeException("A KafkaSpout must have a default stream"); } brokerHosts = new ZkHosts(host); SpoutConfig spoutConfig = new SpoutConfig(brokerHosts, topic, path, consumerId); spoutConfig.scheme = new SchemeAsMultiScheme(new ParserScheme(parser, defaultFields)); spout = new storm.kafka.KafkaSpout(spoutConfig); spout.open(config, context, collector); } @Override public void nextTuple() { spout.nextTuple(); } @Override public void fail(Object msgId) { spout.fail(msgId); } @Override public void ack(Object msgId) { spout.ack(msgId); } @Override public void deactivate() { spout.deactivate(); } @Override public void close() { spout.close(); } private class ParserScheme extends StringScheme { private final Parser parser; private final Fields fields; public ParserScheme(Parser parser, Fields fields) { this.parser = parser; this.fields = fields; } @Override public List<Object> deserialize(byte[] bytes) { String value = deserializeString(bytes); List<StreamValues> tuples = parser.parse(value); if (tuples != null && tuples.size() > 0) { return tuples.get(0); } return null; } @Override public Fields getOutputFields() { return fields; } } }