/**
* Copyright 2013-2014 Recruit Technologies Co., Ltd. and contributors
* (see CONTRIBUTORS.md)
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. A copy of the
* License is distributed with this work in the LICENSE.md file. You may
* also obtain a copy of the License from
*
* 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 org.gennai.gungnir.topology.component;
import static org.gennai.gungnir.GungnirConfig.*;
import static org.gennai.gungnir.GungnirConst.*;
import java.util.List;
import java.util.Map;
import org.gennai.gungnir.GungnirConfig;
import org.gennai.gungnir.topology.GungnirContext;
import org.gennai.gungnir.topology.dispatcher.BaseDispatcher;
import org.gennai.gungnir.topology.dispatcher.Dispatcher;
import org.gennai.gungnir.topology.dispatcher.FilterDispatcher;
import org.gennai.gungnir.topology.dispatcher.MultiDispatcher;
import org.gennai.gungnir.topology.operator.PartitionOperator;
import org.gennai.gungnir.topology.operator.SpoutOperator;
import org.gennai.gungnir.topology.operator.metrics.Metrics;
import org.gennai.gungnir.tuple.TupleValues;
import org.gennai.gungnir.utils.SnapshotTimer;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import backtype.storm.spout.SpoutOutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.topology.base.BaseRichSpout;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Values;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
public class ExecSpout extends BaseRichSpout implements GungnirComponent {
private static final long serialVersionUID = SERIAL_VERSION_UID;
private static final Logger LOG = LoggerFactory.getLogger(ExecSpout.class);
private static final String SPOUT_NAME = "EXEC_SPOUT";
private GungnirContext context;
private SpoutOperator incomingOperator;
private List<PartitionOperator> outgoingOperators;
private TopologyContext topologyContext;
private SnapshotTimer snapshotTimer;
private Map<String, Metrics> metricsMap;
private static class SpoutDispatcher extends BaseDispatcher {
private static final long serialVersionUID = SERIAL_VERSION_UID;
private SpoutOutputCollector collector;
@Override
protected void prepare() {
}
@Override
public void dispatch(TupleValues tupleValues) {
collector.emit(getSource().getName(), new Values(tupleValues));
if (LOG.isDebugEnabled()) {
LOG.debug("Dispatch {} from {}", tupleValues, getSource().getName());
}
}
@Override
public String toString() {
return "-SpoutDispatcher(" + getSource().getName() + ")->>";
}
}
public ExecSpout(GungnirContext context) {
this.context = context;
}
@Override
public String getName() {
return SPOUT_NAME;
}
@Override
public TopologyContext getTopologyContext() {
return topologyContext;
}
@Override
public SnapshotTimer getShapshotTimer() {
return snapshotTimer;
}
public void setIncomingOperator(SpoutOperator incomingOperator) {
this.incomingOperator = incomingOperator;
}
public SpoutOperator getIncomingOperator() {
return incomingOperator;
}
public List<PartitionOperator> getOutgoingOperators() {
return outgoingOperators;
}
public void addOutgoingOperators(List<PartitionOperator> outgoingOperators) {
if (this.outgoingOperators == null) {
this.outgoingOperators = Lists.newArrayList();
}
this.outgoingOperators.addAll(outgoingOperators);
}
public void registerMetrics(Map<String, Metrics> metrics) {
if (metrics != null && metrics.size() > 0) {
if (metricsMap == null) {
metricsMap = Maps.newHashMap();
}
metricsMap.putAll(metrics);
}
}
@Override
public void open(@SuppressWarnings("rawtypes") Map stormConf, TopologyContext context,
SpoutOutputCollector collector) {
LOG.info("open({}[{}]) TaskId: {}, ThisComponetTasks: {}, ThisWorkerTasks: {}",
context.getThisComponentId(), context.getThisTaskIndex(), context.getThisTaskId(),
context.getComponentTasks(context.getThisComponentId()), context.getThisWorkerTasks());
@SuppressWarnings("unchecked")
GungnirConfig config = GungnirConfig.wrap((Map<String, Object>) stormConf.get(GUNGNIR_CONFIG));
topologyContext = context;
if (config.getBoolean(TOPOLOGY_METRICS_ENABLED) && metricsMap != null) {
for (Map.Entry<String, Metrics> entry : metricsMap.entrySet()) {
topologyContext.registerMetric(entry.getKey(), entry.getValue(),
config.getInteger(TOPOLOGY_METRICS_INTERVAL_SECS));
}
}
this.context.setComponent(this);
incomingOperator.doPrepare(config, this.context);
for (PartitionOperator partitionOperator : outgoingOperators) {
SpoutDispatcher spoutDispatcher = new SpoutDispatcher();
spoutDispatcher.collector = collector;
Dispatcher dispatcher = partitionOperator.getDispatcher();
if (dispatcher == null) {
partitionOperator.setDispatcher(spoutDispatcher);
} else if (dispatcher instanceof MultiDispatcher) {
for (Dispatcher d : ((MultiDispatcher) dispatcher).getDispatchers()) {
if (d instanceof FilterDispatcher) {
((FilterDispatcher) d).setDispatcher(spoutDispatcher);
}
}
} else if (dispatcher instanceof FilterDispatcher) {
((FilterDispatcher) dispatcher).setDispatcher(spoutDispatcher);
}
}
// TODO parallelization
// snapshotTimer = new SnapshotTimer(config.getInteger(COMPONENT_SNAPSHOT_QUEUE_SIZE),
// config.getInteger(COMPONENT_SNAPSHOT_PARALLELISM));
snapshotTimer = new SnapshotTimer(getName() + "_" + topologyContext.getThisTaskIndex());
}
@Override
public void nextTuple() {
incomingOperator.nextTuple();
if (LOG.isDebugEnabled()) {
LOG.debug("nextTuple({} {}[{}])", context.getTopologyId(), getName(),
this.getTopologyContext().getThisTaskIndex());
}
}
@Override
public void close() {
incomingOperator.doCleanup();
try {
snapshotTimer.stop();
} catch (SchedulerException e) {
LOG.error("Failed to stop snapshot timer", e);
}
}
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
for (PartitionOperator partitionOperator : outgoingOperators) {
declarer.declareStream(partitionOperator.getName(), new Fields(TUPLE_FIELD));
}
}
}