i have a timer job which i want to run only once per day, for the entire farm. How do I
It looks like you need a farm-scoped feature, that installs a service that runs this job. Here is how I did it (using code written by a colleague, to be honest, but he's not in SO).
Create a feature.xml file with a feature event receiver.
<Feature
Id="..."
Title="..."
Description="..."
Scope="Farm"
Version="1.0.0.0"
Hidden="FALSE"
ReceiverAssembly="XXX.FarmService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=xxx"
ReceiverClass="XXX.FarmService.XXXFarmFeatureEventReceiver"
xmlns="http://schemas.microsoft.com/sharepoint/">
</Feature>
Inside the event receiver:
public override void OnFeatureActivated(SPFeatureReceiverProperties properties)
{
try
{
SPFarm farm = SPFarm.Local;
// Get Service, if it already exists
JobService myService = null; // JobService is a subclass of SPService
foreach (SPService service in farm.Services)
{
if (String.Compare(service.Name, JobService.XXXServiceName, true) == 0)
{
myService = (service as JobService);
break;
}
}
if (cegService == null)
{
// Create service
myService = new JobService(farm);
myService.Update();
// Create service instances
JobServiceInstance myServiceInstance; // JobServiceInstance is a subclas of SPServiceInstance
foreach (SPServer server in farm.Servers)
{
myServiceInstance = new JobServiceInstance(server, myService);
myServiceInstance.Update();
}
}
// Dayly schedule
SPDailySchedule schedule = new SPDailySchedule();
schedule.BeginHour = 1;
schedule.EndHour = 1;
schedule.BeginMinute = 0;
schedule.EndMinute = 59;
// Our own job; JobCheckDocDates is a subclass of SPJobDefinition
JobCheckDocDates newJob = new JobCheckDocDates(cegService, null, SPJobLockType.Job);
newJob.Schedule = schedule;
newJob.Update();
myService.JobDefinitions.Add(newJob);
myService.Update();
}
catch (Exception e)
{
Logger.Error("[" + properties.Feature.Definition.DisplayName + "] Error during feature activation", e);
}
}
This creates a service available on every server in the farm.
Some more details on the subclasses:
public class JobCheckDocDates: Common.BaseJob
{
/// <summary>
/// The job name
/// </summary>
public static string JobName = "XXX job";
/// <summary>
/// Constructor
/// </summary>
public JobCheckDocDates() : base() { }
/// <summary>
/// Constructor
/// </summary>
/// <param name="service"></param>
/// <param name="server"></param>
/// <param name="lockType"></param>
public JobCheckDocDates(SPService service, SPServer server, SPJobLockType lockType)
: base(JobName, service, server, lockType) { }
...
and of course the Execute method.
public class JobService : SPService
{
public static string XXXServiceName = "XXX Service";
public override string DisplayName
{
get
{
return XXXServiceName;
}
}
public override string TypeName
{
get
{
return "XXX Service Type";
}
}
/* An empty public constructor required for serialization. */
public JobService() { }
public JobService(SPFarm farm)
: base(XXXServiceName, farm)
{
}
}
public class JobServiceInstance : SPServiceInstance
{
/// <summary>
/// Eos Service Instance Name
/// </summary>
public static string XXXServiceInstanceName = "XXXServiceInstance";
/// <summary>
/// Manage Link
/// </summary>
private SPActionLink _manageLink;
/// <summary>
/// Provision Link
/// </summary>
private SPActionLink _provisionLink;
/// <summary>
/// Unprovision Link
/// </summary>
private SPActionLink _unprovisionLink;
/// <summary>
/// Roles
/// </summary>
private ICollection<string> _roles;
/// <summary>
/// Manage Link
/// </summary>
public override SPActionLink ManageLink
{
get
{
if (_manageLink == null)
{
_manageLink = new SPActionLink(SPActionLinkType.None);
}
return _manageLink;
}
}
/// <summary>
/// Provision Link
/// </summary>
public override SPActionLink ProvisionLink
{
get
{
if (_provisionLink == null)
{
_provisionLink = new SPActionLink(SPActionLinkType.ObjectModel);
}
return _provisionLink;
}
}
/// <summary>
/// Unprovision Link
/// </summary>
public override SPActionLink UnprovisionLink
{
get
{
if (_unprovisionLink == null)
{
_unprovisionLink = new SPActionLink(SPActionLinkType.ObjectModel);
}
return _unprovisionLink;
}
}
/// <summary>
/// Roles
/// </summary>
public override ICollection<string> Roles
{
get
{
if (_roles == null)
{
_roles = new string[1] { "Custom" };
}
return _roles;
}
}
/// <summary>
/// Empty constructor
/// </summary>
public JobServiceInstance() : base() { }
/// <summary>
/// Constructor
/// </summary>
/// <param name="server">The server</param>
/// <param name="service">The Eos service</param>
public JobServiceInstance(SPServer server, JobService service)
: base(XXXServiceInstanceName, server, service)
{
}
Now, in Central Admin, go to Operations / Services on server. Choose the desired server and start the service.
To answer your list of questions: 1. Deploy the solution just once, independently of a WFE. 2. As the feature is farm-scoped, it should be activated in Central Admin. 3. The SPJobLockType is SPJobLockType.Job
This is not exactly what you imagined, but it has the advantage of letting you easily choose where the job is run, even long after you install the feature (e.g. if a server becomes overloaded with other stuff).
The OnFeatureActivated method could be smarter and check for each server if the service exists and add it if needed. But it is simpler to remove the service from every service in OnFeatureDeactivated. So, if you add new servers, deactivate then reactivate the feature.