private async Task <WorkflowProcessingResult> ProcessMessageInternal(IDelayedTransitionWorkflowMessage delayedTransitionWorkflowMessage, CancellationToken cancellationToken)
        {
            Guard.ArgumentNotNull(delayedTransitionWorkflowMessage, nameof(delayedTransitionWorkflowMessage));
            Guard.ArgumentNotNull(delayedTransitionWorkflowMessage.WorkflowInstanceId, nameof(delayedTransitionWorkflowMessage.WorkflowInstanceId));
            Debug.Assert(delayedTransitionWorkflowMessage.WorkflowInstanceId != null, "workflowMessage.WorkflowInstanceId != null");

            var workflowInstance = await GetWorkflowInstanceWithLock(delayedTransitionWorkflowMessage.WorkflowInstanceId.Value, cancellationToken).ConfigureAwait(false);

            if (null == workflowInstance)
            {
                throw new WorkflowException($"Workflow message [{delayedTransitionWorkflowMessage.WorkflowMessageId:D}] is referring to non existing workflow instance");
            }

            var workflowConfiguration = await GetWorkflowConfiguration(workflowInstance.WorkflowId, cancellationToken).ConfigureAwait(false);

            Guard.ArgumentNotNull(workflowConfiguration, nameof(workflowConfiguration));

            var stateConfiguration = workflowConfiguration.GetStateConfigurationByCode(workflowInstance.CurrentStateCode);

            Guard.ArgumentNotNull(stateConfiguration, nameof(stateConfiguration));

            if (stateConfiguration.Type == StateTypeConfiguration.Final || stateConfiguration.Type == StateTypeConfiguration.Failed)
            {
                Log.Debug("Skipping delayed transition message [{messageId}] because {workflowInstanceId} is in [{state}]",
                          delayedTransitionWorkflowMessage.WorkflowMessageId, delayedTransitionWorkflowMessage.WorkflowInstanceId, stateConfiguration.Code);

                return(new WorkflowProcessingResult(workflowInstance));
            }

            var workflowContext = new WorkflowContext(this, WorkflowEngineBuilder, workflowInstance, workflowConfiguration);

            // ensure that domain entity is available for all/any coded logic for read-only purposes
            await TryGetDomainEntity(workflowContext, cancellationToken).ConfigureAwait(false);

            var stateExecutionContext   = new StateExecutionContext(workflowContext, stateConfiguration);
            var fromStateConfiguration  = workflowConfiguration.GetStateConfigurationByCode(delayedTransitionWorkflowMessage.MoveFromState);
            var transitionConfiguration = fromStateConfiguration.Transitions
                                          .Single(t => string.Equals(t.MoveToState, delayedTransitionWorkflowMessage.MoveToState, StringComparison.OrdinalIgnoreCase));

            var transitionEvaluationResult = await _transitionProcessorLazy.Value.Evaluate(stateExecutionContext, transitionConfiguration, cancellationToken).ConfigureAwait(false);

            if (transitionEvaluationResult.Status == TransitionEvaluationStatus.EvaluatedTrue)
            {
                await TransitionToState(workflowContext, stateConfiguration, transitionConfiguration, cancellationToken).ConfigureAwait(false);

                return(new WorkflowProcessingResult(workflowInstance, workflowContext.WorkflowExecutionState));
            }

            Log.Debug("Skipping delayed transition message [{messageId}] for {workflowInstanceId}] because transition was evaluated to {state}",
                      delayedTransitionWorkflowMessage.WorkflowMessageId, delayedTransitionWorkflowMessage.WorkflowInstanceId, transitionEvaluationResult.Status);

            return(new WorkflowProcessingResult(workflowInstance, workflowContext.WorkflowExecutionState));
        }
