Creating the timer job
Sometimes you need to deploy settings to every web front end, or have a piece of code execute on every web front end. Your choices in SharePoint are to either have an administrator run a script on each front end, or use a timer job. This blog post describes how to use a timer job to accomplish this.
When you log to the event log using the native event log APIs or using the new SharePoint 2010 logging capabilities the event source used for the event logged must already exist, or the code must be running in a context that has privileges to create the event source, which means permissions to write to the registry. If the code doesn't have those permissions, then a security exception will be thrown. This condition may also show up as an security exception for attempting to access logs to which you don't have access. This may occur as the Event Log searches for the event source registration in different event logs, in particular the security log, to which your code may also not have access to. In any case, if you are attempting to log an event using the SharePoint 2010 APIs and are receiving a security exception, there is a good chance that the event source being used hasn't been created on the machine. Since event sources require registration in the local machine registry, the registration process must take place on each physical machine (i.e., code must run in a high privileged account on each WFE to register the event sources).
I'll state the obvious first. The timer job must be running under an account that can write to the registry. The timer job runs under the managed account called "farm account", and its pretty common to make this a relatively high permission account. By default it is running as NETWORK SERVICE, and NETWORK SERVICE will not have permission to write the settings to the registry, and therefore the process described in this blog will not work with default settings. This obvious one got me when I first developed this solution. You can added managed accounts in central admin->security->configure managed accounts, and you can set the farm account in central admin->security->configure service accounts.
There are two components that need to be developed; the timer job, and a feature to activate the timer job. We'll start with the timer job. A SharePoint timer job runs under a SharePoint managed process for timer execution (OWSTimer.exe, SharePoint 2010 Timer service). Timers run as one might expect periodically. The timer jobs that are scheduled, and the intervals at which they run are described here: http://technet.microsoft.com/en-us/library/cc678870.aspx. The type of timer job we'll create for this particular task will run only once, but will run on every web front end.
This example implementation uses the patterns and practices SharePoint Guidance Library that can be downloaded from http://www.microsoft.com/spg.
The following listing shows the full source code for the timer job:
We’ll take a look now at specific portions of the timer job. First every timer job will derive from SPJobDefinition. When the timer job is constructed, the base class is initialized with a few parameters:
- JobName – the name of our job.
- SPFarm.Logal.TimerService – We need to associate either a service or a web application with the timer job we want to run. In this case we specify the the timer service as the associated service.
- null – this specifies the SharePoint server to run the job on. By specifying null it will be ran on all servers.
- SPJobLockType.None – this isn’t obvious, but by defining a lock job type of none, we are telling SharePoint to run this on every machine. For more information, see http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.administration.spjoblocktype.aspx.
There are two other interesting pieces to the timer job. First we need to schedule the job to run. The class provides a convenience function to schedule this timer job for code that has registered a new logging category for the logger to use. The convenience function is a static function that registers an instance of the timer to execute. The timer is a single shot timer that will only run once. It is scheduled to run immediately with the timer service.
When the job is registered we need to clean up any previous jobs otherwise the job will fail due to a duplicate definition. CleanUpJobs will clean out any previous instances for us before the current job is scheduled. CleanupJobs will construct an instance of the job, so you can’t do this logic easily in the constructor, rather it’s done before the timer is rescheduled as shown above.
The final piece is where the work is actually done. The timer will construct this class to run the job, then call the Execute method. The work is actually done in the Execute method.
In this method, a convenience method is called in the SharePoint Guidance Library that will read all of the diagnostic areas, and register an event source for each (SharePoint logging has diagnostic areas, and each diagnostic area has one or more diagnostic category. An example of a diagnostic area would be search or taxonomy). The following code is provided by the guidance library – if you had your own implementation based upon the SPDiagnosticsServiceBase class, you would need to implement similar logic:
At this point all of the logic has been demonstrated for registering the events.
Registering Diagnostic Areas and Categories with SharePoint Logger
The SharePointLogger supports adding new diagnostic areas and categories through configuration rather than implementation. Typically this will be done through a feature receiver. These configuration values are stored using the ConfigurationManager in farm level configuration. SharePoint prevents writing to Farm Level configuration from a content web. FeatureActivated for site and web scoped features will run in the context of the content web (unless activated from powershell/stsadm). Therefore you should not register this information in the FeatureActivated event of a web or site scoped feature. There are two approaches that can be taken.
- The first, and recommended approach is to scope the feature as Farm. Since the configuration settings apply across the farm, its reasonable to use a farm scoped feature to set the values. A farm scoped feature will run at a sufficiently high permission level, outside of the content web, to write to farm level configuration. In the case of a farm scoped feature, the logic may either be in the FeatureActivated or FeatureInstalled event, and by default SharePoint will activate a farm scoped feature when it is installed.
- The second choice is to put the logic in the FeatureInstalled event of a Site or Web scoped feature. This method is less preferred, but still reasonable since the feature must be deactivated on all webs or sites in the farm before it can be removed.
The following example shows how to register the events from a farm scoped event receiver:
In this example the areas are added for the logger, and then the job is scheduled to create the event sources.
One more caution. The timer will tend to lock the assembly. You should recycle the timer service whenever you are rebuilding to ensure the assembly isn’t locked and therefore not updated in the GAC.
I have attached the full source as well. Hope this is helpful.