//--------------------------------------------------------------------- // Static members /// <summary> /// Normalizes the options passed by creating or cloning a new instance as /// required and filling unset properties using default client settings. /// </summary> /// <param name="client">The associated Cadence client.</param> /// <param name="options">The input options or <c>null</c>.</param> /// <param name="activityInterface">Optionally specifies the activity interface definition.</param> /// <param name="method">Optionally specifies the target workflow method.</param> /// <returns>The normalized options.</returns> /// <exception cref="ArgumentNullException">Thrown if a valid task list is not specified.</exception> internal static ActivityOptions Normalize(CadenceClient client, ActivityOptions options, Type activityInterface = null, MethodInfo method = null) { Covenant.Requires <ArgumentNullException>(client != null, nameof(client)); ActivityInterfaceAttribute interfaceAttribute = null; ActivityMethodAttribute methodAttribute = null; if (options == null) { options = new ActivityOptions(); } else { options = options.Clone(); } if (activityInterface != null) { CadenceHelper.ValidateActivityInterface(activityInterface); interfaceAttribute = activityInterface.GetCustomAttribute <ActivityInterfaceAttribute>(); } if (method != null) { methodAttribute = method.GetCustomAttribute <ActivityMethodAttribute>(); } if (string.IsNullOrEmpty(options.Domain)) { if (!string.IsNullOrEmpty(methodAttribute?.Domain)) { options.Domain = methodAttribute.Domain; } if (string.IsNullOrEmpty(options.Domain) && !string.IsNullOrEmpty(interfaceAttribute?.Domain)) { options.Domain = interfaceAttribute.Domain; } } if (string.IsNullOrEmpty(options.TaskList)) { if (!string.IsNullOrEmpty(methodAttribute?.TaskList)) { options.TaskList = methodAttribute.TaskList; } if (string.IsNullOrEmpty(options.TaskList) && !string.IsNullOrEmpty(interfaceAttribute?.TaskList)) { options.TaskList = interfaceAttribute.TaskList; } } if (options.ScheduleToCloseTimeout <= TimeSpan.Zero) { if (methodAttribute != null && methodAttribute.ScheduleToCloseTimeoutSeconds > 0) { options.ScheduleToCloseTimeout = TimeSpan.FromSeconds(methodAttribute.ScheduleToCloseTimeoutSeconds); } if (options.ScheduleToCloseTimeout <= TimeSpan.Zero) { options.ScheduleToCloseTimeout = client.Settings.ActivityScheduleToCloseTimeout; } } if (options.ScheduleToStartTimeout <= TimeSpan.Zero) { if (methodAttribute != null && methodAttribute.ScheduleToStartTimeoutSeconds > 0) { options.ScheduleToStartTimeout = TimeSpan.FromSeconds(methodAttribute.ScheduleToStartTimeoutSeconds); } if (options.ScheduleToStartTimeout <= TimeSpan.Zero) { options.ScheduleToStartTimeout = client.Settings.ActivityScheduleToStartTimeout; } } if (options.StartToCloseTimeout <= TimeSpan.Zero) { if (methodAttribute != null && methodAttribute.StartToCloseTimeoutSeconds > 0) { options.StartToCloseTimeout = TimeSpan.FromSeconds(methodAttribute.StartToCloseTimeoutSeconds); } if (options.StartToCloseTimeout <= TimeSpan.Zero) { options.StartToCloseTimeout = client.Settings.ActivityStartToCloseTimeout; } } return(options); }
//--------------------------------------------------------------------- // Static members /// <summary> /// Normalizes the options passed by creating or cloning a new instance as /// required and filling unset properties using default client settings. /// </summary> /// <param name="client">The associated Cadence client.</param> /// <param name="options">The input options or <c>null</c>.</param> /// <param name="workflowInterface">Optionally specifies the workflow interface definition.</param> /// <param name="method">Optionally specifies the target workflow method.</param> /// <returns>The normalized options.</returns> /// <exception cref="ArgumentNullException">Thrown if a valid task list is not specified.</exception> internal static WorkflowOptions Normalize(CadenceClient client, WorkflowOptions options, Type workflowInterface = null, MethodInfo method = null) { Covenant.Requires <ArgumentNullException>(client != null, nameof(client)); WorkflowInterfaceAttribute interfaceAttribute = null; WorkflowMethodAttribute methodAttribute = null; if (options == null) { options = new WorkflowOptions(); } else { options = options.Clone(); } if (workflowInterface != null) { CadenceHelper.ValidateWorkflowInterface(workflowInterface); interfaceAttribute = workflowInterface.GetCustomAttribute <WorkflowInterfaceAttribute>(); } if (method != null) { methodAttribute = method.GetCustomAttribute <WorkflowMethodAttribute>(); } if (string.IsNullOrEmpty(options.Domain)) { if (!string.IsNullOrEmpty(methodAttribute?.Domain)) { options.Domain = methodAttribute.Domain; } if (string.IsNullOrEmpty(options.Domain) && !string.IsNullOrEmpty(interfaceAttribute?.Domain)) { options.Domain = interfaceAttribute.Domain; } if (string.IsNullOrEmpty(options.Domain)) { options.Domain = client.Settings.DefaultDomain; } if (string.IsNullOrEmpty(options.Domain)) { throw new ArgumentNullException(nameof(options), "You must specify a valid domain explicitly in [CadenceSettings], [ActivityOptions] or via an [ActivityInterface] or [ActivityMethod] attribute on the target activity interface or method."); } } if (string.IsNullOrEmpty(options.TaskList)) { if (!string.IsNullOrEmpty(methodAttribute?.TaskList)) { options.TaskList = methodAttribute.TaskList; } if (string.IsNullOrEmpty(options.TaskList) && !string.IsNullOrEmpty(interfaceAttribute?.TaskList)) { options.TaskList = interfaceAttribute.TaskList; } if (string.IsNullOrEmpty(options.TaskList)) { options.TaskList = client.Settings.DefaultTaskList; } if (string.IsNullOrEmpty(options.TaskList)) { throw new ArgumentNullException(nameof(options), "You must specify a valid task list explicitly via [WorkflowOptions] or using an [WorkflowInterface] or [WorkflowMethod] attribute on the target workflow interface or method."); } } if (options.StartToCloseTimeout <= TimeSpan.Zero) { if (methodAttribute != null && methodAttribute.StartToCloseTimeoutSeconds > 0) { options.StartToCloseTimeout = TimeSpan.FromSeconds(methodAttribute.StartToCloseTimeoutSeconds); } if (options.StartToCloseTimeout <= TimeSpan.Zero) { options.StartToCloseTimeout = client.Settings.WorkflowStartToCloseTimeout; } } if (options.ScheduleToStartTimeout <= TimeSpan.Zero) { if (methodAttribute != null && methodAttribute.ScheduleToStartTimeoutSeconds > 0) { options.ScheduleToStartTimeout = TimeSpan.FromSeconds(methodAttribute.ScheduleToStartTimeoutSeconds); } if (options.ScheduleToStartTimeout <= TimeSpan.Zero) { options.ScheduleToStartTimeout = client.Settings.WorkflowScheduleToStartTimeout; } } if (options.DecisionTaskTimeout <= TimeSpan.Zero) { if (methodAttribute != null && methodAttribute.DecisionTaskTimeoutSeconds > 0) { options.DecisionTaskTimeout = TimeSpan.FromSeconds(methodAttribute.DecisionTaskTimeoutSeconds); } if (options.DecisionTaskTimeout <= TimeSpan.Zero) { options.DecisionTaskTimeout = client.Settings.WorkflowDecisionTaskTimeout; } } if (options.WorkflowIdReusePolicy == Cadence.WorkflowIdReusePolicy.UseDefault) { if (methodAttribute != null && methodAttribute.WorkflowIdReusePolicy != WorkflowIdReusePolicy.UseDefault) { options.WorkflowIdReusePolicy = methodAttribute.WorkflowIdReusePolicy; } if (options.WorkflowIdReusePolicy == Cadence.WorkflowIdReusePolicy.UseDefault) { options.WorkflowIdReusePolicy = client.Settings.WorkflowIdReusePolicy; } } if (string.IsNullOrEmpty(options.CronSchedule) && !string.IsNullOrEmpty(methodAttribute?.CronSchedule)) { options.CronSchedule = methodAttribute.CronSchedule; } return(options); }
//--------------------------------------------------------------------- // Static members /// <summary> /// <b>INTERNAL USE ONLY:</b> Normalizes the options passed by creating or cloning a new /// instance as required and filling unset properties using default client settings. /// </summary> /// <param name="client">The associated Cadence client.</param> /// <param name="options">The input options or <c>null</c>.</param> /// <param name="workflowInterface">Optionally specifies the workflow interface definition.</param> /// <param name="method">Optionally specifies the target workflow method.</param> /// <returns>The normalized options.</returns> /// <exception cref="ArgumentNullException">Thrown if a valid task list is not specified.</exception> public static ChildWorkflowOptions Normalize(CadenceClient client, ChildWorkflowOptions options, Type workflowInterface = null, MethodInfo method = null) { Covenant.Requires <ArgumentNullException>(client != null, nameof(client)); WorkflowInterfaceAttribute interfaceAttribute = null; WorkflowMethodAttribute methodAttribute = null; if (options == null) { options = new ChildWorkflowOptions(); } else { options = options.Clone(); } if (workflowInterface != null) { CadenceHelper.ValidateWorkflowInterface(workflowInterface); interfaceAttribute = workflowInterface.GetCustomAttribute <WorkflowInterfaceAttribute>(); } if (method != null) { methodAttribute = method.GetCustomAttribute <WorkflowMethodAttribute>(); } if (string.IsNullOrEmpty(options.Domain)) { if (!string.IsNullOrEmpty(methodAttribute?.Domain)) { options.Domain = methodAttribute.Domain; } if (string.IsNullOrEmpty(options.Domain) && !string.IsNullOrEmpty(interfaceAttribute?.Domain)) { options.Domain = interfaceAttribute.Domain; } } if (string.IsNullOrEmpty(options.TaskList)) { if (!string.IsNullOrEmpty(methodAttribute?.TaskList)) { options.TaskList = methodAttribute.TaskList; } if (string.IsNullOrEmpty(options.TaskList) && !string.IsNullOrEmpty(interfaceAttribute?.TaskList)) { options.TaskList = interfaceAttribute.TaskList; } } if (options.StartToCloseTimeout <= TimeSpan.Zero) { if (methodAttribute != null && methodAttribute.StartToCloseTimeoutSeconds > 0) { options.StartToCloseTimeout = TimeSpan.FromSeconds(methodAttribute.StartToCloseTimeoutSeconds); } if (options.StartToCloseTimeout <= TimeSpan.Zero) { options.StartToCloseTimeout = client.Settings.WorkflowStartToCloseTimeout; } } if (options.ScheduleToStartTimeout <= TimeSpan.Zero) { if (methodAttribute != null && methodAttribute.ScheduleToStartTimeoutSeconds > 0) { options.ScheduleToStartTimeout = TimeSpan.FromSeconds(methodAttribute.ScheduleToStartTimeoutSeconds); } if (options.ScheduleToStartTimeout <= TimeSpan.Zero) { options.ScheduleToStartTimeout = client.Settings.WorkflowScheduleToStartTimeout; } } if (options.DecisionTaskTimeout <= TimeSpan.Zero) { if (methodAttribute != null && methodAttribute.DecisionTaskTimeoutSeconds > 0) { options.DecisionTaskTimeout = TimeSpan.FromSeconds(methodAttribute.DecisionTaskTimeoutSeconds); } if (options.DecisionTaskTimeout <= TimeSpan.Zero) { options.DecisionTaskTimeout = client.Settings.WorkflowDecisionTaskTimeout; } } if (options.WorkflowIdReusePolicy == Cadence.WorkflowIdReusePolicy.UseDefault) { if (methodAttribute != null && methodAttribute.WorkflowIdReusePolicy != WorkflowIdReusePolicy.UseDefault) { options.WorkflowIdReusePolicy = methodAttribute.WorkflowIdReusePolicy; } if (options.WorkflowIdReusePolicy == Cadence.WorkflowIdReusePolicy.UseDefault) { options.WorkflowIdReusePolicy = client.Settings.WorkflowIdReusePolicy; } } return(options); }
public WorkflowInstanceKey(CadenceClient client, long contextId) { this.clientId = client.ClientId; this.contextId = contextId; }
/// <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> /// Handles workflow invocation. /// </summary> /// <param name="client">The associated Cadence client.</param> /// <param name="request">The request message.</param> /// <returns>The reply message.</returns> internal static async Task<WorkflowInvokeReply> OnInvokeAsync(CadenceClient client, WorkflowInvokeRequest request) { Covenant.Requires<ArgumentNullException>(client != null, nameof(client)); Covenant.Requires<ArgumentNullException>(request != null, nameof(request)); Covenant.Requires<ArgumentException>(request.ReplayStatus != InternalReplayStatus.Unspecified, nameof(request)); WorkflowBase workflow; WorkflowRegistration registration; var contextId = request.ContextId; var workflowKey = new WorkflowInstanceKey(client, contextId); lock (syncLock) { if (idToWorkflow.TryGetValue(workflowKey, out workflow)) { return new WorkflowInvokeReply() { Error = new CadenceError($"A workflow with [ID={workflowKey}] is already running on this worker.") }; } registration = GetWorkflowRegistration(client, request.WorkflowType); if (registration == null) { return new WorkflowInvokeReply() { Error = new CadenceError($"Workflow type name [Type={request.WorkflowType}] is not registered for this worker.") }; } } workflow = (WorkflowBase)Activator.CreateInstance(registration.WorkflowType); workflow.Workflow = new Workflow( parent: (WorkflowBase)workflow, client: client, contextId: contextId, workflowTypeName: request.WorkflowType, domain: request.Domain, taskList: request.TaskList, workflowId: request.WorkflowId, runId: request.RunId, isReplaying: request.ReplayStatus == InternalReplayStatus.Replaying, methodMap: registration.MethodMap); lock (syncLock) { idToWorkflow.Add(workflowKey, workflow); } // Register any workflow signal and/or query methods with cadence-proxy. foreach (var signalName in registration.MethodMap.GetSignalNames()) { var reply = (WorkflowSignalSubscribeReply)await client.CallProxyAsync( new WorkflowSignalSubscribeRequest() { ContextId = contextId, SignalName = signalName }); reply.ThrowOnError(); } foreach (var queryType in registration.MethodMap.GetQueryTypes()) { var reply = (WorkflowSetQueryHandlerReply)await client.CallProxyAsync( new WorkflowSetQueryHandlerRequest() { ContextId = contextId, QueryName = queryType }); reply.ThrowOnError(); } // Start the workflow by calling its workflow entry point method. // This method will indicate that it has completed via one of these // techniques: // // 1. The method returns normally with the workflow result. // // 2. The method calls [RestartAsync(result, args)] which throws an // [InternalWorkflowRestartException] which will be caught and // handled here. // // 3. The method throws another exception which will be caught // and be used to indicate that the workflow failed. try { WorkflowBase.CallContext.Value = WorkflowCallContext.Entrypoint; var workflowMethod = registration.WorkflowMethod; var resultType = workflowMethod.ReturnType; var args = client.DataConverter.FromDataArray(request.Args, registration.WorkflowMethodParameterTypes); var serializedResult = emptyBytes; if (resultType.IsGenericType) { // Workflow method returns: Task<T> var result = await NeonHelper.GetTaskResultAsObjectAsync((Task)workflowMethod.Invoke(workflow, args)); serializedResult = client.DataConverter.ToData(result); } else { // Workflow method returns: Task await (Task)workflowMethod.Invoke(workflow, args); } return new WorkflowInvokeReply() { Result = serializedResult }; } catch (ForceReplayException) { return new WorkflowInvokeReply() { ForceReplay = true }; } catch (ContinueAsNewException e) { return new WorkflowInvokeReply() { ContinueAsNew = true, ContinueAsNewArgs = e.Args, ContinueAsNewWorkflow = e.Workflow, ContinueAsNewDomain = e.Domain, ContinueAsNewTaskList = e.TaskList, ContinueAsNewExecutionStartToCloseTimeout = CadenceHelper.ToCadence(e.ExecutionStartToCloseTimeout), ContinueAsNewScheduleToCloseTimeout = CadenceHelper.ToCadence(e.ScheduleToCloseTimeout), ContinueAsNewScheduleToStartTimeout = CadenceHelper.ToCadence(e.ScheduleToStartTimeout), ContinueAsNewStartToCloseTimeout = CadenceHelper.ToCadence(e.TaskStartToCloseTimeout), }; } catch (CadenceException e) { log.LogError(e); return new WorkflowInvokeReply() { Error = e.ToCadenceError() }; } catch (Exception e) { log.LogError(e); return new WorkflowInvokeReply() { Error = new CadenceError(e) }; } finally { WorkflowBase.CallContext.Value = WorkflowCallContext.None; } }
/// <summary> /// Handles workflow queries. /// </summary> /// <param name="client">The Cadence client.</param> /// <param name="request">The request message.</param> /// <returns>The reply message.</returns> internal static async Task<WorkflowQueryInvokeReply> OnQueryAsync(CadenceClient client, WorkflowQueryInvokeRequest request) { Covenant.Requires<ArgumentNullException>(client != null, nameof(client)); Covenant.Requires<ArgumentNullException>(request != null, nameof(request)); try { WorkflowBase.CallContext.Value = WorkflowCallContext.Query; var workflow = GetWorkflow(client, request.ContextId); if (workflow != null) { // Handle built-in queries. if (request.QueryName == "__stack_trace") { var trace = string.Empty; if (workflow.StackTrace != null) { trace = workflow.StackTrace.ToString(); } return new WorkflowQueryInvokeReply() { RequestId = request.RequestId, Result = NeonHelper.JsonSerializeToBytes(trace) }; } // Handle user queries. var method = workflow.Workflow.MethodMap.GetQueryMethod(request.QueryName); if (method != null) { var resultType = method.ReturnType; var methodParameterTypes = method.GetParameterTypes(); var serializedResult = emptyBytes; if (resultType.IsGenericType) { // Query method returns: Task<T> var result = await NeonHelper.GetTaskResultAsObjectAsync((Task)method.Invoke(workflow, client.DataConverter.FromDataArray(request.QueryArgs, methodParameterTypes))); serializedResult = client.DataConverter.ToData(result); } else { // Query method returns: Task await (Task)method.Invoke(workflow, client.DataConverter.FromDataArray(request.QueryArgs, methodParameterTypes)); } return new WorkflowQueryInvokeReply() { RequestId = request.RequestId, Result = serializedResult }; } else { return new WorkflowQueryInvokeReply() { Error = new EntityNotExistsException($"Workflow type [{workflow.GetType().FullName}] does not define a query handler for [queryType={request.QueryName}].").ToCadenceError() }; } } else { return new WorkflowQueryInvokeReply() { Error = new EntityNotExistsException($"Workflow with [contextID={request.ContextId}] does not exist.").ToCadenceError() }; } } catch (Exception e) { log.LogError(e); return new WorkflowQueryInvokeReply() { Error = new CadenceError(e) }; } finally { WorkflowBase.CallContext.Value = WorkflowCallContext.None; } }
/// <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(); } }
public ActivityKey(CadenceClient client, long contextId) { this.clientId = client.ClientId; this.contextId = contextId; }
/// <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(); } }
/// <summary> /// Handles received <see cref="ActivityInvokeRequest"/> messages. /// </summary> /// <param name="client">The receiving Cadence client.</param> /// <param name="request">The request message.</param> /// <returns>The reply message.</returns> private static async Task <ActivityInvokeReply> OnActivityInvokeRequest(CadenceClient client, ActivityInvokeRequest request) { await SyncContext.Clear; ActivityRegistration invokeInfo; lock (syncLock) { if (!nameToRegistration.TryGetValue(GetActivityTypeKey(client, request.Activity), out invokeInfo)) { throw new KeyNotFoundException($"Cannot resolve [activityTypeName = {request.Activity}] to a registered activity type and activity method."); } } var activity = CreateNormal(client, invokeInfo, request.ContextId); try { var result = await activity.OnInvokeAsync(client, request.Args); if (activity.CompleteExternally) { return(new ActivityInvokeReply() { Pending = true }); } else { return(new ActivityInvokeReply() { Result = result, }); } } catch (CadenceException e) { activity.logger.LogError(e); return(new ActivityInvokeReply() { Error = e.ToCadenceError() }); } catch (TaskCanceledException e) { return(new ActivityInvokeReply() { Error = new CancelledException(e.Message).ToCadenceError() }); } catch (Exception e) { activity.logger.LogError(e); return(new ActivityInvokeReply() { Error = new CadenceError(e) }); } }
/// <summary> /// Constructor. /// </summary> /// <param name="client">The associated client.</param> /// <param name="execution">The workflow execution.</param> internal ExternalWorkflowFuture(CadenceClient client, WorkflowExecution execution) { this.client = client; this.Execution = execution; }