<PackageReference Include="Relativity.Server.Transfer.SDK" Version="24000.0.1" />

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 HandleUnauthorizedAccessException(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); } } }