示例#1
0
        /// <summary>
        /// Attempts to cancel the associated workflow.
        /// </summary>
        /// <returns>The tracking <see cref="Task"/>.</returns>
        public async Task CancelAsync()
        {
            await SyncContext.Clear;
            EnsureStarted();

            if (Execution == null)
            {
                throw new InvalidOperationException("The stub can't cancel the workflow because it doesn't have the workflow execution.");
            }

            await client.CancelWorkflowAsync(Execution, client.ResolveDomain(Options?.Domain));
        }
示例#2
0
        /// <summary>
        /// Internal constructor for use outside of a workflow.
        /// </summary>
        /// <param name="client">Specifies the associated client.</param>
        /// <param name="execution">Specifies the target workflow execution.</param>
        /// <param name="domain">Optionally specifies the target domain (defaults to the client's default domain).</param>
        internal ExternalWorkflowStub(CadenceClient client, WorkflowExecution execution, string domain = null)
        {
            Covenant.Requires <ArgumentNullException>(client != null, nameof(client));
            Covenant.Requires <ArgumentNullException>(execution != null, nameof(execution));

            this.client    = client;
            this.domain    = client.ResolveDomain(domain);
            this.Execution = execution;
        }
示例#3
0
        /// <summary>
        /// Internal constructor for use within a workflow.
        /// </summary>
        /// <param name="parentWorkflow">Specifies the parent workflow.</param>
        /// <param name="execution">Specifies the target workflow execution.</param>
        /// <param name="domain">Optionally specifies the target domain (defaults to the client's default domain).</param>
        internal ExternalWorkflowStub(Workflow parentWorkflow, WorkflowExecution execution, string domain = null)
        {
            Covenant.Requires <ArgumentNullException>(parentWorkflow != null, nameof(parentWorkflow));
            Covenant.Requires <ArgumentNullException>(execution != null, nameof(execution));

            this.parentWorkflow = parentWorkflow;
            this.client         = parentWorkflow.Client;
            this.domain         = client.ResolveDomain(domain);
            this.Execution      = execution;
        }
示例#4
0
 /// <summary>
 /// Constructor.
 /// </summary>
 /// <param name="client">The associated client.</param>
 /// <param name="execution">The workflow execution.</param>
 /// <param name="domain">Optionally specifies the target domain.  This defaults to the default client domain.</param>
 internal ExternalWorkflowFuture(CadenceClient client, WorkflowExecution execution, string domain = null)
 {
     this.client    = client;
     this.Execution = execution;
     this.domain    = client.ResolveDomain(domain);
 }
示例#5
0
        /// <summary>
        /// Registers a workflow implementation.
        /// </summary>
        /// <param name="client">The associated client.</param>
        /// <param name="workflowType">The workflow implementation type.</param>
        /// <param name="workflowTypeName">The name used to identify the implementation.</param>
        /// <param name="domain">Specifies the target domain.</param>
        /// <exception cref="InvalidOperationException">Thrown if a different workflow class has already been registered for <paramref name="workflowTypeName"/>.</exception>
        internal static async Task RegisterAsync(CadenceClient client, Type workflowType, string workflowTypeName, string domain)
        {
            Covenant.Requires<ArgumentNullException>(client != null, nameof(client));
            Covenant.Requires<ArgumentNullException>(!string.IsNullOrEmpty(domain), nameof(domain));
            CadenceHelper.ValidateWorkflowImplementation(workflowType);

            var methodMap = WorkflowMethodMap.Create(workflowType);

            // We need to register each workflow method that implements a workflow interface method
            // with the same signature that that was tagged by [WorkflowMethod].
            //
            // First, we'll create a dictionary that maps method signatures from any inherited
            // interfaces that are tagged by [WorkflowMethod] to the attribute.

            var methodSignatureToAttribute = new Dictionary<string, WorkflowMethodAttribute>();

            foreach (var interfaceType in workflowType.GetInterfaces())
            {
                foreach (var method in interfaceType.GetMethods(BindingFlags.Public | BindingFlags.Instance))
                {
                    var workflowMethodAttribute = method.GetCustomAttribute<WorkflowMethodAttribute>();

                    if (workflowMethodAttribute == null)
                    {
                        continue;
                    }

                    var signature = method.ToString();

                    if (methodSignatureToAttribute.ContainsKey(signature))
                    {
                        throw new NotSupportedException($"Workflow type [{workflowType.FullName}] cannot implement the [{signature}] method from two different interfaces.");
                    }

                    methodSignatureToAttribute.Add(signature, workflowMethodAttribute);
                }
            }

            // Next, we need to register the workflow methods that implement the
            // workflow interface.

            foreach (var method in workflowType.GetMethods())
            {
                if (!methodSignatureToAttribute.TryGetValue(method.ToString(), out var workflowMethodAttribute))
                {
                    continue;
                }

                var workflowTypeKey = GetWorkflowTypeKey(client, workflowTypeName, workflowMethodAttribute);

                lock (syncLock)
                {
                    if (nameToRegistration.TryGetValue(workflowTypeName, out var existingRegistration))
                    {
                        if (!object.ReferenceEquals(existingRegistration.WorkflowType, workflowType))
                        {
                            throw new InvalidOperationException($"Conflicting workflow interface registration: Workflow interface [{workflowType.FullName}] is already registered for workflow type name [{workflowTypeName}].");
                        }
                    }
                    else
                    {
                        nameToRegistration[workflowTypeKey] =
                            new WorkflowRegistration()
                            {
                                WorkflowType                 = workflowType,
                                WorkflowMethod               = method,
                                WorkflowMethodParameterTypes = method.GetParameterTypes(),
                                MethodMap                    = methodMap
                            };
                    }
                }

                var reply = (WorkflowRegisterReply)await client.CallProxyAsync(
                    new WorkflowRegisterRequest()
                    {
                        Name   = GetWorkflowTypeNameFromKey(workflowTypeKey),
                        Domain = client.ResolveDomain(domain)
                    });

                // $hack(jefflill): 
                //
                // We're going to ignore any errors here to handle:
                //
                //      https://github.com/nforgeio/neonKUBE/issues/668

                // reply.ThrowOnError();
            }
        }
