/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * 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 com.jstorm.example.unittests.window; import backtype.storm.Config; 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.TupleUtils; import org.apache.storm.starter.tools.NthLastModifiedTimeTracker; import org.apache.storm.starter.tools.SlidingWindowCounter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.Map; /** * @author binyang.dby on 2016/7/11. */ public class WindowTestRollingCountBolt extends BaseRichBolt { private static Logger LOG = LoggerFactory.getLogger("WindowTestRollingCountBolt"); private SlidingWindowCounter<Object> counter; private int windowLengthInSeconds; private int emitFrequencyInSeconds; private int slots; private OutputCollector collector; private NthLastModifiedTimeTracker lastModifiedTracker; public WindowTestRollingCountBolt(int windowLengthInSeconds, int emitFrequencyInSeconds) { this.windowLengthInSeconds = windowLengthInSeconds; this.emitFrequencyInSeconds = emitFrequencyInSeconds; if (windowLengthInSeconds % emitFrequencyInSeconds != 0) throw new IllegalArgumentException("WindowLengthInSeconds should be times of EmitFrequencyInSeconds!"); this.slots = windowLengthInSeconds / emitFrequencyInSeconds; this.counter = new SlidingWindowCounter<Object>(slots); } @Override public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) { this.collector = collector; this.lastModifiedTracker = new NthLastModifiedTimeTracker(slots); } @Override public void execute(Tuple input) { if (TupleUtils.isTick(input)) { System.out.println("### tuple = isTick"); emitCurrentWindowCounts(); } else { countObjAndAck(input); } } private void emitCurrentWindowCounts() { Map<Object, Long> counts = counter.getCountsThenAdvanceWindow(); int actualWindowLengthInSeconds = lastModifiedTracker.secondsSinceOldestModification(); lastModifiedTracker.markAsModified(); if (actualWindowLengthInSeconds != windowLengthInSeconds) { //LOG.warn(String.format(WINDOW_LENGTH_WARNING_TEMPLATE, actualWindowLengthInSeconds, windowLengthInSeconds)); } emit(counts, actualWindowLengthInSeconds); } private void emit(Map<Object, Long> counts, int actualWindowLengthInSeconds) { for (Map.Entry<Object, Long> entry : counts.entrySet()) { Object obj = entry.getKey(); Long count = entry.getValue(); collector.emit(new Values(obj, count, actualWindowLengthInSeconds)); } } private void countObjAndAck(Tuple tuple) { Object obj = tuple.getValue(0); counter.incrementCount(obj); collector.ack(tuple); } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("obj", "count", "actualWindowLengthInSeconds")); } @Override public Map<String, Object> getComponentConfiguration() { LOG.info("%%%% getComponentConfiguration"); Map<String, Object> conf = new HashMap<String, Object>(); conf.put(Config.TOPOLOGY_TICK_TUPLE_FREQ_SECS, emitFrequencyInSeconds); return conf; } }