/* * 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 org.apache.beam.runners.core.triggers; import java.util.ArrayList; import java.util.List; import org.apache.beam.sdk.common.runner.v1.RunnerApi; import org.apache.beam.sdk.transforms.windowing.Trigger; import org.joda.time.Duration; import org.joda.time.Instant; /** Translates a {@link Trigger} to a {@link TriggerStateMachine}. */ public class TriggerStateMachines { private TriggerStateMachines() {} public static TriggerStateMachine stateMachineForTrigger(RunnerApi.Trigger trigger) { switch (trigger.getTriggerCase()) { case AFTER_ALL: return AfterAllStateMachine.of( stateMachinesForTriggers(trigger.getAfterAll().getSubtriggersList())); case AFTER_ANY: return AfterFirstStateMachine.of( stateMachinesForTriggers(trigger.getAfterAny().getSubtriggersList())); case AFTER_END_OF_WINDOW: return stateMachineForAfterEndOfWindow(trigger.getAfterEndOfWindow()); case ELEMENT_COUNT: return AfterPaneStateMachine.elementCountAtLeast( trigger.getElementCount().getElementCount()); case AFTER_SYNCHRONIZED_PROCESSING_TIME: return AfterSynchronizedProcessingTimeStateMachine.ofFirstElement(); case DEFAULT: return DefaultTriggerStateMachine.of(); case NEVER: return NeverStateMachine.ever(); case ALWAYS: return ReshuffleTriggerStateMachine.create(); case OR_FINALLY: return stateMachineForTrigger(trigger.getOrFinally().getMain()) .orFinally(stateMachineForTrigger(trigger.getOrFinally().getFinally())); case REPEAT: return RepeatedlyStateMachine.forever( stateMachineForTrigger(trigger.getRepeat().getSubtrigger())); case AFTER_EACH: return AfterEachStateMachine.inOrder( stateMachinesForTriggers(trigger.getAfterEach().getSubtriggersList())); case AFTER_PROCESSING_TIME: return stateMachineForAfterProcessingTime(trigger.getAfterProcessingTime()); case TRIGGER_NOT_SET: throw new IllegalArgumentException( String.format("Required field 'trigger' not set on %s", trigger)); default: throw new IllegalArgumentException(String.format("Unknown trigger type %s", trigger)); } } private static TriggerStateMachine stateMachineForAfterEndOfWindow( RunnerApi.Trigger.AfterEndOfWindow trigger) { if (!trigger.hasEarlyFirings() && !trigger.hasLateFirings()) { return AfterWatermarkStateMachine.pastEndOfWindow(); } else { AfterWatermarkStateMachine.AfterWatermarkEarlyAndLate machine = AfterWatermarkStateMachine.pastEndOfWindow() .withEarlyFirings(stateMachineForTrigger(trigger.getEarlyFirings())); if (trigger.hasLateFirings()) { machine = machine.withLateFirings(stateMachineForTrigger(trigger.getLateFirings())); } return machine; } } private static TriggerStateMachine stateMachineForAfterProcessingTime( RunnerApi.Trigger.AfterProcessingTime trigger) { AfterDelayFromFirstElementStateMachine stateMachine = AfterProcessingTimeStateMachine.pastFirstElementInPane(); for (RunnerApi.TimestampTransform transform : trigger.getTimestampTransformsList()) { switch (transform.getTimestampTransformCase()) { case ALIGN_TO: stateMachine = stateMachine.alignedTo( Duration.millis(transform.getAlignTo().getPeriod()), new Instant(transform.getAlignTo().getOffset())); break; case DELAY: stateMachine = stateMachine.plusDelayOf(Duration.millis(transform.getDelay().getDelayMillis())); break; case TIMESTAMPTRANSFORM_NOT_SET: throw new IllegalArgumentException( String.format("Required field 'timestamp_transform' not set in %s", transform)); default: throw new IllegalArgumentException( String.format( "Unknown timestamp transform case: %s", transform.getTimestampTransformCase())); } } return stateMachine; } private static List<TriggerStateMachine> stateMachinesForTriggers( List<RunnerApi.Trigger> triggers) { List<TriggerStateMachine> stateMachines = new ArrayList<>(triggers.size()); for (RunnerApi.Trigger trigger : triggers) { stateMachines.add(stateMachineForTrigger(trigger)); } return stateMachines; } }