Problem
It's easy to find blogs and msdn documentation on creating Custom
Timer jobs, but still developers face issues while deploying and running
them on SharePoint farms due to lack of development guidelines for
timers. In this blog, I will discuss one issue
which is related to permission levels for a custom timer job. Let's say
we
have the following scenario:
| List |
list_A |
list_B |
| SiteCollection |
SiteCollection: sitecollection_A |
sitecollection_B |
| WebApplication |
webapplication_A |
webapplication_B |
Based on changes made to this list_A, list_B has to be updated
which is in sitecollection_B in WebApplication webapplication_B. Out
of
the box webparts or
configurations cannot achieve this. I thought about writing event
handlers, but the success rate with event handlers working on two
webapplications is quite low, especially in a farm environment. Also I
wanted to
do a sync only at non-peak usage hours like every night at 11pm, but
this cannot be achieved
with event handlers.
So how do we sync two lists in different web applications or based on
updates to one list, make appropriate changes in another
webapplication?
Solution
The solution is to write a Custom Timer job!
Since two different web applications are involved, they can be in two different "Application
Pools" i.e. their System Account user can be different.
So we need to elevate privileges to their respective System Accounts in order to run our code.
But how do we do that?
SPSecurity.RunwithElevatedPrivileges doesn't work for Timer Jobs, so how can we elevate
Privileges in a Timer job?
The solution is to use USER TOKENS of System Account for elevating privileges instead of
using SPSecurity.RunwithElevatedPrivileges.
The below example shows how to use a User Token to instantiate the site collection.
SPUserToken myUserToken_A = null;
SPUserToken myUserToken_B = null;
using (SPSite mySiteCollection_A = new SPSite("http://webapplication_A/sites/sitecollection_A"))
{
myUserToken_A = mySiteCollection_A.SystemAccount.UserToken;
}
using (SPSite mySiteCollection_B = new SPSite("http://webapplication_B/sites/sitecollection_B"))
{
myUserToken_B = mySiteCollection_B.SystemAccount.UserToken;
}
using (SPSite tempSite_A = new SPSite("http://webapplication_A/sites/sitecollection_A",
myUserToken_A))
{
using (SPSite tempSite_B = new SPSite(("http://webapplication_B/sites/sitecollection_B",
myUserToken_B))
{
//Write your Custom Code here
//Example:
//
//or
//
}
}
How does this work?
"mySiteCollection_A.SystemAccount.UserToken" in the above example uses the user
token of the site collection's System Account (SharePoint\System). Using this User Token, we open the site collection with elevated privileges. Now using this site
collection's instance, we can do our custom coding.