/// <summary>
        /// Processes a re-occuring hourly task.
        /// </summary>
        /// <param name="job">The hourly task job.</param>
        private void ProcessHourlyTask(TaskProcessorJob job)
        {
            // Debug Logging.
            if (this.Configuration.LoggingEnabled)
            {
                Debug.WriteLine($"Hourly task processing with task id => {job.Data?.Value.Id}.");
            }

            // Bind to the completed event ( called always ) of the job.
            // That way even if the job is canceled, fails, or finishes successfully
            // ...we always schedule the next run.
            job.ProcessingCompleted += (s, op) =>
                                       this.TaskProcessor.CreateApplicationTaskSafe(
                true,
                VaultApplication.BackgroundOperationTaskQueueId,
                VaultApplication.TaskTypeHourlyRecurringTask,
                null,
                DateTime.Now.AddHours(1).ToUniversalTime());

            // The hourly task has come due and is being processed.
            job.ThrowIfCancellationRequested();

            // Update has having been assigned.
            this.TaskProcessor.UpdateTaskAsAssignedToProcessor(job);

            // TODO: Do hourly work here...
        }
        private void ProcessBroadcastTask(TaskProcessorJob job)
        {
            // Ensure cancellation has not been requested.
            job.ThrowIfCancellationRequested();

            // Update the progress of the task in the task queue.
            this.TaskProcessor.UpdateTaskAsAssignedToProcessor(job);

            // Sanity.
            if (null == job.Data?.Value)
            {
                return;
            }

            // Deserialize the directive.
            EmailByUserPatchDirective dir = TaskQueueDirective.Parse <EmailByUserPatchDirective>(job.Data.Value);

            if (dir.GeneratedFromGuid != CurrentServer.ServerID)
            {
                // Debug Logging.
                if (this.Configuration.LoggingEnabled)
                {
                    SysUtils.ReportInfoToEventLog($"Broadcast processing with task id => {job.Data.Value.Id} on server {CurrentServer.ServerID}.");
                }

                // The task was created on another server, so we should process it on this server.
                AddOrUpdateEmailByUserId(dir.UserAccount, dir.Email);
            }
        }
        /// <summary>
        /// Processes a single job.
        /// </summary>
        /// <param name="job">Task processor job.</param>
        private void ProcessSequentialTask(TaskProcessorJob job)
        {
            // Debug Logging.
            if (this.Configuration.LoggingEnabled)
            {
                Debug.WriteLine($"Sequential task processing with task id => {job.Data?.Value.Id}.");
            }

            // Ensure cancellation has not been requested.
            job.ThrowIfCancellationRequested();

            // Update the progress of the task in the task queue.
            this.TaskProcessor.UpdateTaskAsAssignedToProcessor(job);

            // Sanity.
            if (null == job.Data?.Value)
            {
                return;
            }

            // Deserialize the directive.
            var dir = TaskQueueDirective.Parse <ObjVerExTaskQueueDirective>(job.Data?.Value);

            // Sanity.
            if (string.IsNullOrWhiteSpace(dir?.ObjVerEx))
            {
                return;
            }

            // Update the object.
            try
            {
                // Mark that we're updating the object.
                this.TaskProcessor.UpdateTaskInfo
                (
                    job.Data?.Value,
                    MFTaskState.MFTaskStateInProgress,
                    $"Updating object {dir.ObjVerEx}",
                    false
                );

                // Load the object, check it out, update, check it in.
                var objVerEx = ObjVerEx.Parse(job.Vault, dir.ObjVerEx);
                objVerEx.CheckOut();
                objVerEx.SetProperty
                (
                    MFBuiltInPropertyDef.MFBuiltInPropertyDefNameOrTitle,
                    MFDataType.MFDatatypeText,
                    DateTime.Now.ToLongTimeString()
                );
                objVerEx.SaveProperties();
                objVerEx.CheckIn();

                // Updated.
                this.TaskProcessor.UpdateTaskInfo
                (
                    job.Data?.Value,
                    MFTaskState.MFTaskStateCompleted,
                    $"Object {dir.ObjVerEx} updated",
                    false
                );
            }
            catch (Exception e)
            {
                // Exception.
                this.TaskProcessor.UpdateTaskInfo
                (
                    job.Data?.Value,
                    MFTaskState.MFTaskStateFailed,
                    e.Message,
                    false
                );
            }
        }
        /// <summary>
        /// Handles when a job from a task requires processing.
        /// Note that all tasks/jobs in the queue use the same task type ID,
        /// so the directive <see cref="BackgroundOperationTaskQueueDirective.BackgroundOperationName"/>
        /// is used to identify which tasks should be executed by
        /// which background operation.
        /// </summary>
        /// <param name="job">The job to process.</param>
        protected virtual void ProcessJobHandler(TaskProcessorJob job)
        {
            // Sanity.
            if (null == job)
            {
                return;
            }

            // Ensure cancellation has not been requested.
            job.ThrowIfCancellationRequested();

            // What is the current state?
            var state = job.AppTaskState;

            // Update the progress of the task in the task queue.
            try
            {
                this.TaskProcessor.UpdateTaskAsAssignedToProcessor(job);
            }
            catch
            {
                // Could not mark the task as assigned to a processor.
                SysUtils.ReportToEventLog
                (
                    $"Could not mark task {job.AppTaskId} as assigned to a processor (queue id: {job.AppTaskQueueId}, state: {state}).",
                    System.Diagnostics.EventLogEntryType.Warning
                );
                return;
            }

            // Sanity.
            if (null == job.Data?.Value)
            {
                // This is an issue.  We have no way to decide what background operation should run it.  Die.
                SysUtils.ReportErrorToEventLog
                (
                    $"Job loaded with no application task (queue: {job.AppTaskQueueId}, task id: {job.AppTaskId})."
                );
                return;
            }

            // Deserialize the background directive.
            var backgroundOperationDirective = job.GetTaskQueueDirective <BackgroundOperationTaskQueueDirective>();

            if (null == backgroundOperationDirective)
            {
                // This is an issue.  We have no way to decide what background operation should run it.  Die.
                SysUtils.ReportErrorToEventLog
                (
                    $"Job loaded with no background operation name loaded (queue: {job.AppTaskQueueId}, task id: {job.AppTaskId})."
                );
                return;
            }

            // If we have a directive then extract it.
            var dir = backgroundOperationDirective.GetParsedInternalDirective();

            // If it is a broadcast directive, then was it generated on the same server?
            // If so then ignore.
            if (dir is BroadcastDirective broadcastDirective)
            {
                if (broadcastDirective.GeneratedFromGuid == TaskQueueBackgroundOperationManager.CurrentServer.ServerID)
                {
                    return;
                }
            }

            // Find the background operation to run.
            TaskQueueBackgroundOperationOverview bo = null;

            lock (_lock)
            {
                if (false == this.BackgroundOperations.TryGetValue(backgroundOperationDirective.BackgroundOperationName, out bo))
                {
                    // We have no registered background operation to handle the callback.
                    SysUtils.ReportErrorToEventLog
                    (
                        $"No background operation found with name {backgroundOperationDirective.BackgroundOperationName}(queue: {job.AppTaskQueueId}, task id: {job.AppTaskId})."
                    );
                    return;
                }
            }

            // Should we repeat?
            DateTime?nextRun = null;

            switch (bo.BackgroundOperation.RepeatType)
            {
            case TaskQueueBackgroundOperationRepeatType.Interval:

                // Add the interval to the current datetime.
                if (bo.BackgroundOperation.Interval.HasValue)
                {
                    nextRun = DateTime.UtcNow.Add(bo.BackgroundOperation.Interval.Value);
                }
                break;

            case TaskQueueBackgroundOperationRepeatType.Schedule:

                // Get the next execution time from the schedule.
                nextRun = bo.BackgroundOperation.Schedule?.GetNextExecution(DateTime.Now);
                break;
            }

            // If we have a next run time then re-run.
            if (null != nextRun)
            {
                // Bind to the completed event ( called always ) of the job.
                // That way even if the job is canceled, fails, or finishes successfully
                // ...we always schedule the next run.
                job.ProcessingCompleted += (s, op)
                                           =>
                {
                    // Ensure that if two threads both run this at once we don't end up with a race condition.
                    lock (_lock)
                    {
                        // Cancel any future executions (we only want the single one created below).
                        this.CancelFutureExecutions(bo.BackgroundOperation.Name);

                        // Now schedule it to run according to the interval.
                        this.RunOnce
                        (
                            bo.BackgroundOperation.Name,
                            nextRun.Value.ToUniversalTime(),
                            dir
                        );
                    }
                };
            }

            // Perform the action.
            try
            {
                // Mark the background operation as running.
                bo.Status  = TaskQueueBackgroundOperationStatus.Running;
                bo.LastRun = DateTime.UtcNow;

                // Delegate to the background operation.
                bo.BackgroundOperation.RunJob(job, dir);
            }
            catch (Exception e)
            {
                // Exception.
                this.TaskProcessor.UpdateTaskInfo
                (
                    job.Data?.Value,
                    MFTaskState.MFTaskStateFailed,
                    e.Message,
                    false
                );
                // TODO: throw?
            }
            finally
            {
                // If the status is running then stop it.
                if (bo.Status == TaskQueueBackgroundOperationStatus.Running)
                {
                    bo.Status = TaskQueueBackgroundOperationStatus.Stopped;
                }
            }
        }
