Microsoft & .NETRunning Timed Jobs within SharePoint

Running Timed Jobs within SharePoint

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Introduction

Microsoft Windows SharePoint Services (WSS) and its Portal version, Microsoft Office SharePoint Server (MOSS), carries out various automatic, scheduled jobs to realize repetitive tasks in a standard way. For example, the alerts and notifications engine scans new items added to a site in a timetabled way and sends emails to users. Additionally, information generated from the log files to make statistical analysis occurs in pre-determined time intervals.

The SharePoint Timed Jobs infrastructure takes care of the scheduled tasks needed to ensure proper functioning of the system. Timed Jobs run in the background and are based in the Microsoft SharePoint Timer service, which is always installed in the default setup of WSS and MOSS. Installed Timed Jobs can be configured from SharePoint’s Central Administration using the Command-Line administrator’s tool or programmed using the Object Model.

Timed Jobs can be set up to run immediately or in a time frame based on “one,” meaning a SharePoint Job can run each minute, day, or week but not, for example, every two hours or days. Another limitation is that it is impossible to schedule Jobs to run on relative time frames; for example, the second Monday of every month. A Timed Job requires a starting time, and the configured time frame determines the interval it will run at. The starting time can be an absolute time (at 12:00 am, for example) or a time range (between 8:00 and 9:00 am); in the first instance, the Job would require a few seconds to execute because the service needs to be initiated; in the second case, the Job will run at a random time within the range, giving the different servers in the farm the opportunity to progress at varied periods, balancing the load on the system.

Finally, the Timed Jobs system relies on the Gregorian calendar, based on a 24-hour clock. The user configures the initial time using the local time of the SharePoint server, and the system calculates the offset from the Universal Coordinate Time (UTC) to store it.

Using the SharePoint Administration Pages

The Central Administration of SharePoint provides the location to configure a specified Job. For instance, it is possible to set up “Use Confirmation and Deletion” of sites in a Portal: If there is no traffic in a site, the system sends a notification to the owner. If there is no confirmation of use, after a number of send notification without confirmation, the site can be configured to delete.

This is a characteristic use of Timed Jobs; the Job engine runs the process in a time-tabled way, detects whether a site has been approached, and compares the time span with the configured value. It then sends an email if necessary, and eventually, if unconfirmed, deletes the site. Figure 1 displays the Central Administration Page to configurate the procedure. In the example, the Timed Job has been scheduled to run every day at 12:00 am, and after 3 unanswered messages, the site is deleted.

Figure 1: Configuration of a Timed Job

For the general administration of all Jobs, the SharePoint Central Administration has “Timer job status” and “Timer job definitions” pages (in the Central Administration, under the “Operations,” “Global Configuration” section).

The “Timer job status” page provides information about the server: where the Job has run (it can be a separate entry for each server in a farm), the Status of the Job, Progress indication if the Job is still running, and the date when the Job ran for the first time.

The “Timer job definitions” page shows all the Timed Jobs defined in the system, the scope of the Job, and the schedule type (time frame). Each entry in the page is a link to a specific page, where it is possible to enable/disable the Job and to change its name.

Figure 2: Timer Job Definitions

Using the Command Line

The “stsadm” Administrators command-line application can be used to control certain Timed Jobs functionality. Unfortunately, there are no default commands to install or start/stop Timed Jobs, but there is a command to run all Jobs immediately and commands to reschedule a number of Jobs.

The command “execadmsvcjobs” executes all the configured Timed Jobs present in the system immediately, as an alternative to waiting for the expiration of the time frame. This command does not require any parameters; the syntax is:

stsadm -o execadmsvcjobs

Bear in mind that, as all Jobs commence at the same time, it may present a burden on SharePoint servers. Also, if the Windows SharePoint Services Timer service has been stopped for any reason, the aforementioned command may be used to manually run the Jobs in the Portal.

There are also SharePoint components that use a Timed Job and can be controlled using the command-line tool. For example, the commands “setpolicyschedule”, “setcontentdeploymentjobschedule”, and “setsitedirectoryscanschedule” are components that support the Timed Job infrastructure. Each of the commands accepts a “-schedule” parameter (named “recurrence string” or “schedule” in the help function of stsadm) that is a free string of the form:

"[Time frame] [Interval] between [value] and [value]"

For example:

“every 5 minutes between 0 and 59”
“hourly between 0 and 59”
“daily at 15:00:00”
“weekly between Fri 22:00 and Sun 06:00:00”
“yearly at Jan 1 15:00:00”

The syntax looks like this:

stsadm -o setsitedirectoryscanschedule
       -schedule "daily at 03:00:00"

Using the Object Model

The Object Model of SharePoint also enables developers to manage Timed Jobs. The classes SPJobDefinitionCollection and SPJobDefinition from the NameSpace Microsoft.SharePoint.Administration contain the methods and properties necessary to operate Timed Jobs.

