All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.uber.cadence.internal.testservice.StateMachines Maven / Gradle / Ivy

/*
 *  Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 *  Modifications copyright (C) 2017 Uber Technologies, Inc.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"). You may not
 *  use this file except in compliance with the License. A copy of the License is
 *  located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 *  or in the "license" file accompanying this file. This file 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.uber.cadence.internal.testservice;

import static com.uber.cadence.internal.testservice.StateMachines.Action.CANCEL;
import static com.uber.cadence.internal.testservice.StateMachines.Action.COMPLETE;
import static com.uber.cadence.internal.testservice.StateMachines.Action.CONTINUE_AS_NEW;
import static com.uber.cadence.internal.testservice.StateMachines.Action.FAIL;
import static com.uber.cadence.internal.testservice.StateMachines.Action.INITIATE;
import static com.uber.cadence.internal.testservice.StateMachines.Action.REQUEST_CANCELLATION;
import static com.uber.cadence.internal.testservice.StateMachines.Action.START;
import static com.uber.cadence.internal.testservice.StateMachines.Action.TIME_OUT;
import static com.uber.cadence.internal.testservice.StateMachines.Action.UPDATE;
import static com.uber.cadence.internal.testservice.StateMachines.State.CANCELED;
import static com.uber.cadence.internal.testservice.StateMachines.State.CANCELLATION_REQUESTED;
import static com.uber.cadence.internal.testservice.StateMachines.State.COMPLETED;
import static com.uber.cadence.internal.testservice.StateMachines.State.CONTINUED_AS_NEW;
import static com.uber.cadence.internal.testservice.StateMachines.State.FAILED;
import static com.uber.cadence.internal.testservice.StateMachines.State.INITIATED;
import static com.uber.cadence.internal.testservice.StateMachines.State.NONE;
import static com.uber.cadence.internal.testservice.StateMachines.State.STARTED;
import static com.uber.cadence.internal.testservice.StateMachines.State.TIMED_OUT;

import com.uber.cadence.ActivityTaskCancelRequestedEventAttributes;
import com.uber.cadence.ActivityTaskCanceledEventAttributes;
import com.uber.cadence.ActivityTaskCompletedEventAttributes;
import com.uber.cadence.ActivityTaskFailedEventAttributes;
import com.uber.cadence.ActivityTaskScheduledEventAttributes;
import com.uber.cadence.ActivityTaskStartedEventAttributes;
import com.uber.cadence.ActivityTaskTimedOutEventAttributes;
import com.uber.cadence.BadRequestError;
import com.uber.cadence.CancelTimerDecisionAttributes;
import com.uber.cadence.CancelWorkflowExecutionDecisionAttributes;
import com.uber.cadence.ChildWorkflowExecutionCanceledEventAttributes;
import com.uber.cadence.ChildWorkflowExecutionCompletedEventAttributes;
import com.uber.cadence.ChildWorkflowExecutionFailedCause;
import com.uber.cadence.ChildWorkflowExecutionFailedEventAttributes;
import com.uber.cadence.ChildWorkflowExecutionStartedEventAttributes;
import com.uber.cadence.ChildWorkflowExecutionTimedOutEventAttributes;
import com.uber.cadence.CompleteWorkflowExecutionDecisionAttributes;
import com.uber.cadence.ContinueAsNewWorkflowExecutionDecisionAttributes;
import com.uber.cadence.DecisionTaskCompletedEventAttributes;
import com.uber.cadence.DecisionTaskFailedEventAttributes;
import com.uber.cadence.DecisionTaskScheduledEventAttributes;
import com.uber.cadence.DecisionTaskStartedEventAttributes;
import com.uber.cadence.DecisionTaskTimedOutEventAttributes;
import com.uber.cadence.EntityNotExistsError;
import com.uber.cadence.EventType;
import com.uber.cadence.ExternalWorkflowExecutionSignaledEventAttributes;
import com.uber.cadence.FailWorkflowExecutionDecisionAttributes;
import com.uber.cadence.GetWorkflowExecutionHistoryRequest;
import com.uber.cadence.History;
import com.uber.cadence.HistoryEvent;
import com.uber.cadence.InternalServiceError;
import com.uber.cadence.PollForActivityTaskRequest;
import com.uber.cadence.PollForActivityTaskResponse;
import com.uber.cadence.PollForDecisionTaskRequest;
import com.uber.cadence.PollForDecisionTaskResponse;
import com.uber.cadence.RequestCancelActivityTaskDecisionAttributes;
import com.uber.cadence.RequestCancelWorkflowExecutionRequest;
import com.uber.cadence.RespondActivityTaskCanceledByIDRequest;
import com.uber.cadence.RespondActivityTaskCanceledRequest;
import com.uber.cadence.RespondActivityTaskCompletedByIDRequest;
import com.uber.cadence.RespondActivityTaskCompletedRequest;
import com.uber.cadence.RespondActivityTaskFailedByIDRequest;
import com.uber.cadence.RespondActivityTaskFailedRequest;
import com.uber.cadence.RespondDecisionTaskCompletedRequest;
import com.uber.cadence.RespondDecisionTaskFailedRequest;
import com.uber.cadence.RetryPolicy;
import com.uber.cadence.ScheduleActivityTaskDecisionAttributes;
import com.uber.cadence.SignalExternalWorkflowExecutionDecisionAttributes;
import com.uber.cadence.SignalExternalWorkflowExecutionFailedCause;
import com.uber.cadence.SignalExternalWorkflowExecutionFailedEventAttributes;
import com.uber.cadence.SignalExternalWorkflowExecutionInitiatedEventAttributes;
import com.uber.cadence.StartChildWorkflowExecutionDecisionAttributes;
import com.uber.cadence.StartChildWorkflowExecutionFailedEventAttributes;
import com.uber.cadence.StartChildWorkflowExecutionInitiatedEventAttributes;
import com.uber.cadence.StartTimerDecisionAttributes;
import com.uber.cadence.StartWorkflowExecutionRequest;
import com.uber.cadence.TimeoutType;
import com.uber.cadence.TimerCanceledEventAttributes;
import com.uber.cadence.TimerFiredEventAttributes;
import com.uber.cadence.TimerStartedEventAttributes;
import com.uber.cadence.WorkflowExecution;
import com.uber.cadence.WorkflowExecutionAlreadyStartedError;
import com.uber.cadence.WorkflowExecutionCancelRequestedEventAttributes;
import com.uber.cadence.WorkflowExecutionCanceledEventAttributes;
import com.uber.cadence.WorkflowExecutionCompletedEventAttributes;
import com.uber.cadence.WorkflowExecutionContinuedAsNewEventAttributes;
import com.uber.cadence.WorkflowExecutionFailedEventAttributes;
import com.uber.cadence.WorkflowExecutionStartedEventAttributes;
import com.uber.cadence.WorkflowExecutionTimedOutEventAttributes;
import com.uber.cadence.internal.testservice.TestWorkflowStore.ActivityTask;
import com.uber.cadence.internal.testservice.TestWorkflowStore.DecisionTask;
import com.uber.cadence.internal.testservice.TestWorkflowStore.TaskListId;
import java.util.List;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class StateMachines {

  private static final Logger log = LoggerFactory.getLogger(StateMachines.class);

  private static final int NO_EVENT_ID = -1;
  private static final String TIMEOUT_ERROR_REASON = "cadenceInternal:Timeout";

  enum State {
    NONE,
    INITIATED,
    STARTED,
    FAILED,
    TIMED_OUT,
    CANCELLATION_REQUESTED,
    CANCELED,
    COMPLETED,
    CONTINUED_AS_NEW,
  }

  enum Action {
    INITIATE,
    START,
    FAIL,
    TIME_OUT,
    REQUEST_CANCELLATION,
    CANCEL,
    UPDATE,
    COMPLETE,
    CONTINUE_AS_NEW
  }

  static final class WorkflowData {
    Optional retryState = Optional.empty();
    int backoffStartIntervalInSeconds;
    String cronSchedule;
    byte[] lastCompletionResult;

    WorkflowData(
        Optional retryState,
        int backoffStartIntervalInSeconds,
        String cronSchedule,
        byte[] lastCompletionResult) {
      this.retryState = retryState;
      this.backoffStartIntervalInSeconds = backoffStartIntervalInSeconds;
      this.cronSchedule = cronSchedule;
      this.lastCompletionResult = lastCompletionResult;
    }
  }

  static final class DecisionTaskData {

    final long previousStartedEventId;

    final TestWorkflowStore store;

    long startedEventId = NO_EVENT_ID;

    PollForDecisionTaskResponse decisionTask;

    long scheduledEventId = NO_EVENT_ID;

    int attempt;

    DecisionTaskData(long previousStartedEventId, TestWorkflowStore store) {
      this.previousStartedEventId = previousStartedEventId;
      this.store = store;
    }
  }

  static final class ActivityTaskData {

    StartWorkflowExecutionRequest startWorkflowExecutionRequest;
    ActivityTaskScheduledEventAttributes scheduledEvent;
    ActivityTask activityTask;

    final TestWorkflowStore store;

    long scheduledEventId = NO_EVENT_ID;
    long startedEventId = NO_EVENT_ID;
    public HistoryEvent startedEvent;
    byte[] heartbeatDetails;
    long lastHeartbeatTime;
    RetryState retryState;
    long nextBackoffIntervalSeconds;

    ActivityTaskData(
        TestWorkflowStore store, StartWorkflowExecutionRequest startWorkflowExecutionRequest) {
      this.store = store;
      this.startWorkflowExecutionRequest = startWorkflowExecutionRequest;
    }
  }

  static final class SignalExternalData {

    long initiatedEventId = NO_EVENT_ID;
    public SignalExternalWorkflowExecutionInitiatedEventAttributes initiatedEvent;
  }

  static final class ChildWorkflowData {

    final TestWorkflowService service;
    StartChildWorkflowExecutionInitiatedEventAttributes initiatedEvent;
    long initiatedEventId;
    long startedEventId;
    WorkflowExecution execution;

    public ChildWorkflowData(TestWorkflowService service) {
      this.service = service;
    }
  }

  static final class TimerData {

    TimerStartedEventAttributes startedEvent;
    public long startedEventId;
  }

  static StateMachine newWorkflowStateMachine(WorkflowData data) {
    return new StateMachine<>(data)
        .add(NONE, START, STARTED, StateMachines::startWorkflow)
        .add(STARTED, COMPLETE, COMPLETED, StateMachines::completeWorkflow)
        .add(STARTED, CONTINUE_AS_NEW, CONTINUED_AS_NEW, StateMachines::continueAsNewWorkflow)
        .add(STARTED, FAIL, FAILED, StateMachines::failWorkflow)
        .add(STARTED, TIME_OUT, TIMED_OUT, StateMachines::timeoutWorkflow)
        .add(
            STARTED,
            REQUEST_CANCELLATION,
            CANCELLATION_REQUESTED,
            StateMachines::requestWorkflowCancellation)
        .add(CANCELLATION_REQUESTED, COMPLETE, COMPLETED, StateMachines::completeWorkflow)
        .add(CANCELLATION_REQUESTED, CANCEL, CANCELED, StateMachines::cancelWorkflow)
        .add(CANCELLATION_REQUESTED, FAIL, FAILED, StateMachines::failWorkflow)
        .add(CANCELLATION_REQUESTED, TIME_OUT, TIMED_OUT, StateMachines::timeoutWorkflow);
  }

  static StateMachine newDecisionStateMachine(
      long previousStartedEventId, TestWorkflowStore store) {
    return new StateMachine<>(new DecisionTaskData(previousStartedEventId, store))
        .add(NONE, INITIATE, INITIATED, StateMachines::scheduleDecisionTask)
        .add(INITIATED, START, STARTED, StateMachines::startDecisionTask)
        .add(STARTED, COMPLETE, COMPLETED, StateMachines::completeDecisionTask)
        .add(STARTED, FAIL, FAILED, StateMachines::failDecisionTask)
        .add(STARTED, TIME_OUT, TIMED_OUT, StateMachines::timeoutDecisionTask)
        .add(TIMED_OUT, INITIATE, INITIATED, StateMachines::scheduleDecisionTask)
        .add(FAILED, INITIATE, INITIATED, StateMachines::scheduleDecisionTask);
  }

  public static StateMachine newActivityStateMachine(
      TestWorkflowStore store, StartWorkflowExecutionRequest workflowStartedEvent) {
    return new StateMachine<>(new ActivityTaskData(store, workflowStartedEvent))
        .add(NONE, INITIATE, INITIATED, StateMachines::scheduleActivityTask)
        .add(INITIATED, START, STARTED, StateMachines::startActivityTask)
        .add(INITIATED, TIME_OUT, TIMED_OUT, StateMachines::timeoutActivityTask)
        .add(
            INITIATED,
            REQUEST_CANCELLATION,
            CANCELLATION_REQUESTED,
            StateMachines::requestActivityCancellation)
        .add(STARTED, COMPLETE, COMPLETED, StateMachines::completeActivityTask)
        // Transitions to initiated in case of the a retry
        .add(STARTED, FAIL, new State[] {FAILED, INITIATED}, StateMachines::failActivityTask)
        // Transitions to initiated in case of a retry
        .add(
            STARTED,
            TIME_OUT,
            new State[] {TIMED_OUT, INITIATED},
            StateMachines::timeoutActivityTask)
        .add(STARTED, UPDATE, STARTED, StateMachines::heartbeatActivityTask)
        .add(
            STARTED,
            REQUEST_CANCELLATION,
            CANCELLATION_REQUESTED,
            StateMachines::requestActivityCancellation)
        .add(
            CANCELLATION_REQUESTED, CANCEL, CANCELED, StateMachines::reportActivityTaskCancellation)
        .add(CANCELLATION_REQUESTED, COMPLETE, COMPLETED, StateMachines::completeActivityTask)
        .add(
            CANCELLATION_REQUESTED,
            UPDATE,
            CANCELLATION_REQUESTED,
            StateMachines::heartbeatActivityTask)
        .add(CANCELLATION_REQUESTED, TIME_OUT, TIMED_OUT, StateMachines::timeoutActivityTask)
        .add(CANCELLATION_REQUESTED, FAIL, FAILED, StateMachines::failActivityTask);
  }

  public static StateMachine newChildWorkflowStateMachine(
      TestWorkflowService service) {
    return new StateMachine<>(new ChildWorkflowData(service))
        .add(NONE, INITIATE, INITIATED, StateMachines::initiateChildWorkflow)
        .add(INITIATED, START, STARTED, StateMachines::childWorkflowStarted)
        .add(INITIATED, FAIL, FAILED, StateMachines::startChildWorkflowFailed)
        .add(INITIATED, TIME_OUT, TIMED_OUT, StateMachines::timeoutChildWorkflow)
        .add(STARTED, COMPLETE, COMPLETED, StateMachines::childWorkflowCompleted)
        .add(STARTED, FAIL, FAILED, StateMachines::childWorkflowFailed)
        .add(STARTED, TIME_OUT, TIMED_OUT, StateMachines::timeoutChildWorkflow)
        .add(STARTED, CANCEL, CANCELED, StateMachines::childWorkflowCanceled);
  }

  public static StateMachine newTimerStateMachine() {
    return new StateMachine<>(new TimerData())
        .add(NONE, START, STARTED, StateMachines::startTimer)
        .add(STARTED, COMPLETE, COMPLETED, StateMachines::fireTimer)
        .add(STARTED, CANCEL, CANCELED, StateMachines::cancelTimer);
  }

  public static StateMachine newSignalExternalStateMachine() {
    return new StateMachine<>(new SignalExternalData())
        .add(NONE, INITIATE, INITIATED, StateMachines::initiateExternalSignal)
        .add(INITIATED, FAIL, FAILED, StateMachines::failExternalSignal)
        .add(INITIATED, COMPLETE, COMPLETED, StateMachines::completeExternalSignal);
  }

  private static void timeoutChildWorkflow(
      RequestContext ctx, ChildWorkflowData data, TimeoutType timeoutType, long notUsed) {
    StartChildWorkflowExecutionInitiatedEventAttributes ie = data.initiatedEvent;
    ChildWorkflowExecutionTimedOutEventAttributes a =
        new ChildWorkflowExecutionTimedOutEventAttributes()
            .setDomain(ie.getDomain())
            .setStartedEventId(data.startedEventId)
            .setWorkflowExecution(data.execution)
            .setWorkflowType(ie.getWorkflowType())
            .setTimeoutType(timeoutType)
            .setInitiatedEventId(data.initiatedEventId);
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.ChildWorkflowExecutionTimedOut)
            .setChildWorkflowExecutionTimedOutEventAttributes(a);
    ctx.addEvent(event);
  }

  private static void startChildWorkflowFailed(
      RequestContext ctx,
      ChildWorkflowData data,
      StartChildWorkflowExecutionFailedEventAttributes a,
      long notUsed) {
    a.setInitiatedEventId(data.initiatedEventId);
    a.setWorkflowType(data.initiatedEvent.getWorkflowType());
    a.setWorkflowId(data.initiatedEvent.getWorkflowId());
    if (data.initiatedEvent.isSetDomain()) {
      a.setDomain(data.initiatedEvent.getDomain());
    }
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.StartChildWorkflowExecutionFailed)
            .setStartChildWorkflowExecutionFailedEventAttributes(a);
    ctx.addEvent(event);
  }

  private static void childWorkflowStarted(
      RequestContext ctx,
      ChildWorkflowData data,
      ChildWorkflowExecutionStartedEventAttributes a,
      long notUsed) {
    a.setInitiatedEventId(data.initiatedEventId);
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.ChildWorkflowExecutionStarted)
            .setChildWorkflowExecutionStartedEventAttributes(a);
    long startedEventId = ctx.addEvent(event);
    ctx.onCommit(
        (historySize) -> {
          data.startedEventId = startedEventId;
          data.execution = a.getWorkflowExecution();
        });
  }

  private static void childWorkflowCompleted(
      RequestContext ctx,
      ChildWorkflowData data,
      ChildWorkflowExecutionCompletedEventAttributes a,
      long notUsed) {
    a.setInitiatedEventId(data.initiatedEventId).setStartedEventId(data.startedEventId);
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.ChildWorkflowExecutionCompleted)
            .setChildWorkflowExecutionCompletedEventAttributes(a);
    ctx.addEvent(event);
  }

  private static void childWorkflowFailed(
      RequestContext ctx,
      ChildWorkflowData data,
      ChildWorkflowExecutionFailedEventAttributes a,
      long notUsed) {
    a.setInitiatedEventId(data.initiatedEventId);
    a.setStartedEventId(data.startedEventId);
    a.setWorkflowExecution(data.execution);
    a.setWorkflowType(data.initiatedEvent.getWorkflowType());
    if (data.initiatedEvent.domain != null) {
      a.setDomain(data.initiatedEvent.domain);
    }
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.ChildWorkflowExecutionFailed)
            .setChildWorkflowExecutionFailedEventAttributes(a);
    ctx.addEvent(event);
  }

  private static void childWorkflowCanceled(
      RequestContext ctx,
      ChildWorkflowData data,
      ChildWorkflowExecutionCanceledEventAttributes a,
      long notUsed) {
    a.setInitiatedEventId(data.initiatedEventId);
    a.setStartedEventId(data.startedEventId);
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.ChildWorkflowExecutionCanceled)
            .setChildWorkflowExecutionCanceledEventAttributes(a);
    ctx.addEvent(event);
  }

  private static void initiateChildWorkflow(
      RequestContext ctx,
      ChildWorkflowData data,
      StartChildWorkflowExecutionDecisionAttributes d,
      long decisionTaskCompletedEventId) {
    StartChildWorkflowExecutionInitiatedEventAttributes a =
        new StartChildWorkflowExecutionInitiatedEventAttributes()
            .setControl(d.getControl())
            .setInput(d.getInput())
            .setChildPolicy(d.getChildPolicy())
            .setDecisionTaskCompletedEventId(decisionTaskCompletedEventId)
            .setDomain(d.getDomain() == null ? ctx.getDomain() : d.getDomain())
            .setExecutionStartToCloseTimeoutSeconds(d.getExecutionStartToCloseTimeoutSeconds())
            .setTaskStartToCloseTimeoutSeconds(d.getTaskStartToCloseTimeoutSeconds())
            .setTaskList(d.getTaskList())
            .setWorkflowId(d.getWorkflowId())
            .setWorkflowIdReusePolicy(d.getWorkflowIdReusePolicy())
            .setWorkflowType(d.getWorkflowType())
            .setRetryPolicy(d.getRetryPolicy())
            .setCronSchedule(d.getCronSchedule());
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.StartChildWorkflowExecutionInitiated)
            .setStartChildWorkflowExecutionInitiatedEventAttributes(a);
    long initiatedEventId = ctx.addEvent(event);
    ctx.onCommit(
        (historySize) -> {
          data.initiatedEventId = initiatedEventId;
          data.initiatedEvent = a;
          StartWorkflowExecutionRequest startChild =
              new StartWorkflowExecutionRequest()
                  .setDomain(d.getDomain() == null ? ctx.getDomain() : d.getDomain())
                  .setExecutionStartToCloseTimeoutSeconds(
                      d.getExecutionStartToCloseTimeoutSeconds())
                  .setTaskStartToCloseTimeoutSeconds(d.getTaskStartToCloseTimeoutSeconds())
                  .setTaskList(d.getTaskList())
                  .setWorkflowId(d.getWorkflowId())
                  .setWorkflowIdReusePolicy(d.getWorkflowIdReusePolicy())
                  .setWorkflowType(d.getWorkflowType())
                  .setRetryPolicy(d.getRetryPolicy())
                  .setCronSchedule(d.getCronSchedule());
          if (d.isSetInput()) {
            startChild.setInput(d.getInput());
          }
          addStartChildTask(ctx, data, initiatedEventId, startChild);
        });
  }

  private static void addStartChildTask(
      RequestContext ctx,
      ChildWorkflowData data,
      long initiatedEventId,
      StartWorkflowExecutionRequest startChild) {
    ForkJoinPool.commonPool()
        .execute(
            () -> {
              try {
                data.service.startWorkflowExecutionImpl(
                    startChild,
                    0,
                    Optional.of(ctx.getWorkflowMutableState()),
                    OptionalLong.of(data.initiatedEventId),
                    Optional.empty());
              } catch (WorkflowExecutionAlreadyStartedError workflowExecutionAlreadyStartedError) {
                StartChildWorkflowExecutionFailedEventAttributes failRequest =
                    new StartChildWorkflowExecutionFailedEventAttributes()
                        .setInitiatedEventId(initiatedEventId)
                        .setCause(ChildWorkflowExecutionFailedCause.WORKFLOW_ALREADY_RUNNING);
                try {
                  ctx.getWorkflowMutableState()
                      .failStartChildWorkflow(data.initiatedEvent.getWorkflowId(), failRequest);
                } catch (Throwable e) {
                  log.error("Unexpected failure inserting failStart for a child workflow", e);
                }
              } catch (Exception e) {
                log.error("Unexpected failure starting a child workflow", e);
              }
            });
  }

  private static void startWorkflow(
      RequestContext ctx, WorkflowData data, StartWorkflowExecutionRequest request, long notUsed)
      throws BadRequestError {
    WorkflowExecutionStartedEventAttributes a = new WorkflowExecutionStartedEventAttributes();
    if (request.isSetIdentity()) {
      a.setIdentity(request.getIdentity());
    }
    if (!request.isSetTaskStartToCloseTimeoutSeconds()) {
      throw new BadRequestError("missing taskStartToCloseTimeoutSeconds");
    }
    a.setTaskStartToCloseTimeoutSeconds(request.getTaskStartToCloseTimeoutSeconds());
    if (!request.isSetWorkflowType()) {
      throw new BadRequestError("missing workflowType");
    }
    a.setWorkflowType(request.getWorkflowType());
    if (!request.isSetTaskList()) {
      throw new BadRequestError("missing taskList");
    }
    a.setTaskList(request.getTaskList());
    if (!request.isSetExecutionStartToCloseTimeoutSeconds()) {
      throw new BadRequestError("missing executionStartToCloseTimeoutSeconds");
    }
    a.setExecutionStartToCloseTimeoutSeconds(request.getExecutionStartToCloseTimeoutSeconds());
    if (request.isSetInput()) {
      a.setInput(request.getInput());
    }
    if (data.retryState.isPresent()) {
      a.setAttempt(data.retryState.get().getAttempt());
    }
    a.setLastCompletionResult(data.lastCompletionResult);
    a.setMemo(request.getMemo());
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.WorkflowExecutionStarted)
            .setWorkflowExecutionStartedEventAttributes(a);
    ctx.addEvent(event);
  }

  private static void completeWorkflow(
      RequestContext ctx,
      WorkflowData data,
      CompleteWorkflowExecutionDecisionAttributes d,
      long decisionTaskCompletedEventId) {
    WorkflowExecutionCompletedEventAttributes a =
        new WorkflowExecutionCompletedEventAttributes()
            .setResult(d.getResult())
            .setDecisionTaskCompletedEventId(decisionTaskCompletedEventId);
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.WorkflowExecutionCompleted)
            .setWorkflowExecutionCompletedEventAttributes(a);
    ctx.addEvent(event);
  }

  private static void continueAsNewWorkflow(
      RequestContext ctx,
      WorkflowData data,
      ContinueAsNewWorkflowExecutionDecisionAttributes d,
      long decisionTaskCompletedEventId) {
    StartWorkflowExecutionRequest sr = ctx.getWorkflowMutableState().getStartRequest();
    WorkflowExecutionContinuedAsNewEventAttributes a =
        new WorkflowExecutionContinuedAsNewEventAttributes();
    a.setInput(d.getInput());
    if (d.isSetExecutionStartToCloseTimeoutSeconds()) {
      a.setExecutionStartToCloseTimeoutSeconds(d.getExecutionStartToCloseTimeoutSeconds());
    } else {
      a.setExecutionStartToCloseTimeoutSeconds(sr.getExecutionStartToCloseTimeoutSeconds());
    }
    if (d.isSetTaskList()) {
      a.setTaskList(d.getTaskList());
    } else {
      a.setTaskList(sr.getTaskList());
    }
    if (d.isSetWorkflowType()) {
      a.setWorkflowType(d.getWorkflowType());
    } else {
      a.setWorkflowType(sr.getWorkflowType());
    }
    if (d.isSetTaskStartToCloseTimeoutSeconds()) {
      a.setTaskStartToCloseTimeoutSeconds(d.getTaskStartToCloseTimeoutSeconds());
    } else {
      a.setTaskStartToCloseTimeoutSeconds(sr.getTaskStartToCloseTimeoutSeconds());
    }
    a.setDecisionTaskCompletedEventId(decisionTaskCompletedEventId);
    a.setBackoffStartIntervalInSeconds(d.getBackoffStartIntervalInSeconds());
    a.setLastCompletionResult(d.getLastCompletionResult());
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.WorkflowExecutionContinuedAsNew)
            .setWorkflowExecutionContinuedAsNewEventAttributes(a);
    ctx.addEvent(event);
  }

  private static void failWorkflow(
      RequestContext ctx,
      WorkflowData data,
      FailWorkflowExecutionDecisionAttributes d,
      long decisionTaskCompletedEventId) {
    WorkflowExecutionFailedEventAttributes a =
        new WorkflowExecutionFailedEventAttributes()
            .setReason(d.getReason())
            .setDetails(d.getDetails())
            .setDecisionTaskCompletedEventId(decisionTaskCompletedEventId);
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.WorkflowExecutionFailed)
            .setWorkflowExecutionFailedEventAttributes(a);
    ctx.addEvent(event);
  }

  private static void timeoutWorkflow(
      RequestContext ctx, WorkflowData data, TimeoutType timeoutType, long notUsed) {
    WorkflowExecutionTimedOutEventAttributes a =
        new WorkflowExecutionTimedOutEventAttributes().setTimeoutType(timeoutType);
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.WorkflowExecutionTimedOut)
            .setWorkflowExecutionTimedOutEventAttributes(a);
    ctx.addEvent(event);
  }

  private static void cancelWorkflow(
      RequestContext ctx,
      WorkflowData data,
      CancelWorkflowExecutionDecisionAttributes d,
      long decisionTaskCompletedEventId) {
    WorkflowExecutionCanceledEventAttributes a =
        new WorkflowExecutionCanceledEventAttributes()
            .setDetails(d.getDetails())
            .setDecisionTaskCompletedEventId(decisionTaskCompletedEventId);
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.WorkflowExecutionCanceled)
            .setWorkflowExecutionCanceledEventAttributes(a);
    ctx.addEvent(event);
  }

  private static void requestWorkflowCancellation(
      RequestContext ctx,
      WorkflowData data,
      RequestCancelWorkflowExecutionRequest cancelRequest,
      long notUsed) {
    WorkflowExecutionCancelRequestedEventAttributes a =
        new WorkflowExecutionCancelRequestedEventAttributes()
            .setIdentity(cancelRequest.getIdentity());
    HistoryEvent cancelRequested =
        new HistoryEvent()
            .setEventType(EventType.WorkflowExecutionCancelRequested)
            .setWorkflowExecutionCancelRequestedEventAttributes(a);
    ctx.addEvent(cancelRequested);
  }

  private static void scheduleActivityTask(
      RequestContext ctx,
      ActivityTaskData data,
      ScheduleActivityTaskDecisionAttributes d,
      long decisionTaskCompletedEventId)
      throws BadRequestError {
    int scheduleToCloseTimeoutSeconds = d.getScheduleToCloseTimeoutSeconds();
    int scheduleToStartTimeoutSeconds = d.getScheduleToStartTimeoutSeconds();
    RetryState retryState;
    RetryPolicy retryPolicy = d.getRetryPolicy();
    if (retryPolicy != null) {
      long expirationInterval =
          TimeUnit.SECONDS.toMillis(retryPolicy.getExpirationIntervalInSeconds());
      long expirationTime = data.store.currentTimeMillis() + expirationInterval;
      retryState = new RetryState(retryPolicy, expirationTime);
      // Override activity timeouts to allow retry policy to run up to its expiration.
      int overriddenTimeout;
      if (retryPolicy.getExpirationIntervalInSeconds() > 0) {
        overriddenTimeout = retryPolicy.getExpirationIntervalInSeconds();
      } else {
        overriddenTimeout =
            data.startWorkflowExecutionRequest.getExecutionStartToCloseTimeoutSeconds();
      }
      scheduleToCloseTimeoutSeconds = overriddenTimeout;
      scheduleToStartTimeoutSeconds = overriddenTimeout;
    } else {
      retryState = null;
    }

    ActivityTaskScheduledEventAttributes a =
        new ActivityTaskScheduledEventAttributes()
            .setInput(d.getInput())
            .setActivityId(d.getActivityId())
            .setActivityType(d.getActivityType())
            .setDomain(d.getDomain() == null ? ctx.getDomain() : d.getDomain())
            .setHeartbeatTimeoutSeconds(d.getHeartbeatTimeoutSeconds())
            .setScheduleToCloseTimeoutSeconds(scheduleToCloseTimeoutSeconds)
            .setScheduleToStartTimeoutSeconds(scheduleToStartTimeoutSeconds)
            .setStartToCloseTimeoutSeconds(d.getStartToCloseTimeoutSeconds())
            .setTaskList(d.getTaskList())
            .setRetryPolicy(retryPolicy)
            .setDecisionTaskCompletedEventId(decisionTaskCompletedEventId);
    data.scheduledEvent =
        a; // Cannot set it in onCommit as it is used in the processScheduleActivityTask
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.ActivityTaskScheduled)
            .setActivityTaskScheduledEventAttributes(a);
    long scheduledEventId = ctx.addEvent(event);

    PollForActivityTaskResponse taskResponse =
        new PollForActivityTaskResponse()
            .setActivityType(d.getActivityType())
            .setWorkflowExecution(ctx.getExecution())
            .setActivityId(d.getActivityId())
            .setInput(d.getInput())
            .setHeartbeatTimeoutSeconds(d.getHeartbeatTimeoutSeconds())
            .setScheduleToCloseTimeoutSeconds(scheduleToCloseTimeoutSeconds)
            .setStartToCloseTimeoutSeconds(d.getStartToCloseTimeoutSeconds())
            .setScheduledTimestamp(ctx.currentTimeInNanoseconds())
            .setAttempt(0);

    TaskListId taskListId = new TaskListId(ctx.getDomain(), d.getTaskList().getName());
    ActivityTask activityTask = new ActivityTask(taskListId, taskResponse);
    ctx.addActivityTask(activityTask);
    ctx.onCommit(
        (historySize) -> {
          data.scheduledEventId = scheduledEventId;
          data.activityTask = activityTask;
          data.retryState = retryState;
        });
  }

  private static void requestActivityCancellation(
      RequestContext ctx,
      ActivityTaskData data,
      RequestCancelActivityTaskDecisionAttributes d,
      long decisionTaskCompletedEventId) {
    ActivityTaskCancelRequestedEventAttributes a =
        new ActivityTaskCancelRequestedEventAttributes()
            .setActivityId(d.getActivityId())
            .setDecisionTaskCompletedEventId(decisionTaskCompletedEventId);
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.ActivityTaskCancelRequested)
            .setActivityTaskCancelRequestedEventAttributes(a);
    ctx.addEvent(event);
  }

  private static void scheduleDecisionTask(
      RequestContext ctx,
      DecisionTaskData data,
      StartWorkflowExecutionRequest request,
      long notUsed) {
    DecisionTaskScheduledEventAttributes a =
        new DecisionTaskScheduledEventAttributes()
            .setStartToCloseTimeoutSeconds(request.getTaskStartToCloseTimeoutSeconds())
            .setTaskList(request.getTaskList())
            .setAttempt(data.attempt);
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.DecisionTaskScheduled)
            .setDecisionTaskScheduledEventAttributes(a);
    long scheduledEventId = ctx.addEvent(event);
    PollForDecisionTaskResponse decisionTaskResponse = new PollForDecisionTaskResponse();
    if (data.previousStartedEventId > 0) {
      decisionTaskResponse.setPreviousStartedEventId(data.previousStartedEventId);
    }
    decisionTaskResponse.setWorkflowExecution(ctx.getExecution());
    decisionTaskResponse.setWorkflowType(request.getWorkflowType());
    decisionTaskResponse.setAttempt(data.attempt);
    TaskListId taskListId = new TaskListId(ctx.getDomain(), request.getTaskList().getName());
    DecisionTask decisionTask = new DecisionTask(taskListId, decisionTaskResponse);
    ctx.setDecisionTask(decisionTask);
    ctx.onCommit(
        (historySize) -> {
          data.scheduledEventId = scheduledEventId;
          data.decisionTask = decisionTaskResponse;
        });
  }

  private static void startDecisionTask(
      RequestContext ctx, DecisionTaskData data, PollForDecisionTaskRequest request, long notUsed) {
    DecisionTaskStartedEventAttributes a =
        new DecisionTaskStartedEventAttributes()
            .setIdentity(request.getIdentity())
            .setScheduledEventId(data.scheduledEventId);
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.DecisionTaskStarted)
            .setDecisionTaskStartedEventAttributes(a);
    long startedEventId = ctx.addEvent(event);
    ctx.onCommit(
        (historySize) -> {
          data.decisionTask.setStartedEventId(startedEventId);
          DecisionTaskToken taskToken = new DecisionTaskToken(ctx.getExecutionId(), historySize);
          data.decisionTask.setTaskToken(taskToken.toBytes());
          GetWorkflowExecutionHistoryRequest getRequest =
              new GetWorkflowExecutionHistoryRequest()
                  .setDomain(request.getDomain())
                  .setExecution(ctx.getExecution());
          List events;
          try {
            events =
                data.store
                    .getWorkflowExecutionHistory(ctx.getExecutionId(), getRequest)
                    .getHistory()
                    .getEvents();

            if (ctx.getWorkflowMutableState().getStickyExecutionAttributes() != null) {
              events = events.subList((int) data.previousStartedEventId, events.size());
            }
            // get it from pervious started event id.
          } catch (EntityNotExistsError entityNotExistsError) {
            throw new InternalServiceError(entityNotExistsError.toString());
          }
          data.decisionTask.setHistory(new History().setEvents(events));
          data.startedEventId = startedEventId;
          data.attempt++;
        });
  }

  private static void startActivityTask(
      RequestContext ctx, ActivityTaskData data, PollForActivityTaskRequest request, long notUsed) {
    ActivityTaskStartedEventAttributes a =
        new ActivityTaskStartedEventAttributes()
            .setIdentity(request.getIdentity())
            .setScheduledEventId(data.scheduledEventId);
    if (data.retryState != null) {
      a.setAttempt(data.retryState.getAttempt());
    }
    // Setting timestamp here as the default logic will set it to the time when it is added to the
    // history. But in the case of retry it happens only after an activity completion.
    long timestamp = TimeUnit.MILLISECONDS.toNanos(data.store.currentTimeMillis());
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.ActivityTaskStarted)
            .setTimestamp(timestamp)
            .setActivityTaskStartedEventAttributes(a);
    long startedEventId;
    if (data.retryState == null) {
      startedEventId = ctx.addEvent(event);
    } else {
      startedEventId = NO_EVENT_ID;
    }
    ctx.onCommit(
        (historySize) -> {
          data.startedEventId = startedEventId;
          data.startedEvent = event;
          PollForActivityTaskResponse task = data.activityTask.getTask();
          task.setTaskToken(new ActivityId(ctx.getExecutionId(), task.getActivityId()).toBytes());
          task.setStartedTimestamp(timestamp);
        });
  }

  private static void completeDecisionTask(
      RequestContext ctx,
      DecisionTaskData data,
      RespondDecisionTaskCompletedRequest request,
      long notUsed) {
    DecisionTaskCompletedEventAttributes a =
        new DecisionTaskCompletedEventAttributes()
            .setIdentity(request.getIdentity())
            .setScheduledEventId(data.scheduledEventId);
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.DecisionTaskCompleted)
            .setDecisionTaskCompletedEventAttributes(a);
    ctx.addEvent(event);
    ctx.onCommit((historySize) -> data.attempt = 0);
  }

  private static void failDecisionTask(
      RequestContext ctx,
      DecisionTaskData data,
      RespondDecisionTaskFailedRequest request,
      long notUsed) {
    DecisionTaskFailedEventAttributes a =
        new DecisionTaskFailedEventAttributes()
            .setIdentity(request.getIdentity())
            .setCause(request.getCause())
            .setDetails(request.getDetails())
            .setStartedEventId(data.startedEventId)
            .setScheduledEventId(data.scheduledEventId);
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.DecisionTaskFailed)
            .setDecisionTaskFailedEventAttributes(a);
    ctx.addEvent(event);
  }

  private static void timeoutDecisionTask(
      RequestContext ctx, DecisionTaskData data, Object ignored, long notUsed) {
    DecisionTaskTimedOutEventAttributes a =
        new DecisionTaskTimedOutEventAttributes()
            .setStartedEventId(data.startedEventId)
            .setTimeoutType(TimeoutType.START_TO_CLOSE)
            .setScheduledEventId(data.scheduledEventId);
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.DecisionTaskTimedOut)
            .setDecisionTaskTimedOutEventAttributes(a);
    ctx.addEvent(event);
  }

  private static void completeActivityTask(
      RequestContext ctx, ActivityTaskData data, Object request, long notUsed) {
    if (data.retryState != null) {
      ctx.addEvent(data.startedEvent);
    }
    if (request instanceof RespondActivityTaskCompletedRequest) {
      completeActivityTaskByTaskToken(ctx, data, (RespondActivityTaskCompletedRequest) request);
    } else if (request instanceof RespondActivityTaskCompletedByIDRequest) {
      completeActivityTaskById(ctx, data, (RespondActivityTaskCompletedByIDRequest) request);
    } else {
      throw new IllegalArgumentException("Unknown request: " + request);
    }
  }

  private static void completeActivityTaskByTaskToken(
      RequestContext ctx, ActivityTaskData data, RespondActivityTaskCompletedRequest request) {
    ActivityTaskCompletedEventAttributes a =
        new ActivityTaskCompletedEventAttributes()
            .setIdentity(request.getIdentity())
            .setScheduledEventId(data.scheduledEventId)
            .setResult(request.getResult())
            .setIdentity(request.getIdentity())
            .setStartedEventId(data.startedEventId);
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.ActivityTaskCompleted)
            .setActivityTaskCompletedEventAttributes(a);
    ctx.addEvent(event);
  }

  private static void completeActivityTaskById(
      RequestContext ctx, ActivityTaskData data, RespondActivityTaskCompletedByIDRequest request) {
    ActivityTaskCompletedEventAttributes a =
        new ActivityTaskCompletedEventAttributes()
            .setIdentity(request.getIdentity())
            .setScheduledEventId(data.scheduledEventId)
            .setResult(request.getResult())
            .setIdentity(request.getIdentity())
            .setStartedEventId(data.startedEventId);
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.ActivityTaskCompleted)
            .setActivityTaskCompletedEventAttributes(a);
    ctx.addEvent(event);
  }

  private static State failActivityTask(
      RequestContext ctx, ActivityTaskData data, Object request, long notUsed) {
    if (request instanceof RespondActivityTaskFailedRequest) {
      return failActivityTaskByTaskToken(ctx, data, (RespondActivityTaskFailedRequest) request);
    } else if (request instanceof RespondActivityTaskFailedByIDRequest) {
      return failActivityTaskById(ctx, data, (RespondActivityTaskFailedByIDRequest) request);
    } else {
      throw new IllegalArgumentException("Unknown request: " + request);
    }
  }

  private static State failActivityTaskByTaskToken(
      RequestContext ctx, ActivityTaskData data, RespondActivityTaskFailedRequest request) {
    if (attemptActivityRetry(ctx, request.getReason(), data)) {
      return INITIATED;
    }
    ActivityTaskFailedEventAttributes a =
        new ActivityTaskFailedEventAttributes()
            .setIdentity(request.getIdentity())
            .setScheduledEventId(data.scheduledEventId)
            .setDetails(request.getDetails())
            .setReason(request.getReason())
            .setIdentity(request.getIdentity())
            .setStartedEventId(data.startedEventId);
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.ActivityTaskFailed)
            .setActivityTaskFailedEventAttributes(a);
    ctx.addEvent(event);
    return FAILED;
  }

  private static State failActivityTaskById(
      RequestContext ctx, ActivityTaskData data, RespondActivityTaskFailedByIDRequest request) {
    if (attemptActivityRetry(ctx, request.getReason(), data)) {
      return INITIATED;
    }
    ActivityTaskFailedEventAttributes a =
        new ActivityTaskFailedEventAttributes()
            .setIdentity(request.getIdentity())
            .setScheduledEventId(data.scheduledEventId)
            .setDetails(request.getDetails())
            .setReason(request.getReason())
            .setIdentity(request.getIdentity())
            .setStartedEventId(data.startedEventId);
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.ActivityTaskFailed)
            .setActivityTaskFailedEventAttributes(a);
    ctx.addEvent(event);
    return FAILED;
  }

  private static State timeoutActivityTask(
      RequestContext ctx, ActivityTaskData data, TimeoutType timeoutType, long notUsed) {
    // ScheduleToStart (queue timeout) is not retriable. Instead of the retry, a customer should set
    // a larger ScheduleToStart timeout.
    if (timeoutType != TimeoutType.SCHEDULE_TO_START
        && attemptActivityRetry(ctx, TIMEOUT_ERROR_REASON, data)) {
      return INITIATED;
    }
    ActivityTaskTimedOutEventAttributes a =
        new ActivityTaskTimedOutEventAttributes()
            .setScheduledEventId(data.scheduledEventId)
            .setDetails(data.heartbeatDetails)
            .setTimeoutType(timeoutType)
            .setStartedEventId(data.startedEventId);
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.ActivityTaskTimedOut)
            .setActivityTaskTimedOutEventAttributes(a);
    ctx.addEvent(event);
    return TIMED_OUT;
  }

  private static boolean attemptActivityRetry(
      RequestContext ctx, String errorReason, ActivityTaskData data) {
    if (data.retryState != null) {
      RetryState nextAttempt = data.retryState.getNextAttempt();
      data.nextBackoffIntervalSeconds =
          data.retryState.getBackoffIntervalInSeconds(errorReason, data.store.currentTimeMillis());
      if (data.nextBackoffIntervalSeconds > 0) {
        data.activityTask.getTask().setHeartbeatDetails(data.heartbeatDetails);
        ctx.onCommit(
            (historySize) -> {
              data.retryState = nextAttempt;
              data.activityTask.getTask().setAttempt(nextAttempt.getAttempt());
            });
        return true;
      } else {
        data.startedEventId = ctx.addEvent(data.startedEvent);
      }
    }
    return false;
  }

  private static void reportActivityTaskCancellation(
      RequestContext ctx, ActivityTaskData data, Object request, long notUsed) {
    byte[] details = null;
    if (request instanceof RespondActivityTaskCanceledRequest) {
      details = ((RespondActivityTaskCanceledRequest) request).getDetails();
    } else if (request instanceof RespondActivityTaskCanceledByIDRequest) {
      details = ((RespondActivityTaskCanceledByIDRequest) request).getDetails();
    }
    ActivityTaskCanceledEventAttributes a =
        new ActivityTaskCanceledEventAttributes()
            .setScheduledEventId(data.scheduledEventId)
            .setStartedEventId(data.startedEventId);
    if (details != null) {
      a.setDetails(details);
    }
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.ActivityTaskCanceled)
            .setActivityTaskCanceledEventAttributes(a);
    ctx.addEvent(event);
  }

  private static void heartbeatActivityTask(
      RequestContext nullCtx, ActivityTaskData data, byte[] details, long notUsed) {
    data.heartbeatDetails = details;
  }

  private static void startTimer(
      RequestContext ctx,
      TimerData data,
      StartTimerDecisionAttributes d,
      long decisionTaskCompletedEventId) {
    TimerStartedEventAttributes a =
        new TimerStartedEventAttributes()
            .setDecisionTaskCompletedEventId(decisionTaskCompletedEventId)
            .setStartToFireTimeoutSeconds(d.getStartToFireTimeoutSeconds())
            .setTimerId(d.getTimerId());
    HistoryEvent event =
        new HistoryEvent().setEventType(EventType.TimerStarted).setTimerStartedEventAttributes(a);
    long startedEventId = ctx.addEvent(event);
    ctx.onCommit(
        (historySize) -> {
          data.startedEvent = a;
          data.startedEventId = startedEventId;
        });
  }

  private static void fireTimer(RequestContext ctx, TimerData data, Object ignored, long notUsed) {
    TimerFiredEventAttributes a =
        new TimerFiredEventAttributes()
            .setTimerId(data.startedEvent.getTimerId())
            .setStartedEventId(data.startedEventId);
    HistoryEvent event =
        new HistoryEvent().setEventType(EventType.TimerFired).setTimerFiredEventAttributes(a);
    ctx.addEvent(event);
  }

  private static void cancelTimer(
      RequestContext ctx,
      TimerData data,
      CancelTimerDecisionAttributes d,
      long decisionTaskCompletedEventId) {
    TimerCanceledEventAttributes a =
        new TimerCanceledEventAttributes()
            .setDecisionTaskCompletedEventId(decisionTaskCompletedEventId)
            .setTimerId(d.getTimerId())
            .setStartedEventId(data.startedEventId);
    HistoryEvent event =
        new HistoryEvent().setEventType(EventType.TimerCanceled).setTimerCanceledEventAttributes(a);
    ctx.addEvent(event);
  }

  private static void initiateExternalSignal(
      RequestContext ctx,
      SignalExternalData data,
      SignalExternalWorkflowExecutionDecisionAttributes d,
      long decisionTaskCompletedEventId) {
    SignalExternalWorkflowExecutionInitiatedEventAttributes a =
        new SignalExternalWorkflowExecutionInitiatedEventAttributes();
    a.setDecisionTaskCompletedEventId(decisionTaskCompletedEventId);
    if (d.isSetControl()) {
      a.setControl(d.getControl());
    }
    if (d.isSetInput()) {
      a.setInput(d.getInput());
    }
    if (d.isSetDomain()) {
      a.setDomain(d.getDomain());
    }
    if (d.isSetChildWorkflowOnly()) {
      a.setChildWorkflowOnly(d.isChildWorkflowOnly());
    }
    a.setSignalName(d.getSignalName());
    a.setWorkflowExecution(d.getExecution());
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.SignalExternalWorkflowExecutionInitiated)
            .setSignalExternalWorkflowExecutionInitiatedEventAttributes(a);
    long initiatedEventId = ctx.addEvent(event);
    ctx.onCommit(
        (historySize) -> {
          data.initiatedEventId = initiatedEventId;
          data.initiatedEvent = a;
        });
  }

  private static void failExternalSignal(
      RequestContext ctx,
      SignalExternalData data,
      SignalExternalWorkflowExecutionFailedCause cause,
      long notUsed) {
    SignalExternalWorkflowExecutionInitiatedEventAttributes initiatedEvent = data.initiatedEvent;
    SignalExternalWorkflowExecutionFailedEventAttributes a =
        new SignalExternalWorkflowExecutionFailedEventAttributes()
            .setInitiatedEventId(data.initiatedEventId)
            .setWorkflowExecution(initiatedEvent.getWorkflowExecution())
            .setControl(initiatedEvent.getControl())
            .setCause(cause)
            .setDomain(initiatedEvent.getDomain());
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.SignalExternalWorkflowExecutionFailed)
            .setSignalExternalWorkflowExecutionFailedEventAttributes(a);
    ctx.addEvent(event);
  }

  private static void completeExternalSignal(
      RequestContext ctx, SignalExternalData data, String runId, long notUsed) {
    SignalExternalWorkflowExecutionInitiatedEventAttributes initiatedEvent = data.initiatedEvent;
    WorkflowExecution signaledExecution =
        initiatedEvent.getWorkflowExecution().deepCopy().setRunId(runId);
    ExternalWorkflowExecutionSignaledEventAttributes a =
        new ExternalWorkflowExecutionSignaledEventAttributes()
            .setInitiatedEventId(data.initiatedEventId)
            .setWorkflowExecution(signaledExecution)
            .setControl(initiatedEvent.getControl())
            .setDomain(initiatedEvent.getDomain());
    HistoryEvent event =
        new HistoryEvent()
            .setEventType(EventType.ExternalWorkflowExecutionSignaled)
            .setExternalWorkflowExecutionSignaledEventAttributes(a);
    ctx.addEvent(event);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy