public override bool OnResume(IRunState context, IWorkflowEvent resumeEvent) { if (resumeEvent is NotifyResumeFailedEvent) { throw new WorkflowRunException("Workflow failed with an internal error."); } var notification = context.GetArgValue <IEntity>(ActivityInstance, GetArgumentKey("core:outNotificationRecord")); if (notification == null) { throw new ArgumentException("notification"); } var writable = notification.AsWritable <Notification>(); if (writable.NPendingRun != null) { writable.NPendingRun = null; writable.Save(); } HandleTimeout(context, resumeEvent); return(true); // keep going }
public string ResumeWorkflowAsync_new(WorkflowRun workflowRun, IWorkflowEvent resumeEvent) { Factory.WorkflowRunTaskManager.RegisterStart(workflowRun.TaskId); bool runInForeground = WorkflowRunContext.Current.RunTriggersInCurrentThread; var queueEvent = resumeEvent as IWorkflowQueuedEvent; if (queueEvent == null) { EventLog.Application.WriteError("Attempted to queue a resume task that cannot be queued, running it on the foreground thread instead. EventType: {resumeEvent.GetType()}"); runInForeground = true; } if (runInForeground) { ResumeWorkflow(workflowRun, resumeEvent); } else { var task = ResumeWorkflowHandler.CreateBackgroundTask(workflowRun, queueEvent); Factory.BackgroundTaskManager.EnqueueTask(task); HandleDiagnostics(workflowRun, "Queued"); } return(workflowRun.TaskId); }
private string ResumeWorkflowAsync_old(WorkflowRun workflowRun, IWorkflowEvent resumeEvent) { Action runAction = () => { using (new WorkflowRunContext { RunTriggersInCurrentThread = true }) { ResumeWorkflow(workflowRun, resumeEvent); } }; Factory.WorkflowRunTaskManager.RegisterStart(workflowRun.TaskId); HandleDiagnostics(workflowRun, "Queued"); if (WorkflowRunContext.Current.RunTriggersInCurrentThread) { // Ignore the Asyn and process it syncronously runAction(); } else // Async { WorkflowRunContext.Current.QueueAction(() => { runAction(); }); } return(workflowRun.TaskId); }
/// <summary> /// Continue a paused activity /// </summary> /// <returns>True if the activity has completed, false if it is paused.</returns> public override bool OnResume(IRunState context, IWorkflowEvent resumeEvent) { var userCompletesEvent = resumeEvent as PromptUserTaskCompletedEvent; if (userCompletesEvent != null) { context.ExitPointId = new EntityRef("promptUserCompleted"); var userTaskId = userCompletesEvent.UserTaskId; var userTask = Entity.Get <PromptUserTask>(userTaskId, PromptUserTask.PromptForTaskStateInfo_Field); if (userTask != null) { // load the state information in the task back into the workflow run context foreach (var arg in userTask.PromptForTaskArguments) { var input = userTask.PromptForTaskStateInfo.FirstOrDefault(si => si.StateInfoArgument.Id == arg.ActivityPromptArgument.Id); if (input == null) { continue; } context.SetArgValue(input.StateInfoArgument, ActivityArgumentHelper.GetArgParameterValue(input.StateInfoValue)); } Entity.Delete(userTask); } } return(true); }
/// <summary> /// Cancel a timeout if one has been set. If a timeout has occured set the exit point to the timeout exit /// </summary> /// <param name="context"></param> /// <returns>true is a time-out has occured</returns> protected bool HandleTimeout(IRunState context, IWorkflowEvent resumeEvent) { if (context.HasTimeout) { TimeoutActivityHelper.Instance.CancelTimeout(context.WorkflowRun); context.HasTimeout = false; } var timeoutEvent = resumeEvent as TimeoutEvent; if (timeoutEvent != null) { var timeoutTrans = GetTimeoutTransition(); if (timeoutTrans == null) { throw new ApplicationException("There should never be a time-out without a time-out exit point."); } context.ExitPointId = timeoutTrans.FromExitPoint; return(true); } else { return(false); } }
bool IResumableActivity.OnResume(IRunState context, IWorkflowEvent resumeEvent) { var run = ((ChildWorkflowCompletedEvent)resumeEvent).CompletedRun; ProcessResult(context, run); return(true); }
/// <summary> /// Run the next step in the workflow by scheduling the activity. This call in turn will schedule other activities. /// </summary> private void ScheduleResumeStep(IRunState context, IWorkflowEvent resumeEvent, WfActivity childActivity) { // set the input var windowsActivity = (ActivityImplementationBase)context.Metadata.CachedInstances[childActivity.Id]; var resumable = (IResumableActivity)windowsActivity; context.WorkflowInvoker.ScheduleResume(context, windowsActivity, resumeEvent, NextStepCompletionCallback, NextStepPausedCallback); }
/// <summary> /// Raise an event. Events are handled in the order raised after the other processing has completed /// </summary> /// <param name="nextEvent"></param> public void PostEvent(IWorkflowEvent nextEvent) { Action action = null; var asChildRunStart = nextEvent as ChildWorkflowStartEvent; if (asChildRunStart != null) { var parentRun = asChildRunStart.ParentRun; if (parentRun != null) { var triggerDepth = WorkflowRunContext.Current.TriggerDepth; action = () => { // Need to handle deferred here otherwise "double-resume" fails (BUG #27863) using (new WorkflowRunContext(true) { TriggerDepth = triggerDepth + 1 }) { var startEvent = new WorkflowStartEvent(asChildRunStart.WorkflowToStart) { Arguments = asChildRunStart.Inputs, ParentRun = parentRun, Trace = parentRun.RunTrace ?? false }; // TODO: Change this into a call into the BackgroundTaskManager WorkflowRunner.Instance.RunWorkflow(startEvent); } }; } } else { var asChildRunCompleted = nextEvent as ChildWorkflowCompletedEvent; if (asChildRunCompleted != null) { var completedRun = asChildRunCompleted.CompletedRun; if (completedRun != null) { // Need to decrement the trigger depth otherwise "child workflow in a foreach" fails (BUG #27863) if (WorkflowRunContext.Current.TriggerDepth > 0) { WorkflowRunContext.Current.TriggerDepth--; } action = () => WorkflowRunner.Instance.ResumeWorkflow(completedRun.ParentRun, asChildRunCompleted); } } } if (action != null) { // NOTE: runBeforeSave was passed as "true" here, however, this led to issues where previously // unsaved copies of the same run were already present in the queue, causing endless looping (BUG #27863) WorkflowRunContext.Current.DeferAction(action, false); } }
/// <summary> /// Resume a workflow. The workflow will run async unless it is in a RunTriggersInCurrentThread WorkflowContext. /// </summary> /// <param name="workflowRun"></param> /// <param name="resumeEvent"></param> public string ResumeWorkflowAsync(WorkflowRun workflowRun, IWorkflowEvent resumeEvent) { if (Factory.FeatureSwitch.Get("longRunningWorkflow")) { return(ResumeWorkflowAsync_new(workflowRun, resumeEvent)); } else { return(ResumeWorkflowAsync_old(workflowRun, resumeEvent)); } }
public void HandleEvent(IWorkflowEvent workflowEvent) { foreach (var rule in TransitionRules) { var nextState = rule.Transition(workflowEvent, this, _state); if (nextState != null) { _currentState = nextState; } } }
public override bool OnResume(IRunState context, IWorkflowEvent resumeEvent) { if (!HandleTimeout(context, resumeEvent)) // handle timeout will set the exit point { HandleResumeTransition(context, (IWorkflowUserTransitionEvent)resumeEvent); //if (!(userTask.HideComment ?? false)) // context.SetArgValue(ActivityInstance, GetArgumentKey("core:outTaskComment"), userTask.TaskComment); } return(true); }
/// <summary> /// Responds to a paused activity being resumed. /// </summary> /// <param name="context">The current running context of the workflow.</param> /// <param name="resumeEvent">Information about the event that caused the resumption of the activity.</param> /// <returns>True if the activity has completed, false if it is paused.</returns> public bool OnResume(IRunState context, IWorkflowEvent resumeEvent) { TRequest request = null; TResponse response = null; using (Profiler.Measure("CastActivityImplementation.OnResume")) { try { if (resumeEvent is ICastActivityResponseEvent) { LogToRun(context, string.Format("CAST Activity '{0}' is resuming.", ActivityInstance.Name)); var castEvent = resumeEvent as CastActivityResponseEvent <TRequest, TResponse>; if (castEvent != null) { request = castEvent.Request; response = castEvent.Response; } if (response != null && response.IsError) { throw new WorkflowRunException("CAST Activity failed at the remote end. ({0})", response.Error); } OnResponse(context, request, response); } } catch (Exception e) { EventLog.Application.WriteError("An unexpected error occurred when resuming a CAST activity. {0}", e.ToString()); LogToRun(context, string.Format("CAST Activity '{0}' failed to resume.", ActivityInstance.Name)); if (string.IsNullOrEmpty(FailureExitPointAlias)) { throw; } context.ExitPointId = new EntityRef(FailureExitPointAlias); } } return(true); }
/// <summary> /// Run activity using its own context. The activity can not have any pending actions when it completes) /// </summary> /// <returns>True if the run completed.</returns> bool ProcessWorkflow(Workflow wf, IRunState runState, IWorkflowEvent wfEvent) { var activityInst = wf.Cast <WfActivity>(); var activityImp = activityInst.CreateWindowsActivity(); var metadata = runState.Metadata; var invoker = runState.WorkflowInvoker; if (!metadata.HasViolations) { var startEvent = wfEvent as WorkflowStartEvent; if (startEvent != null) { var inputArgs = activityInst.GetInputArguments(); var inputs = new ActivityInputs(inputArgs, startEvent.Arguments); invoker.ScheduleActivity(runState, activityImp, inputs, null, null); } else { invoker.ScheduleResume(runState, activityImp, wfEvent, null, null); } try { var result = RunTillCompletion(invoker, runState); return(result); } catch (PlatformSecurityException ex) { throw new WorkflowRunException(ex.Message, ex); } catch (WorkflowRunException_Internal ex) { throw new WorkflowRunException(ex.Message, ex.InnerException); } } var act = GetActivityForError(runState, activityInst); throw new WorkflowValidationException(runState.WorkflowRunId, act.ContainingWorkflow, act, metadata.ValidationMessages); }
public override bool OnResume(IRunState context, IWorkflowEvent resumeEvent) { var userTaskEntity = context.GetArgValue <IEntity>(ActivityInstance, GetArgumentKey("core:outDisplayFormUserTask")); var keepHistory = context.GetArgValue <bool>(ActivityInstance, GetArgumentKey("core:dfaInternalKeepHistory")); var userTask = userTaskEntity != null?userTaskEntity.As <DisplayFormUserTask>() : null; // We could have a combination of a deleted user task and a time-out if (!HandleTimeout(context, resumeEvent)) // handle timeout will set the exit point { HandleResumeTransition(context, (IWorkflowUserTransitionEvent)resumeEvent); if (!(userTask.HideComment ?? false)) { context.SetArgValue(ActivityInstance, GetArgumentKey("core:outTaskComment"), userTask.TaskComment); } if (keepHistory) { var writableTask = userTask.AsWritable <DisplayFormUserTask>(); writableTask.TaskStatus_Enum = TaskStatusEnum_Enumeration.TaskStatusCompleted; writableTask.Save(); } } if (userTask != null) { if (keepHistory) { if (userTask.LogEntryForUserAction != null) { UpdateLogEntry(userTask); } } else { context.SetArgValue(ActivityInstance, GetArgumentKey("core:outDisplayFormUserTask"), null); Entity.Delete(userTask); } } return(true); }
/// <summary> /// CALLED FROM INVOKER. Run the activity in the provided context. Used to execute an activity within a workflow. If this activity metadata has not been added to the context it /// will be done now. /// </summary> /// <returns>True if completed, false if bookmarked.</returns> public bool ResumeInContext(IRunState runState, IWorkflowEvent resumeEvent) { bool hasCompleted = true; var resumable = this as IResumableActivity; if (resumable == null) { throw new ApplicationException("Attempted to resume an activity that is not resumable. This should never happen."); } hasCompleted = resumable.OnResume(runState, resumeEvent); if (hasCompleted && runState.ExitPointId == null) { runState.ExitPointId = ActivityInstance.GetDefaultExitPoint(); } return(hasCompleted); }
bool IResumableActivity.OnResume(IRunState context, IWorkflowEvent resumeEvent) { bool hasCompleted = true; context.SyncFromRun(); var currentActivity = context.PendingActivity; context.PendingActivity = null; if (currentActivity == null) { throw new ApplicationException("Current Activity missing from resumed activity. This should never occur."); } if (resumeEvent is WorkflowRestoreEvent) { if (context.RunStatus == WorkflowRunState_Enumeration.WorkflowRunCancelled) { return(true); } // Unsuspend a workflow Debug.Assert(context.RunStatus == WorkflowRunState_Enumeration.WorkflowRunSuspended); ScheduleNextStep(context, currentActivity); } else { // Unpause an activity Debug.Assert(context.RunStatus == WorkflowRunState_Enumeration.WorkflowRunPaused); ScheduleResumeStep(context, resumeEvent, currentActivity); } hasCompleted = context.WorkflowInvoker.RunTillCompletion(context); return(hasCompleted); }
/// <summary> /// Resume workflow, the workflow runs immediatly in the current thread. /// </summary> /// <param name="run"></param> /// <param name="resumeEvent"></param> /// <param name="invoker"></param> /// <returns></returns> public WorkflowRun ResumeWorkflow(WorkflowRun run, IWorkflowEvent resumeEvent) { if (run == null) { throw new ArgumentNullException(nameof(run)); } if (resumeEvent == null) { throw new ArgumentNullException(nameof(resumeEvent)); } HandleDiagnostics(run, "Resumed"); Factory.WorkflowRunTaskManager.RegisterStart(run.TaskId); using (new WorkflowRunContext { RunTriggersInCurrentThread = true }) { run = ProcessWorkflowInContext(run, resumeEvent); } return(run); }
public abstract bool OnResume(IRunState context, IWorkflowEvent resumeEvent);
public override WorkflowState Transition(IWorkflowEvent workflowEvent, Workflow workflow, object state) { return((workflowEvent.GetType() == typeof(UserLoggedIn)) ? new UserLoggedInState(workflow, state) : null); }
public override WorkflowState Transition(IWorkflowEvent workflowEvent, Workflow workflow, object state) { return(workflowEvent.GetType() == typeof(LoggedOut) ? new LoggedOutState(workflow) : null); }
public ValueTask OnTrigger(IExecutionContext context, IWorkflowEvent wfEvent, Object result) { SetComplete(context); ScheduleOutgoing(context); return(ValueTask.CompletedTask); }
/// <summary> /// Resume a paused activity on the invoker /// </summary> /// <param name="runState">The runstate</param> /// <param name="windowsActivity">The activity</param> /// <param name="resumeEvent">The trigger event for the resume</param> /// <returns></returns> public bool Resume(IRunState runState, ActivityImplementationBase windowsActivity, IWorkflowEvent resumeEvent) { ScheduleResume(runState, windowsActivity, resumeEvent, null, null); return(RunTillCompletion(runState)); }
/// <summary> /// Add a resume activity to the pending list. NOTE! This does not resume the activity. /// /// </summary> public void ScheduleResume(IRunState runState, ActivityImplementationBase windowsActivity, IWorkflowEvent resumeEvent, Action <IRunState, ActivityImplementationBase, EntityRef> actionOnCompletion, Action <IRunState, ActivityImplementationBase> actionOnPause) { Func <bool> step = () => { runState.RecordTrace(windowsActivity.ActivityInstance.Name); return(windowsActivity.ResumeInContext(runState, resumeEvent)); }; ProcessStep(runState, windowsActivity, actionOnCompletion, actionOnPause, step); }
public abstract WorkflowState Transition(IWorkflowEvent workflowEvent, Workflow workflow, object state);
public WorkflowRun ResumeWorkflow(WorkflowRun run, IWorkflowEvent resumeEvent) { throw new NotImplementedException(); }
public string ResumeWorkflowAsync(WorkflowRun workflowRun, IWorkflowEvent resumeEvent) { throw new NotImplementedException(); }
public override WorkflowState Transition(IWorkflowEvent workflowEvent, Workflow workflow, object state) { return(null); }
public override WorkflowState Transition(IWorkflowEvent workflowEvent, Workflow workflow, object state) { return(workflowEvent.GetType() == typeof(AdminLoggedIn) ? new AdministratorLoggedInState(workflow, state) : null); }
/// <summary> /// Continue a paused activity /// </summary> /// <returns>True if the activity has completed, false if it is paused.</returns> public override bool OnResume(IRunState context, IWorkflowEvent resumeEvent) { HandleTimeout(context, resumeEvent); return(true); }
private WorkflowRun ProcessWorkflowInContext(WorkflowRun run, IWorkflowEvent wfEvent) { var workflow = run.WorkflowBeingRun; using (Profiler.Measure("WorkflowRunner.Instance.StartWorkflowInContext " + workflow.Id)) { var stopWatch = StartWorkflowTimer(); IRunState runState = null; try { if (!run.IsTemporaryId) { PrecacheWorkflow(run.Id); } using (new SecurityBypassContext()) { var metadata = MetadataFactory.Create(workflow); runState = CreateRunState(metadata, run); if (runState.EffectiveSecurityContext == null) { throw new WorkflowMissingOwnerException(); } } // Wrap a Security bypass with the effective context. This less us "Pop" to run as the effective context. using (CustomContext.SetContext(runState.EffectiveSecurityContext)) { using (new SecurityBypassContext()) { if (runState.Metadata.HasViolations) { MarkRunFailedHasErrors(runState); } else if (run.TriggerDepth > WorkflowTriggerHelper.MaxTriggerDepth) { MarkRunFailedTriggerDepth(runState); } else { var isCompleted = ProcessWorkflow(workflow, runState, wfEvent); } } } } catch (WorkflowRunException ex) { MarkRunFailed(runState, ex); if (runState != null) { runState.FlushInternalArgs(); } } catch (Exception ex) { MarkRunInternalError(runState, ex); if (runState != null) { runState.FlushInternalArgs(); } } finally { if (!Factory.WorkflowRunTaskManager.HasCancelled(runState.RunTaskId)) { run = FinalizeRun(runState); } } EndWorkflowTimer(stopWatch); } return(run); }