Showing posts with label Timer. Show all posts
Showing posts with label Timer. Show all posts

Thursday, May 6, 2010

SharePoint Timer Job vs Windows Task Scheduler

Scenario:
This is another common question which confuses lot of developers as which option is better. Traditionally they have been writing console application and running them using Windows Task Scheduler, then why do we need SharePoint Timer Jobs.

Pros and Cons:
Considering a FARM scenario...

Single point of failure : Windows Task Scheduler need to be configured on all the web servers. If you configure to run the job on 1 server only, and this server crashes, job will not work at all.

Interface : Windows Task Scheduler have much easier interface for scheduling option. SharePoint doesn't have a UI to configure the jobs. There is a codeplex project though to bridge the gap. Still its hard to configure a job to run every X number of hours in share point, where-as it's easier with Windows Task Scheduler using multiple schedule options.

Status Reporting : Windows Task Scheduler doesn't have any reporting on when was the last time job got executed and what was the status. Only option is logging. Whereas SharePoint have a UI to show status of all the jobs and their status.

Security : In case of Windows Task Scheduler, you will need go to IT Admins and request for a special username/password to run such jobs where as SharePoint Timer Jobs automatically run under SharePoint Timer Job account.

Deployment : There is no easy way to deploy Windows Task Scheduler tasks and application which need to executed in a FARM environment. This will require lot of manual steps by IT Admin. SharePoint jobs can be deployed using WSP's.

Winner
SharePoint Timer Job

Saturday, October 3, 2009

One time scheduled timer job

Scenario:
Creating a onetime scheduled timer job.

Solution:
SPSchedule abstract class support other schedule options including SPOneTimeSchedule.

The one-time scheduled job is run during the next available time that meets the filter criteria. For example, if only the starting and ending seconds are set, the job runs during the next minute at a random point between the starting and ending seconds. If the starting second/ending second is set to 0/30 and the starting hour/ending hour is set to 22/23, the job starts between 11:00:00 P.M. and 11:00:30 P.M.

Code:

// Create a one time job and run it.
SPOneTimeSchedule oneTimeSchedule = new SPOneTimeSchedule(DateTime.Now);

// Get a new instance of your Timer class with proper constructor parameters
CustomSiteCreationJob newJob = new CustomSiteCreationJob("Job One Time", webApplication);

newJob.Schedule = oneTimeSchedule;
newJob.Update();
Article:
SPOneTimeSchedule , Writing a timer job

Testing Expiration Information Management Policy

Scenario:
I have been asked this question so many times and there are lot of funny things associated with this one :-)

So as we all know about Information Management Policy feature and of the most commonly used options is Expiring the content. I have written special Workflows which should run as per the Information Management Policy.

As by default Expiration Job runs once every day, I know people who really waited one day to see it working.. lol ( told you its funny )

But question is how do we test it ?

Solution:
SharePoint Central Administration has solution for you. Kinda hidden

Navigate to Central Administration > Operations > Information Management Policy Configuration > Expiration

Link to the page:
http://servername:8888/_admin/featuresettings.aspx?id=Microsoft.Office.RecordsManagement.PolicyFeatures.Expiration

Thursday, October 1, 2009

An object in the SharePoint administrative framework does not exist

Scenario:
You got the following error , while adding a solution.

Error: An object in the SharePoint administrative framework, "SPSolutionLanguagePack Na
me=0 Parent=SPSolution Name=xxxx.wsp", depends on other objects which do not exist. Ensure that all of the objects dependencies are created and retry this operation.
C:\SKN\xxxx.wsp: The Solution installation failed.


Solution:

- Reset the timer service

net stop "Window sharepoint services timer" 
net start "Window sharepoint services timer"

If you still facing the same issue, try to change the Solution ID and repackage it, this should solve your problem 100%.

Sunday, September 20, 2009

Timer Job running on all servers

Scenario:
Lot of people said Custom Timer Jobs are unreliable. But we are using Custom Timer Job for our most of the projects without any issues except one common mistake I have seen developers making is that even though we just want it to run on one machine, it is firing multiple times.