The class SPJobDefinitionCollection contains the entire Timed Jobs selection of a SharePoint Web application, each of the type SPJobDefinition. The SPJobDefinition class contains the properties that define a Job (Name, Title, Version, and in which Server and WebApplication it is working, and so forth) and the methods to process it (Execute, Delete, Update). A special property, “Schedule”, of the type “SPJobDefinition.Schedule”, sees to the time frame for the Job.

To enumerate all the Jobs in an application, the following routine can be used in a command-line application to show the title, last run time, and type of each Job:

static void GetAllJobs()
{
   SPSite mySite = new SPSite("http://ServerName");

   foreach (SPJobDefinition oneJob in
            mySite.WebApplication.JobDefinitions)
   {
      Console.WriteLine(oneJob.Title + " -" +
         oneJob.LastRunTime.ToLongTimeString() + " - " +
         oneJob.Schedule.GetType().ToString().Remove(0, 21));
   }
}

Note that the string “Microsoft.SharePoint” has been stripped from the schedule type.

Figure 3: Output from Timed Jobs for the application href=”http://ServerName”, together with the Central Administration Page showing the same results

The routine loops through the Job definitions of the application, displaying various properties of each found Job. It is necessary to set a reference to Windows SharePoint Services (Microsoft.SharePoint.dll) and “using” directives to Microsoft.SharePoint and Microsoft.SharePoint.Administration.

The next routine adds a new Job Definition to the application:

static void CreateJob()
{
   SPServer myServer = SPServer.Local;
   SPSite mySite = new SPSite("http://ServerName");
   SPWebApplication myApplication = mySite.WebApplication;
   SPJobDefinitionCollection myJobsColl = myApplication.JobDefinitions;

   SPJobDefinition myJob = new SPJobDefinition("FakeTestJob",
      myApplication, myServer, SPJobLockType.Job);
   myJob.Title = "Fake Test Job";

   SPDailySchedule myDailySchedule = new SPDailySchedule();
   myDailySchedule.BeginHour = 3;
   myDailySchedule.BeginMinute = 0;
   myDailySchedule.BeginSecond = 0;
   myJob.Schedule = myDailySchedule;

   myJobsColl.Add(myJob);
   myApplication.Update();

   Console.WriteLine("Job Installed");
}

First, local variables are created to contain the instance of the local server (type SPServer), the site (type SPSite), and Web application (type SPWebApplication) that will run the Job and the collection of Jobs in the Web application (type SPJobDefinitionCollection).

Then, a new Timed Job is generated using the constructor of SPJobDefinition, together with a name, the reference to the application and server, and one of the type lock elements of the SPJobLockType enumeration (“Job”, “ContentDatabase” or “None”) as parameters. The new Job in the example is not a proper functioning Job because there is no “logic” behind the source code of the constructor (therefore the name “fake”). After creation, the properties can be set (the Title, in the example).

Subsequent to the formation of the Job, a new schedule variable (type SPDailySchedule in the example, but there are other options such as SPMinuteSchedule, SPHourlySchedule and SPOneTimeSchedule) is prepared, its properties (begin and end time in hours, minutes and seconds) are specified, and the schedule is ascribed to the recently created Job.

Finally, the Job is added to the Jobs Collection and the application is updated. Figure 4 shows the output of a console application running the source code, and the same output on the screen of SharePoint’s Central Administration.

Figure 4: A new Job added

To remove a Job, the “Delete” method of the class SPJobDefinition is used:

static void DeleteJob()
{
   SPSite mySite = new SPSite("http://ServerName");

   foreach (SPJobDefinition oneJob in
            mySite.WebApplication.JobDefinitions)
   {
      if(oneJob.Title == "Fake Test Job")
      {
         oneJob.Delete();

         Console.WriteLine("Job Deleted");
      }
   }
}

An alternate method to eliminate the Job is to locate the ID and delete it from the Job Collection using the “Remove” method from the SPJobDefinitionCollection:

static void DeleteJob2()
{
   Guid myId = new Guid();

   SPSite mySite = new SPSite("http://ServerName");
   SPWebApplication myApplication = mySite.WebApplication;
   SPJobDefinitionCollection myJobsColl =
      myApplication.JobDefinitions;

   foreach (SPJobDefinition oneJob in
            mySite.WebApplication.JobDefinitions)
   {
      if (oneJob.Title == "Fake Test Job")
         myId = oneJob.Id;
   }

   myJobsColl.Remove(myId);
   myApplication.Update();

   Console.WriteLine("Job Deleted");
}

Programming a Timed Job

The Job installed in the aforementioned paragraph does not actually work because there is no “logic” to execute. To create a new Job, it is necessary to program a new class library and add it to the Global Assembly Cache (GAC) to make the logic accessible to SharePoint.

The first step is to create a new “Class Library” Visual Studio 2005 project with a suitable name, then add a reference to Windows SharePoint Services (Microsoft.SharePoint.dll) and “using” directives to Microsoft.SharePoint and Microsoft.SharePoint.Administration. The Class needs to inherit from “SPJobDefinition” and to have at least one constructor to initialize the new objects. The definition of the empty class reads:

using System;
using System.IO;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

