Home > C#, MOSS 2007, SharePoint > Custom Action to Trigger a SharePoint Timer Job

Custom Action to Trigger a SharePoint Timer Job


In my last post I demonstrated how to create a SharePoint Custom Action to appear on a specific SharePoint List. When a user clicked the action it ran some of our custom code.

Today we will try to extend that example and run a SharePoint Timer Job on the click of our custom action.

I am making the following assumptions:

  • You have read my previous post and have created the custom action
  • You know how to create and deploy Custom Timer Job definitions

The Problem

SharePoint Timer Job instances are persisted in the configuration database. Typically the SharePoint Farm service account or other accounts that have been given permissions on this database explicitly are able to write to this database.

For this reason when you try to execute a SharePoint Timer Job from within a site collection it will generally fail even if you run it within a SPSecurity.RunWithElevatedPrivileges code block. Running with elevated priviledges results in the code being run in the context of the Application Pool account (referred to as the System Account by SharePoint). Unless you have deviated from the best practices guidelines this will result in the following error:

Security Exception
Description: The application attempted to perform an operation not allowed by the security policy. To grant this application the required permission please contact your system administrator or change the application's trust level in the configuration file.

Exception Details: System.Security.SecurityException: Access denied.

You could always get around this by explicitly giving the Application Pool account permissions on the Configuration Database or by setting the Farm account as also being the Application Pool account for your Web Application. However, both these approaches are not recommended and are a deviation from the best practices guidelines.

The Solution

In my previous blog post, we created a custom action and deployed it. We used our custom action to send the user to a custom aspx page (with code-behind) that was located in the layouts folder. In the code behind we ran the custom code we wanted to execute. In this way we were able to create a custom action that ran some custom code on the click of our custom action.

To use our custom action to trigger a SharePoint Timer Job we need to add the following code in the aspx code-behind page:

namespace MyProject.CustomJobDefinitions
{
    public class JobInitiator: SPJobDefinition
    {
        public JobInitiator()
            : base()
        {
        }
        public JobInitiator(string jobName, SPService service, SPServer server, SPJobLockType targetType)	 
	            : base(jobName, service, server, targetType)
        {
	 
        }
        public JobInitiator(string jobName, SPWebApplication webApplication)
            : base(jobName, webApplication, null, SPJobLockType.None)
        {
            this.Title = "JobInitiator";
        }
         public override void Execute(Guid targetInstanceId)
        {
            using (SPSite site = this.WebApplication.Sites["/"])
            {
                using (SPWeb web = site.RootWeb)
                {
                    if (web.Properties["InitiatorJobFlag"] != null)
                    {
                        // Reset the flag 
                        Helper.SetSiteProperty(site, "InitiatorJobFlag", null);
                        // This is the job we want to run that does some stuff. It could be any job we want to run
                        CustomJobToRun process = new CustomJobToRun("My Job", WebApplication);
                        process.Execute(targetInstanceId);
                    }
                }
            }
        } 
    }
}

And below is the very useful ‘SetSiteProperty’ helper method:

        public static void SetSiteProperty(SPSite site, string propertyName, string value)
        {
            bool unsafeUpdateValue = site.RootWeb.AllowUnsafeUpdates;
            site.RootWeb.AllowUnsafeUpdates = true;
            if (site.RootWeb.Properties.ContainsKey(propertyName))
            {
                site.RootWeb.Properties[propertyName] = value;
            }
            else
            {
                site.RootWeb.Properties.Add(propertyName, value);
            }

            site.RootWeb.Properties.Update();
            site.RootWeb.AllowUnsafeUpdates = unsafeUpdateValue;
        }

Now that we have created our Custom Timer Job Definition, we can create a feature (scoped at Farm or WebApplication) and in the FeatureReciever feature activated event we add the following code:

JobInitiator job = new JobInitiator("JobInitiator", webApp)
            {
                Schedule = new SPMinuteSchedule
                {
                    BeginSecond = 0,
                    EndSecond = 59,
                    Interval = 1
                }
            };
            job.Update(true);

The schedule above ensures that our Custom Timer Job will run every minute. So our timer job executes every single minute and checks a property on the RootWeb of our Site Collection. If this property is set then it runs the specified Job of our choice.

And then finally in the code-behind file of our aspx page we add the following code:

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            Helper.SetSiteProperty(SPContext.Current.Site, "InitiatorJobFlag", "RunNow");
        }

So there we have it, by following this way we were able to create a Custom Action that, when clicked, triggers any SharePoint Timer Job we would like to run.

You might wonder why am I using a Job to execute another Job? Well there could be a scenario where you might want to run an OTB SharePoint Timer Job in which case this way would work.

In my specific scenario I had created a Job that was scheduled to run once every day. By using the above method I can easily trigger the execution of this Job on demand without interfering with its normal schedule.

Advertisements
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: