My recent content management engagement required to have several long running jobs or tasks that must run in background. For instance:
1. Analyzing Checked out files and sending reminders to the individuals if the number of days (since the file was checked out) exceed certain threshold limits
2. Removing links from other content when a particular content is removed or archived from the system
3. User wants to see all the alerts that he subscribed in all webs in the site collection
4. Sending a reminders to authors to review the content when a review time was specified in the content and that date is approaching
wow, the list goes on…
Well, we could have created one timer job for each scenario and scheduled them at different time. While that is perfectly fine, had some additional design goals for which this approach fall short. Our primary goals were:
1. Ability to monitor the background job and check its status right from site collection, without logging on to server on which it was run and go through ULS logs
2. Ability to run the job on demand without requiring farm level permissions
3. Ability to look into all individual tasks that the job performed and receive an email if anything unusual happened during the execution.
4. Ability to spin up a one time timer job based on an action by an end user and the timer job sends the required information or processes the request later.
So, I came up with a custom architecture which satisfies all of the above goals and achieve even more. While, I won’t be able to share the code because of confidentiality, I can share the details of the architecture. Please feel free to leave comments or contact me if you like the approach and need additional detail.
Here are the main components of this timer framework:
1. When a feature is enabled at Site Collection level, the framework creates two lists: Task Definition List, Task Trace List.
In task The Task definition list contains at least following information:
- Title of the Task as it appears in the timer job definitions on central admin
- GUID to track this definition
- Schedule – to store an XML based schedule definition for the timer job
- Schedule Type: matches with SharePoint Schedule Types such as one time, hourly, weekly etc.
- Trace Level – one of the few trace level that my framework supports. A concept similar to OTB diagnostic logging
- Provisioning Status – a status which shows whether a corresponding SharePoint timer job is created or not
- Assembly – Full Name of the assembly which contains the execution code of this timer job
- Class – Full name of the class which is derived from our custom interface ITimerTask interface
- TaskData – A random XML data that your timer job needs. Framework will automatically make this data available when above Class is called when the timer job runs
- NotifyTo – Can contains email address of person(s) who is interested in knowing the status of the timer job. The framework takes care of sending the email notification when developer sets the status of the job to anything other than “Success”
The Task Trace list contains at least following columns:
- GUID – Identifies the corresponding timer job definition
- ExecutionDateTime – Date and time when the timer job was executed
- Status – Custom Status such as Success, Warning, Failed, SuccessWithWarning, FailedWithWarning etc.
- Trace – Contains detailed trace information registered by developer while implementing the execute method of custom ITimerTask interface. Framework takes care of putting the trace information in this list. All that developer needs to to is call a method. For example, .RegisterTrace (“required list was not found”, TraceLevel.Warning)
2. A Web Application level feature is installed by the framework which creates two timer job definitions at Web Application level: Timer Task Scheduler Job and Timer Task Cleanup job.
Let’s see what exactly they do!
TimerSchedulerJob runs at a regular interval (say 5 minutes) and looks for any un-provisioned job in the The Task definition list that I mentioned above. Once it finds, it create a SharePoint Timer Job and schedules it as specified in the definition list. This newly created timer job is a generic timer job, all it does is loads the assembly and the class specified in the definition, prepares the required data for the job and calls the execute method. That means developer’s implementation of Execute method of my custom interface gets called.
Developer of this framework is responsible for:
- Getting the required data passed as Execute method parameter, along with lot of other information like an instance of SPSite object in which the definition was created
- Write core logic
- Set the final status of the job
- Register all required trace messages by calling method parameter’s RegisterMessage method
After calling the execute method, framework looks at the registered trace messages and the status of the task set by developer. The framework sends email notification specified in the definition. The framework also creates an entry in the Timer Task Trace list at the site collection level that I specified above.
Aha! so finally all dots are connected!
Finally, the Timer Task Trace clean up job removes the trace older than pre configured number of days.
So here is the experience of creating a new timer job for a developer, once the framework is enabled using Web App and Site Collection level feature:
- Create a class derived from ITimerTask class of my framework which has just one method called Execute
- The core responsibility of a developer in order to use the framework effectively are specified above..
- Deploy the assembly to GAC
- Create a definition in the Timer Task Definition list at site collection level. Alternatively, create the onetime definition dynamically on click of a user action
- Monitor the log and see what happened when the framework executed the timer job defined in the definition.
This is one of the most useful framework that I have built and the developers in the team love it because it opens up a lot of possibilities. My developers don’t need to learn how to develop a timer job but they are able to create one and execute them with so much ease. Without a doubt this is my favorite too!