i.e. Custom Timer Job is sending emails more than one time,

Reason:
Most of people start writing Custom Timer Job from AC blog , and it has issues. Infact 2 issues

(1st) Scope = Site which should be Web Application , otherwise users can activate and deactivate it many time in a web application which may not be a good idea.

(2nd) SPJobLockTypes is set to ContentDatabase which may not what you want.

Solution:
After checking the MSDN documentation I found significance of various enumeation values.

ContentDatabase : Locks the content database before processing.
Job : Locks the job to prevent it from running on more than one machine.
None : No locks

So setting the SPJobLockTypes = Job solved the issue. Now timer job runs on one box only. Keep in mind there is no way ( i know of ) to control on which server it is running.

Code:

public SharePointWarmupJob (SPWebApplication webApp)
: base(Globals.JobName, webApp, null, SPJobLockType.Job) {
this.Title = Globals.JobName;
Article:
Similar Entry I found after I posted this :-)

Matt Morse,Robin Meure,Peter Deleu

Friday, September 4, 2009

Deployment command issues

Scenario:
This is one of the common issues I have seen with people developing solution on single WFE server farm and deploying sharepoint solution to multi-server WFE farm.

"The solution has not been deployed and may require clean up"

Reason:
Though there can be several issues, I am pointing out few here.

1. STSADM -o execadmsvcjobs
Often this command is used after retract solution and deploy solution command. This command executes all administrative timer jobs immediately instead of waiting for the timer job to run. Think of it as a console application with the same code as of Timer Job, only thing is it will run instantly. So far so good. But assuming that this means it has executed the job on all server is not true, which can be very problematic in multi-server farm.

Common Error: The solution-deployment-trainingwebparts.wsp-0 job completed successfully, but could not be properly cleaned up. This job may execute again on this server.

Recommendation : Wait for timer job to execute the deployment/retracting jobs and not to use it at all in deployment /retracting scripts in multi-server farm.

2. IISReset / Application Pool Reset
This command basically restarts the IIS services which is required to make sure web.config / GAC and other XML file based changes done on the file system get picked up.

Recommendation : Make sure you reset IIS / Application pool on all the WFEs

3. Resetting Timer service
net stop "Windows Sharepoint Services Timer"
net start "Windows Sharepoint Services Timer"

This command basically restarts the Timer services, which is required to make sure it picks up the code changes done to Custom Timer Job.

Recommendation : Make sure you reset Timer job on all the WFEs

4.Feature Activation:
Try to keep these commands in separate file or add 30sec pause between deploy solution and feature activation , other you might get error complaining feature is not installed ( on another server ).

Recommendation : Use separate file for these kind of commands.

5.Use of Force attribute:
Lot of times we use Force attribute as a rule of thumb.. :-) , try to understand the need of this attribute.

As per MSDN , using it in UninstallFeature forces an installation of a feature that is already installed and in ActivateFeature it activates a feature. This causes any custom code associated with the feature to rerun.

Recommendation : Try to avoid the use of force attribute and only use it when retracting is not able to clean up the features.

5.Use of AllContentUrls attribute:
You may not have used it directly but if you remember the drop down saying deploy on All Web Applications ? , that what it is.

Don't use this.

Recommendation : Deploying the solution to each of the web application. If you have a lot of Web Applications then you can script it.

Friday, June 19, 2009

Getting a download count for the documents

Scenario:
Getting a download count for the documents.

Solution:
I personally feel this may not be the best way to get an accurate counter. But if this is just to get an idea of document popularity , then this will work.

Code below shows my test console application , this need need to run in timer job to able to update the counter regularly.

Code:

using Microsoft.SharePoint;

