package com.alipay.bluewhale.core.task.executer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import org.apache.log4j.Logger;
import backtype.storm.task.IOutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.tuple.MessageId;
import backtype.storm.tuple.Tuple;
import backtype.storm.utils.TimeCacheMap;
import com.alipay.bluewhale.core.task.acker.Acker;
import com.alipay.bluewhale.core.stats.BaseTaskStatsRolling;
import com.alipay.bluewhale.core.stats.BoltTaskStatsRolling;
import com.alipay.bluewhale.core.stats.Stats;
import com.alipay.bluewhale.core.task.common.TasksCommon;
import com.alipay.bluewhale.core.task.error.ITaskReportErr;
import com.alipay.bluewhale.core.task.transfer.UnanchoredSend;
import com.alipay.bluewhale.core.task.transfer.TaskSendTargets;
import com.alipay.bluewhale.core.utils.StormUtils;
import com.alipay.bluewhale.core.work.transfer.WorkerTransfer;
/**
* bolt ����Ҫ��IInternalOutputCollector�ӿڵ�ʵ��
* �û������û��ύ��emit,ack,fail����
* @author yannian
*
*/
public class BoltCollector implements IOutputCollector {
private static Logger LOG = Logger.getLogger(BoltCollector.class);
private ITaskReportErr reportError;
private TaskSendTargets sendTargets;
private WorkerTransfer workerTransfer;
private TopologyContext topologyContext;
private Integer task_id;
private TimeCacheMap<Tuple, Long> tuple_start_times;
private BaseTaskStatsRolling task_stats;
private TimeCacheMap<Tuple, Long> pending_acks;
public BoltCollector(int message_timeout_secs,ITaskReportErr report_error, TaskSendTargets _send_fn, WorkerTransfer _transfer_fn, TopologyContext _topology_context,Integer task_id,TimeCacheMap<Tuple, Long> tuple_start_times,BaseTaskStatsRolling _task_stats
) {
this.reportError=report_error;
this.sendTargets = _send_fn;
this.workerTransfer = _transfer_fn;
this.topologyContext = _topology_context;
this.task_id=task_id;
this.task_stats = _task_stats;
//ԭ���� pending-acks (ConcurrentHashMap.)����
// ��Ҫԭ���� ��bolt�У����ҵ������bug����ijһ���������ǵ���ack��fail���ͻᵼ������mapԽ��Խ��ֱ���ڴ治����
//Ŀǰ��ʱʱ��������spoutcollector����һ�� ���Ƕ���storm_conf.get(Config.TOPOLOGY_MESSAGE_TIMEOUT_SECS) ���ֵ
this.pending_acks = new TimeCacheMap<Tuple, Long>(message_timeout_secs);
this.tuple_start_times=tuple_start_times;
}
@Override
public List<Integer> emit(String streamId, Collection<Tuple> anchors,List<Object> tuple) {
return boltEmit(streamId, anchors, tuple, null);
}
@Override
public void emitDirect(int taskId, String streamId,Collection<Tuple> anchors, List<Object> tuple) {
boltEmit(streamId, anchors, tuple, taskId);
}
private List<Integer> boltEmit(String out_stream_id,Collection<Tuple> anchors, List<Object> values, Integer out_task_id) {
try {
java.util.Set<Integer> out_tasks = null;
if (out_task_id != null) {
out_tasks = sendTargets.get(out_task_id, out_stream_id, values);
} else {
out_tasks = sendTargets.get(out_stream_id, values);
}
for (Integer t : out_tasks) {
HashMap anchors_to_ids = new HashMap();
if (anchors != null) {
for (Tuple a : anchors) {
Long edge_id = MessageId.generateId();
TasksCommon.put_xor(pending_acks, a, edge_id);
for (Long root_id : a.getMessageId().getAnchorsToIds().keySet()) {
TasksCommon.put_xor(anchors_to_ids, root_id, edge_id);
}
}
}
MessageId msgid=MessageId.makeId(anchors_to_ids);
workerTransfer.transfer(t,new Tuple(topologyContext, values, task_id,out_stream_id, msgid));
}
return StormUtils.mk_list(out_tasks);
} catch (Exception e) {
LOG.error("bolt emit", e);
}
return new ArrayList<Integer>();
}
@Override
public void ack(Tuple input) {
Object ack_val = pending_acks.remove(input);
if (ack_val == null) {
ack_val = 0l;
}
for (Entry<Long, Long> e : input.getMessageId().getAnchorsToIds().entrySet()) {
UnanchoredSend.send(
topologyContext,
sendTargets,
workerTransfer,
Acker.ACKER_ACK_STREAM_ID,
StormUtils.mk_list((Object)e.getKey(),StormUtils.bit_xor(e.getValue(), ack_val)));
}
Long delta = TasksCommon.tuple_time_delta(tuple_start_times, input);
if (delta != null) {
Stats.bolt_acked_tuple((BoltTaskStatsRolling) task_stats,
input.getSourceComponent(), input.getSourceStreamId(),
delta);
}
}
@Override
public void fail(Tuple input) {
pending_acks.remove(input);
for (Entry<Long, Long> e : input.getMessageId().getAnchorsToIds().entrySet()) {
UnanchoredSend.send(topologyContext, sendTargets,
workerTransfer, Acker.ACKER_FAIL_STREAM_ID,
StormUtils.mk_list((Object) e.getKey()));
}
Long delta = TasksCommon.tuple_time_delta(tuple_start_times,input);
if (delta != null) {
Stats.bolt_failed_tuple((BoltTaskStatsRolling) task_stats,
input.getSourceComponent(), input.getSourceStreamId(),
delta);
}
}
@Override
public void reportError(Throwable error) {
reportError.report(error);
}
}