Tuesday, December 23, 2008

Creating Timer Job Definitions

At my current workplace, developers do not have the full access we require at times to run console applications on production servers. To get around this problem I have started creating my batch processes as SharePoint Timer Jobs.

Firstly, the code that does the batch processing is contained in its own class. This class has one static method Execute with a parameter of type SPWeb. I contain it in its own class so I can call if from anywhere (timer job, feature, console app, web part etc).

eg.

    public class SynchronizeGroupsManager

    {

        public static void Execute(SPWeb web)

        {

            //Do something here...

        }

    }



From there I create a SharePoint job definition. This class is a subclass of SPJobDefinition. This class implements an override of the Execute method. In this method, I create an SPWeb instance and pass this onto the static method I created early. This is the method that gets executed when the timer job fires.

eg.

    public class SynchronizeGroupsJob : SPJobDefinition

    {

        private const string siteIdPropertyName = "SiteCollectionId";

 

        public SynchronizeGroupsJob() : base()

        {

        }

 

        public SynchronizeGroupsJob(string jobName, SPService service, SPServer server, SPJobLockType targetType) : base(jobName, service, server, targetType)

        {

        }

 

        public SynchronizeGroupsJob(string jobName, SPWebApplication webApp, Guid siteId) : base(jobName, webApp, null, SPJobLockType.ContentDatabase)

        {

            this.Title = JobName.SynchronizeGroups;

            this.Properties.Add(siteIdPropertyName, siteId);

        }

 

        public override void Execute(Guid targetInstanceId)

        {

            HandleEventFiring handleEventFiring = new HandleEventFiring();

 

            handleEventFiring.CustomDisableEventFiring();

            try

            {

                Guid siteId = (Guid)this.Properties[siteIdPropertyName];

 

                using (SPSite site = new SPSite(siteId))

                {

                    using (SPWeb web = site.OpenWeb())

                    {

                        try

                        {

                            SynchronizeGroupsManager.Execute(web);

                        }

                        catch (Exception ex)

                        {

                            ErrorLogger.WriteErrorToTraceLog(web, ex);

                        }

                    }

                }

            }

            finally

            {

                handleEventFiring.CustomEnableEventFiring();

            }

        }

    }



The final step is to create a feature to install the timer job (and to do an initial execution if needed). I like to do the initial execution because I can deactivate and reactivate the feature at any time and perform the batch processing immediately.

You have plenty of options to create different schedules for your timer job. The following example is a daily schedule. As I have not specified any times, the following timer job will fire at 12:00am by default.

eg.

    public class InstallSynchronizeGroupsJob : SPFeatureReceiver

    {

        public override void FeatureActivated(SPFeatureReceiverProperties properties)

        {

            SPSite site = properties.Feature.Parent as SPSite;

 

            RemoveExistingTimerJob(site);

 

            SPDailySchedule schedule = new SPDailySchedule();

 

            SynchronizeGroupsJob synchronizeGroupsJob = new SynchronizeGroupsJob(JobName.SynchronizeGroups, site.WebApplication, site.ID);

            synchronizeGroupsJob.Schedule = schedule;

            synchronizeGroupsJob.Update();

 

            InitialSynchronization(site);

        }

 

        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)

        {

            SPSite site = properties.Feature.Parent as SPSite;

            RemoveExistingTimerJob(site);

        }

 

        public override void FeatureInstalled(SPFeatureReceiverProperties properties)

        {

        }

 

        public override void FeatureUninstalling(SPFeatureReceiverProperties properties)

        {

        }

 

        private static void RemoveExistingTimerJob(SPSite site)

        {

            foreach (SPJobDefinition job in site.WebApplication.JobDefinitions)

            {

                if (job.Name == JobName.SynchronizeGroups)

                {

                    job.Delete();

                    break;

                }

            }

        }

 

        private static void InitialSynchronization(SPSite site)

        {

            using (SPWeb web = site.OpenWeb())

            {

                SynchronizeGroupsManager.Execute(web);

            }

        }

    }