namespace Training {

class Program
{
static void Main(string[] args)
{
var lastVersion = "N/A";

SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (var elevatedSiteCollection = new SPSite("http://localhost"))
{
using (var elevatedSite = elevatedSiteCollection.OpenWeb())
{
var list = elevatedSite.Lists["Shared Documents"];
var item = list.Items.GetItemById(6);

var wssQuery = new SPAuditQuery(elevatedSiteCollection);
wssQuery.RestrictToListItem(item);
SPAuditEntryCollection auditCol = elevatedSite.Audit.GetEntries(wssQuery);

item["Counter"] = 0;
foreach (SPAuditEntry entry in auditCol)
{
if ((entry.Event == SPAuditEventType.View) ||
(entry.Event == SPAuditEventType.View))
{
if ((lastVersion == "N/A") ||
(ParseVersionNumber(entry.EventData) == lastVersion)) {
item["Counter"] = int.Parse(item["Counter"].ToString()) + 1;
}
else {
lastVersion = ParseVersionNumber(entry.EventData);
}

}
}
item.SystemUpdate(false);
}
}
});
}

static string ParseVersionNumber(string versionString)
{
try
{
int startMajor = versionString.IndexOf("<Major>") + 7;
int endMajor = versionString.IndexOf("</Major>");
int lengthMajor = endMajor - startMajor;
int startMinor = versionString.IndexOf("<Minor>") + 7;
int endMinor = versionString.IndexOf("</Minor>");
int lengthMinor = endMinor - startMinor;

string majorNumber = versionString.Substring(startMajor, lengthMajor);
string minorNumber = versionString.Substring(startMinor, lengthMinor);

if (majorNumber == "0" && minorNumber == "-1")
return "N/A";

return majorNumber + "." + minorNumber;
}
catch{
return "N/A";
}
}
}
}
Follow me on Twitter

Tuesday, April 21, 2009

Restarting sharepoint services using script

Scenario:
I wanted to restart the Timer service using command line script

Code:

net stop "Windows SharePoint Services Timer"
net start "Windows SharePoint Services Timer"
Notes:
Other share point services can also be restarted like this.

Code:
net stop "Windows SharePoint Services Administration"
net stop "Office SharePoint Server Search"
net stop "Windows SharePoint Services Search"
net stop "Windows SharePoint Services Tracing"

Tuesday, February 3, 2009

Debugging Timer Job

Scenario:
I have already written some articles about writing and managing the Timer Job.One of my student asked me how to debug the timer job.

Solution:
Its preety simple. Follow the following step to debug the Timer Job.
1. Open the Visual Studio and the Timer Job code base
2. Set the break point in the code
3. Attach the debugger to OWSTimer.exe
4. Wait for the next cycle of the timer job.

Make sure that you dont set Timer Job interval more than 5 min for development purpose, otherwise you need to wait for longer.

You can also execute the Timer Job forcefully by writing a small console application.
or
using STSADM command line

Code:

stsadm -o execadmsvcjobs
Other Consideration:
When you change the code base for the Timer Job, deploy the new binary in GAC and RESET the OWSTimer.exe service.

Yes you are right I said OWSTimer and not IIS Reset , because timer job run inside the OWStimer process only and till you restart it, it will always run it from previously cached copy.

:-)

Sunday, January 25, 2009

Workflow Auto cleanup days fix

Scenario:
This is not new, most of us who have written atlease one Workflow in sharepoint knows this. Workflow History List is a the place where we log the progress/ comments during the Workflow progress and because of performance issues reason this list is cleaned every 60 days. But your client may not be very happy about this as they usually want to retain the history for X,Y,Z reasons.

Solution:
Fortunately SPWorkflowAssociation class exposes AutoCleanupDays property and the clean up job checks the values ( in days ) before deleting the entry from the Workflow List.

Here's a small utility which can help you fix the values of AutoCleanupDays.

Code:

Code Snippet
/*
* Date: September 17, 2007
** Program Description:
* ====================
* This program is a workaround for Microsoft Office SharePoint Server 2007
* bug #19849, where the AutoCleanupDays is set to 60 by default and by design
* in MOSS installations. This program gives the customer the oppotunity to
* change this number.
* Workflow histories would not show after 60 days by default.
*/

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Workflow;

