Monday, November 5, 2007

Provide Status Updates for Long Running Operation Jobs

A simple way to give users some feedback on the progress status of long running jobs is provided by the Microsoft.SharePoint.Publishing assembly. When a class that inherits from the LongRunningOperationJob class has its start method invoked, a list item is added to the Long Running Operation Status list in the root web. Providing a link to the LongRunningOperationProgress.aspx page along with the guid of this list item will display an update page with a progress bar similar to that shown below:

Long Running Job Status Page

A LongRunningOperationJob might not necessary be designed to run as a SPJobDefinition timer job, although there's no reason you couldn't combine the two to provide more granular information than the _admin/ServiceRunningJobs.aspx page provides.

Some example code follows. Before running it, make sure that your site collection includes a list named 'Long Running Operation Status' at the root web. This list is created when you install the Office SharePoint Server Publishing Infrastructure feature at the Site Collection Features level. The list apparently isn't removed when this feature is uninstalled so it's not necessary to have the Publishing Infrastructure feature activated if you don't want it to be - just activate then deactivate it.

The Microsoft.SharePoint.Publishing assembly can be found here:
%Program Files%\Microsoft Office Servers\12.0\Bin\Microsoft.SharePoint.Publishing.dll

using System;
using System.Diagnostics;
using System.Threading;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Publishing;
using Microsoft.SharePoint.Publishing.Internal;
 
namespace DevHoleDemo
{
    class Program
    {
        const string STATUS_LIST_NAME = "Long Running Operation Status";
        const string PROGRESS_PAGE_URL = "/_layouts/LongRunningOperationProgress.aspx";
        static void Main(string[] args)
        {
            string webURL = "http://localhost/";
            using (SPSite site = new SPSite(webURL))
            {
                using (SPWeb web = site.RootWeb)
                {
                    LongRunningJob longRunningJob = new LongRunningJob();
                    longRunningJob.Title = "Demo Long Running Job";
                    longRunningJob.TotalOperationsToBePerformed = 30;
                    longRunningJob.RedirectWhenFinished = true;
                    longRunningJob.NavigateWhenDoneUrl = web.Url + "/" + STATUS_LIST_NAME;
                    longRunningJob.Start(web);
                    Process.Start("iexplore.exe", string.Format("{0}{1}?JobId={2}", web.Url, PROGRESS_PAGE_URL, longRunningJob.JobId));
                    LongRunningOperationStatus jobStatus;
                    do
                    {
                        jobStatus = LongRunningOperationStatus.GetJob(site, longRunningJob.JobId);
                        Console.WriteLine(jobStatus.StatusDescription);
                        Thread.Sleep(1000);
                    } while (jobStatus.Status != LongRunningOperationJob.OperationStatus.Successful);
                }
            }
        }
    }
 
    class LongRunningJob : LongRunningOperationJob
    {
        public override void DoWork()
        {
            for (this.OperationsPerformed = 0; this.OperationsPerformed < this.TotalOperationsToBePerformed; this.OperationsPerformed++)
            {
                this.StatusDescription = string.Format("im in ur long running job, doing ur work {0} of {1}...", this.OperationsPerformed, this.TotalOperationsToBePerformed);
                this.UpdateStatus();
                Thread.Sleep(1000);
            }
        }
    }
}

2 comments:

Jorge said...

Hi
I've customized the newform.aspx of a calendar list so when the user clicks on the OK button some code runs (the code will copy the new calendar's event to another calendar).
Everythings ok with that, the only problem is that it can take a while to do everything.
I've tried to use your code to have a LongRunningOperationProgress.aspx page showed to the user will my code is running but I wasnt able to do that.
However I was able to test your code like you've showed on your post and it worked.
So my question is, do you know how can I use your code to have the newform.aspx redirect to LongRunningOperationProgress.aspx??

Thanks a lot.

txs8311 said...

I've posted a Part II to this post which shows the code running in an aspx page. The redirection in this case is handled by the SPUtility.Redirect method.