Esempio n. 2
0
        public async Task <TransitionEvaluationResult> Evaluate(StateExecutionContext stateExecutionContext, TransitionConfiguration transitionConfiguration, CancellationToken cancellationToken)
        {
            ITransition transition = null;

            if (!string.IsNullOrEmpty(transitionConfiguration.Code))
            {
                transition = _workflowEngine.WorkflowEngineBuilder
                             .TransitionFactory.CreateTransition(transitionConfiguration.Code);
            }

            if (null == transition && !string.IsNullOrEmpty(transitionConfiguration.ConditionScript) &&
                transitionConfiguration.ConditionScriptType == ScriptTypeConfiguration.CSharp)
            {
                transition = new CSharpScriptTransition(stateExecutionContext.StateConfiguration.Code,
                                                        transitionConfiguration.ConditionScriptType, transitionConfiguration.ConditionScript);
            }

            if (null == transition)
            {
                // there is neither code nor condition for transition
                //     ==> assume transition always evaluates to true
                return(new TransitionEvaluationResult(TransitionEvaluationStatus.EvaluatedTrue));
            }

            Log.Verbose("Starting evaluation of transition [{code}] [{condition}] for [{workflowInstanceId}]",
                        transitionConfiguration.Code, transitionConfiguration.ConditionScriptType,
                        stateExecutionContext.WorkflowContext.WorkflowInstance.Id);

            try
            {
                var transitionEvaluationContext = new TransitionEvaluationContext(stateExecutionContext, transitionConfiguration);
                var transitionEvaluationResult  = await transition.Evaluate(transitionEvaluationContext, cancellationToken).ConfigureAwait(false);

                if (transitionEvaluationResult.Status == TransitionEvaluationStatus.EvaluationFailed)
                {
                    throw new WorkflowException(string.Format(CultureInfo.InvariantCulture,
                                                              "Evaluation of an transition [code={0}], [condition={1}] returns [EvaluationFailed] for workflow instance [{2:D}]",
                                                              transitionConfiguration.Code, transitionConfiguration.ConditionScript,
                                                              stateExecutionContext.WorkflowContext.WorkflowInstance.Id));
                }

                return(transitionEvaluationResult);
            }
            catch (Exception ex)
            {
                throw new WorkflowException(string.Format(CultureInfo.InvariantCulture,
                                                          "An error has occurred during evaluation of an transition [code={0}], [condition={1}] for workflow instance [{2:D}]",
                                                          transitionConfiguration.Code, transitionConfiguration.ConditionScript,
                                                          stateExecutionContext.WorkflowContext.WorkflowInstance.Id), ex);
            }
        }
        public void Setup()
        {
            _workflowInstance                        = new Mock <IWorkflowInstance>();
            _runtimeWorkflowEngine                   = new Mock <IRuntimeWorkflowEngine>();
            _endpointConfiguration                   = new Mock <IEndpointConfiguration>();
            _workflowMessageTransport                = new Mock <IWorkflowMessageTransport>();
            _workflowMessageTransportFactory         = new Mock <IWorkflowMessageTransportFactory>();
            _workflowMessageTransportFactoryProvider = new Mock <IWorkflowMessageTransportFactoryProvider>();
            _workflowEngineBuilder                   = new WorkflowEngineBuilder().WithMessageTransportFactoryProvider(_workflowMessageTransportFactoryProvider.Object);

            _endpointConfiguration.Setup(f => f.Code).Returns("activity");
            _endpointConfiguration.Setup(f => f.Address).Returns(new Uri("rabbitmq://localhost/Diadem.Workflow.Core.Model:IActivityRequestWorkflowMessage"));

            var workflowId = Guid.NewGuid();

            _workflowInstance.Setup(f => f.Id).Returns(workflowId);
            _workflowConfiguration = new WorkflowConfiguration(workflowId, "unit.test", "unit.test", "Unit Test", new Version(0, 0, 1))
            {
                RuntimeConfiguration = new WorkflowRuntimeConfiguration(workflowId, "unit.test", "unit.test")
                {
                    EndpointConfiguration  = _endpointConfiguration.Object,
                    EndpointConfigurations = new[] { new KeyValuePair <string, IEndpointConfiguration>("remote.activity.*", _endpointConfiguration.Object) }
                }
            };

            _activityResponseWorkflowMessage = new ActivityResponseWorkflowMessage {
                ActivityExecutionResult = new ActivityExecutionResult(ActivityExecutionStatus.Completed)
            };
            _workflowMessageTransport
            .Setup(f => f.Request <IActivityRequestWorkflowMessage, IActivityResponseWorkflowMessage>(
                       It.IsAny <IEndpointConfiguration>(), It.IsAny <IActivityRequestWorkflowMessage>(), It.IsAny <CancellationToken>()))
            .Returns <IEndpointConfiguration, IActivityRequestWorkflowMessage, CancellationToken>((epc, wm, ct) => Task.FromResult(_activityResponseWorkflowMessage));

            _workflowMessageTransportFactoryProvider
            .Setup(f => f.CreateMessageTransportFactory(It.IsAny <EndpointConfigurationType>()))
            .Returns <EndpointConfigurationType>(uri => _workflowMessageTransportFactory.Object);

            _workflowMessageTransportFactory
            .Setup(f => f.CreateMessageTransport(It.IsAny <Uri>()))
            .Returns <Uri>(uri => _workflowMessageTransport.Object);

            _workflowContext          = new WorkflowContext(_runtimeWorkflowEngine.Object, _workflowEngineBuilder, _workflowInstance.Object, _workflowConfiguration);
            _stateExecutionContext    = new StateExecutionContext(_workflowContext, new StateConfiguration());
            _activityExecutionContext = new ActivityExecutionContext(_stateExecutionContext, new ActivityConfiguration());
        }
