I have been working on a application that provides a repeatable way to create sites. This application is very similar to the SharePoint Test Data Population Tool.
One of the first tasks was to create new web applications. A quick look at the API and I thought well this is simple enough.
First instantiate a new instance of SPWebApplicationBuilder. Set some properties on my object and then call create. Use the SPWebApplication.Provision() method and I am done.
Well that is not exactly true. What I discovered was that the Provision method only creates the web application on the local server. This is great for a single web front end, but for a more typical setup more work is required.
So I dug into the Administrative screens using Reflector. I discovered that the SharePoint team is instantiating a timer job that will provision the newly created web on the other web front ends.
if (SPFarm.Local.TimerService.Instances.Count > 1)
{
SPWebApplicationProvisioningJobDefinition definition = new SPWebApplicationProvisioningJobDefinition(application, this.ApplicationPoolSection.ResetIis);
definition.Schedule = new SPOneTimeSchedule(DateTime.Now);
definition.Update();
}
So I thought okay this is simple enough. Then I discovered that SPWebApplicationProvisioningJobDefinition as marked as internal. Agghhhh!!!!
This pattern of finding a useful SharePoint routine only to have it hidden with the Internal keyword happens way too often. I am sure that usually there is a good reason to hide away the functionality, but I could see no logical reason to hide SPWebApplicationProvisioningJobDefinition.
So after throwing a few darts at the SharePoint Development Team dartboard (No I do not actually have one, but it could be a great gift SharePoint developers) I sat down and created a timer job definition that does basically the same thing as SPWebApplicationProvisioningJobDefinition.
This was the first timer job I had ever written so I made a *few* mistakes.
First I failed to understand a basic concept of timer jobs. That is any classes inheriting from SPJobDefinition need to be deployed to the GAC on each SharePoint server. If they are not then the job will not be created correctly. The frustrating thing I encountered was the fact that no runtime error was raised. So it looked like the job definition was created correctly, but it was not.
Second I failed to put a default constructor on my job definition class. This resulted in a runtime error with the job definition. The runtime error indicated that the class failed to serialize properly.
Third I failed to decorate my member variables with the [Persisted] attribute. This resulted in me losing my variable values.
Fourth I did not realize that I have to manually delete the job definitions when they fail. To delete the timer job definition bring up the SharePoint Central Administration web site. On the Operations tab choose Timer job definitions (under the Global Configuration header). Next click the timer job definition in the list and then click the Delete button.
Fifth I failed to understand that job definitions must be uniquely named. This was a problem for me because my application would create one timer job per Web Application. Sometimes a new web application job would be created before the other job was finished. To work around the issue I included a timestamp in the name. I am not sure if this was smart, but it works.
Sixth I failed to understand that the OWSTimer.exe service must be restarted when deploying a new version of my timer job class library. The OWSTimer.exe service is what manages the timer jobs. It actually instantiates and executes the timer job classes. Since my timer job class was deployed to the GAC it meant I had to restart the OWSTimer.exe (on each SharePoint server ughhh!!!).
In the end I accomplished my goal, but it would have been a lot easier if the SharePoint development team had not marked SPWebApplicationProvisioningJobDefinition as internal.

1 comments:
Microsoft has added a new method called ProvisionGlobally which should accomplish the same as what you're trying to do with the timer job.
It was apparently added officially with one of the rollups.
Read more about it here:
http://powershell.web-log.nl/root/2008/04/creating-a-new.html
Post a Comment