Exemple #1
0
        public string StoreWorkflow(WorkflowObj wfObj)
        {
            try
            {
                var wf =
                    (from w in Workflows
                     where w.Name == wfObj.Name && w.Version == ConvertVersion(wfObj.Version)
                     select w).SingleOrDefault();

                if (wf == null)
                {
                    wf = new Workflow
                    {
                        Name    = wfObj.Name,
                        Version = ConvertVersion(wfObj.Version),
                        // Description = acObj.Description,
                        Json = JsonConvert.SerializeObject(wfObj)
                    };
                    Workflows.InsertOnSubmit(wf);
                }

                wf.Json = JsonConvert.SerializeObject(wfObj);
                SubmitChanges(ConflictMode.FailOnFirstConflict);
                return("\"OK\"");
            }
            catch (Exception ex)
            {
                return("\"" + ex.Message + "\"");
            }
        }
Exemple #2
0
 private static void AttemptCleanup(Database db, Execution execution, WorkflowObj workflow, List <History> decisions)
 {
     if (ExState.Create(execution.ExecutionState.State) == ExState.Cleanup)
     {
         StopWorkflow(execution, decisions);
         return;
     }
     try
     {
         var cleanupStartTask = workflow.Tasks.SingleOrDefault(t => t.ActivityName == "cleanup");
         if (cleanupStartTask == null)
         {
             StopWorkflow(execution, decisions);
             return;
         }
         var cleanupTaskId = cleanupStartTask.Outflows.Single(o => o.Name == "Out").Target;
         var cleanupTask   = workflow.Tasks.Single(t => t.TaskId == cleanupTaskId);
         decisions.Add(new WorkflowCleanupStartedEvent());
         CreateTaskScheduledEvent(db, execution, cleanupTask, decisions);
     }
     catch (Exception ex)
     {
         log.Error("Exception thrown from cleanup", ex);
         StopWorkflow(execution, decisions);
     }
 }
Exemple #3
0
        /// <summary>
        /// Two stage workflow execution start.
        /// First create and initialise the workflow variables
        /// Then schedule the first task
        /// If this process is interrupted then the variables
        /// </summary>
        /// <param name="db"></param>
        /// <param name="workflow"></param>
        /// <param name="execution"></param>
        /// <param name="history"></param>
        /// <param name="decisions"></param>
        private static void ProcessWorkflowExecutionStartedEvent(Database db, WorkflowObj workflow, Execution execution, History history, List <History> decisions)
        {
            var evt = WorkflowExecutionStartedEvent.Create(history);

            CreateVariables(db, evt, execution, workflow);
            // Ensure that the variables are set up for the first task
            // This should never fail due to conflict
            db.SubmitChanges();

            // Scheduling the first task
            var startTaskId = workflow.Tasks.Single(t => t.ActivityName == "start").Outflows.Single(o => o.Name == "Out").Target;
            var startTask   = workflow.Tasks.Single(t => t.TaskId == startTaskId);

            CreateTaskScheduledEvent(db, execution, startTask, decisions);
        }
Exemple #4
0
        /// <summary>
        /// When a task fails, follow the error outflow if one exists else fail the workflow
        /// </summary>
        /// <param name="db"></param>
        /// <param name="workflow"></param>
        /// <param name="execution"></param>
        /// <param name="history"></param>
        /// <param name="decisions"></param>
        private static void ProcessActivityTaskFailedEvent(Database db, WorkflowObj workflow, Execution execution, History history, List <History> decisions)
        {
            // There should(!) be no contention for the data modified in this process
            var evt        = ActivityTaskFailedEvent.Create(history);
            var se         = ActivityTaskScheduledEvent.Create(db.GetHistoryEvent(execution.ExecutionId, evt.SchedulingEventId));
            var failedTask = workflow.Tasks.Single(t => t.TaskId == se.TaskId);

            var nextTaskId = failedTask.FailOutflow?.Target;

            if (!string.IsNullOrEmpty(nextTaskId))
            {
                var nextTask = workflow.Tasks.Single(t => t.TaskId == nextTaskId);
                CreateTaskScheduledEvent(db, execution, nextTask, decisions);
            }
            decisions.Add(new WorkflowExecutionFailedEvent
            {
                Reason = $"Task {se.TaskId} failed with no recovery action defined"
            });

            AttemptCleanup(db, execution, workflow, decisions);
        }