Esempio n. 4
0
 public TransitionEvaluationContext(StateExecutionContext stateExecutionContext,
                                    TransitionConfiguration transitionConfiguration)
 {
     TransitionConfiguration = transitionConfiguration;
     StateExecutionContext   = stateExecutionContext;
 }
 public ActivityExecutionContext(StateExecutionContext stateExecutionContext,
                                 ActivityConfiguration activityConfiguration)
 {
     ActivityConfiguration = activityConfiguration;
     StateExecutionContext = stateExecutionContext;
 }
        private async Task ProcessState(WorkflowContext workflowContext, StateConfiguration stateConfiguration, CancellationToken cancellationToken = default)
        {
            if (stateConfiguration.Type == StateTypeConfiguration.Initial || stateConfiguration.Type == StateTypeConfiguration.Application)
            {
                var cycleDetected = DetectCycle(workflowContext, stateConfiguration);
                if (cycleDetected)
                {
                    await TransitionToState(workflowContext, workflowContext.WorkflowConfiguration.GetFailedStateConfiguration(), null, cancellationToken)
                    .ConfigureAwait(false);

                    Log.Warning("Cycle has been detected for {workflowInstanceId} moving to failed state", workflowContext.WorkflowInstance.Id);

                    return;
                }
            }

            StateExecutionResult stateExecutionResult;
            var stateExecutionContext = new StateExecutionContext(workflowContext, stateConfiguration);

            var started   = DateTime.UtcNow;
            var stopwatch = Stopwatch.StartNew();

            try
            {
                stateExecutionResult = await _stateProcessorLazy.Value.Process(stateExecutionContext, cancellationToken).ConfigureAwait(false);
            }
            finally
            {
                stopwatch.Stop();
                Log.Debug("Finished execution of {state}, {duration}, {workflowInstanceId}",
                          stateConfiguration.Code, stopwatch.Elapsed, workflowContext.WorkflowInstance.Id);

                var workflowInstanceStateLog = new WorkflowInstanceStateLog(workflowContext.WorkflowInstance.Id, stateConfiguration.Code, started, (int)stopwatch.ElapsedMilliseconds);
                await WorkflowEngineBuilder.WorkflowStore.SaveWorkflowInstanceStateLog(workflowInstanceStateLog, cancellationToken).ConfigureAwait(false);
            }

            if (stateExecutionResult.Status == StateExecutionStatus.Finished)
            {
                Log.Debug("{workflowInstanceId} has been finished processing", workflowContext.WorkflowInstance.Id);

                // if (stateExecutionContext.StateConfiguration.Type == StateTypeConfiguration.Final)
                // {
                //    await PulseFinalizedToParentWorkflow(workflowContext, cancellationToken).ConfigureAwait(false);
                // }
                // else
                if (stateExecutionContext.StateConfiguration.Type == StateTypeConfiguration.Failed)
                {
                    await PulseFailedToParentWorkflow(workflowContext, cancellationToken).ConfigureAwait(false);
                    await PulseFailedToNestedWorkflows(workflowContext, cancellationToken).ConfigureAwait(false);
                }

                return;
            }

            if (stateExecutionResult.Status == StateExecutionStatus.Failed || stateExecutionResult.Status == StateExecutionStatus.Undefined)
            {
                // check if current state is already 'Failed' then just ignore state activity execution failure
                if (stateConfiguration.Type == StateTypeConfiguration.Failed)
                {
                    Log.Error("Activities execution error for failed state of {workflowInstanceId} has occurred, ignoring failure...",
                              workflowContext.WorkflowInstance.Id);
                    return;
                }

                // state/activity failure could have custom transition to use in case of failure
                if (null != stateExecutionResult.Transition.TransitionConfiguration)
                {
                    await TransitionAfterStateExecution(workflowContext, stateExecutionResult, cancellationToken).ConfigureAwait(false);

                    Log.Verbose("{workflowInstanceId} has been moved to {state}",
                                workflowContext.WorkflowInstance.Id, stateExecutionResult.Transition.TransitionConfiguration.MoveToState);
                    return;
                }

                await TransitionToState(workflowContext, workflowContext.WorkflowConfiguration.GetFailedStateConfiguration(), null, cancellationToken)
                .ConfigureAwait(false);

                Log.Verbose("{workflowInstanceId} has been moved to failed state", workflowContext.WorkflowInstance.Id);
                return;
            }

            if (stateExecutionResult.Status == StateExecutionStatus.Completed)
            {
                await TransitionAfterStateExecution(workflowContext, stateExecutionResult, cancellationToken).ConfigureAwait(false);
            }
        }