Synchronizing Membership Provider for Project Workspaces
Back on May 17th I posted code that showed how to update the workspace data with the RDB:
Here is a similar program to synchronize project workspace membership. The interesting twist is that I show how to be kinder to the Project Queue by adding a few jobs at a time and waiting for them to complete.
Here is the code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Data;
using System.Web.Services.Protocols;
using System.Diagnostics;
using PSLibrary = Microsoft.Office.Project.Server.Library;
namespace WorkspaceUpdate
{
class Program
{
static void Main(string[] args)
{
int count = 0;
bool verbose = false; // Verbose Output switch
Guid job = Guid.Empty; // The latest job submitted to the queue.
int timeOut = 60; // Default timeout before terminating the queue job
string ls_projURL = "";
const string PROJECT_SERVICE_PATH = "_vti_bin/psi/Project.asmx";
const string WSSINTEROP_SERVICE_PATH = "_vti_bin/PSI/WSSInterop.asmx";
const string WSQUEUESYSTEM_SERVICE_PATH = "_vti_bin/PSI/QueueSystem.asmx";
if (args.Length == 0 || args.Length > 3)
{
Message();
}
else if (args[0] == "/?")
{
Message();
}
else
{
ls_projURL = args[0];
if (args.Length > 2 && args[2].ToLower() == "verbose")
{
verbose = true;
}
try
{
timeOut = Convert.ToInt32(args[1]);
}
catch
{
Event("Warning: Invalid timeout, defaulting to 60 seconds.", EventLogEntryType.Warning, verbose);
}
WSProject.Project ws_Project = new WSProject.Project();
WSSInterop.WssInterop ws_WssInterop = new WSSInterop.WssInterop();
WSQueueSystem.QueueSystem qsWS = new WSQueueSystem.QueueSystem();
if (!ls_projURL.EndsWith("/"))
{
ls_projURL += "/";
}
try
{
ws_Project.Url = ls_projURL + PROJECT_SERVICE_PATH;
ws_Project.Credentials = CredentialCache.DefaultCredentials;
ws_WssInterop.Url = ls_projURL + WSSINTEROP_SERVICE_PATH;
ws_WssInterop.Credentials = CredentialCache.DefaultCredentials;
qsWS.Url = ls_projURL + WSQUEUESYSTEM_SERVICE_PATH;
qsWS.Credentials = CredentialCache.DefaultCredentials;
Guid lo_projGUID;
string ls_projName;
WSProject.ProjectDataSet lo_projs = null;
WSProject.ProjectDataSet lo_projDS;
try
{
lo_projs = ws_Project.ReadProjectList();
DataRowCollection lo_projects = lo_projs.Tables[lo_projs.Project.TableName].Rows;
for (int i = 0; i < lo_projects.Count; i++)
{
lo_projGUID = new Guid(lo_projects[i][0].ToString());
ls_projName = lo_projects[i][1].ToString();
try
{
lo_projDS = ws_Project.ReadProjectEntities(lo_projGUID, 1, WSProject.DataStoreEnum.PublishedStore);
// Check if the Project has a Workspace
if (lo_projDS.Tables[lo_projDS.Project.TableName].Rows[0][lo_projDS.Project.WSTS_SERVER_UIDColumn.ColumnName] != null && lo_projDS.Tables[lo_projDS.Project.TableName].Rows[0][lo_projDS.Project.WSTS_SERVER_UIDColumn.ColumnName].ToString() != "")
{
Message("Synchronizing Workspace for Project" + ls_projName, verbose);
//Wait to let the server process the work
if (count % 4 == 3)
{
if (WaitForQueue(timeOut, job, qsWS) == false)
{
Event("Warning: Queue Job not Processed for Project:" + ls_projName + " Check Project Server Queue.", EventLogEntryType.Warning, verbose);
}
}
job = Guid.NewGuid();
ws_WssInterop.QueueSynchronizeMembershipForWssSite(lo_projGUID, job);
count++;
}
else
{
Message("Notice: Project" + ls_projName + " does not have a workspace.", verbose);
}
}
catch (SoapException lo_ex)
{
PSLibrary.PSClientError psiError = new PSLibrary.PSClientError(lo_ex);
PSLibrary.PSErrorInfo[] psiErrors = psiError.GetAllErrors();
if (psiErrors.Length == 1)
{
if (psiErrors[0].ToString() == "ProjectNotFound")
{
Message("Notice: Project" + ls_projName + " is not published.", verbose);
}
}
}
}
Event("Successfully Synchronized Membership for Workspaces", EventLogEntryType.Information, verbose);
}
catch (WebException lo_ex)
{
Event("Error:" + lo_ex.Message, EventLogEntryType.Error, verbose);
}
catch (Exception lo_ex)
{
Event("Unknown Error:" + lo_ex.Message, EventLogEntryType.Error, verbose);
}
}
catch (UriFormatException lo_ex)
{
Event("Unknown Error:" + lo_ex.Message, EventLogEntryType.Error, verbose); ;
}
}
}
private static bool WaitForQueue(int timeOut, Guid jobId, WSQueueSystem.QueueSystem qsWS)
{
int sleep = 3;
int timeSlept = 0; // Total time slept (seconds)
bool jobSuccess = false;
string xmlError;
WSQueueSystem.JobState jobState; // Status of the queue job
timeOut = timeOut * 1000;
while (true)
{
jobState = qsWS.GetJobCompletionState(jobId, out xmlError);
if (jobState == WSQueueSystem.JobState.Success)
{
jobSuccess = true;
break;
}
else if (jobState == WSQueueSystem.JobState.Unknown
|| jobState == WSQueueSystem.JobState.Failed
|| jobState == WSQueueSystem.JobState.FailedNotBlocking
|| jobState == WSQueueSystem.JobState.CorrelationBlocked
|| jobState == WSQueueSystem.JobState.Canceled)
{
jobSuccess = false;
break;
}
else if (timeSlept > timeOut)
{
jobSuccess = true;
//qsWS.CancelJobSimple(jobId);
break;
}
System.Threading.Thread.Sleep(sleep * 1000);
timeSlept = +sleep * 1000;
}
return jobSuccess;
}
static private void Message()
{
System.Console.WriteLine("");
System.Console.WriteLine("WorkspaceUpdate url timeout [verbose]");
System.Console.WriteLine(" url - The URL to the project server.");
System.Console.WriteLine(" timeout - Seconds to wait for the queue job to process the User Sync");
System.Console.WriteLine(" verbose - An optional parameter that outputs progress.");
}
static private string Message(string as_msg, bool verbose)
{
as_msg = DateTime.Now.ToString() + ":" + as_msg;
if (verbose)
System.Console.WriteLine(as_msg);
return as_msg;
}
static private void Event(string as_msg, EventLogEntryType eventType, bool verbose)
{
EventLog lo_eventLog = new EventLog();
lo_eventLog.Source = "WorkspaceUpdate Sync Job";
as_msg = Message(as_msg, verbose);
lo_eventLog.WriteEntry(as_msg, eventType, 3652);
}
}
}
Comments
Anonymous
October 04, 2007
PingBack from http://msdnrss.thecoderblogs.com/2007/10/04/synchronizing-membership-provider-for-project-workspaces/Anonymous
February 10, 2009
Hello, Last year I shared a couple of samples that showed how to sync the membership for the Project