Extending scheduled task to set execution time

In a lot of cases it would make sense to tell Sitecore at what time to execute scheduled tasks. If for example a heavy import job is to be executed once a week it would make sense to do it at a time when the editors are not active or when the traffic to the site is low.

We will create an extension template for a schedule that tells Sitecore when it is ok to execute the schedule. So lets start with creating the template. Just two single line text fields. Set the default values of the fields to 00:00:01 and 23:59:59 respectively. This will ensure that existing scheduled tasks won’t be affected by the new code and the new template.

Schedule extension template
Inherit this template on the schedule template. It can be found at /sitecore/templates/System/Tasks/Schedule.

Then go ahead and create a task and a schedule for the task.

Scheduled task

The schedule should look something like this.

schedule

So to sum up what we have done so far is create a task and a schedule. The schedule template has been extended with two new fields called “Time frame start” and “Time frame end”. The schedule is set up to run from 2017-01-01 to 2100-01-01 every sunday with at least 23:59:59 between each run. We have also set the time frame to run the task from 02:00:00 to 04:00:00. Now all that is left to do is to create the code that handles this. For more information on scheduled tasks see John West’s classic post All About Sitecore Scheduling.

So let us get started with a Database agent that can handle the time frame and replace the original database agent provided by Sitecore. As usual when you extend classes that were provided by Sitecore you end up duplicating some code due to methods being declared as private in the original class. Most of this code is copied from the original database agent.

    public class DatabaseAgent : Sitecore.Tasks.DatabaseAgent
    {
        public DatabaseAgent(string databaseName, string scheduleRoot) : base(databaseName, scheduleRoot)
        {
        }

        public new void Run()
        {
            this.LogInfo("Scheduling.DatabaseAgent started. Database: " + this.Database.Name);
            Job job = Context.Job;
            ScheduleItem[] schedules = this.GetSchedules();
            this.LogInfo("Examining schedules (count: " + (object)schedules.Length + ")");
            if (this.IsValidJob(job))
                job.Status.Total = (long)schedules.Length;
            foreach (ScheduleItem scheduleItem in schedules)
            {
                try
                {
                    if (scheduleItem.IsDue && IsInTimeFrame(scheduleItem))
                    {
                        this.LogInfo("Starting: " + scheduleItem.Name + (scheduleItem.Asynchronous ? " (asynchronously)" : string.Empty));
                        scheduleItem.Execute();
                        this.LogInfo("Ended: " + scheduleItem.Name);
                    }
                    else
                        this.LogInfo("Not due: " + scheduleItem.Name);
                    if (scheduleItem.AutoRemove)
                    {
                        if (scheduleItem.Expired)
                        {
                            this.LogInfo("Schedule is expired. Auto removing schedule item: " + scheduleItem.Name);
                            scheduleItem.Remove();
                        }
                    }
                }
                catch
                {
                }
                if (this.IsValidJob(job))
                    ++job.Status.Processed;
            }
        }

        private bool IsInTimeFrame(ScheduleItem scheduleItem)
        {
            var startTimeString = scheduleItem.InnerItem.Fields["ScheduledItemExtensionDataTimeframeStart"].Value;
            var endTimeString = scheduleItem.InnerItem.Fields["ScheduledItemExtensionDataTimeframeEnd"].Value;

            return IsDue(startTimeString, endTimeString);
        }

        private bool IsDue(string startTime, string endTime)
        {
            DateTime start;
            DateTime end;
            DateTime.TryParse(startTime, out start);
            DateTime.TryParse(endTime, out end);
            return (CheckTime(DateTime.Now, start, end));
        }

        private bool CheckTime(DateTime time, DateTime after, DateTime before)
        {
            return ((time >= after) && (time <= before));
        }

        private ScheduleItem[] GetSchedules()
        {
            Item obj = this.Database.Items[this.ScheduleRoot];
            if (obj == null)
                return new ScheduleItem[0];
            ArrayList arrayList = new ArrayList();
            foreach (Item innerItem in obj.Axes.GetDescendants())
            {
                if (innerItem.TemplateID == TemplateIDs.Schedule)
                    arrayList.Add((object)new ScheduleItem(innerItem));
            }
            return arrayList.ToArray(typeof(ScheduleItem)) as ScheduleItem[];
        }

        private bool IsValidJob(Job job)
        {
            if (job != null)
                return job.Category == "schedule";
            return false;
        }

        private void LogInfo(string message)
        {
            if (!this.LogActivity)
                return;
            Log.Info(message, (object)this);
        }

    }

So if we start to look into this code the Run() method is a copy of the Run() method from the Sitecore.Tasks.Databaseagent, except for the addition IsInTimeFrame() call in the if clause.

The IsInTimeFrame() method simply gets the field values and pass them on to The IsDue() method which converts them to DateTimes and Checks if the task should be executed or not.

Now all that is left to do is to do some configuring. This config exchanges the siteocre database agent for our newly created database agent.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set/">
  <sitecore>
    <scheduling>
      <agent type="MyNamespace.DatabaseAgent" method="Run" interval="00:10:00" name="Master_Database_Agent" patch:instead="agent[@name='Master_Database_Agent']">
        <param desc="database">master</param>
        <param desc="schedule root">/sitecore/system/tasks/schedules</param>
        <LogActivity>true</LogActivity>
      </agent>
    </scheduling>
  </sitecore>
</configuration>

This is an updated and extended version of Brian’s blog post Run Sitecore scheduled task at the same time every day.

Leave a Reply

Your email address will not be published. Required fields are marked *