Creating a Timer Job in SharePoint, complete guide

Timer Job definition

Timer jobs are tasks that run at a scheduled time. Just like MSSQL Server Agent jobs, which executes different database task, SharePoint uses timer jobs to maintain or monitor the server farm. If you have the curiosity bug and want to see the timer jobs that are on your server then go to SharePoint Central Administration, select Monitoring from the menu and in the Timer Jobs section click on Review job definitions. Depending on your SharePoint edition and your web applications you can have quite a few or just a few timer jobs.

Image alternative text

Figure 1. SharePoint Central Job Definitions list

Timer Job advantages

The timer job is a scheduled executable tasks that run in background. Some of these jobs are connected with long-running processes which you don’t want to run on your front-end servers. From a developer’s point of view your code in a custom timer job will be executed with high privileges at an interval that you will choose.

Creating a custom timer job

Timer job definition is a class that inherits from the SPJobDefinition. A timer job instance is an object instance of the Timer Job Definition class. Since the definition is a .NET class it has constructors, methods, properties. There are two main constructors that allow us to associate the timer job to a web application or a service. The purpose of this post is to create a timer job that will be associated to a web application. Before starting with development make sure that you have installed Visual Studio 2010 and SharePoint 2010 on the development machine. Select File, New, Project and select Empty SharePoint Project (make sure that .NET Framework 3.5 is selected). Give your timer job a name, choose the location where you want the project to be saved and click OK. A SharePoint Customization Wizard will show immediately. Type in the URL of your site, select Deploy as a farm solution and click Finish. If you started with a blank project, then you will need to add a reference to Microsoft.SharePoint.dll. Most of these assemblies are located in C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI

Image alternative text

Figure 2. Visual Studio New Project dialog box

After clicking OK a SharePoint Customization Wizard window will appear. Enter the URL of your SharePoint site, choose Deploy as a farm solution and click Finish.

Image alternative text

Figure 3. SharePoint Customization Wizard dialog box

First we need to add a class that inherits from the SPJobDefinition class. Right click on your project, choose Add, Class. Give a name to your class and click Add.

Image alternative text

Figure 4. Visual Studio Add New Item dialog box

The SpJobDefinition and SPSchedule reside in Microsoft.SharePoint.Administration namespace so in your using section you need to add the following statement using Microsoft.SharePoint.Administration;

Start with changing the visibility of your class to public and make the class inherit from SPJobDefinition. First you need to implement a default constructor without parameters. Then you need to implement a non-default constructor and pass values for four parameters: - Name (the name of the job) - webApplication (instance of the SPWebApplication class that owns this job) - Server (instance of the SPServer class associated with this job. You can pass null if the job is not associated with a specific server) - lockType (An SPJobLockType value that indicates the circumstances under which multiple instances of the job can be run simultaneously)

using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.SharePoint.Administration;

namespace DocumentTimerJob { public class TimerJobClass : SPJobDefinition { //Constructor public TimerJobClass() : base() {

    }

    //Constructor
    public TimerJobClass(string name, SPWebApplication webApp,
                         SPServer server, SPJobLockType lockType)  : base(name, webApp, server, lockType) 
    { 

    }

    public override void Execute(Guid targetInstanceId)
    {
        //Your code goes here

    }
}

}

Your timer job has two properties that are related to naming, Name and DisplayName. The Name property is used for identifying the timer job and it must be unique under the parent SPService or SPWebApplication. The DisplayName will show in Central Administration and if you don’t define it then it will take the value of the Name property. The SPJobLockType is a parameter that must be provided and its value defines where and how many times the job will be executed. The possible values are: - None (locking is disabled and the job will run on all servers in the farm unless you pass a SPServer object) - ContentDatabase (the job runs for each content database that is associated with the job’s web application) - Job (only one server can run the job at a time) After creating the constructors, you must override the Execute method and replace the code in that method with the code that your job requires.

A real-life example

A client has a document management system but they are still using some old software which is creating/scanning pdf files and throws them to a shared folder on a server. We need to create a solution that will go through that folder, grab the files, uploads them to a SharePoint library and create a record in a MSSQL database. Because the client doesn’t want all of this to be manually done (a user that will have to click a button) we have come to the idea to use a Timer Job. The code that will do all of these operations will be inside the overridden Execute method. The path to the folder with the files will be saved to a configuration file. The simplest way to do this is to open the OWSTIMER.EXE configuration file and add your keys. This file can be found in C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\BIN This is my OWSTIMER.EXE configuration file after changing it:

In it I also keep information about the path of the log file, the web application URL and a connection string to my MSSQL database. Doing it like this is very convenient if later you need to take this timer job to a production environment. All you need to do is just add the correct values in the configuration file. The next code is my Execute method (without some of the logic for the problem): public override void Execute(Guid targetInstanceId) { int count = 0, total = 0; string location = ConfigurationSettings.AppSettings["FileTransferLocation"];

        string[] files = Directory.GetFiles(location, "*.pdf", SearchOption.AllDirectories);
        total = files.Length;

        foreach (string item in files)
        {
            string name = item;
            FileStream fileStream = new FileStream(name, FileMode.Open, FileAccess.Read);
            int length = (int)fileStream.Length;

            try
            {
                if (length > 0)
                {
                    // Get the file and upload it to a SharePoint library

                    count++;

                    // Delete the file when over
                    File.Delete(name);
                }
            }
            catch (Exception ex)
            {
                // Write the error to the log file
            }
            finally
            {
                // Close the stream
                fileStream.Close();
            }
        }

        if (count > 0)
        {
            // Write in the log file how many documents where uploaded
        }
    }