namespace CountDocs
{
   public class CountDocsJob : SPJobDefinition
   {
      public CountDocsJob()
      {
      }

      //public CountDocsJob(string MyJobName,
      //SPService MyService,
      //SPServer MyServer, SPJobLockType MyTargetType) :
      //base(MyJobName, MyService, MyServer, MyTargetType)
      //{
      //}

      public CountDocsJob(string MyJobName, SPWebApplication
         MyWebApplication) : base(MyJobName, MyWebApplication,
         null, SPJobLockType.Job)
      {
      }

      public override void Execute(Guid ContentDataBaseID)
      {
      }
   }
}

The base class has two constructors but if two are unnecessary, it is possible to define only one (the second one is commented in the example). The constructor of the sub-classing indicates that the third parameter (the SPServer parameter) is superfluous in this case.

The functional source code is written in the override “Execute” function. This function always has a parameter that receives the ID of the SharePoint’s content DataBase that is executing the Job. For the example, the code reads the number of entries in a List and each time it runs, a new entry is created indicating the time and number of found entries:

public override void Execute(Guid ContentDataBaseID)
{
   try
   {
      SPSite mySite = new SPSite("http://ServerName");
      SPWeb myWeb = mySite.OpenWeb();
      SPList myList = myWeb.Lists["MyJobs"];

      int intCounter = 0;
      foreach (SPListItem myItem in myList.Items)
         intCounter++;

      SPListItem newTask = myList.Items.Add();
      newTask["Title"] = "Counter of Items at " +
         DateTime.Now.ToString();
      newTask["Description"] = "At " + DateTime.Now.ToString() +
         " there are " + intCounter.ToString() +
         " items in the List";
      newTask.Update();
   }
   catch (Exception ex)
   {
      TextWriter myWriter =
         new StreamWriter(@"c:CountDocsJobError.txt");
      myWriter.WriteLine(ex.ToString());
      myWriter.Close();
   }
}

In the case in point, the code makes a reference to the Site, Web, and List, counts the number of entries in the List, and creates a new entry. The names of the Site and List are fast-coded, but they can be configured in the web.config file of the WebApplication using code similar to this:

Configuration MyConfig =
   WebConfigurationManager.OpenWebConfiguration("/", "Sharepoint -80");
string myValue = myConfig.AppSettings.Settings["MyKeyName"].value;

or extracted from the “ContentDataBaseID” variable.

Sign the assembly with a Safe Name and install it in the GAC (obligatory). After resetting the IIS (iisreset), the previously discussed “CreateJob” routine can be modified to install the Job in the server:

static void CreateJob()
{
   SPServer myServer = SPServer.Local;
   SPSite mySite = new SPSite("http://ServerName");
   SPWebApplication myApplication = mySite.WebApplication;
   SPJobDefinitionCollection myJobsColl =
      myApplication.JobDefinitions;

   CountDocsJob myJob = new CountDocsJob("myCountDocsJob",
      myApplication);
   myJob.Title = "Docs Counter Job";

   SPMinuteSchedule myMinuteSchedule = new SPMinuteSchedule();
   myMinuteSchedule.BeginSecond = 0;
   myMinuteSchedule.EndSecond = 59;
   myMinuteSchedule.Interval = 1;
   myJob.Schedule = myMinuteSchedule;

   myJobsColl.Add(myJob);
   myApplication.Update();

   Console.WriteLine("Job Installed");
}

Remember to add a reference in the installation program to the “CountDocsJob” assembly. After compiling and executing the installation program, the Job is activated in the server. For test purposes, the installation uses a schedule time of one minute, but any time frame can be used, as explained earlier in the article.

If, after compilation, it is necessary to make modifications in the source code, delete the DLL from the GAC, copy the new version, execute an “iisreset” and, very important, stop and start the “Windows SharePoint Services Timer” from the Windows Services Manager (because the Job is cached by the service).

Figure 5: Output of Count Docs Job

Bear in mind that the Job expects to find a List in the main Web site of the portal, with the correct name (“MyJobs” in the example) and a “Description” field. An important point is that the error catching needs to be consistent: If an exception occurs, the Job will run continuously without issuing a warning. In the example, the error is logged in an external text file, but an entry in the Event Viewer might be a better solution.

Conclusions

Timed Jobs are an important component of SharePoint because they handle repetitive tasks without user involvement. Unfortunately, there are no facilities within SharePoint to install/de-install or activate Jobs; to achieve this, additional, custom-built software is necessary. On the positive side, programming Jobs does not differ from any standard SharePoint code used to realize common tasks with servers.

About the Author

Gustavo Velez is a MCSD Senior Application Developer for Winvision (http://www.winvision.nl), a Microsoft Gold Partner in the Netherlands. He has many years experience developing Windows and Office applications, and more than five years of daily programming with SharePoint. The author’s articles, in English, Dutch, and Spanish, are found in many of the leading trade magazines and he’s pleased to be Webmaster of http://www.gavd.net/servers, the only Spanish-language dedicated SharePoint site. Spanish-language readers may want to consult his book; Programacisn con SharePoint 2007.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories