//--------------------------------------------------------------------- // Public Cadence workflow related operations. /// <summary> /// Registers a workflow implementation with Cadence. /// </summary> /// <typeparam name="TWorkflow">The <see cref="IWorkflowBase"/> derived class implementing the workflow.</typeparam> /// <param name="workflowTypeName"> /// Optionally specifies a custom workflow type name that will be used /// for identifying the workflow implementation in Cadence. This defaults /// to the fully qualified <typeparamref name="TWorkflow"/> type name. /// </param> /// <param name="domain">Optionally overrides the default client domain.</param> /// <returns>The tracking <see cref="Task"/>.</returns> /// <exception cref="InvalidOperationException">Thrown if another workflow class has already been registered for <paramref name="workflowTypeName"/>.</exception> /// <exception cref="CadenceWorkflowWorkerStartedException"> /// Thrown if a workflow worker has already been started for the client. You must /// register activity workflow implementations before starting workers. /// </exception> /// <remarks> /// <note> /// Be sure to register all of your workflow implementations before starting a workflow worker. /// </note> /// </remarks> public async Task RegisterWorkflowAsync <TWorkflow>(string workflowTypeName = null, string domain = null) where TWorkflow : WorkflowBase { CadenceHelper.ValidateWorkflowImplementation(typeof(TWorkflow)); CadenceHelper.ValidateWorkflowTypeName(workflowTypeName); if (string.IsNullOrEmpty(workflowTypeName)) { workflowTypeName = workflowTypeName ?? typeof(TWorkflow).FullName; } if (workflowWorkerStarted) { throw new CadenceWorkflowWorkerStartedException(); } // We need to register a workflow type name for each workflow method, // appending the method type separator and method name for workflow // methods that specify names. if (!WorkflowBase.Register(this, typeof(TWorkflow), workflowTypeName)) { var reply = (WorkflowRegisterReply) await CallProxyAsync( new WorkflowRegisterRequest() { Name = workflowTypeName, Domain = ResolveDomain(domain) }); reply.ThrowOnError(); } }
//--------------------------------------------------------------------- // Public Cadence workflow related operations. /// <summary> /// Registers a workflow implementation with Cadence. /// </summary> /// <typeparam name="TWorkflow">The <see cref="WorkflowBase"/> derived class implementing the workflow.</typeparam> /// <param name="workflowTypeName"> /// Optionally specifies a custom workflow type name that will be used /// for identifying the workflow implementation in Cadence. This defaults /// to the fully qualified <typeparamref name="TWorkflow"/> type name. /// </param> /// <param name="domain">Optionally overrides the default client domain.</param> /// <returns>The tracking <see cref="Task"/>.</returns> /// <exception cref="InvalidOperationException">Thrown if another workflow class has already been registered for <paramref name="workflowTypeName"/>.</exception> /// <exception cref="WorkflowWorkerStartedException"> /// Thrown if a workflow worker has already been started for the client. You must /// register workflow implementations before starting workers. /// </exception> /// <remarks> /// <note> /// Be sure to register all of your workflow implementations before starting workers. /// </note> /// </remarks> public async Task RegisterWorkflowAsync <TWorkflow>(string workflowTypeName = null, string domain = null) where TWorkflow : WorkflowBase { await SyncContext.ClearAsync; CadenceHelper.ValidateWorkflowImplementation(typeof(TWorkflow)); CadenceHelper.ValidateWorkflowTypeName(workflowTypeName); EnsureNotDisposed(); if (workflowWorkerStarted) { throw new WorkflowWorkerStartedException(); } var workflowType = typeof(TWorkflow); if (string.IsNullOrEmpty(workflowTypeName)) { workflowTypeName = CadenceHelper.GetWorkflowTypeName(workflowType, workflowType.GetCustomAttribute <WorkflowAttribute>()); } await WorkflowBase.RegisterAsync(this, workflowType, workflowTypeName, ResolveDomain(domain)); lock (registeredWorkflowTypes) { registeredWorkflowTypes.Add(CadenceHelper.GetWorkflowInterface(typeof(TWorkflow))); } }
/// <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> /// <returns><c>true</c> if the workflow was already registered.</returns> /// <exception cref="InvalidOperationException">Thrown if a different workflow class has already been registered for <paramref name="workflowTypeName"/>.</exception> internal static bool Register(CadenceClient client, Type workflowType, string workflowTypeName) { Covenant.Requires <ArgumentNullException>(client != null); CadenceHelper.ValidateWorkflowImplementation(workflowType); // We need to register each workflow method defined for the workflow. var methodMap = WorkflowMethodMap.Create(workflowType); foreach (var method in workflowType.GetMethods()) { var workflowMethodAttribute = method.GetCustomAttribute <WorkflowMethodAttribute>(); if (workflowMethodAttribute == null) { continue; } workflowTypeName = GetWorkflowTypeKey(client, workflowTypeName, method); 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}]."); } return(true); } else { nameToRegistration[workflowTypeName] = new WorkflowRegistration() { WorkflowType = workflowType, WorkflowMethod = method, MethodMap = methodMap }; } } } return(false); }
/// <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(); } }