Ejemplo n.º 1
0
        /// <summary>
        /// Registers an activity implementation with Cadence.
        /// </summary>
        /// <typeparam name="TActivity">The <see cref="ActivityBase"/> derived class implementing the activity.</typeparam>
        /// <param name="activityTypeName">
        /// Optionally specifies a custom activity type name that will be used
        /// for identifying the activity implementation in Cadence.  This defaults
        /// to the fully qualified <typeparamref name="TActivity"/> 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 a different activity class has already been registered for <paramref name="activityTypeName"/>.</exception>
        /// <exception cref="ActivityWorkerStartedException">
        /// Thrown if an activity worker has already been started for the client.  You must
        /// register activity implementations before starting workers.
        /// </exception>
        /// <remarks>
        /// <note>
        /// Be sure to register all services you will be injecting into activities via
        /// <see cref="NeonHelper.ServiceContainer"/> before you call this as well as
        /// registering of your activity implementations before starting workers.
        /// </note>
        /// </remarks>
        public async Task RegisterActivityAsync <TActivity>(string activityTypeName = null, string domain = null)
            where TActivity : ActivityBase
        {
            await SyncContext.Clear;

            CadenceHelper.ValidateActivityImplementation(typeof(TActivity));
            CadenceHelper.ValidateActivityTypeName(activityTypeName);
            EnsureNotDisposed();

            if (activityWorkerStarted)
            {
                throw new ActivityWorkerStartedException();
            }

            var activityType = typeof(TActivity);

            if (string.IsNullOrEmpty(activityTypeName))
            {
                activityTypeName = CadenceHelper.GetActivityTypeName(activityType, activityType.GetCustomAttribute <ActivityAttribute>());
            }

            await ActivityBase.RegisterAsync(this, activityType, activityTypeName, ResolveDomain(domain));

            lock (registeredActivityTypes)
            {
                registeredActivityTypes.Add(CadenceHelper.GetActivityInterface(typeof(TActivity)));
            }
        }
