/// <summary> /// Constructor for the workflow event processor. /// </summary> /// <param name="decisionTask">Decision task passed in from SWF as decision task response.</param> /// <param name="workflows">IEnumerable set of string for workflow name and Type for workflow class.</param> /// <param name="request">The request used to retrieve <paramref name="decisionTask"/>, which will be used to retrieve subsequent history event pages.</param> /// <param name="swfClient">An SWF client.</param> public WorkflowEventsProcessor(DecisionTask decisionTask, IEnumerable<KeyValuePair<string, Type>> workflows, PollForDecisionTaskRequest request, IAmazonSimpleWorkflow swfClient) { // Decision task can't be null. if (decisionTask == null) { throw new ArgumentNullException("decisionTask"); } if (request == null) { throw new ArgumentNullException("request"); } // Store the decision task and allocate a new decision context and event dictionary which // we will use as we walk through the chain of events _decisionTask = decisionTask; _request = request; _decisionContext = new WorkflowDecisionContext(); _swfClient = swfClient; // Set up our events data structure _events = new WorkflowEventsIterator(ref decisionTask, _request, _swfClient); _workflows = (Dictionary<string, Type>) workflows; }
public override List<Decision> Decide(DecisionTask task) { var decisions = new List<Decision>(); List<ActivityState> activityStates; string startingInput; //ProcessHistory(task, out startingInput, out activityStates); HistoryIterator iterator = new HistoryIterator(this.Client, task); foreach (var evnt in iterator) { if (evnt.EventType == EventType.WorkflowExecutionStarted) { //startingInput = evnt.WorkflowExecutionStartedEventAttributes.Input; //this.Deserialize<WorkFlowExecutionInput>(evnt.WorkflowExecutionStartedEventAttributes.Input); decisions.Add(new Decision() { DecisionType = DecisionType.ScheduleActivityTask, ScheduleActivityTaskDecisionAttributes = new ScheduleActivityTaskDecisionAttributes() { ActivityType = new ActivityType() { Name = Constants.ActivityName, Version = Constants.ActivityVersion }, ActivityId = Constants.ActivityIdPrefix + DateTime.Now.TimeOfDay, Input = evnt.WorkflowExecutionStartedEventAttributes.Input } }); } if (evnt.EventType == EventType.ActivityTaskCompleted) { //ActivityState state = this.Deserialize<ActivityState>(evnt.ActivityTaskCompletedEventAttributes.Result); //activityStates.Add(state); decisions.Add(new Decision() { DecisionType = DecisionType.CompleteWorkflowExecution, CompleteWorkflowExecutionDecisionAttributes = new CompleteWorkflowExecutionDecisionAttributes { Result = "We are done" } }); } } //if (decisions.Count == 0) //{ // decisions.Add(new Decision() // { // DecisionType = DecisionType.CompleteWorkflowExecution, // CompleteWorkflowExecutionDecisionAttributes = new CompleteWorkflowExecutionDecisionAttributes // { // Result = "We are done" // } // }); //} return decisions; }
void ProcessHistory(DecisionTask task, out string startingInput, out List<ActivityState> activityStates) { startingInput = null; activityStates = new List<ActivityState>(); HistoryIterator iterator = new HistoryIterator(this.Client, task); foreach (var evnt in iterator) { if (evnt.EventType == EventType.WorkflowExecutionStarted) { startingInput = evnt.WorkflowExecutionStartedEventAttributes.Input; //this.Deserialize<WorkFlowExecutionInput>(evnt.WorkflowExecutionStartedEventAttributes.Input); } if (evnt.EventType == EventType.ActivityTaskCompleted) { ActivityState state = this.Deserialize<ActivityState>(evnt.ActivityTaskCompletedEventAttributes.Result); activityStates.Add(state); } } }
/// <summary> /// Process the history of events to find all completed activity events and the start event. Using that we can find out /// what image is being resized and what images still need to be completed. /// </summary> /// <param name="task"></param> /// <param name="startingInput"></param> /// <param name="activityStates"></param> void ProcessHistory(DecisionTask task, out Common.WorkflowExecutionStartedInput startingInput, out List<Common.ActivityTaskCompletedResult> activityStates) { startingInput = null; activityStates = new List<Common.ActivityTaskCompletedResult>(); Dictionary<long, Common.ActivityTaskCompletedResult> timerActivityStates = new Dictionary<long, Common.ActivityTaskCompletedResult>(); Shared.HistoryIterator iterator = new Shared.HistoryIterator(this._swfClient, task); foreach (var evnt in iterator) { if (evnt.EventType == EventType.TimerStarted) { timerActivityStates.Add(long.Parse(evnt.TimerStartedEventAttributes.TimerId), Common.Utils.DeserializeFromJSON<Common.ActivityTaskCompletedResult>(evnt.TimerStartedEventAttributes.Control)); } if (evnt.EventType == EventType.TimerFired) { activityStates.Add(timerActivityStates[long.Parse(evnt.TimerFiredEventAttributes.TimerId)]); } if (evnt.EventType == EventType.WorkflowExecutionStarted) { startingInput = Common.Utils.DeserializeFromJSON<Common.WorkflowExecutionStartedInput>(evnt.WorkflowExecutionStartedEventAttributes.Input); } if (evnt.EventType == EventType.ActivityTaskCompleted) { Common.ActivityTaskCompletedResult state = Common.Utils.DeserializeFromJSON<Common.ActivityTaskCompletedResult>(evnt.ActivityTaskCompletedEventAttributes.Result); activityStates.Add(state); } } }
/// <summary> /// Looks at the events on the task to find all the completed activities. Using the list of completed activites it figures out /// what thumbnail hasn't been created yet and create a decision to start an activity task for that image size. /// </summary> /// <param name="task"></param> /// <returns></returns> List<Decision> Decide(DecisionTask task) { Console.WriteLine("Processing decision task ..."); List<Decision> decisions = new List<Decision>(); List<Common.ActivityTaskCompletedResult> activityStates; Common.WorkflowExecutionStartedInput startingInput; ProcessHistory(task, out startingInput, out activityStates); //get the current request... Request RequestCurrent = _tracker.GetRequestWithActions(startingInput.RequestId); //if the request has no current state. initialize the request... if (RequestCurrent.CurrentStateId == null) { _tracker.RequestInitialize(startingInput.RequestId); //the request should have a new state and actions... //RequestCurrent = _tracker.GetRequestWithActions(startingInput.RequestId); //code to tell SWF to fire activity for the new state //decisions.Add(CreateActivityDecision(startingInput, RequestCurrent.RequestActions.Where(x => (x.ActionTypeId == Common.Constants.AWSActivityTypeId) && x.IsActive && !x.IsComplete).FirstOrDefault().RequestActionId ?? 0)); decisions = DecideHelper(startingInput, activityStates); } //compare the "active" SWF request actions that are not "complete" against the history //this line finds the "intersection" of those two lists //(filter this list down to only Simple Workflow actions) List<Common.ActivityTaskCompletedResult> results = activityStates.Join(RequestCurrent.RequestActions.Where(x => (x.ActionTypeId == Common.Constants.AWSActivityTypeId || x.ActionTypeId == 2) && x.IsActive && !x.IsComplete).ToList(), a => a.RequestActionId, b => b.RequestActionId, (c, d) => c).ToList(); //mark any "active" but not "complete" request actions as "complete" foreach (Common.ActivityTaskCompletedResult result in results) { _tracker.RequestActionComplete(result.RequestActionId); } //check whether the request's actions have reached a point where a transition can be followed... Transition Transition = _tracker.GetCompletedTransitionForRequest(startingInput.RequestId); //if a transition can be followed... if (Transition != null) { //follow the transition _tracker.RequestFollowTransition(startingInput.RequestId, Transition.TransitionId); ////the request should have a new state and actions now... //RequestCurrent = _tracker.GetRequestWithActions(startingInput.RequestId); //IEnumerable<RequestAction> ActiveNotComplete = RequestCurrent.RequestActions.Where(x => (x.ActionTypeId == Common.Constants.AWSActivityTypeId || x.ActionTypeId == 2) && x.IsActive && !x.IsComplete); ////if a request action was found that was "active" but not "complete" it must be run now //if (ActiveNotComplete.Count() != 0) //{ // //iterate through the active but not complete actions... // foreach (RequestAction ra in ActiveNotComplete) // { // if (ra.ActionTypeId == 1) //if state completed... // { // //code to tell SWF to fire activity for the new state // decisions.Add(CreateActivityDecision(startingInput, ra.RequestActionId ?? 0)); // } // if (ra.ActionTypeId == 2) //if timer // { // // setup the input for the activity task. // Common.ActivityTaskCompletedResult state = new Common.ActivityTaskCompletedResult // { // StartingInput = startingInput, // RequestActionId = ra.RequestActionId ?? 0 // }; // Decision decision = new Decision() // { // DecisionType = DecisionType.StartTimer, // StartTimerDecisionAttributes = new StartTimerDecisionAttributes() // { // Control = Common.Utils.SerializeToJSON<Common.ActivityTaskCompletedResult>(state), // StartToFireTimeout = "3", // TimerId = DateTime.Now.Ticks.ToString() // } // }; // decisions.Add(decision); // } // } //} //else //{ // Console.WriteLine("Workflow execution complete for " + startingInput.RequestId); // //code to tell SWF to complete workflow execution // decisions.Add(CreateCompleteWorkflowExecutionDecision(activityStates)); //} decisions = DecideHelper(startingInput, activityStates); } //// Loop through all the diffrent image sizes the workflow will create and //// when we find one missing create that activity task for the missing image. //// //// To keep the sample simple each activity is scheduled one at a time. For better performance //// the activities could be scheduled in parallel and then use the decider to check if all the activities //// have been completed. //for (int size = 256; size >= 16; size /= 2) //{ // if (activityStates.FirstOrDefault(x => x.ImageSize == size) == null) // { // decisions.Add(CreateActivityDecision(startingInput, size)); // break; // } //} //// If there were no decisions that means all the thumbnails have been created so we decided the workflow execution is complete. //if (decisions.Count == 0) //{ // Console.WriteLine("Workflow execution complete for " + startingInput.SourceImageKey); // decisions.Add(CreateCompleteWorkflowExecutionDecision(activityStates)); //} return decisions; }
List<Decision> Decide(DecisionTask task) { try { List<Decision> decisions = new List<Decision>(); var startEvent = task.Events.FirstOrDefault(x => x.EventType == EventType.WorkflowExecutionStarted); if (startEvent != null) { var pollId = startEvent.WorkflowExecutionStartedEventAttributes.Input; Logger.LogMessage("Processing decision task for poll id: " + pollId); var poll = PollProcessor.Instance.GetPollAsync(pollId).Result; if (poll != null) { if (poll.State == PollDefinition.POLL_STATE_UNSCHEDULE && DateTime.Now < poll.StartTime) { // Add second to compensate for the rounding var timeDelay = (int)(new TimeSpan(poll.StartTime.Ticks - DateTime.Now.Ticks).TotalSeconds) + 1; var decision = new Decision { DecisionType = DecisionType.StartTimer, StartTimerDecisionAttributes = new StartTimerDecisionAttributes { StartToFireTimeout = timeDelay.ToString(), TimerId = Guid.NewGuid().ToString() } }; decisions.Add(decision); PollProcessor.Instance.UpdatePollStateAsync(pollId, PollDefinition.POLL_STATE_SCHEDULE).Wait(); Logger.LogMessage("Scheduled timer for {0} seconds till activating poll.", timeDelay); } else if (poll.State == PollDefinition.POLL_STATE_SCHEDULE) { Decision decision = new Decision() { DecisionType = DecisionType.ScheduleActivityTask, ScheduleActivityTaskDecisionAttributes = new ScheduleActivityTaskDecisionAttributes() { ActivityType = new ActivityType() { Name = Constants.SWF_ACTIVTY_START_TIMER_EXPIRED, Version = Constants.SWF_ACTIVTY_START_TIMER_EXPIRED_VERSION }, TaskList = new TaskList { Name = Constants.SWF_ACTIVTY_START_TIMER_EXPIRED_TASKLIST }, HeartbeatTimeout = Constants.SWF_TIMEOUT, ScheduleToCloseTimeout = Constants.SWF_TIMEOUT, ScheduleToStartTimeout = Constants.SWF_TIMEOUT, StartToCloseTimeout = Constants.SWF_TIMEOUT, ActivityId = string.Format("{0}:{1}", Constants.SWF_ACTIVTY_START_TIMER_EXPIRED, DateTime.Now.Ticks), Input = poll.Id } }; decisions.Add(decision); Logger.LogMessage("Start timer complete now deciding to run the {0} activity to activate poll.", Constants.SWF_ACTIVTY_START_TIMER_EXPIRED); } else if (poll.State == PollDefinition.POLL_STATE_ACTIVE && DateTime.Now < poll.EndTime) { // Add second to compensate for the rounding var timeDelay = (int)(new TimeSpan(poll.EndTime.Ticks - DateTime.Now.Ticks).TotalSeconds) + 1; var decision = new Decision { DecisionType = DecisionType.StartTimer, StartTimerDecisionAttributes = new StartTimerDecisionAttributes { StartToFireTimeout = timeDelay.ToString(), TimerId = Guid.NewGuid().ToString() } }; decisions.Add(decision); Logger.LogMessage("Scheduled timer for {0} seconds till poll expires.", timeDelay); } else if (poll.State == PollDefinition.POLL_STATE_ACTIVE) { Decision decision = new Decision() { DecisionType = DecisionType.ScheduleActivityTask, ScheduleActivityTaskDecisionAttributes = new ScheduleActivityTaskDecisionAttributes() { ActivityType = new ActivityType() { Name = Constants.SWF_ACTIVTY_END_TIMER_EXPIRED, Version = Constants.SWF_ACTIVTY_END_TIMER_EXPIRED_VERSION }, TaskList = new TaskList { Name = Constants.SWF_ACTIVTY_END_TIMER_EXPIRED_TASKLIST }, HeartbeatTimeout = Constants.SWF_TIMEOUT, ScheduleToCloseTimeout = Constants.SWF_TIMEOUT, ScheduleToStartTimeout = Constants.SWF_TIMEOUT, StartToCloseTimeout = Constants.SWF_TIMEOUT, ActivityId = string.Format("{0}:{1}", Constants.SWF_ACTIVTY_END_TIMER_EXPIRED, DateTime.Now.Ticks), Input = poll.Id } }; decisions.Add(decision); Logger.LogMessage("End timer complete now deciding to run the {0} activity to expire poll.", Constants.SWF_ACTIVTY_END_TIMER_EXPIRED); } } } if (decisions.Count == 0) { Decision decision = new Decision() { DecisionType = DecisionType.CompleteWorkflowExecution, CompleteWorkflowExecutionDecisionAttributes = new CompleteWorkflowExecutionDecisionAttributes { } }; decisions.Add(decision); Logger.LogMessage("Workflow execution complete for {0}", task.WorkflowExecution.WorkflowId); } return decisions; } catch(Exception e) { string message = string.Format("Error processing work flow execution {0} and is being aborted: {1}\n", task.WorkflowExecution.WorkflowId, e.Message, e.StackTrace); Logger.LogMessage(message); Decision decision = new Decision() { DecisionType = DecisionType.CompleteWorkflowExecution, CompleteWorkflowExecutionDecisionAttributes = new CompleteWorkflowExecutionDecisionAttributes { Result = message } }; return new List<Decision> { decision }; } }