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
One thought on “Setting the Audience Compilation Schedule via STSADM”
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
Comments are closed.