namespace ProjectName{

class Program{

static string siteName;
static int newCleanupDays, assoCounter;
static string libraryName, wfAssoName;
static SPSite wfSite;
static SPWeb wfWeb;
static SPList wfList;

static void Main(string[] args){

try{

switch (args.Length){

case 0:{ //no parameters entered by user
System.Console.WriteLine("Error: No arguments entered (site, library, workflow and days)");
showHelpUsage();
break;
}

case 4:{ //correct number of parameters
siteName = args[0];
libraryName = args[1];
wfAssoName = args[2];
newCleanupDays = Convert.ToInt32(args[3]);
assoCounter = 0;
wfSite = new SPSite(siteName);
wfWeb = wfSite.OpenWeb();
wfList = wfWeb.Lists[libraryName];
SPWorkflowAssociation _wfAssociation = null;

foreach (SPWorkflowAssociation a in wfList.WorkflowAssociations){

if (a.Name == wfAssoName){
a.AutoCleanupDays = newCleanupDays;
_wfAssociation = a;
assoCounter++;
}
else{
_wfAssociation = a;
}
}

wfList.UpdateWorkflowAssociation(_wfAssociation);

System.Console.WriteLine("\n" + wfAssoName + ": " + assoCounter.ToString() + " workflow association(s) changed successfuly!\n");
break;
}

default: {//default number of parameters

System.Console.WriteLine("Incorrect number of arguments entered (" + args.Length.ToString() + " arguments)");
showHelpUsage();
break;
}
}
}
catch (Exception e){
System.Console.WriteLine("An error has occurred. Details:\n" + e.ToString());
}
finally {
if (wfSite != null)
wfSite.Dispose();

if (wfWeb != null)
wfWeb.Dispose();

System.Console.WriteLine("\nFinished setting AutoCleanupDays!");
}
}

static void showHelpUsage() //help screen
{
System.Console.WriteLine("\n\nMOSS Workflow Set AutoCleanup Usage:");
System.Console.WriteLine("====================================");
System.Console.WriteLine("ShowWFs siteURL library workflow days");
System.Console.WriteLine(" - siteURL (e.g. http://serverURL/site)");
System.Console.WriteLine(" - library (e.g. \"Shared Documents\")");
System.Console.WriteLine(" - workflow (e.g. \"Approval\")");
System.Console.WriteLine(" - days for auto clean up (e.g. 120)");

}
}
}
Article:
Thanks to Shola Shaloko for the Code

MSDN

Thursday, January 15, 2009

Reading web.config from Timer Job

Scenario:
I wrote a timer job and basically in this timer job I wanted to update the DB depending upon sharepoint information. That means i need to have a connection string to make the connection ( mostly in some config file ). This timer job was attached to a Web Application , so I decided to read the connection string from the Web.config of the Web Application

Solution:
WebConfigurationManager class exposes a method OpenWebConfiguration to make it easier to read.

Code:

Configuration config = WebConfigurationManager.OpenWebConfiguration("/", this.WebApplication.Name);
string _sqlConnectionString = config.ConnectionStrings.ConnectionStrings["DBConnectionString"].ToString();

Article:
Points To Share

Thursday, January 8, 2009

Enabling or Disabling Timer Jobs

Scenario:
I am sure at some point of time during your development you wrote a Timer Job.
But now for some reason I had a requirement to Disable it for some time.

Steps:

1. From Central Administration, click the Operations tab on the top navigation bar.

2. On the Operations page, in the Global Configuration section, click Timer job definitions.

3. On the Timer Job Definitions page, click on the Name of Timer Job to edit the appropriate timer job.

4. On the Edit Timer Job page, click Disable and then click OK to disable the Timer Job

That was easy , isn't it.

Workflow clean-up job

Scenario:
SharePoint workflow activities include LogToWorkflowHistory and basically it logs an entry into the Workflow History List you selected while associating the workflow with List or Item.

Also another thing is that usually we write entries to this History List quite often to see the workflow progress which means multiple entries per Workflow execution. As with any SharePoint list, if your workflow history list exceeds 2000 items, site performance may be impacted. So sharepoint out of box has a Workflow clean-up job , which runs every day and delete the items older than 60 days.

Workflow history is not intended to be used as a means of auditing workflow events and is not necessarily secure.There are other better way to do it. Read about auditing.

