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
  2using System;
  3using System.Collections.Specialized;
  4using System.Reflection;
  5using System.Text;
  6using System.Threading;
  7using Lapointe.SharePoint.STSADM.Commands.SPValidators;
  8using Microsoft.Office.Server;
  9using Microsoft.Office.Server.UserProfiles;
 10using Microsoft.SharePoint;
 11using Microsoft.SharePoint.Administration;
 12using Microsoft.SharePoint.StsAdmin;
 13using PropertyInfo=System.Reflection.PropertyInfo;
 14using Lapointe.SharePoint.STSADM.Commands.OperationHelpers;
 15 
 16namespace 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 NameAvailabilityBuild Date
gl-setaudiencecompilationscheduleMOSS 2007Release: 8/14/2008
Parameter NameShort FormRequiredDescriptionExample Usage
sspnamesspNoThe name of the SSP that the audiences to compile are associated with. If omitted the default SSP will be used.-sspname SSP1, -ssp SSP1
occurrenceocYesSpecifies how frequently the compilation should occur. Valid values are “daily”, “weekly”, and “monthly”.-occurrence daily, -oc monthly
hourYesThe 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
dayNo, unless occurrence is monthlyThe day of the month to run the compilation job. Valid values are between 1 and 31.-day 1
dayofweekNo, unless occurrence is weeklyThe day of the week to run the compilation job. Valid values are “sunday”, “monday”, “tuesday”, “wednesday”, “thursday”, and “saturday”.-dayofweek saturday
enabledNo“true” to enable the compilation schedule, “false” to disable it. If not specified then the compilation schedule will be enabled.-enabled true
runjobrunNoIf 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