示例#6
0
        /// <summary>
        /// Registers an activity type.
        /// </summary>
        /// <param name="client">The associated client.</param>
        /// <param name="activityType">The activity type.</param>
        /// <param name="activityTypeName">The name used to identify the implementation.</param>
        /// <param name="domain">Specifies the target domain.</param>
        /// <returns><c>true</c> if the activity was already registered.</returns>
        /// <exception cref="InvalidOperationException">Thrown if a different activity class has already been registered for <paramref name="activityTypeName"/>.</exception>
        internal async static Task RegisterAsync(CadenceClient client, Type activityType, string activityTypeName, string domain)
        {
            Covenant.Requires <ArgumentNullException>(client != null, nameof(client));
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(domain), nameof(domain));
            CadenceHelper.ValidateActivityImplementation(activityType);

            var constructor = activityType.GetConstructor(Type.EmptyTypes);

            if (constructor == null)
            {
                throw new ArgumentException($"Activity type [{activityType.FullName}] does not have a default constructor.", nameof(activityType));
            }

            // We need to register each activity method that implements an activity interface method
            // with the same signature that that was tagged by [ActivityMethod].
            //
            // First, we'll create a dictionary that maps method signatures from any inherited
            // interfaces that are tagged by [ActivityMethod] to the attribute.

            var methodSignatureToAttribute = new Dictionary <string, ActivityMethodAttribute>();

            foreach (var interfaceType in activityType.GetInterfaces())
            {
                foreach (var method in interfaceType.GetMethods(BindingFlags.Public | BindingFlags.Instance))
                {
                    var activityMethodAttribute = method.GetCustomAttribute <ActivityMethodAttribute>();

                    if (activityMethodAttribute == null)
                    {
                        continue;
                    }

                    var signature = method.ToString();

                    if (methodSignatureToAttribute.ContainsKey(signature))
                    {
                        throw new NotSupportedException($"Activity type [{activityType.FullName}] cannot implement the [{signature}] method from two different interfaces.");
                    }

                    methodSignatureToAttribute.Add(signature, activityMethodAttribute);
                }
            }

            // Next, we need to register the activity methods that implement the
            // activity interface.

            foreach (var method in activityType.GetMethods())
            {
                if (!methodSignatureToAttribute.TryGetValue(method.ToString(), out var activityMethodAttribute))
                {
                    continue;
                }

                var activityTypeKey = GetActivityTypeKey(client, activityTypeName, activityMethodAttribute);

                lock (syncLock)
                {
                    if (nameToRegistration.TryGetValue(activityTypeKey, out var registration))
                    {
                        if (!object.ReferenceEquals(registration.ActivityType, registration.ActivityType))
                        {
                            throw new InvalidOperationException($"Conflicting activity type registration: Activity type [{activityType.FullName}] is already registered for activity type name [{activityTypeKey}].");
                        }
                    }
                    else
                    {
                        nameToRegistration[activityTypeKey] =
                            new ActivityRegistration()
                        {
                            ActivityType                 = activityType,
                            ActivityConstructor          = constructor,
                            ActivityMethod               = method,
                            ActivityMethodParamaterTypes = method.GetParameterTypes()
                        };
                    }
                }

                var reply = (ActivityRegisterReply)await client.CallProxyAsync(
                    new ActivityRegisterRequest()
                {
                    Name   = GetActivityTypeNameFromKey(activityTypeKey),
                    Domain = client.ResolveDomain(domain)
                });

                // $hack(jefflill):
                //
                // We're going to ignore any errors here to handle:
                //
                //      https://github.com/nforgeio/neonKUBE/issues/668

                // reply.ThrowOnError();
            }
        }