com.google.cloud.dataflow.sdk.util.TriggerContextFactory Maven / Gradle / Ivy
Show all versions of google-cloud-dataflow-java-sdk-all Show documentation
/*
* Copyright (C) 2015 Google Inc.
*
* Licensed 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.google.cloud.dataflow.sdk.util;
import com.google.cloud.dataflow.sdk.coders.Coder;
import com.google.cloud.dataflow.sdk.transforms.windowing.BoundedWindow;
import com.google.cloud.dataflow.sdk.transforms.windowing.Trigger;
import com.google.cloud.dataflow.sdk.transforms.windowing.Trigger.MergingTriggerInfo;
import com.google.cloud.dataflow.sdk.transforms.windowing.Trigger.TriggerInfo;
import com.google.cloud.dataflow.sdk.util.state.MergingStateAccessor;
import com.google.cloud.dataflow.sdk.util.state.State;
import com.google.cloud.dataflow.sdk.util.state.StateAccessor;
import com.google.cloud.dataflow.sdk.util.state.StateInternals;
import com.google.cloud.dataflow.sdk.util.state.StateNamespace;
import com.google.cloud.dataflow.sdk.util.state.StateNamespaces;
import com.google.cloud.dataflow.sdk.util.state.StateTag;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import org.joda.time.Instant;
import java.util.Collection;
import java.util.Map;
import javax.annotation.Nullable;
/**
* Factory for creating instances of the various {@link Trigger} contexts.
*
* These contexts are highly interdependent and share many fields; it is inadvisable
* to create them via any means other than this factory class.
*/
public class TriggerContextFactory {
private final WindowingStrategy, W> windowingStrategy;
private StateInternals> stateInternals;
// Future triggers may be able to exploit the active window to state address window mapping.
@SuppressWarnings("unused")
private ActiveWindowSet activeWindows;
private final Coder windowCoder;
public TriggerContextFactory(WindowingStrategy, W> windowingStrategy,
StateInternals> stateInternals, ActiveWindowSet activeWindows) {
this.windowingStrategy = windowingStrategy;
this.stateInternals = stateInternals;
this.activeWindows = activeWindows;
this.windowCoder = windowingStrategy.getWindowFn().windowCoder();
}
public Trigger.TriggerContext base(W window, Timers timers,
ExecutableTrigger rootTrigger, FinishedTriggers finishedSet) {
return new TriggerContextImpl(window, timers, rootTrigger, finishedSet);
}
public Trigger.OnElementContext createOnElementContext(
W window, Timers timers, Instant elementTimestamp,
ExecutableTrigger rootTrigger, FinishedTriggers finishedSet) {
return new OnElementContextImpl(window, timers, rootTrigger, finishedSet, elementTimestamp);
}
public Trigger.OnMergeContext createOnMergeContext(W window, Timers timers,
ExecutableTrigger rootTrigger, FinishedTriggers finishedSet,
Map finishedSets) {
return new OnMergeContextImpl(window, timers, rootTrigger, finishedSet, finishedSets);
}
public StateAccessor> createStateAccessor(W window, ExecutableTrigger trigger) {
return new StateAccessorImpl(window, trigger);
}
public MergingStateAccessor, W> createMergingStateAccessor(
W mergeResult, Collection mergingWindows, ExecutableTrigger trigger) {
return new MergingStateAccessorImpl(trigger, mergingWindows, mergeResult);
}
private class TriggerInfoImpl implements Trigger.TriggerInfo {
protected final ExecutableTrigger trigger;
protected final FinishedTriggers finishedSet;
private final Trigger.TriggerContext context;
public TriggerInfoImpl(ExecutableTrigger trigger, FinishedTriggers finishedSet,
Trigger.TriggerContext context) {
this.trigger = trigger;
this.finishedSet = finishedSet;
this.context = context;
}
@Override
public boolean isMerging() {
return !windowingStrategy.getWindowFn().isNonMerging();
}
@Override
public Iterable> subTriggers() {
return trigger.subTriggers();
}
@Override
public ExecutableTrigger subTrigger(int subtriggerIndex) {
return trigger.subTriggers().get(subtriggerIndex);
}
@Override
public boolean isFinished() {
return finishedSet.isFinished(trigger);
}
@Override
public boolean isFinished(int subtriggerIndex) {
return finishedSet.isFinished(subTrigger(subtriggerIndex));
}
@Override
public boolean areAllSubtriggersFinished() {
return Iterables.isEmpty(unfinishedSubTriggers());
}
@Override
public Iterable> unfinishedSubTriggers() {
return FluentIterable
.from(trigger.subTriggers())
.filter(new Predicate>() {
@Override
public boolean apply(ExecutableTrigger trigger) {
return !finishedSet.isFinished(trigger);
}
});
}
@Override
public ExecutableTrigger firstUnfinishedSubTrigger() {
for (ExecutableTrigger subTrigger : trigger.subTriggers()) {
if (!finishedSet.isFinished(subTrigger)) {
return subTrigger;
}
}
return null;
}
@Override
public void resetTree() throws Exception {
finishedSet.clearRecursively(trigger);
trigger.invokeClear(context);
}
@Override
public void setFinished(boolean finished) {
finishedSet.setFinished(trigger, finished);
}
@Override
public void setFinished(boolean finished, int subTriggerIndex) {
finishedSet.setFinished(subTrigger(subTriggerIndex), finished);
}
}
private class TriggerTimers implements Timers {
private final Timers timers;
private final W window;
public TriggerTimers(W window, Timers timers) {
this.timers = timers;
this.window = window;
}
@Override
public void setTimer(Instant timestamp, TimeDomain timeDomain) {
timers.setTimer(timestamp, timeDomain);
}
@Override
public void deleteTimer(Instant timestamp, TimeDomain timeDomain) {
if (timeDomain == TimeDomain.EVENT_TIME
&& timestamp.equals(window.maxTimestamp())) {
// Don't allow triggers to unset the at-max-timestamp timer. This is necessary for on-time
// state transitions.
return;
}
timers.deleteTimer(timestamp, timeDomain);
}
@Override
public Instant currentProcessingTime() {
return timers.currentProcessingTime();
}
@Override
@Nullable
public Instant currentSynchronizedProcessingTime() {
return timers.currentSynchronizedProcessingTime();
}
@Override
@Nullable
public Instant currentEventTime() {
return timers.currentEventTime();
}
}
private class MergingTriggerInfoImpl
extends TriggerInfoImpl implements Trigger.MergingTriggerInfo {
private final Map finishedSets;
public MergingTriggerInfoImpl(
ExecutableTrigger trigger,
FinishedTriggers finishedSet,
Trigger.TriggerContext context,
Map finishedSets) {
super(trigger, finishedSet, context);
this.finishedSets = finishedSets;
}
@Override
public boolean finishedInAnyMergingWindow() {
for (FinishedTriggers finishedSet : finishedSets.values()) {
if (finishedSet.isFinished(trigger)) {
return true;
}
}
return false;
}
@Override
public boolean finishedInAllMergingWindows() {
for (FinishedTriggers finishedSet : finishedSets.values()) {
if (!finishedSet.isFinished(trigger)) {
return false;
}
}
return true;
}
@Override
public Iterable getFinishedMergingWindows() {
return Maps.filterValues(finishedSets, new Predicate() {
@Override
public boolean apply(FinishedTriggers finishedSet) {
return finishedSet.isFinished(trigger);
}
}).keySet();
}
}
private class StateAccessorImpl implements StateAccessor