TransferPathCommandBase
using Relativity.Transfer.Paths;
using Relativity.Transfer.Resources;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
namespace Relativity.Transfer
{
public abstract class TransferPathCommandBase : ITransferPathCommand
{
private readonly ConcurrentBag<ITransferIssue> issues = new ConcurrentBag<ITransferIssue>();
private DateTime startTime;
private bool? fileNotFoundErrorsDisabled;
private bool? filePermissionErrorsRetry;
private int? fileSystemChunkSize;
private int? maxJobRetryAttempts;
private bool? overwriteFiles;
private bool? preserveDates;
protected internal IPathStatusChecker PathStatusChecker { get; }
public IReadOnlyList<ITransferIssue> Issues => new List<ITransferIssue>(issues);
protected IFileSystemService FileSystemService { get; }
protected ITransferJobService JobService { get; }
protected ClientConfiguration Configuration { get; }
protected bool FileNotFoundErrorsDisabled {
get {
if (!fileNotFoundErrorsDisabled.HasValue)
fileNotFoundErrorsDisabled = Configuration.FileNotFoundErrorsDisabled;
return fileNotFoundErrorsDisabled.Value;
}
}
protected bool FilePermissionErrorsRetry {
get {
if (!filePermissionErrorsRetry.HasValue)
filePermissionErrorsRetry = Configuration.PermissionErrorsRetry;
return filePermissionErrorsRetry.Value;
}
}
protected int FileSystemChunkSize {
get {
if (!fileSystemChunkSize.HasValue)
fileSystemChunkSize = ((Configuration.FileSystemChunkSize > 0) ? Configuration.FileSystemChunkSize : 16384);
return fileSystemChunkSize.Value;
}
}
protected ITransferLog Log { get; }
protected int MaxJobRetryAttempts {
get {
if (!maxJobRetryAttempts.HasValue)
maxJobRetryAttempts = Configuration.MaxJobRetryAttempts;
return maxJobRetryAttempts.Value;
}
}
protected IProgress<LargeFileProgressEventArgs> LargeFileProgressProvider { get; }
protected double LargeFileProgressRateSeconds => (LargeFileProgressProvider as LargeFileProgress)?.LargeFileProgressRateSeconds ?? 0;
protected bool OverwriteFiles {
get {
if (!overwriteFiles.HasValue)
overwriteFiles = Configuration.OverwriteFiles;
return overwriteFiles.Value;
}
}
protected bool PreserveDates {
get {
if (!preserveDates.HasValue)
preserveDates = Configuration.PreserveDates;
return preserveDates.Value;
}
}
protected ITransferRequest Request { get; }
protected TransferPathCommandBase(ITransferLog log, ITransferRequest request, ITransferJobService jobService, IFileSystemService fileSystemService, ClientConfiguration configuration)
{
if (log == null)
throw new ArgumentNullException("log");
if (request == null)
throw new ArgumentNullException("request");
if (configuration == null)
throw new ArgumentNullException("configuration");
if (jobService == null)
throw new ArgumentNullException("jobService");
if (fileSystemService == null)
throw new ArgumentNullException("fileSystemService");
Log = log;
Request = request;
Configuration = configuration;
JobService = jobService;
FileSystemService = fileSystemService;
LargeFileProgressProvider = null;
PathStatusChecker = new RetryablePathStatusChecker(Configuration);
if (request.Context != null && request.Context.LargeFileProgressEnabled)
LargeFileProgressProvider = new LargeFileProgress(OnLargeFileProgress, request.Context.LargeFileProgressRateSeconds);
}
public virtual TransferPathResult Execute(TransferPath path, CancellationToken token)
{
if (path == (TransferPath)null)
throw new ArgumentNullException("path");
if (string.IsNullOrEmpty(path.SourcePath))
throw new ArgumentException(CoreStrings.SourcePathArgumentExceptionMessage, "path");
if (string.IsNullOrEmpty(path.TargetPath))
throw new ArgumentException(CoreStrings.TargetPathArgumentExceptionMessage, "path");
if (token.IsCancellationRequested)
return new TransferPathResult {
Path = path,
Status = TransferPathStatus.Canceled,
StartTime = new DateTime?(DateTime.Now)
};
TransferPathResult transferPathResult = new TransferPathResult {
Path = path,
Status = TransferPathStatus.Started,
StartTime = new DateTime?(DateTime.Now)
};
if (token.IsCancellationRequested) {
transferPathResult.Status = TransferPathStatus.Canceled;
return transferPathResult;
}
PublishTransferPathProgress(transferPathResult);
transferPathResult = OnExecute(path, token);
transferPathResult.Completed = JobService.IsCompleted(transferPathResult.Status);
PublishTransferPathProgress(transferPathResult);
return transferPathResult;
}
public virtual void PreExecute(CancellationToken token)
{
Log.LogTransferInformation(Request, "Preparing to perform a pre-execute check for the '{ClientId}' client...", Configuration.ClientId);
OnPreExecute(token);
startTime = DateTime.Now;
Log.LogTransferInformation(Request, "The '{ClientId}' pre-execute check is successful.", Configuration.ClientId);
}
public virtual void PostExecute(CancellationToken token)
{
OnPostExecute(token);
}
protected abstract TransferPathResult OnExecute(TransferPath path, CancellationToken token);
protected virtual void OnPostExecute(CancellationToken token)
{
PublishTransferStatistics(true);
}
protected virtual void OnPreExecute(CancellationToken token)
{
}
protected void PublishTransferPathProgress(TransferPathResult result)
{
if (result == null)
throw new ArgumentNullException("result");
if (result.Path.PathAttributes.HasFlag(TransferPathAttributes.File) && string.IsNullOrEmpty(result.TargetFile))
try {
result.TargetFile = PathHelper.GetTargetFilePath(result.Path.SourcePath, result.Path.TargetPath, result.Path.TargetFileName);
} catch (ArgumentException exception) {
Log.LogTransferWarning(exception, Request, "Failed to retrieve the {TransferDirection} target file path from the {TransferClient} transfer path object.", result.Path.Direction, Configuration.Client);
}
Request.Context?.PublishTransferPathProgress(Request, result);
}
protected void PublishTransferJobIssue(ITransferIssue issue)
{
Request.Context?.PublishTransferJobIssue(Request, issue);
}
protected void PublishTransferFileIssue(ITransferIssue issue)
{
Request.Context?.PublishTransferPathIssue(Request, issue);
}
protected void PublishTransferStatistics(bool force)
{
JobService.Statistics.TransferTimeSeconds = (DateTime.Now - startTime).TotalSeconds;
JobService.PublishStatistics(force);
}
protected void PublishLargeFileProgress(TransferPath path, long totalTransferredBytes, long totalBytes, double progress)
{
PublishLargeFileProgress(new LargeFileProgressEventArgs(path, totalTransferredBytes, totalBytes, progress));
}
protected void PublishLargeFileProgress(TransferPath path, long totalTransferredBytes, long totalBytes, int chunkNumber, int totalChunks)
{
PublishLargeFileProgress(new LargeFileProgressEventArgs(path, totalTransferredBytes, totalBytes, chunkNumber, totalChunks));
}
protected void PublishLargeFileProgress(LargeFileProgressEventArgs args)
{
if (args == null)
throw new ArgumentNullException("args");
LargeFileProgressProvider?.Report(args);
}
protected void PublishTransferRequest(TransferRequestStatus status)
{
Request.Context?.PublishTransferRequest(Request, status);
}
protected void RegisterIssue(ITransferIssue issue)
{
if (issue != null) {
issues.Add(issue);
if (issue.Path == (TransferPath)null)
PublishTransferJobIssue(issue);
else
PublishTransferFileIssue(issue);
}
}
protected void HandleCancel(TransferPathResult result)
{
if (result == null)
throw new ArgumentNullException("result");
result.Status = TransferPathStatus.Canceled;
DateTime now = DateTime.Now;
if (!result.StartTime.HasValue)
result.StartTime = now;
if (!result.EndTime.HasValue)
result.EndTime = now;
RegisterIssue(new TransferIssue {
Attributes = (IssueAttributes.Canceled | IssueAttributes.Job),
MaxRetryAttempts = Configuration.MaxJobRetryAttempts,
Message = CoreStrings.TransferCancelledMessage,
RetryAttempt = JobService.Statistics.RetryAttempt,
IsRetryable = false,
Timestamp = DateTime.Now
});
Log.LogInformation(CoreStrings.TransferCommandCancellationRequestMessage, Array.Empty<object>());
}
protected IssueAttributes GetWarningErrorAttribute()
{
if (JobService.Statistics.RetryAttempt >= Configuration.MaxJobRetryAttempts)
return IssueAttributes.Error;
return IssueAttributes.Warning;
}
protected virtual void OnLargeFileProgress(LargeFileProgressEventArgs e)
{
if (e == null)
throw new ArgumentNullException("e");
JobTransferPath jobTransferPath = JobService.GetJobTransferPath(e.Path);
if (jobTransferPath != null) {
jobTransferPath.Path.Bytes = e.TotalBytes;
jobTransferPath.BytesTransferred = e.TotalTransferredBytes;
}
Request.Context?.PublishLargeFileProgress(e);
PublishTransferStatistics(false);
}
protected TransferPathResult (TransferPath path, UnauthorizedAccessException exception, TransferPathResult result, int maxRetryAttempts)
{
if (path == (TransferPath)null)
throw new ArgumentNullException("path");
if (exception == null)
throw new ArgumentNullException("exception");
if (result == null)
throw new ArgumentNullException("result");
string message = "Insufficient permissions to transfer files: Error: \"" + exception.Message + "\". Check logs for more details";
string messageTemplate = "The user doesn't have permissions to the {SourcePath} or {TargetPath} path.";
if (FilePermissionErrorsRetry) {
Log.LogWarning(exception, messageTemplate, path.SourcePath, path.TargetPath, LogRedaction.OnPositions(0, 1));
RegisterIssue(new TransferIssue {
Attributes = (IssueAttributes.File | IssueAttributes.ReadWritePermissions | GetWarningErrorAttribute()),
Code = new int?(exception.HResult),
MaxRetryAttempts = maxRetryAttempts,
Message = message,
Path = path,
IsRetryable = true,
RetryAttempt = JobService.Statistics.RetryAttempt,
Timestamp = DateTime.Now
});
result.Status = TransferPathStatus.PermissionsError;
result.EndTime = DateTime.Now;
return result;
}
Log.LogError(exception, messageTemplate, path.SourcePath, path.TargetPath, LogRedaction.OnPositions(0, 1));
RegisterIssue(new TransferIssue {
Attributes = (IssueAttributes.Error | IssueAttributes.File | IssueAttributes.ReadWritePermissions),
Code = new int?(exception.HResult),
MaxRetryAttempts = maxRetryAttempts,
Message = message,
Path = path,
IsRetryable = false,
RetryAttempt = JobService.Statistics.RetryAttempt,
Timestamp = DateTime.Now
});
throw new TransferException(exception.Message, exception, true);
}
}
}