Solution:
1. You can create a separate history list for each workflow association. But still you need to find out a way for history getting deleted which is 60 days old.
2. You can disable automatic cleanup of workflow history. ( This is the only fix to avoid deletion of the history information , but then you need to plan how to keep the list within 2000 item limit to avoid performance issues )

:-)

Monday, September 22, 2008

Executing any Timer job using code

using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;

using Microsoft.SharePoint.Administration;

public ExecuteSharePointJob(string sJobName)
{
SPSite site = new SPSite("http://localhost");
//foreach (SPService srv in SPFarm.Local.Services)
//{
//foreach (SPJobDefinition job in srv.JobDefinitions)
foreach (SPJobDefinition job in site.WebApplication.JobDefinitions)
{
string jobTitle = job.Title;

if (jobTitle == JobName)
{
Trace.WriteLine("***************** Start of execution for job");
job.Execute(new Guid("PassTheContentDBGUID"));
Trace.WriteLine("***************** End of execution for job");
}
}
//}
}

Saturday, September 6, 2008

Writing a Timer Job

Scenario:
You want to run some code on a regular interval and decided to write a timer Job.

Sample Timer Job Code:

using System;
using System.Net;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

namespace SharePoint.TrainingSamples {

public class SharePointTimerJob:SPJobDefinition {

public SharePointTimerJob():base(){ }

public SharePointTimerJob(string jobName,SPWebApplication webApplication)
:base(jobName, webApplication, null, SPJobLockType.ContentDatabase) {
this.Title = "MyTimerJob";
}

public override void Execute (Guid targetInstanceId) {
foreach (SPSite siteCollection in this.WebApplication.Sites) {
// Code to be executed as a part of Timer Job
foreach (SPSite siteCollection in this.WebApplication.Sites)
{
SPWebApplication webApplication = this.Parent as SPWebApplication;
SPContentDatabase contentDb = webApplication.ContentDatabases[contentDbId];

// get reference to "Tasks" list in the RootWeb of
// the first site collection in the content database
SPList taskList = contentDb.Sites[0].RootWeb.Lists["Tasks"];

// create a new task, and update item
SPListItem newTask = taskList.Items.Add();
newTask["Title"] = DateTime.Now.ToString();
newTask.Update();

}
}
}
}
}
Feature Activation Code:
Now that timer job is ready you need to add it to SharePoint Timer Jobs Store, One way is to add it as a part of Feature Activation
// get a reference to our job class in the GAC
public override void FeatureActivating (SPFeatureReceiverProperties properties) {

SPSite site = properties.Feature.Parent as SPSite;

//This is the timer class you have created above
SharePoint.TrainingSamples.SharePointTimerJob oSharePointTimerJob = new SharePoint.TrainingSamples.SharePointTimerJob("MyTimerJob", site.WebApplication);
// set the execution schedule to every 5 minute
SPMinuteSchedule schedule = new SPMinuteSchedule();
schedule.BeginSecond = 0;
schedule.EndSecond = 59;
schedule.Interval = 5;
oSharePointTimerJob.Schedule = schedule;
//update the job
oSharePointTimerJob.Update();
}
Feature De-Activation Code:
You might want to remove it from SharePoint Timer Jobs Store on de-activation
public override void FeatureDeactivating (SPFeatureReceiverProperties properties) {
SPSite site = properties.Feature.Parent as SPSite;

// Delete the job.
foreach (SPJobDefinition job in site.WebApplication.JobDefinitions) {
if (job.Name == "MyTimerJob")
job.Delete();
}
}
Articles:
MUST READ : Issues with Timer job , OTHER Helpful links MSDN , Must Read , Andrew Connell

Friday, September 5, 2008

What is Timer Job ?

The SharePoint Timer service is similar to tasks that you can create in any version of Windows by using the Task Scheduler application. The major benefits of using the SharePoint Timer service compared with Windows Task Scheduler jobs is that the timer service knows the topology of the server farm, and you can load balance the jobs across all the servers in the farm or tie them to specific servers that run particular services.