Exemple #5
0
        /// <summary>
        /// Create all workflow variables and initialise them from the workflow input data
        /// </summary>
        /// <param name="db"></param>
        /// <param name="evt"></param>
        /// <param name="execution"></param>
        /// <param name="workflow"></param>
        private static void CreateVariables(Database db, WorkflowExecutionStartedEvent evt, Execution execution, WorkflowObj workflow)
        {
            foreach (var vdefn in workflow.Variables)
            {
                // Get the initial value of the variable
                JToken value = null;

                if (!string.IsNullOrWhiteSpace(vdefn.Value.Path))
                {
                    value = evt.Input.SelectToken(vdefn.Value.Path);

                    if (value == null)  // The path expression failed
                    {
                        if (vdefn.Value.Required)
                        {
                            throw new ApplicationException("Uninitialised required variable - " + vdefn.Key);
                        }
                    }

                    // Empty default is equivalent to an explicit null
                    if (IsNullOrUndefined(value) && !string.IsNullOrWhiteSpace(vdefn.Value.Default))
                    {
                        value = JToken.Parse(vdefn.Value.Default);
                    }
                }
                // Empty literal is equivalent to an explicit null
                else if (!string.IsNullOrWhiteSpace(vdefn.Value.Lit))
                {
                    value = JToken.Parse(vdefn.Value.Lit);
                }

                // TODO: Check datatypes for validity

                db.Variables.InsertOnSubmit(new Variable
                {
                    ExecutionId = execution.ExecutionId,
                    Name        = vdefn.Key,
                    Json        = JsonConvert.SerializeObject(value)
                });
            }
        }
Exemple #6
0
        /// <summary>
        /// Process task completion by scheduling the next task or completing the workflow
        /// </summary>
        /// <param name="db"></param>
        /// <param name="workflow"></param>
        /// <param name="execution"></param>
        /// <param name="history"></param>
        /// <param name="decisions"></param>
        private static void ProcessActivityTaskCompletedEvent(Database db, WorkflowObj workflow, Execution execution, History history, List <History> decisions)
        {
            // There should(!) be no contention for the data modified in this process
            var evt           = ActivityTaskCompletedEvent.Create(history);
            var se            = ActivityTaskScheduledEvent.Create(db.GetHistoryEvent(execution.ExecutionId, evt.SchedulingEventId));
            var completedTask = workflow.Tasks.Single(t => t.TaskId == se.TaskId);

            // Default task outflow
            var outflow = "Out";

            // Update variables
            if (evt.Result != null)
            {
                // Update the variables if there are any normal results (not prefixed with "$")
                if (evt.Result.Properties().Any(p => !p.Name.StartsWith("$")))
                {
                    var variables = db.Variables.Where(v => v.Execution == execution).ToArray();
                    foreach (var o in completedTask.Outputs.Where(o => o.Value.Var != null))
                    {
                        // If the activity has not returned a value for an output then we don't update the mapped variable
                        // In general it is probably best if activities return values for all outputs to avoid confusion
                        JToken value;
                        if (evt.Result.TryGetValue(o.Key, out value))
                        {
                            variables.Single(v => v.Name == o.Value.Var).Json = value.ToString(Formatting.None);
                        }
                    }
                }
                // Get the correct outflow
                JToken outflowToken;
                if (evt.Result.TryGetValue("$outflow", out outflowToken))
                {
                    if (outflowToken.Type == JTokenType.String)
                    {
                        outflow = (string)outflowToken;
                    }
                    else
                    {
                        throw new ApplicationException("Task outflow identifier must be a string");
                    }
                }
            }

            var nextTaskId = completedTask.Outflows.Single(o => o.Name == outflow).Target;
            var nextTask   = workflow.Tasks.Single(t => t.TaskId == nextTaskId);

            // A task with no outflows is an end
            if (nextTask.Outflows.Length == 0)
            {
                Console.WriteLine($"Execution state = {execution.ExecutionState.State}");
                if (ExState.Create(execution.ExecutionState.State) != ExState.Cleanup)
                {
                    CreateWorkflowCompletedEvent(execution, decisions);
                }
                AttemptCleanup(db, execution, workflow, decisions);
            }
            else
            {
                CreateTaskScheduledEvent(db, execution, nextTask, decisions);
            }
        }