AcUtils
A high performance abstraction layer for AccuRev
AcCommand.cs
Go to the documentation of this file.
1 
16 using System;
17 using System.ComponentModel;
18 using System.Diagnostics;
19 using System.Text;
20 using System.Threading.Tasks;
22 
23 namespace AcUtils
24 {
28  [Serializable]
29  public sealed class AcResult
30  {
31  #region class variables
32  private int _retVal = -1;
33  private string _cmdResult;
34  #endregion
35 
36  #region Constructors
37 
39  public AcResult() { }
43 
49  public AcResult(int retval, string cmdresult)
50  {
51  _retVal = retval;
52  _cmdResult = cmdresult;
53  }
55  #endregion
56 
60  public int RetVal
61  {
62  get { return _retVal; }
63  set { _retVal = value; }
64  }
65 
69  public string CmdResult
70  {
71  get { return _cmdResult ?? String.Empty; }
72  set { _cmdResult = value; }
73  }
74  }
75 
80  public interface ICmdValidate
81  {
89  bool isValid(string command, int retval);
90  }
91 
96  [Serializable]
97  public class CmdValidate : ICmdValidate
98  {
112 
122  public virtual bool isValid(string command, int retval)
123  {
124  if (retval == 0 || (retval == 1 && (
125  command.StartsWith("diff", StringComparison.OrdinalIgnoreCase) ||
126  command.StartsWith("merge", StringComparison.OrdinalIgnoreCase)))
127  )
128  return true;
129  else
130  return false;
131  }
132  }
133 
137  [Serializable]
138  public static class AcCommand
139  {
140  #region class variables
141  private static readonly int MaxConcurrencyDefault = 8; // default when ACUTILS_MAXCONCURRENT environment variable doesn't exist
142  private static TaskFactory _taskFactory;
143  #endregion
144 
148 
150  static AcCommand()
151  {
152  try
153  {
154  string maxconcurrent = Environment.GetEnvironmentVariable("ACUTILS_MAXCONCURRENT");
155  int max = (maxconcurrent != null) ? Int32.Parse(maxconcurrent) : MaxConcurrencyDefault;
157  _taskFactory = new TaskFactory(ts);
158  }
159 
160  catch (Exception ecx)
161  {
162  AcDebug.Log($"Exception caught and logged in AcCommand constructor{Environment.NewLine}{ecx.Message}");
163  }
164  }
165 
183 
184  public static async Task<AcResult> runAsync(string command, ICmdValidate validator = null)
185  {
186  TaskCompletionSource<AcResult> tcs = new TaskCompletionSource<AcResult>();
187  StringBuilder error = new StringBuilder(512);
188  try
189  {
190  await _taskFactory.StartNew(() =>
191  {
192  using (Process process = new Process())
193  {
194  process.StartInfo.FileName = "accurev";
195  process.StartInfo.Arguments = command;
196  process.StartInfo.UseShellExecute = false;
197  process.StartInfo.CreateNoWindow = true;
198  process.StartInfo.RedirectStandardInput = true; // fix for AccuRev defect 29059
199  process.StartInfo.RedirectStandardOutput = true;
200  process.StartInfo.RedirectStandardError = true;
201  process.StartInfo.StandardOutputEncoding = Encoding.UTF8;
202  process.StartInfo.StandardErrorEncoding = Encoding.UTF8;
203  process.ErrorDataReceived += (sender, e) =>
204  {
205  error.AppendLine(e.Data);
206  };
207  process.Start();
208  process.BeginErrorReadLine();
209  Task<string> output = process.StandardOutput.ReadToEndAsync();
210  process.WaitForExit();
211  if (process.HasExited)
212  {
213  string err = error.ToString().Trim();
214  if (!String.IsNullOrEmpty(err) &&
215  !(String.Equals("You are not in a directory associated with a workspace", err)))
216  {
217  AcDebug.Log(err, false);
218  }
219 
220  ICmdValidate validate = validator ?? new CmdValidate();
221  if (validate.isValid(command, process.ExitCode))
222  {
223  tcs.SetResult(new AcResult(process.ExitCode, output.Result));
224  }
225  else
226  {
227  tcs.SetException(new AcUtilsException($"AccuRev program return: {process.ExitCode}{Environment.NewLine}accurev {command}")); // let calling method handle
228  }
229  }
230  }
231  }).ConfigureAwait(false);
232  }
233 
234  catch (Win32Exception ecx)
235  {
236  string msg = String.Format(@"Win32Exception caught and logged in AcCommand.runAsync{0}{1}{0}""accurev {2}""{0}errorcode: {3}{0}native errorcode: {4}{0}{5}{0}{6}{0}{7}{0}{8}",
237  Environment.NewLine, ecx.Message, command, ecx.ErrorCode.ToString(), ecx.NativeErrorCode.ToString(), ecx.StackTrace, ecx.Source, ecx.GetBaseException().Message, error.ToString());
238  AcDebug.Log(msg);
239  tcs.SetException(ecx);
240  }
241 
242  catch (InvalidOperationException ecx)
243  {
244  string msg = String.Format(@"InvalidOperationException caught and logged in AcCommand.runAsync{0}{1}{0}""accurev {2}""{0}{3}",
245  Environment.NewLine, ecx.Message, command, error.ToString());
246  AcDebug.Log(msg);
247  tcs.SetException(ecx);
248  }
249 
250  return await tcs.Task.ConfigureAwait(false);
251  }
252 
265 
266  public static AcResult run(string command, ICmdValidate validator = null)
267  {
268  AcResult result = new AcResult();
269  StringBuilder error = new StringBuilder(512);
270  try
271  {
272  using (Process process = new Process())
273  {
274  process.StartInfo.FileName = "accurev";
275  process.StartInfo.Arguments = command;
276  process.StartInfo.UseShellExecute = false;
277  process.StartInfo.CreateNoWindow = true;
278  process.StartInfo.RedirectStandardInput = true; // fix for AccuRev defect 29059
279  process.StartInfo.RedirectStandardOutput = true;
280  process.StartInfo.RedirectStandardError = true;
281  process.StartInfo.StandardOutputEncoding = Encoding.UTF8;
282  process.StartInfo.StandardErrorEncoding = Encoding.UTF8;
283  process.ErrorDataReceived += (sender, e) =>
284  {
285  error.AppendLine(e.Data);
286  };
287  StringBuilder output = new StringBuilder();
288  output.Capacity = 4096;
289  process.OutputDataReceived += (sender, e) =>
290  {
291  output.AppendLine(e.Data);
292  };
293  process.Start();
294  process.BeginErrorReadLine();
295  process.BeginOutputReadLine();
296  process.WaitForExit(); // blocks here. recommended to ensure output buffer has been flushed
297  if (process.HasExited)
298  {
299  string err = error.ToString().Trim();
300  if (!String.IsNullOrEmpty(err) &&
301  !(String.Equals("You are not in a directory associated with a workspace", err)))
302  {
303  AcDebug.Log(err, false);
304  }
305 
306  ICmdValidate validate = validator ?? new CmdValidate();
307  if (validate.isValid(command, process.ExitCode))
308  {
309  result.RetVal = process.ExitCode;
310  result.CmdResult = output.ToString();
311  }
312  else
313  {
314  throw new AcUtilsException($"AccuRev program return: {process.ExitCode}{Environment.NewLine}accurev {command}"); // let calling method handle
315  }
316  }
317  }
318  }
319 
320  catch (Win32Exception ecx)
321  {
322  string msg = String.Format(@"Win32Exception caught and logged in AcCommand.run{0}{1}{0}""accurev {2}""{0}errorcode: {3}{0}native errorcode: {4}{0}{5}{0}{6}{0}{7}{0}{8}",
323  Environment.NewLine, ecx.Message, command, ecx.ErrorCode.ToString(), ecx.NativeErrorCode.ToString(), ecx.StackTrace, ecx.Source, ecx.GetBaseException().Message, error.ToString());
324  AcDebug.Log(msg);
325  }
326 
327  catch (InvalidOperationException ecx)
328  {
329  string msg = String.Format(@"InvalidOperationException caught and logged in AcCommand.run{0}{1}{0}""accurev {2}""{0}{3}",
330  Environment.NewLine, ecx.Message, command, error.ToString());
331  AcDebug.Log(msg);
332  }
333 
334  return result;
335  }
336  }
337 }
AccuRev program return value and command result.
Definition: AcCommand.cs:29
The default logic for determining if an AcUtilsException should be thrown based on the command's Accu...
Definition: AcCommand.cs:97
virtual bool isValid(string command, int retval)
Default logic used to determine if an AcUtilsException should be thrown based on AccuRev's program re...
Definition: AcCommand.cs:122
AcResult()
Default constructor.
Definition: AcCommand.cs:42
static AcResult run(string command, ICmdValidate validator=null)
Run the AccuRev command synchronously (blocks) on the current thread.
Definition: AcCommand.cs:266
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
Exception thrown when an AccuRev command fails. The AccuRev program return value is zero (0) on succe...
static AcCommand()
Initialize our task scheduler that allows no more than n tasks to execute simultaneously.
Definition: AcCommand.cs:150
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
AcResult(int retval, string cmdresult)
Constructor for object initialization with retval and cmdresult params.
Definition: AcCommand.cs:49
Implement to change the default logic used to determine if an AcUtilsException should be thrown based...
Definition: AcCommand.cs:80
bool isValid(string command, int retval)
Defines the logic used to determine if an AcUtilsException should be thrown based on the AccuRev prog...
Provides a task scheduler that ensures a maximum concurrency level while running on top of the Thread...