MOSS MVP

I've moved my blog to http://blog.falchionconsulting.com!. Please update your links. This blog is no longer in use--you can find all posts and comments at my new blog; I will no longer be posting to this site and comments have been disabled.

Thursday, August 14, 2008

Setting the Audience Compilation Schedule via STSADM

In an effort to wrap up my audience related STSADM commands I created a command that allows me to set the audience compilation schedule via STSADM.  I had to do some disassembling to figure out how to do this and it turned out that the code was virtually identical to what I had done for the gl-setuserprofileimportschedule command.  So it turned out that I was able to create this command by simply coping the code from my other command and then just tweaking a couple lines to load up different class types.  I named the command gl-setaudiencecompilationschedule.  The downside of this code (and the code it's based off of) is that I had to use reflection to get it done as all the classes are marked internally (no idea why).  If anyone knows of a way to do this without all the reflect I'm all ears.

Here's the code - it's ugly, but it works:

   1: #if MOSS
   2: using System;
   3: using System.Collections.Specialized;
   4: using System.Reflection;
   5: using System.Text;
   6: using System.Threading;
   7: using Lapointe.SharePoint.STSADM.Commands.SPValidators;
   8: using Microsoft.Office.Server;
   9: using Microsoft.Office.Server.UserProfiles;
  10: using Microsoft.SharePoint;
  11: using Microsoft.SharePoint.Administration;
  12: using Microsoft.SharePoint.StsAdmin;
  13: using PropertyInfo=System.Reflection.PropertyInfo;
  14: using Lapointe.SharePoint.STSADM.Commands.OperationHelpers;
  15:  
  16: namespace Lapointe.SharePoint.STSADM.Commands.TimerJob
  17: {
  18:     public class SetAudienceCompilationSchedule : SPOperation
  19:     {
  20:         private enum OccurrenceType
  21:         {
  22:             daily,
  23:             weekly,
  24:             monthly
  25:         }
  26:  
  27:         /// <summary>
  28:         /// Initializes a new instance of the <see cref="SetAudienceCompilationSchedule"/> class.
  29:         /// </summary>
  30:         public SetAudienceCompilationSchedule()
  31:         {
  32:             SPParamCollection parameters = new SPParamCollection();
  33:             parameters.Add(new SPParam("sspname", "ssp", false, null, new SPNonEmptyValidator(), "Please specify the SSP name."));
  34:             parameters.Add(new SPParam("occurrence", "oc", true, null, new SPRegexValidator("^daily$|^weekly$|^monthly$")));
  35:             parameters.Add(new SPParam("hour", "hour", true, null, new SPIntRangeValidator(0, 23)));
  36:             parameters.Add(new SPParam("day", "day", false, null, new SPIntRangeValidator(1, 31)));
  37:             string regex = "^" + string.Join("$|^", Enum.GetNames(typeof (DayOfWeek))) + "$";
  38:             parameters.Add(new SPParam("dayofweek", "dayofweek", false, null, new SPRegexValidator(regex.ToLowerInvariant() + "|" + regex)));
  39:             parameters.Add(new SPParam("enabled", "enabled", false, "true", new SPTrueFalseValidator()));
  40:             parameters.Add(new SPParam("runjob", "run"));
  41:            
  42:             StringBuilder sb = new StringBuilder();
  43:             sb.Append("\r\n\r\nSets the audience compilation schedule.\r\n\r\nParameters:");
  44:             sb.Append("\r\n\t[-sspname <SSP name>]");
  45:             sb.Append("\r\n\t-occurrence <daily|weekly|monthly>");
  46:             sb.Append("\r\n\t-hour <hour to run (0-23)>");
  47:             sb.Append("\r\n\t[-day <the day to run if monthly is specified>]");
  48:             sb.AppendFormat("\r\n\t[-dayofweek <the day of week to run if weekly is specified ({0})>]", string.Join("|", Enum.GetNames(typeof(DayOfWeek))).ToLowerInvariant());
  49:             sb.Append("\r\n\t[-enabled <true|false> (default is true)]");
  50:             sb.Append("\r\n\t[-runjob]");
  51:             Init(parameters, sb.ToString());
  52:         }
  53:  
  54:         #region ISPStsadmCommand Members
  55:  
  56:         /// <summary>
  57:         /// Gets the help message.
  58:         /// </summary>
  59:         /// <param name="command">The command.</param>
  60:         /// <returns></returns>
  61:         public override string GetHelpMessage(string command)
  62:         {
  63:             return HelpMessage;
  64:         }
  65:  
  66:         /// <summary>
  67:         /// Runs the specified command.
  68:         /// </summary>
  69:         /// <param name="command">The command.</param>
  70:         /// <param name="keyValues">The key values.</param>
  71:         /// <param name="output">The output.</param>
  72:         /// <returns></returns>
  73:         public override int Execute(string command, StringDictionary keyValues, out string output)
  74:         {
  75:             output = string.Empty;
  76:  
  77:             
  78:  
  79:             #region Check Arguments
  80:  
  81:             OccurrenceType occurrence = (OccurrenceType)Enum.Parse(typeof(OccurrenceType), Params["occurrence"].Value, true);
  82:             if (occurrence == OccurrenceType.monthly && !Params["day"].UserTypedIn)
  83:             {
  84:                 output = "Please specify the day to run the import.";
  85:                 output += GetHelpMessage(command);
  86:                 return (int)ErrorCodes.SyntaxError;
  87:             }
  88:             if (occurrence == OccurrenceType.weekly && !Params["dayofweek"].UserTypedIn)
  89:             {
  90:                 output = "Please specify the day of week to run the import.";
  91:                 output += GetHelpMessage(command);
  92:                 return (int)ErrorCodes.SyntaxError;
  93:             }
  94:  
  95:             #endregion
  96:  
  97:             string day = Params["day"].Value;
  98:             string dayofweek = Params["dayofweek"].Value;
  99:             string sspname = Params["sspname"].Value;
 100:             int hour = int.Parse(Params["hour"].Value);
 101:             bool enabled = bool.Parse(Params["enabled"].Value);
 102:             bool runJob = Params["runjob"].UserTypedIn;
 103:             if (!enabled && runJob)
 104:                 throw new SPSyntaxException("The runjob parameter cannot be specified when enabled is set to false.");
 105:  
 106:             ServerContext current;
 107:             if (Params["sspname"].UserTypedIn)
 108:                 current = ServerContext.GetContext(sspname);
 109:             else
 110:                 current = ServerContext.Default;
 111:  
 112:             // What follows is a whole lot of reflection which is required in order to get the SPScheduledJob object.
 113:             // Problem is that the only way to get the correct instance of this object is to use several internal
 114:             // classes, methods, and properties - why on earth these were not made public is absolutely beyond me!
 115:  
 116:             // The bulk of the reflection is recreating the following which was taken from 
 117:             // Microsoft.SharePoint.Portal.UserProfiles.AdminUI.Sched.InitializeComponent().
 118:             // Once we have the job objects we can start setting properties.
 119:             /*
 120:             private void InitializeComponent()
 121:             {
 122:                 ServerContext current = ServerContext.Current;
 123:                 UserProfileApplication userProfileApplication = current.UserProfileApplication;
 124:                 try
 125:                 {
 126:                     using (PortalApplication.BeginSecurityContext())
 127:                     {
 128:                         JobSchedulerSharedApplicationCollection applications = new JobSchedulerSharedApplicationCollection(SPFarm.Local.Services.GetValue<JobSchedulerService>(string.Empty));
 129:                         JobSchedulerSharedApplication sharedApplication = (JobSchedulerSharedApplication) applications[current.SharedResourceProvider];
 130:                         ScheduledJobCollection jobs = new ScheduledJobCollection(sharedApplication);
 131:                         this.AudienceCompileScheduler.Job = jobs[userProfileApplication.AudienceCompilationJobId];
 132:                     }
 133:                 }
 134:                 catch (Exception)
 135:                 {
 136:                     throw;
 137:                 }
 138:             }
 139:             */
 140:  
 141:             // UserProfileApplication userProfileApplication = current.UserProfileApplication;
 142:             object userProfileApplication = Utilities.GetPropertyValue(current, "UserProfileApplication");
 143:  
 144:             // The SSP is locked down so we need to use reflection to get at it.
 145:             object sharedResourceProvider = Utilities.GetSharedResourceProvider(current);
 146:  
 147:             // JobSchedulerService jobSchedulerService = SPFarm.Local.Services.GetValue(typeof(JobSchedulerService));
 148:             Type jobSchedulerServiceType = Type.GetType("Microsoft.Office.Server.Administration.JobSchedulerService, Microsoft.Office.Server, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c");
 149:  
 150:  
 151:             MethodInfo getValue =
 152:                 SPFarm.Local.Services.GetType().GetMethod("GetValue",
 153:                                                           BindingFlags.NonPublic | BindingFlags.Public |
 154:                                                           BindingFlags.Instance | BindingFlags.InvokeMethod, null, new Type[] {typeof(Type), typeof(string)}, null);
 155:  
 156:             object jobSchedulerService = getValue.Invoke(SPFarm.Local.Services,
 157:                                                           new object[]
 158:                                                               {
 159:                                                                   jobSchedulerServiceType, string.Empty
 160:                                                               });
 161:  
 162:  
 163:             // JobSchedulerSharedApplicationCollection application = new JobSchedulerSharedApplicationCollection(jobSchedulerServiceType);
 164:             Type jobSchedulerSharedApplicationCollectionType = Type.GetType("Microsoft.Office.Server.Administration.JobSchedulerSharedApplicationCollection, Microsoft.Office.Server, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c");
 165:  
 166:             ConstructorInfo jobSchedulerSharedApplicationCollectionConstructor =
 167:                 jobSchedulerSharedApplicationCollectionType.GetConstructor(
 168:                     BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public,
 169:                     null,
 170:                     new Type[] {jobSchedulerService.GetType()}, null);
 171:             object applications = jobSchedulerSharedApplicationCollectionConstructor.Invoke(new object[] { jobSchedulerService });
 172:  
 173:             // JobSchedulerSharedApplication jobSchedulerSharedApplication = applications[sharedResourceProvider];
 174:             PropertyInfo itemProp = applications.GetType().GetProperty("Item",
 175:                                                                      BindingFlags.NonPublic |
 176:                                                                      BindingFlags.Instance |
 177:                                                                      BindingFlags.InvokeMethod |
 178:                                                                      BindingFlags.GetProperty |
 179:                                                                      BindingFlags.Public);
 180:             object jobSchedulerSharedApplication = itemProp.GetValue(applications, new object[] { sharedResourceProvider });
 181:  
 182:  
 183:             //ScheduledJobCollection scheduledJobCollection = new ScheduledJobCollection(sharedApplication);
 184:             Type scheduledJobCollectionType = Type.GetType("Microsoft.Office.Server.Administration.ScheduledJobCollection, Microsoft.Office.Server, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c");
 185:             ConstructorInfo scheduledJobCollectionConstructor =
 186:                 scheduledJobCollectionType.GetConstructor(
 187:                     BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public,
 188:                     null,
 189:                     new Type[] {jobSchedulerSharedApplication.GetType()}, null);
 190:             object scheduledJobCollection = scheduledJobCollectionConstructor.Invoke(new object[] { jobSchedulerSharedApplication });
 191:  
 192:  
 193:             // userProfileApplication.AudienceCompilationJobId
 194:             Guid audienceCompilationJobId = (Guid)Utilities.GetPropertyValue(userProfileApplication, "AudienceCompilationJobId");
 195:  
 196:  
 197:  
 198:             // ScheduledJob compilationJob = scheduledJobCollection[audienceCompilationJobId];
 199:             itemProp = scheduledJobCollection.GetType().GetProperty("Item",
 200:                                                                     BindingFlags.NonPublic |
 201:                                                                     BindingFlags.Instance |
 202:                                                                     BindingFlags.InvokeMethod |
 203:                                                                     BindingFlags.GetProperty |
 204:                                                                     BindingFlags.Public);
 205:             object compilationJob = itemProp.GetValue(scheduledJobCollection, new object[] { audienceCompilationJobId });
 206:  
 207:  
 208:             PropertyInfo scheduleProp = compilationJob.GetType().GetProperty("Schedule",
 209:                                                                             BindingFlags.FlattenHierarchy |
 210:                                                                             BindingFlags.NonPublic |
 211:                                                                             BindingFlags.Instance |
 212:                                                                             BindingFlags.InvokeMethod |
 213:                                                                             BindingFlags.GetProperty |
 214:                                                                             BindingFlags.Public);
 215:  
 216:             MethodInfo update =
 217:                 compilationJob.GetType().GetMethod("Update",
 218:                                                   BindingFlags.NonPublic | 
 219:                                                   BindingFlags.Public |
 220:                                                   BindingFlags.Instance | 
 221:                                                   BindingFlags.InvokeMethod |
 222:                                                   BindingFlags.FlattenHierarchy, 
 223:                                                   null,
 224:                                                   new Type[] {typeof (bool)}, null);
 225:             
 226:             // Woohoo!!! We are finally at a point where we can actually set the schedule - what a pain the @$$ that was!!!
 227:             SPSchedule schedule;
 228:             
 229:             if (occurrence == OccurrenceType.daily)
 230:             {
 231:                 schedule = SetUserProfileImportSchedule.ScheduledJobHelper.GetScheduleDaily(hour);
 232:             }
 233:             else if (occurrence == OccurrenceType.weekly)
 234:             {
 235:                 schedule = SetUserProfileImportSchedule.ScheduledJobHelper.GetScheduleWeekly((DayOfWeek)Enum.Parse(typeof(DayOfWeek), dayofweek, true), hour);
 236:             }
 237:             else if (occurrence == OccurrenceType.monthly)
 238:             {
 239:                 schedule = SetUserProfileImportSchedule.ScheduledJobHelper.GetScheduleMonthly(int.Parse(day), hour);
 240:             }
 241:             else
 242:                 throw new Exception("Unknown occurance type.");
 243:  
 244:             Type scheduledJobType = Type.GetType("Microsoft.Office.Server.Administration.ScheduledJob, Microsoft.Office.Server, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c");
 245:  
 246:  
 247:             // fullImportJob.Schedule = schedule;
 248:             scheduleProp.SetValue(compilationJob, schedule, null);
 249:  
 250:             // fullImportJob.Enabled = enabled;
 251:             Utilities.SetPropertyValue(compilationJob, scheduledJobType, "Disabled", !enabled);
 252:  
 253:             // fullImportJob.Update(true);
 254:             update.Invoke(compilationJob, new object[] { true });
 255:  
 256:             if (runJob)
 257:             {
 258:                 // fullImportJob.Execute();
 259:                 Utilities.ExecuteMethod(compilationJob, "Execute", new Type[] { }, new object[] { });
 260:             }
 261:  
 262:             if (runJob)
 263:             {
 264:                 // We want to wait until the import is finished before moving on in case we are being run in a batch that requires this to complete before continueing.
 265:                 UserProfileConfigManager manager = new UserProfileConfigManager(current);
 266:                 while (manager.IsImportInProgress())
 267:                     Thread.Sleep(500);
 268:             }
 269:  
 270:  
 271:             return OUTPUT_SUCCESS;
 272:         }
 273:  
 274:         #endregion
 275:  
 276:     }
 277: }
 278: #endif

The help for the command is shown below:

C:\>stsadm -help gl-setaudiencecompilationschedule

stsadm -o gl-setaudiencecompilationschedule


Sets the audience compilation schedule.

Parameters:
        [-sspname <SSP name>]
        -occurrence <daily|weekly|monthly>
        -hour <hour to run (0-23)>
        [-day <the day to run if monthly is specified>]
        [-dayofweek <the day of week to run if weekly is specified (sunday|monday|tuesday|wednesday|thursday|friday|saturday)>]
        [-enabled <true|false> (default is true)]
        [-runjob]

The following table summarizes the command and its various parameters:

Command Name Availability Build Date
gl-setaudiencecompilationschedule MOSS 2007 Release: 8/14/2008

Parameter Name Short Form Required Description Example Usage
sspname ssp No The name of the SSP that the audiences to compile are associated with.  If omitted the default SSP will be used. -sspname SSP1

-ssp SSP1
occurrence oc Yes Specifies how frequently the compilation should occur.  Valid values are "daily", "weekly", and "monthly". -occurrence daily

-oc monthly
hour   Yes The hour in which to run the compilation.  This should be an integer between 0 and 23 (where 0 is 12:00am and 23 is 11:00pm). -hour 22
day   No, unless occurrence is monthly The day of the month to run the compilation job.  Valid values are between 1 and 31. -day 1
dayofweek   No, unless occurrence is weekly The day of the week to run the compilation job.  Valid values are "sunday", "monday", "tuesday", "wednesday", "thursday", and "saturday". -dayofweek saturday
enabled   No "true" to enable the compilation schedule, "false" to disable it.  If not specified then the compilation schedule will be enabled. -enabled true
runjob run No If specified then the compilation job will be immediately executed after setting the schedule. -runjob

-run

The following is an example of how to set the compilation schedule to run every Satruday at 10:00pm:

stsadm -o gl-setaudiencecompilationschedule -occurrence weekly -hour 22 -dayofweek saturday -enabled true -runjob

1 comment:

Anonymous said...

Hi Gary!

I would just like to complement you on your fine work with the stsadm commands! They are really helping me alot since I'm not that comfortable in Visual Studio (yet :)). Thak you for this!

Best regards
Mikael