AcUtils
A high performance abstraction layer for AccuRev
AcGroups.cs
Go to the documentation of this file.
1 
16 using System;
17 using System.Collections.Generic;
18 using System.ComponentModel;
19 using System.Diagnostics;
20 using System.Linq;
21 using System.Text;
22 using System.Threading;
23 using System.Threading.Tasks;
24 using System.Xml.Linq;
25 
26 namespace AcUtils
27 {
36  [Serializable]
37  public sealed class AcGroups : List<AcPrincipal>
38  {
39  #region Class variables
40  private bool _includeMembersList;
41  private bool _includeDeactivated;
42  [NonSerialized] private readonly object _locker = new object(); // token for lock keyword scope
43  [NonSerialized] private int _counter; // used to report membership initialization progress back to the caller
44  #endregion
45 
46  #region object construction:
47 
49 
91  public AcGroups(bool includeMembersList = false, bool includeDeactivated = false)
92  {
93  _includeMembersList = includeMembersList;
94  _includeDeactivated = includeDeactivated;
95  }
96 
106 
107  public async Task<bool> initAsync(IProgress<int> progress = null)
108  {
109  bool ret = false; // assume failure
110  try
111  {
112  AcResult r = await AcCommand.runAsync($"show {(_includeDeactivated ? "-fix" : "-fx")} groups")
113  .ConfigureAwait(false);
114  if (r != null && r.RetVal == 0)
115  {
116  XElement xml = XElement.Parse(r.CmdResult);
117  IEnumerable<XElement> query = from element in xml.Elements("Element") select element;
118  List<Task<bool>> tasks = null;
119  if (_includeMembersList)
120  {
121  int num = query.Count();
122  tasks = new List<Task<bool>>(num);
123  }
124  Func<Task<bool>, bool> cf = t =>
125  {
126  bool res = t.Result;
127  if (res && progress != null) progress.Report(Interlocked.Increment(ref _counter));
128  return res;
129  };
130 
131  foreach (XElement e in query)
132  {
133  AcPrincipal group = new AcPrincipal();
134  group.Name = (string)e.Attribute("Name");
135  group.ID = (int)e.Attribute("Number");
136  // XML attribute isActive exists only if the group is inactive, otherwise it isn't there
137  group.Status = (e.Attribute("isActive") == null) ? PrinStatus.Active : PrinStatus.Inactive;
138  lock (_locker) { Add(group); }
139  if (_includeMembersList)
140  {
141  Task<bool> t = initMembersListAsync(group.Name).ContinueWith(cf);
142  tasks.Add(t);
143  }
144  }
145 
146  if (!_includeMembersList)
147  ret = true; // list initialization succeeded
148  else // run membership initialization in parallel
149  {
150  bool[] arr = await Task.WhenAll(tasks).ConfigureAwait(false);
151  ret = (arr != null && arr.All(n => n == true));
152  }
153  }
154  }
155 
156  catch (AcUtilsException ecx)
157  {
158  AcDebug.Log($"AcUtilsException caught and logged in AcGroups.initAsync{Environment.NewLine}{ecx.Message}");
159  }
160 
161  catch (Exception ecx)
162  {
163  AcDebug.Log($"Exception caught and logged in AcGroups.initAsync{Environment.NewLine}{ecx.Message}");
164  }
165 
166  return ret;
167  }
169  #endregion
170 
183 
187  private async Task<bool> initMembersListAsync(string group)
188  {
189  bool ret = false; // assume failure
190  try
191  {
192  AcResult r = await AcCommand.runAsync($@"show -fx -g ""{group}"" members").ConfigureAwait(false);
193  if (r != null && r.RetVal == 0) // if command succeeded
194  {
195  SortedSet<string> members = new SortedSet<string>();
196  XElement xml = XElement.Parse(r.CmdResult);
197  foreach (XElement e in xml.Elements("Element"))
198  {
199  string name = (string)e.Attribute("User");
200  members.Add(name);
201  }
202 
203  lock (_locker)
204  {
205  AcPrincipal prncpl = getPrincipal(group);
206  prncpl.Members = members;
207  }
208 
209  ret = true; // operation succeeded
210  }
211  }
212 
213  catch (AcUtilsException ecx)
214  {
215  AcDebug.Log($"AcUtilsException caught and logged in AcGroups.initMembersListAsync{Environment.NewLine}{ecx.Message}");
216  }
217 
218  catch (Exception ecx)
219  {
220  AcDebug.Log($"Exception caught and logged in AcGroups.initMembersListAsync{Environment.NewLine}{ecx.Message}");
221  }
222 
223  return ret;
224  }
225 
237 
242  public static bool isMember(string user, string group)
243  {
244  bool ret = false; // assume not a member
245  string command = String.Format(@"ismember ""{0}"" ""{1}""", user, group);
246  try
247  {
248  using (Process process = new Process())
249  {
250  process.StartInfo.FileName = "accurev";
251  process.StartInfo.Arguments = command;
252  process.StartInfo.UseShellExecute = false;
253  process.StartInfo.CreateNoWindow = true;
254  process.StartInfo.RedirectStandardInput = true; // fix for AccuRev defect 29059
255  process.StartInfo.RedirectStandardOutput = true;
256  process.StartInfo.RedirectStandardError = true;
257  process.StartInfo.StandardOutputEncoding = Encoding.UTF8;
258  process.StartInfo.StandardErrorEncoding = Encoding.UTF8;
259  process.ErrorDataReceived += new DataReceivedEventHandler(AcDebug.errorDataHandler);
260  process.Start();
261  process.BeginErrorReadLine();
262  string result = process.StandardOutput.ReadToEnd();
263  process.WaitForExit();
264  if (process.ExitCode == 0)
265  {
266  if (result.Length > 0 && result[0] == '1')
267  ret = true;
268  }
269  else
270  {
271  string err = String.Format("AccuRev program return: {0}{1}{2}", process.ExitCode, Environment.NewLine, "accurev " + command);
272  throw new AcUtilsException(err); // let calling method handle
273  }
274  }
275  }
276 
277  catch (Win32Exception ecx)
278  {
279  string msg = String.Format("Win32Exception caught and logged in AcGroups.isMember{0}{1}{0}accurev {2}{0}errorcode: {3}{0}native errorcode: {4}{0}{5}{0}{6}{0}{7}",
280  Environment.NewLine, ecx.Message, command, ecx.ErrorCode.ToString(), ecx.NativeErrorCode.ToString(), ecx.StackTrace, ecx.Source, ecx.GetBaseException().Message);
281  AcDebug.Log(msg);
282  }
283 
284  catch (InvalidOperationException ecx)
285  {
286  string msg = String.Format("InvalidOperationException caught and logged in AcGroups.isMember{0}{1}{0}accurev {2}",
287  Environment.NewLine, ecx.Message, command);
288  AcDebug.Log(msg);
289  }
290 
291  return ret;
292  }
293 
299  public AcPrincipal getPrincipal(string name)
300  {
301  return this.SingleOrDefault(n => n.Name == name);
302  }
303 
310 
311  public string getMembers(string group)
312  {
313  string list = null;
314  AcPrincipal prncpl = getPrincipal(group);
315  if (prncpl != null)
316  {
317  SortedSet<string> members = prncpl.Members;
318  if (members != null)
319  list = String.Join(", ", members);
320  }
321 
322  return list;
323  }
324  }
325 }
AcGroups(bool includeMembersList=false, bool includeDeactivated=false)
A container class for AcPrincipal objects that define AccuRev groups. Elements contain AccuRev group ...
Definition: AcGroups.cs:91
int ID
AccuRev principal ID number for the user or group.
Definition: AcPrincipal.cs:145
async Task< bool > initAsync(IProgress< int > progress=null)
Populate this container with AcPrincipal objects as per constructor parameters.
Definition: AcGroups.cs:107
A container class for AcPrincipal objects that define AccuRev groups.
Definition: AcGroups.cs:37
AccuRev program return value and command result.
Definition: AcCommand.cs:29
AcPrincipal getPrincipal(string name)
Retrieves the AcPrincipal object for AccuRev group name.
Definition: AcGroups.cs:299
PrinStatus
Whether the principal is active or inactive in AccuRev.
Definition: AcPrincipal.cs:28
Contains the AccuRev principal attributes name, ID and status (active or inactive) for users and grou...
Definition: AcPrincipal.cs:52
async Task< bool > initMembersListAsync(string group)
Optionally called during list construction to initialize the list of principals (users and groups) th...
Definition: AcGroups.cs:187
string getMembers(string group)
Returns the list of members in group as a formatted string. List optionally initialized by AcGroups c...
Definition: AcGroups.cs:311
string CmdResult
The command result (usually XML) emitted by AccuRev.
Definition: AcCommand.cs:70
int RetVal
The AccuRev program return value for the command, otherwise minus one (-1) on error.
Definition: AcCommand.cs:61
static void Log(string message, bool formatting=true)
Write the message text to STDOUT, to weekly log files located in %LOCALAPPDATA%\AcTools\Logs, and to trigger.log in the AccuRev server's ..storage\site_slice\logs folder in the case of triggers.
Definition: AcDebug.cs:378
static void errorDataHandler(object sendingProcess, DataReceivedEventArgs errLine)
Centralized error handler.
Definition: AcDebug.cs:554
string Name
AccuRev principal name for the user or group.
Definition: AcPrincipal.cs:154
Exception thrown when an AccuRev command fails. The AccuRev program return value is zero (0) on succe...
AccuRev command processing.
Definition: AcCommand.cs:138
static async Task< AcResult > runAsync(string command, ICmdValidate validator=null)
Run the AccuRev command asynchronously with non-blocking I/O.
Definition: AcCommand.cs:184
Use to log and display error and general purpose text messages, and to save the XML param data sent b...
Definition: AcDebug.cs:100
SortedSet< string > Members
The list of groups a user has membership in, or the list of principals (users and groups) in a group...
Definition: AcPrincipal.cs:177
static bool isMember(string user, string group)
Determines if user is a member of group by way of direct or indirect (implicit) membership, e.g. Mary is implicitly a member of groupA because she's a member of groupB which is a member of groupA.
Definition: AcGroups.cs:242
PrinStatus Status
Whether the principal is active or inactive in AccuRev.
Definition: AcPrincipal.cs:163