Пример #5
0
        /// <summary>
        /// Handles when a job from a task requires processing.
        /// Note that all tasks/jobs in the queue use the same task type ID,
        /// so the directive <see cref="BackgroundOperationTaskQueueDirective.BackgroundOperationName"/>
        /// is used to identify which tasks should be executed by
        /// which background operation.
        /// </summary>
        /// <param name="job">The job to process.</param>
        protected virtual void ProcessJobHandler(TaskProcessorJob job)
        {
            // Sanity.
            if (null == job)
            {
                return;
            }

            // Ensure cancellation has not been requested.
            job.ThrowIfCancellationRequested();

            // Update the progress of the task in the task queue.
            this.TaskProcessor.UpdateTaskAsAssignedToProcessor(job);

            // Sanity.
            if (null == job.Data?.Value)
            {
                // This is an issue.  We have no way to decide what background operation should run it.  Die.
                SysUtils.ReportErrorToEventLog
                (
                    $"Job loaded with no application task (queue: {job.AppTaskQueueId}, task type: {job.AppTaskId})."
                );
                return;
            }

            // Deserialize the background directive.
            var backgroundOperationDirective = job.GetTaskQueueDirective <BackgroundOperationTaskQueueDirective>();

            if (null == backgroundOperationDirective)
            {
                // This is an issue.  We have no way to decide what background operation should run it.  Die.
                SysUtils.ReportErrorToEventLog
                (
                    $"Job loaded with no background operation name loaded (queue: {job.AppTaskQueueId}, task type: {job.AppTaskId})."
                );
                return;
            }

            // If we have a directive then extract it.
            var dir = backgroundOperationDirective.GetParsedInternalDirective();

            // If it is a broadcast directive, then was it generated on the same server?
            // If so then ignore.
            if (dir is BroadcastDirective broadcastDirective)
            {
                if (broadcastDirective.GeneratedFromGuid == TaskQueueBackgroundOperationManager.CurrentServer.ServerID)
                {
                    return;
                }
            }

            // Find the background operation to run.
            TaskQueueBackgroundOperation bo = null;

            lock (_lock)
            {
                if (false == this.BackgroundOperations.TryGetValue(backgroundOperationDirective.BackgroundOperationName, out bo))
                {
                    // We have no registered background operation to handle the callback.
                    SysUtils.ReportErrorToEventLog
                    (
                        $"No background operation found with name {backgroundOperationDirective.BackgroundOperationName}(queue: {job.AppTaskQueueId}, task type: {job.AppTaskId})."
                    );
                    return;
                }
            }

            // If it is recurring then use the ProcessingComplete event to schedule the next execution.
            if (bo.Recurring && bo.Interval.HasValue)
            {
                // Bind to the completed event ( called always ) of the job.
                // That way even if the job is canceled, fails, or finishes successfully
                // ...we always schedule the next run.
                job.ProcessingCompleted += (s, op)
                                           => this.RunOnce
                                           (
                    bo.Name,
                    DateTime.UtcNow.Add(bo.Interval.Value),
                    dir
                                           );
            }

            // Perform the action.
            try
            {
                // Delegate to the background operation.
                bo.RunJob(job, dir);
            }
            catch (Exception e)
            {
                // Exception.
                this.TaskProcessor.UpdateTaskInfo
                (
                    job.Data?.Value,
                    MFTaskState.MFTaskStateFailed,
                    e.Message,
                    false
                );
                // TODO: throw?
            }
        }