Deploying

Your custom timer job will be available in the Site Features or Site Collection Features list on the SharePoint site. To make that possible we need to add a feature to our project. Right click the Features folder in our project and choose Add Feature. Type a title and a description for the feature. These values will be visible in the features list on your site. Set the scope to Web since this feature registers the job that is associated to a web application in your farm. When you are done click Save in the toolbar.

Image alternative text

Figure 5. Add new feature

Next step is adding event receiver to the feature. This event receiver will have the code that is required to register the timer job. An event receiver is a class that runs code when certain events occur in SharePoint. In this case, you add an event receiver to run code when the feature is activated or deactivated. Right click the feature and choose Add Event Receiver. Now uncomment the FeatureActivated and FeatureDeactivating methods. To register our job with SharePoint we must add code to the FeatureActivated method and obviously code in our FeatureDeactivating method. The next code is all you need for the event receiver: public class Feature1EventReceiver : SPFeatureReceiver { const string DocJobName = "ScannedDocumentsJob";

    public override void FeatureActivated(SPFeatureReceiverProperties properties)
    {
        SPSite site = properties.Feature.Parent as SPSite;

        // Checking if the job is already registered
        foreach (SPJobDefinition job in site.WebApplication.JobDefinitions)
        {
            if (job.Name == DocJobName)
                job.Delete();
        }

        // Installing the job
        TimerJobClass docJob = new TimerJobClass(DocJobName, site.WebApplication, 
                                                        SPServer.Local, SPJobLockType.Job);

        // Define the interval
        SPMinuteSchedule schedule = new SPMinuteSchedule();
        schedule.BeginSecond = 0;
        schedule.EndSecond = 59;
        schedule.Interval = 5;

        docJob.Schedule = schedule;
        docJob.Update();
    }

    public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
    {
        SPSite site = properties.Feature.Parent as SPSite;

        // Delete the job
        foreach (SPJobDefinition job in site.WebApplication.JobDefinitions)
        {
            if (job.Name == DocJobName)
                job.Delete();
        }
    }
}

You can delete the other commented methods if you don’t like too much green in your code. The FeatureActivated method gets an instance of the SPSite class as a parent for this feature. Other possible values for this are farm (SPFarm), web application (SPWebApplication) and site (SPWeb). After the job is created we need to set its Schedule property. In this case we used SPMinuteSchedule. Other possible values are: - SPHourlyScheduled (runs every hour) - SPDailySchedule (runs daily) - SPWeeklySchedule (runs weekly) - SPMonthlySchedule (runs monthly) - SpMonthlyByDaySchedule (runs the job once in a month on a specified day and week) - SPYealySchedule (runs yearly) - SpOneTimeSchedule (runs the job once) The properties BeginSecond and EndSecond specify the earliest and latest time, respectively, that the job can start. The Timer service randomly selects a time during that interval to start the job.

Testing and debugging

At this moment we are ready to deploy our code to SharePoint. By pressing F5 Visual Studio will do all of the job for us. It will compile the code, pack the resulting assemblies into a solution file (.wsp), deploy the solution package to SharePoint and after the package is deployed it will activate the feature. Debugging a timer job means attaching to the process that is behind the SharePoint Timer Service. In Visual Studio click on Debug and select Attach to Process. In the dialog box make sure you check Show processes from all users and Show processes in all sessions. From the list locate OWSTIMER.EXE, click on it and the click Attach. Add a break point in the Execute method and wait for the interval to come.

Image alternative text

Figure 6. Attach to Process dialog box

Deploying to production

If you were building the project in debug mode then the created .wsp file will be located in Bin\Debug folder and if you were building in release mode then it will be in Bin\Release folder. This .wsp file is needed to deploy in production environment. In my case its name is DocumentTimerJob.wsp. Take it and save it to your production server. Open Central Administration, choose System Settings and under Serves section choose Manage services on server. In the list find Microsoft SharePoint Foundation Sandboxed Code Service and make sure that the service is started.

Open SharePoint 2010 Management Shell (Run as Administrator). Use the following commands: •To add the solution:

Add-SPSolution {path of wsp file} •To install the solution:

Install-SPSolution -Identity DocumentTimerJob.wsp –GACDeployment •Check if your feature is in the feature list:

Get-SPFeature •Enable the feature

Enable-SPFeature -Identity {feature guid} -URL {url to your site}

If you make any changes to your code just compile the project, grab the .wsp file and use the following command to update the solution:

Update-SPSolution -Identity DocumentTimerJob.wsp -LiteralPath {path of wsp file} –GACDeployment

Remember that SharePoint and its Timer Job have a mind of their own so feel free to restart the SharePoint Timer service and reset the IIS every time after you update the .wsp file.