/// <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)); }
/// <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; }
/// <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; }
/// <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); }
/// <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(); } }
/// <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(); } }