Java tutorial
/** * 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.datatorrent.lib.multiwindow; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.PriorityQueue; import javax.validation.constraints.NotNull; import org.apache.commons.lang.ClassUtils; import com.google.common.base.Function; import com.datatorrent.api.DefaultOutputPort; import com.datatorrent.api.annotation.OutputPortFieldAnnotation; /** * * Provides a sliding window class that sorts all incoming tuples within the window and emit them in the right order. * <p> * Generally, given tuples T, keys K, windows W. All T within Window W are split into |K| buckets and <br> * sort the bucket in the order that Tp_Ki < Tq_Ki if (comparator.compare(Tp_Ki, Tq_Ki) < 0 || ((Tp_Ki instance of Comparable) && Tp_Ki.compareTo(Tq_Ki) <0))</p> * * <b>Properties</b>:<br> * <b>T</b> is the tuple object the operator can process <br> * <b>K</b> is the key object used to categorize the tuples within the sliding window<br> * <b>function</b>: is used transform the tuple T to group key K. It's used to split all tuples into |K| group and sorted them in the group<br> * by default: function is SingleKeyMappingFunction which map all t to null (all tuples are grouped into one group) * <br><b>comparator</b>: is used to determine the order of the tuple<br> * by default: comparator is null which means the tuple must be comparable * <p></p> * * @displayName Sorted Moving Window * @category Stats and Aggregations * @tags sort, list, function, sliding window * @since 0.9.2 */ public class SortedMovingWindow<T, K> extends AbstractSlidingWindow<T, List<T>> { /** * Output port to emit sorted output. */ public transient DefaultOutputPort<T> outputPort = new DefaultOutputPort<T>(); /** * Output port to emit error output. */ @OutputPortFieldAnnotation(error = true) public transient DefaultOutputPort<T> errorOutput = new DefaultOutputPort<T>(); private Map<K, PriorityQueue<T>> sortedListInSlidingWin = new HashMap<K, PriorityQueue<T>>(); private List<T> tuplesInCurrentStreamWindow = new LinkedList<T>(); @NotNull private Function<T, K> function = new SingleKeyMappingFunction<T, K>(); private Comparator<T> comparator = null; @Override protected void processDataTuple(T tuple) { tuplesInCurrentStreamWindow.add(tuple); K key = function.apply(tuple); PriorityQueue<T> sortedList = sortedListInSlidingWin.get(key); if (sortedList == null) { sortedList = new PriorityQueue<T>(10, comparator); sortedListInSlidingWin.put(key, sortedList); } sortedList.add(tuple); } @Override public List<T> createWindowState() { return tuplesInCurrentStreamWindow; } @SuppressWarnings("unchecked") @Override public void endWindow() { super.endWindow(); tuplesInCurrentStreamWindow = new LinkedList<T>(); if (lastExpiredWindowState == null) { // not ready to emit value or empty in a certain window return; } // Assumption: the expiring tuple and any tuple before are already sorted. So it's safe to emit tuples from sortedListInSlidingWin till the expiring tuple for (T expiredTuple : lastExpiredWindowState) { // Find sorted list for the given key PriorityQueue<T> sortedListForE = sortedListInSlidingWin.get(function.apply(expiredTuple)); for (Iterator<T> iterator = sortedListForE.iterator(); iterator.hasNext();) { T minElemInSortedList = iterator.next(); int k = 0; if (comparator == null) { if (expiredTuple instanceof Comparable) { k = ((Comparable<T>) expiredTuple).compareTo(minElemInSortedList); } else { errorOutput.emit(expiredTuple); throw new IllegalArgumentException("Operator \"" + ClassUtils.getShortClassName(this.getClass()) + "\" encounters an invalid tuple " + expiredTuple + "\nNeither the tuple is comparable Nor Comparator is specified!"); } } else { k = comparator.compare(expiredTuple, minElemInSortedList); } if (k < 0) { // If the expiring tuple is less than the first element of the sorted list. No more tuples to emit break; } else { // Emit the element in sorted list if it's less than the expiring tuple outputPort.emit(minElemInSortedList); // remove the element from the sorted list iterator.remove(); } } } } /** * Default grouping function that map all tuples into single group * @param <T> * @param <K> */ private static class SingleKeyMappingFunction<T, K> implements Function<T, K> { @Override public K apply(T input) { return null; } } public void setComparator(Comparator<T> comparator) { this.comparator = comparator; } public void setFunction(Function<T, K> function) { this.function = function; } }