/** * 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 * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * 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 storm.trident.operation.builtin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import storm.trident.operation.BaseAggregator; import storm.trident.operation.TridentCollector; import storm.trident.tuple.TridentTuple; /** * Abstract {@code Aggregator} for comparing two values in a stream. * */ public abstract class ComparisonAggregator<T> extends BaseAggregator<ComparisonAggregator.State> { private static final Logger log = LoggerFactory.getLogger(ComparisonAggregator.class); private Object batchId; public static class State { TridentTuple previousTuple; } private final String inputFieldName; public ComparisonAggregator(String inputFieldName) { this.inputFieldName = inputFieldName; } protected abstract T compare(T value1, T value2); @Override public State init(Object batchId, TridentCollector collector) { this.batchId = batchId; log.debug("Started comparison aggregation for batch: [{}] in operation [{}]", batchId, this); return new State(); } @Override public void aggregate(State state, TridentTuple tuple, TridentCollector collector) { T value1 = valueFromTuple(state.previousTuple); T value2 = valueFromTuple(tuple); log.debug("Aggregated tuple value in state [{}], and received tuple value [{}] in operation [{}]", value1, value2, this); if(value2 == null) { return; } if(value1 == null || compare(value1, value2) == value2) { state.previousTuple = tuple; } } protected T valueFromTuple(TridentTuple tuple) { // when there is no input field then the whole tuple is considered for comparison. Object value = null; if (inputFieldName != null && tuple != null) { value = tuple.getValueByField(inputFieldName); } else { value = tuple; } log.debug("value from tuple is [{}] with input field [{}] and tuple [{}]", value, inputFieldName, tuple); return (T) value; } @Override public void complete(State state, TridentCollector collector) { log.debug("Completed comparison aggregation for batch [{}] with resultant tuple: [{}] in operation [{}]", batchId, state.previousTuple, this); collector.emit(state.previousTuple != null ? state.previousTuple.getValues() : null); } }