package resa.examples.outdet; import backtype.storm.task.OutputCollector; import backtype.storm.task.TopologyContext; import backtype.storm.topology.IRichBolt; import backtype.storm.topology.OutputFieldsDeclarer; import backtype.storm.tuple.Fields; import backtype.storm.tuple.Tuple; import backtype.storm.tuple.Values; import java.util.Arrays; import java.util.BitSet; import java.util.HashMap; import java.util.Map; /** * Created by ding on 14-3-14. */ public class Detector implements IRichBolt { private static final double DEFAULT_PROJECTION_VALUE = Double.MAX_VALUE; public static final String OUTLIER_FIELD = "outlier"; private static class Context { double[] projectionValues; int[] neighborCount; public Context(int size) { this.projectionValues = new double[size]; Arrays.fill(projectionValues, DEFAULT_PROJECTION_VALUE); this.neighborCount = new int[size]; Arrays.fill(neighborCount, 0); } } private int objectCount; private double maxNeighborDistance; private int minNeighborCount; private Map<Integer, Context> objectContext; private OutputCollector collector; public Detector(int objectCount, int minNeighborCount, double maxNeighborDistance) { this.objectCount = objectCount; this.minNeighborCount = minNeighborCount; this.maxNeighborDistance = maxNeighborDistance; } @Override public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) { objectContext = new HashMap<>(); this.collector = collector; } @Override public void execute(Tuple input) { Integer projId = input.getIntegerByField(Projection.PROJECTION_ID_FIELD); Context context = objectContext.get(projId); if (context == null) { context = new Context(objectCount); objectContext.put(projId, context); } int objId = input.getIntegerByField(ObjectSpout.ID_FILED); double newProjValue = input.getDoubleByField(Projection.PROJECTION_VALUE_FIELD); int newNeighborCount = 0; boolean anyObjectMissing = false; BitSet outlier = new BitSet(objectCount); for (int i = 0; i < objectCount; i++) { // check whether if any objects missing if (context.projectionValues[i] == DEFAULT_PROJECTION_VALUE) { anyObjectMissing = true; continue; } if (i == objId) { continue; } boolean isNeighNow = isNeighbor(newProjValue, context.projectionValues[i]); boolean isNeighPast = context.projectionValues[objId] == DEFAULT_PROJECTION_VALUE ? false : isNeighbor(context.projectionValues[objId], context.projectionValues[i]); if (isNeighPast && !isNeighNow) { context.neighborCount[i]--; } else if (!isNeighPast && isNeighNow) { // detect a new neighbor context.neighborCount[i]++; } outlier.set(i, context.neighborCount[i] < minNeighborCount); if (isNeighNow) { newNeighborCount++; } } context.projectionValues[objId] = newProjValue; context.neighborCount[objId] = newNeighborCount; outlier.set(objId, newNeighborCount < minNeighborCount); //Modified by tom, at the initial stat, we force this bolt to emit tuples (although fake) to Updater //if any objects missing, wait for it. This is used when system startup ///if (!anyObjectMissing) { collector.emit(input, new Values(objId, projId, outlier, input.getValueByField(ObjectSpout.TIME_FILED))); ///} collector.ack(input); } protected boolean isNeighbor(double projVal1, double projVal2) { return Math.abs(projVal1 - projVal2) <= maxNeighborDistance; } @Override public void cleanup() { } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields(ObjectSpout.ID_FILED, Projection.PROJECTION_ID_FIELD, OUTLIER_FIELD, ObjectSpout.TIME_FILED)); } @Override public Map<String, Object> getComponentConfiguration() { return null; } }