IoReporter
Represents a class object to perform I/O operations, publish warning messages, and retry the operation.
using Relativity.DataExchange.Logger;
using Relativity.DataExchange.Resources;
using Relativity.Logging;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Threading;
namespace Relativity.DataExchange.Io
{
public class IoReporter : IIoReporter
{
private const int NoRetryInfo = -1;
private int? ioErrorNumberOfRetriesShadowCopy;
private int? ioErrorWaitTimeInSecondsShadowCopy;
public IoReporterContext Context { get; }
protected IAppSettings CachedAppSettings { get; }
protected CancellationToken CancellationToken { get; }
protected int IoErrorNumberOfRetries {
get {
if (!ioErrorNumberOfRetriesShadowCopy.HasValue) {
ioErrorNumberOfRetriesShadowCopy = CachedAppSettings.IoErrorNumberOfRetries;
if (CachedAppSettings.EnforceMinRetryCount) {
int? nullable = ioErrorNumberOfRetriesShadowCopy;
int num = 1;
if ((nullable.GetValueOrDefault() < num) & nullable.HasValue)
ioErrorNumberOfRetriesShadowCopy = 1;
}
}
return ioErrorNumberOfRetriesShadowCopy.Value;
}
}
protected int IoErrorWaitTimeInSeconds {
get {
if (!ioErrorWaitTimeInSecondsShadowCopy.HasValue) {
ioErrorWaitTimeInSecondsShadowCopy = CachedAppSettings.IoErrorWaitTimeInSeconds;
if (CachedAppSettings.EnforceMinWaitTime) {
int? nullable = ioErrorWaitTimeInSecondsShadowCopy;
int num = 1;
if ((nullable.GetValueOrDefault() < num) & nullable.HasValue)
ioErrorWaitTimeInSecondsShadowCopy = 1;
}
}
return ioErrorWaitTimeInSecondsShadowCopy.Value;
}
}
protected ILog Logger { get; }
[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "The context argument is validated via ThrowIfNull.")]
public IoReporter(IoReporterContext context, ILog logger, CancellationToken token)
{
Logger = logger.ThrowIfNull<ILog>("logger");
Context = context.ThrowIfNull("context");
CancellationToken = token;
CachedAppSettings = context.AppSettings.DeepCopy();
}
public static string BuildIoReporterWarningMessage(Exception exception, double timeoutSeconds)
{
return BuildIoReporterWarningMessage(exception, timeoutSeconds, -1, -1);
}
public static string BuildIoReporterWarningMessage(Exception exception, double timeoutSeconds, int retryCount, int totalRetryCount)
{
int num = totalRetryCount - retryCount;
if (num < 0)
num = 0;
if (exception == null) {
if (retryCount == -1 && totalRetryCount == -1)
return string.Format(Strings.IoReporterWarningMessageWithoutException, timeoutSeconds);
return string.Format(Strings.IoReporterWarningMessageWithoutExceptionAndRetryInfo, timeoutSeconds, num);
}
if (retryCount == -1 && totalRetryCount == -1)
return string.Format(Strings.IoReporterWarningMessageWithException, timeoutSeconds, exception.Message);
return string.Format(Strings.IoReporterWarningMessageWithExceptionAndRetryInfo, timeoutSeconds, num, exception.Message);
}
public virtual void CopyFile(string sourceFileName, string destFileName, bool overwrite, int lineNumber)
{
if (string.IsNullOrEmpty(sourceFileName))
throw new ArgumentNullException("sourceFileName");
if (string.IsNullOrEmpty(destFileName))
throw new ArgumentNullException("destFileName");
if (lineNumber < 0)
throw new ArgumentOutOfRangeException("lineNumber", string.Format(Strings.LineNumberOutOfRangeExceptionMessage, "lineNumber"));
Context.FileSystem.File.Copy(sourceFileName, destFileName, overwrite);
}
public virtual bool GetFileExists(string fileName, int lineNumber)
{
if (string.IsNullOrEmpty(fileName))
return false;
if (lineNumber < 0)
throw new ArgumentOutOfRangeException("lineNumber", string.Format(Strings.LineNumberOutOfRangeExceptionMessage, "lineNumber"));
return Exec(lineNumber, fileName, "File Exists", () => Context.FileSystem.CreateFileInfo(fileName).Exists);
}
public virtual long GetFileLength(string fileName, int lineNumber)
{
if (string.IsNullOrEmpty(fileName))
return 0;
if (lineNumber < 0)
throw new ArgumentOutOfRangeException("lineNumber", string.Format(Strings.LineNumberOutOfRangeExceptionMessage, "lineNumber"));
return Exec(lineNumber, fileName, "File Length", () => Context.FileSystem.CreateFileInfo(fileName).Length);
}
public virtual void PublishRetryMessage(Exception exception, TimeSpan timeSpan, int retryCount, int totalRetryCount, long lineNumber)
{
string text = BuildIoReporterWarningMessage(exception, timeSpan.TotalSeconds, retryCount, totalRetryCount);
Context.PublishIoWarningEvent(new IoWarningEventArgs(text, lineNumber));
Logger.LogWarning(exception, text, Array.Empty<object>());
}
public virtual void PublishWarningMessage(IoWarningEventArgs args)
{
if (args == null)
throw new ArgumentNullException("args");
Context.PublishIoWarningEvent(args);
Logger.LogWarning(args.Message, Array.Empty<object>());
}
internal int CalculateWaitTimeInSeconds(int numberOfRetries)
{
int num = 0;
if (numberOfRetries < IoErrorNumberOfRetries - 1) {
num = IoErrorWaitTimeInSeconds;
if (num < 0)
num = 0;
}
return num;
}
private bool ThrowFileInfoInvalidPathException(Exception exception)
{
if (CachedAppSettings.DisableThrowOnIllegalCharacters)
return ExceptionHelper.IsIllegalCharactersInPathException(exception);
return false;
}
private FileInfoInvalidPathException CreateFileInfoInvalidPathException(Exception exception, string fileName)
{
string text = string.Format(CultureInfo.CurrentCulture, Strings.ImportInvalidPathCharactersExceptionMessage, fileName);
Logger.LogError(exception, "{message}", new object[1] {
text.Secure()
});
return new FileInfoInvalidPathException(text);
}
private void LogWarning(Exception exception, TimeSpan timeSpan, int retryCount, int totalRetryCount)
{
string text = BuildIoReporterWarningMessage(exception, timeSpan.TotalSeconds, retryCount, totalRetryCount);
Logger.LogWarning(exception, text, Array.Empty<object>());
}
private void LogCancellation(int lineNumber, string fileName, string description)
{
Logger.LogInformation("The {description} I/O operation for file {fileName} and line number {lineNumber} has been canceled.", new object[3] {
description,
fileName.Secure(),
lineNumber
});
}
private T Exec<T>(int lineNumber, string fileName, string description, Func<T> execFunc)
{
T result = default(T);
int ioErrorNumberOfRetries = IoErrorNumberOfRetries;
int num = ioErrorNumberOfRetries;
Func<Exception, bool> func = null;
while (num > 0) {
if (CancellationToken.IsCancellationRequested) {
LogCancellation(lineNumber, fileName, description);
break;
}
try {
num--;
result = execFunc();
return result;
} catch (OperationCanceledException) {
LogCancellation(lineNumber, fileName, description);
return result;
} catch (Exception ex2) {
if (func == null)
func = RetryExceptionHelper.CreateRetryPredicate(Context.RetryOptions);
if (!func(ex2))
throw;
int retryCount = ioErrorNumberOfRetries - num;
int num2 = CalculateWaitTimeInSeconds(num);
TimeSpan timeSpan = TimeSpan.FromSeconds((double)num2);
if (ThrowFileInfoInvalidPathException(ex2)) {
LogWarning(ex2, timeSpan, retryCount, ioErrorNumberOfRetries);
throw CreateFileInfoInvalidPathException(ex2, fileName);
}
if (num == 0 || Context.RetryOptions == RetryOptions.None) {
LogWarning(ex2, timeSpan, retryCount, ioErrorNumberOfRetries);
throw;
}
PublishRetryMessage(ex2, timeSpan, retryCount, ioErrorNumberOfRetries, lineNumber);
if (num2 > 0)
Thread.CurrentThread.Join(1000 * num2);
}
}
return result;
}
}
}