Ejemplo n.º 2
0
        //---------------------------------------------------------------------
        // Cadence activity related operations.

        /// <summary>
        /// Registers an activity implementation with Cadence.
        /// </summary>
        /// <typeparam name="TActivity">The <see cref="IActivityBase"/> derived class implementing the activity.</typeparam>
        /// <param name="activityTypeName">
        /// Optionally specifies a custom activity type name that will be used
        /// for identifying the activity implementation in Cadence.  This defaults
        /// to the fully qualified <typeparamref name="TActivity"/> type name.
        /// </param>
        /// <returns>The tracking <see cref="Task"/>.</returns>
        /// <exception cref="InvalidOperationException">Thrown if a different activity class has already been registered for <paramref name="activityTypeName"/>.</exception>
        /// <exception cref="CadenceActivityWorkerStartedException">
        /// Thrown if an activity worker has already been started for the client.  You must
        /// register activity implementations before starting workers.
        /// </exception>
        /// <remarks>
        /// <note>
        /// Be sure to register all of your activity implementations before starting a workflow worker.
        /// </note>
        /// </remarks>
        public async Task RegisterActivityAsync <TActivity>(string activityTypeName = null)
            where TActivity : ActivityBase
        {
            CadenceHelper.ValidateActivityImplementation(typeof(TActivity));

            if (string.IsNullOrEmpty(activityTypeName))
            {
                activityTypeName = activityTypeName ?? typeof(TActivity).FullName;
            }

            if (activityWorkerStarted)
            {
                throw new CadenceActivityWorkerStartedException();
            }

            if (!ActivityBase.Register(this, typeof(TActivity), activityTypeName))
            {
                var reply = (ActivityRegisterReply) await CallProxyAsync(
                    new ActivityRegisterRequest()
                {
                    Name = activityTypeName
                });

                reply.ThrowOnError();
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Called internally to initialize the activity.
        /// </summary>
        /// <param name="client">The associated client.</param>
        /// <param name="activityType">Specifies the target activity type.</param>
        /// <param name="activityMethod">Specifies the target activity method.</param>
        /// <param name="dataConverter">Specifies the data converter to be used for parameter and result serilization.</param>
        /// <param name="contextId">The activity's context ID.</param>
        internal void Initialize(CadenceClient client, Type activityType, MethodInfo activityMethod, IDataConverter dataConverter, long contextId)
        {
            Covenant.Requires <ArgumentNullException>(client != null, nameof(client));
            Covenant.Requires <ArgumentNullException>(activityType != null, nameof(activityType));
            Covenant.Requires <ArgumentNullException>(activityMethod != null, nameof(activityMethod));
            Covenant.Requires <ArgumentNullException>(dataConverter != null, nameof(dataConverter));
            CadenceHelper.ValidateActivityImplementation(activityType);

            this.Client                  = client;
            this.Activity                = new Activity(this);
            this.activityType            = activityType;
            this.activityMethod          = activityMethod;
            this.dataConverter           = dataConverter;
            this.ContextId               = contextId;
            this.CancellationTokenSource = new CancellationTokenSource();
            this.CancellationToken       = CancellationTokenSource.Token;
            this.logger                  = LogManager.Default.GetLogger(module: activityType.FullName);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Called internally to initialize the activity.
        /// </summary>
        /// <param name="client">The associated client.</param>
        /// <param name="activityType">Specifies the target activity type.</param>
        /// <param name="activityMethod">Specifies the target activity method.</param>
        /// <param name="dataConverter">Specifies the data converter to be used for parameter and result serilization.</param>
        /// <param name="contextId">The activity's context ID or <c>null</c> for local activities.</param>
        internal void Initialize(CadenceClient client, Type activityType, MethodInfo activityMethod, IDataConverter dataConverter, long?contextId)
        {
            Covenant.Requires <ArgumentNullException>(client != null, nameof(client));
            Covenant.Requires <ArgumentNullException>(activityType != null, nameof(activityType));
            Covenant.Requires <ArgumentNullException>(activityMethod != null, nameof(activityMethod));
            Covenant.Requires <ArgumentNullException>(dataConverter != null, nameof(dataConverter));
            CadenceHelper.ValidateActivityImplementation(activityType);

            var activityTask = new ActivityTask()
            {
                // $todo(jefflill): Need to initialize these properties.
            };

            this.Client                  = client;
            this.ActivityTask            = activityTask;
            this.Activity                = new Activity(this);
            this.activityType            = activityType;
            this.activityMethod          = activityMethod;
            this.dataConverter           = dataConverter;
            this.ContextId               = contextId;
            this.CancellationTokenSource = new CancellationTokenSource();
            this.CancellationToken       = CancellationTokenSource.Token;
        }
Ejemplo n.º 5
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>
        /// <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 static bool Register(CadenceClient client, Type activityType, string activityTypeName)
        {
            Covenant.Requires <ArgumentNullException>(client != null);
            CadenceHelper.ValidateActivityImplementation(activityType);

            activityTypeName = GetActivityTypeKey(client, activityTypeName);

            var constructInfo = new ActivityInvokeInfo();

            constructInfo.ActivityType = activityType;
            constructInfo.Constructor  = constructInfo.ActivityType.GetConstructor(noTypeArgs);

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

            lock (syncLock)
            {
                if (nameToInvokeInfo.TryGetValue(activityTypeName, out var existingEntry))
                {
                    if (!object.ReferenceEquals(existingEntry.ActivityType, constructInfo.ActivityType))
                    {
                        throw new InvalidOperationException($"Conflicting activity type registration: Activity type [{activityType.FullName}] is already registered for workflow type name [{activityTypeName}].");
                    }

                    return(true);
                }
                else
                {
                    nameToInvokeInfo[activityTypeName] = constructInfo;

                    return(false);
                }
            }
        }
Ejemplo n.º 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();
            }
        }