package com.realtimecep.storm.starter.bolts;
import backtype.storm.task.OutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.topology.base.BaseRichBolt;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Tuple;
import backtype.storm.tuple.Values;
import backtype.storm.utils.Utils;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class RollingCountObjects extends BaseRichBolt {
private HashMap<Object, long[]> _objectCounts = new HashMap<Object, long[]>();
private int _numBuckets;
private transient Thread cleaner;
private OutputCollector _collector;
private int _trackMinutes;
public RollingCountObjects(int numBuckets, int trackMinutes) {
_numBuckets = numBuckets;
_trackMinutes = trackMinutes;
}
public long totalObjects (Object obj) {
long[] curr = _objectCounts.get(obj);
long total = 0;
for (long l: curr) {
total+=l;
}
return total;
}
public int currentBucket (int buckets) {
return (currentSecond() / secondsPerBucket(buckets)) % buckets;
}
public int currentSecond() {
return (int) (System.currentTimeMillis() / 1000);
}
public int secondsPerBucket(int buckets) {
return (_trackMinutes * 60 / buckets);
}
public long millisPerBucket(int buckets) {
return (long) secondsPerBucket(buckets) * 1000;
}
public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
_collector = collector;
cleaner = new Thread(new Runnable() {
public void run() {
Integer lastBucket = currentBucket(_numBuckets);
while(true) {
int currBucket = currentBucket(_numBuckets);
if(currBucket!=lastBucket) {
int bucketToWipe = (currBucket + 1) % _numBuckets;
synchronized(_objectCounts) {
Set objs = new HashSet(_objectCounts.keySet());
for (Object obj: objs) {
long[] counts = _objectCounts.get(obj);
long currBucketVal = counts[bucketToWipe];
counts[bucketToWipe] = 0;
long total = totalObjects(obj);
if(currBucketVal!=0) {
_collector.emit(new Values(obj, total));
}
if(total==0) {
_objectCounts.remove(obj);
}
}
}
lastBucket = currBucket;
}
long delta = millisPerBucket(_numBuckets) - (System.currentTimeMillis() % millisPerBucket(_numBuckets));
Utils.sleep(delta);
}
}
});
cleaner.start();
}
public void execute(Tuple tuple) {
Object obj = tuple.getValue(0);
int bucket = currentBucket(_numBuckets);
synchronized(_objectCounts) {
long[] curr = _objectCounts.get(obj);
if(curr==null) {
curr = new long[_numBuckets];
_objectCounts.put(obj, curr);
}
curr[bucket]++;
_collector.emit(new Values(obj, totalObjects(obj)));
_collector.ack(tuple);
}
}
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields("obj", "count"));
}
}