TapiBridgeBase2
Represents a class object to provide a bridge from the Transfer API to existing Import/Export code.
using Polly;
using Relativity.DataExchange.Io;
using Relativity.DataExchange.Logger;
using Relativity.DataExchange.Resources;
using Relativity.Logging;
using Relativity.Transfer;
using Relativity.Transfer.Aspera;
using Relativity.Transfer.FileShare;
using Relativity.Transfer.Http;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Relativity.DataExchange.Transfer
{
public abstract class TapiBridgeBase2 : ITapiBridge, IDisposable
{
private readonly object syncRoot = new object();
private readonly ITapiObjectService tapiObjectService;
private readonly CancellationToken cancellationToken;
private readonly TransferContext transferContext;
private readonly TapiBridgeParameters2 parameters;
private readonly List<TapiListenerBase> transferListeners = new List<TapiListenerBase>();
private readonly TransferDirection currentDirection;
private readonly TapiTotals jobTotals = new TapiTotals();
private readonly TapiTotals batchTotals = new TapiTotals();
private readonly bool useLegacyWebApi;
private Guid? currentJobId;
private int currentJobNumber;
private double maxInactivitySeconds;
private ITransferRequest jobRequest;
private bool raisedPermissionIssue;
private string raisedPermissionIssueMessage;
private IRelativityTransferHost relativityTransferHost;
private ITransferClient transferClient;
private ITransferJob transferJob;
private DateTime transferActivityTimestamp;
private bool disposed;
public TapiClient Client => tapiObjectService.GetTapiClient(ClientId);
public Guid ClientId => transferClient?.Id ?? Guid.Empty;
public Guid InstanceId { get; }
public TapiTotals JobTotals => jobTotals;
public TapiBridgeParameters2 Parameters => parameters;
public string TargetPath { get; set; }
public bool TransfersPending {
get {
if (TransferJob != null)
return TransferJob.JobService.RequestTransferPathCount > 0;
return false;
}
}
public int WorkspaceId => parameters.WorkspaceId;
protected bool RaisedPermissionIssue {
get {
lock (syncRoot) {
return raisedPermissionIssue;
}
}
private set {
lock (syncRoot) {
raisedPermissionIssue = value;
}
}
}
protected string RaisedPermissionIssueMessage {
get {
lock (syncRoot) {
return raisedPermissionIssueMessage;
}
}
private set {
lock (syncRoot) {
raisedPermissionIssueMessage = value;
}
}
}
protected ITransferJob TransferJob {
get {
lock (syncRoot) {
return transferJob;
}
}
private set {
lock (syncRoot) {
transferJob = value;
}
}
}
protected ILog Logger { get; }
protected ITransferLog TransferLog { get; set; }
private string ClientDisplayName => transferClient?.DisplayName ?? Strings.ClientInitializing;
public event EventHandler<TapiMessageEventArgs> TapiStatusMessage;
public event EventHandler<TapiMessageEventArgs> TapiErrorMessage;
public event EventHandler<TapiMessageEventArgs> TapiWarningMessage;
public event EventHandler<TapiClientEventArgs> TapiClientChanged;
public event EventHandler<TapiProgressEventArgs> TapiProgress;
public event EventHandler<TapiLargeFileProgressEventArgs> TapiLargeFileProgress;
public event EventHandler<TapiStatisticsEventArgs> TapiStatistics;
public event EventHandler<TapiMessageEventArgs> TapiFatalError;
internal TapiBridgeBase2(ITapiObjectService service, TapiBridgeParameters2 parameters, TransferDirection direction, TransferContext context, ILog logger, bool useLegacyWebApi, CancellationToken token)
{
if (service == null)
throw new ArgumentNullException("service");
if (parameters == null)
throw new ArgumentNullException("parameters");
if (parameters.Credentials == null)
throw new ArgumentException("The credentials information must be specified.", "parameters");
if (parameters.WorkspaceId < 1)
throw new ArgumentOutOfRangeException("parameters", Strings.WorkspaceExceptionMessage);
if (parameters.WebCookieContainer == null)
parameters.WebCookieContainer = new CookieContainer();
if (context == null)
context = CreateDefaultTransferContext(parameters);
InstanceId = Guid.NewGuid();
tapiObjectService = service;
currentDirection = direction;
this.parameters = parameters;
TargetPath = parameters.TargetPath;
cancellationToken = token;
Logger = (((object)logger) ?? ((object)new NullLogger()));
TransferLog = new RelativityTransferLog(Logger);
currentJobNumber = 0;
transferContext = context;
SetupTransferListeners();
UpdateAllTransferListenersClientName();
this.useLegacyWebApi = useLegacyWebApi;
}
public string AddPath(TransferPath path)
{
if (path == (TransferPath)null)
throw new ArgumentNullException("path");
ValidateTransferPath(path);
CheckDispose();
CreateTransferJob(false, false);
if (TransferJob != null)
try {
Exception transferException = null;
return RetrySyntax.Retry(Policy.Handle<TransferException>(), 3, (Action<Exception, int>)delegate(Exception exception, int count) {
transferException = exception;
if (TransferJob?.JobService.Statistics != null && TransferJob.JobService.Statistics.JobError)
Logger.LogError(exception, "Failed to add a path to the {TransferJobId} transfer job due to a job-level error. Job error: {JobErrorMessage}", new object[2] {
jobRequest?.JobId,
TransferJob.JobService.Statistics.JobErrorMessage
});
else
Logger.LogError(exception, "Failed to add a path to the {TransferJobId} transfer job.", new object[1] {
jobRequest?.JobId
});
SwitchTapiClientMode(exception, (count >= 3) ? TapiClient.Web : Client);
}).Execute<string>((Func<string>)delegate {
if (transferException == null || !GetIsTransferPathInJobQueue(path))
try {
TransferJob.AddPath(path, cancellationToken);
IncrementTotalFileTransferRequests();
} catch {
if (GetIsTransferPathInJobQueue(path))
IncrementTotalFileTransferRequests();
throw;
}
if (string.IsNullOrEmpty(path.TargetFileName))
return FileSystem.Instance.Path.GetFileName(path.SourcePath);
return path.TargetFileName;
});
} catch (ArgumentException ex) {
Logger.LogWarning((Exception)ex, "There was a problem adding the '{SourceFile}' source file to the {TransferJobId} transfer job.", new object[2] {
path.SourcePath.Secure(),
jobRequest?.JobId
});
throw new FileNotFoundException(ex.Message, path.SourcePath);
} catch (FileNotFoundException ex2) {
Logger.LogWarning((Exception)ex2, "The '{SourceFile}' source file doesn't exist.", new object[1] {
path.SourcePath.Secure()
});
throw;
} catch (OperationCanceledException) {
LogCancelRequest();
return (!string.IsNullOrEmpty(path.TargetFileName)) ? path.TargetFileName : FileSystem.Instance.Path.GetFileName(path.SourcePath);
}
throw new InvalidOperationException(Strings.TransferJobNullExceptionMessage);
}
public virtual void CreateTransferClient()
{
CheckDispose();
if (transferClient == null) {
ClientConfiguration clientConfiguration = CreateClientConfiguration();
maxInactivitySeconds = (double)parameters.MaxInactivitySeconds;
if (maxInactivitySeconds < 0)
maxInactivitySeconds = 1.25 * (double)(parameters.WaitTimeBetweenRetryAttempts * (parameters.MaxJobRetryAttempts + 1));
try {
Guid clientId = tapiObjectService.GetClientId(parameters);
if (clientId != Guid.Empty) {
clientConfiguration.ClientId = clientId;
CreateTransferClient(clientConfiguration);
PublishClientChanged(ClientChangeReason.ForceConfig);
} else {
clientConfiguration.ClientId = Guid.Empty;
TransferClientStrategy val;
if (!string.IsNullOrEmpty(parameters.ForceClientCandidates)) {
val = new TransferClientStrategy(parameters.ForceClientCandidates);
Logger.LogInformation("Overriding the default transfer client strategy. Candidates={ForceClientCandidates}", new object[1] {
parameters.ForceClientCandidates
});
} else {
val = new TransferClientStrategy();
Logger.LogInformation("Using the default transfer client strategy.", Array.Empty<object>());
}
Logger.LogInformation("TAPI client configuration {Configuration}", new object[1] {
clientConfiguration
});
IRelativityTransferHost val2 = CreateTransferHost();
transferClient = val2.CreateClientAsync(clientConfiguration, (ITransferClientStrategy)val, cancellationToken).GetAwaiter().GetResult();
Logger.LogInformation("TAPI created the {Client} client via best-fit strategy.", new object[1] {
transferClient.DisplayName
});
PublishClientChanged(ClientChangeReason.BestFit);
}
} catch (Exception ex) {
Logger.LogError(ex, "The transfer client construction failed.", Array.Empty<object>());
clientConfiguration.ClientId = new Guid("18F9E382-288C-4B6D-8AE8-FBA4D88CD841");
CreateTransferClient(clientConfiguration);
PublishClientChanged(ClientChangeReason.HttpFallback);
} finally {
OptimizeClient();
}
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public virtual void LogTransferParameters()
{
Version version = GetType().Assembly.GetName().Version;
Version version2 = typeof(ITransferClient).Assembly.GetName().Version;
Logger.LogInformation("Import/Export Core - Version: {ImportExportCoreVersion}", new object[1] {
version
});
Logger.LogInformation("TAPI - Version: {TapiVersion}", new object[1] {
version2
});
Logger.LogInformation("Application: {Application}", new object[1] {
parameters.Application
});
Logger.LogInformation("Client request id: {ClientRequestId}", new object[1] {
parameters.ClientRequestId
});
Logger.LogInformation("Aspera doc root level: {AsperaDocRootLevels}", new object[1] {
parameters.AsperaDocRootLevels
});
Logger.LogInformation("Aspera datagram size: {AsperaDatagramSize}", new object[1] {
parameters.AsperaDatagramSize
});
Logger.LogInformation("File share: {FileShare}", new object[1] {
parameters.FileShare.Secure()
});
Logger.LogInformation("Force Aspera client: {ForceAsperaClient}", new object[1] {
parameters.ForceAsperaClient
});
Logger.LogInformation("Force Fileshare client: {ForceFileShareClient}", new object[1] {
parameters.ForceFileShareClient
});
Logger.LogInformation("Force HTTP client: {ForceHttpClient}", new object[1] {
parameters.ForceHttpClient
});
Logger.LogInformation("Force client candidates: {ForceClientCandidates}", new object[1] {
parameters.ForceClientCandidates
});
Logger.LogInformation("HTTP timeout: {HttpTimeoutSeconds} seconds", new object[1] {
parameters.TimeoutSeconds
});
Logger.LogInformation("Max inactivity seconds: {MaxInactivitySeconds}", new object[1] {
parameters.MaxInactivitySeconds
});
Logger.LogInformation("Max job parallelism: {MaxJobParallelism}", new object[1] {
parameters.MaxJobParallelism
});
Logger.LogInformation("Max job retry attempts: {MaxJobRetryAttempts}", new object[1] {
parameters.MaxJobRetryAttempts
});
Logger.LogInformation("Min data rate: {MinDataRateMbps} Mbps", new object[1] {
parameters.MinDataRateMbps
});
Logger.LogInformation("Preserve file timestamps: {PreserveFileTimestamps}", new object[1] {
parameters.PreserveFileTimestamps
});
Logger.LogInformation("Retry on file permission error: {PermissionErrorsRetry}", new object[1] {
parameters.PermissionErrorsRetry
});
Logger.LogInformation("Retry on bad path error: {BadPathErrorsRetry}", new object[1] {
parameters.BadPathErrorsRetry.Secure()
});
Logger.LogInformation("Submit APM metrics: {SubmitApmMetrics}", new object[1] {
parameters.SubmitApmMetrics
});
Logger.LogInformation("Target data rate: {TargetDataRateMbps} Mbps", new object[1] {
parameters.TargetDataRateMbps
});
Logger.LogInformation("Wait time between retry attempts: {WaitTimeBetweenRetryAttempts}", new object[1] {
parameters.WaitTimeBetweenRetryAttempts
});
Logger.LogInformation("Workspace identifier: {WorkspaceId}", new object[1] {
parameters.WorkspaceId
});
}
public TapiTotals WaitForTransfers(string waitMessage, string successMessage, string errorMessage, bool keepJobAlive)
{
CheckDispose();
PublishStatusMessage(waitMessage, 0);
try {
LogTransferTotals("Pre", false);
TapiTotals result = (TransferJob != null && (batchTotals.TotalFileTransferRequests != 0 || jobTotals.TotalFileTransferRequests != 0)) ? (keepJobAlive ? WaitForCompletedTransfers() : WaitForCompletedTransferJob()) : (keepJobAlive ? batchTotals.DeepCopy() : jobTotals.DeepCopy());
LogTransferTotals("Post", true);
PublishStatusMessage(successMessage, 0);
return result;
} catch (Exception ex) when (!ex.IsCanceledByUser(cancellationToken)) {
PublishWarningMessage(errorMessage, 0);
Logger.LogError(ex, errorMessage, Array.Empty<object>());
throw;
}
}
internal void ClearAllTotals()
{
batchTotals.Clear();
jobTotals.Clear();
}
protected static TransferContext CreateDefaultTransferContext(TapiBridgeParameters2 parameters)
{
if (parameters == null)
throw new ArgumentNullException("parameters");
return new TransferContext {
StatisticsRateSeconds = 1,
LargeFileProgressEnabled = true,
LargeFileProgressRateSeconds = 1
};
}
protected virtual ClientConfiguration CreateClientConfiguration()
{
parameters.HttpErrorNumberOfRetries = 1;
ClientConfiguration clientConfiguration = new ClientConfiguration {
BadPathErrorsRetry = parameters.BadPathErrorsRetry,
BcpRootFolder = parameters.AsperaBcpRootFolder,
CookieContainer = parameters.WebCookieContainer,
Credential = parameters.TransferCredential,
FileTransferHint = FileTransferHint.Natives,
FileNotFoundErrorsDisabled = parameters.FileNotFoundErrorsDisabled,
FileNotFoundErrorsRetry = parameters.FileNotFoundErrorsRetry,
HttpTimeoutSeconds = (double)parameters.TimeoutSeconds,
MaxJobParallelism = parameters.MaxJobParallelism,
MaxJobRetryAttempts = parameters.MaxJobRetryAttempts,
MaxHttpRetryAttempts = parameters.HttpErrorNumberOfRetries,
MinDataRateMbps = parameters.MinDataRateMbps,
PermissionErrorsRetry = parameters.PermissionErrorsRetry,
PreserveDates = parameters.PreserveFileTimestamps,
SupportCheckPath = parameters.SupportCheckPath,
TargetDataRateMbps = parameters.TargetDataRateMbps,
TransferLogDirectory = parameters.TransferLogDirectory,
ValidateSourcePaths = false,
SavingMemoryMode = true,
UseLegacyWebApi = useLegacyWebApi
};
if (parameters.AsperaDatagramSize != 0)
clientConfiguration["aspera-datagram-size"] = parameters.AsperaDatagramSize;
return clientConfiguration;
}
protected abstract TransferRequest CreateTransferRequestForJob(TransferContext context);
protected abstract void SetupRemotePathResolvers(ITransferRequest request);
protected abstract string TransferFileFatalMessage();
private static string GetTransferErrorMessage(ITransferResult result)
{
string text = string.Empty;
if (result.TransferError != null)
text = result.TransferError.Message;
if (string.IsNullOrEmpty(text) && result.Issues.Count > 0) {
List<ITransferIssue> list = (from x in result.Issues
orderby x.Index
select x).ToList();
ITransferIssue transferIssue = list.FindLast((ITransferIssue x) => x.Path != (TransferPath)null) ?? list.LastOrDefault();
if (transferIssue != null)
text = transferIssue.Message;
}
if (string.IsNullOrEmpty(text))
text = Strings.TransferJobExceptionMessage;
return text;
}
private void CheckDispose()
{
if (!disposed)
return;
throw new ObjectDisposedException(Strings.ObjectDisposedExceptionMessage);
}
private bool CheckCompletedTransfers()
{
bool flag = batchTotals.TotalFileTransferRequests == batchTotals.TotalCompletedFileTransfers;
if (flag)
Logger.LogInformation("Successfully waited for all {TransferJobId} file transfers to complete.", new object[1] {
jobRequest?.JobId
});
return flag;
}
private bool CheckDataInactivityTimeExceeded()
{
lock (syncRoot) {
DateTime dateTime = transferActivityTimestamp;
bool flag = (DateTime.Now - dateTime).TotalSeconds > maxInactivitySeconds;
if (flag)
Logger.LogInformation("Exceeded the max inactivity time of {MaxInactivitySeconds} seconds since the previous {LastTransferActivityTimestamp} timestamp update for the {TransferJobId} transfer job.", new object[3] {
maxInactivitySeconds,
dateTime,
jobRequest?.JobId
});
return flag;
}
}
private bool CheckAbortOnRaisedPermissionIssues()
{
if (TransferJob == null)
return false;
bool flag = RaisedPermissionIssue && !parameters.PermissionErrorsRetry;
if (flag)
Logger.LogInformation("The transfer job {TransferJobId} will abort because a file permission issue was raised. {Error}", new object[2] {
jobRequest?.JobId,
RaisedPermissionIssueMessage
});
return flag;
}
private bool CheckValidTransferJobStatus()
{
if (TransferJob == null)
return false;
TransferJobStatus status = TransferJob.Status;
bool flag = status == TransferJobStatus.RetryPending || status == TransferJobStatus.Retrying || status == TransferJobStatus.Running || status == TransferJobStatus.Canceled;
if (!flag)
Logger.LogWarning("The transfer job {TransferJobId} status {TransferJobStatus} is neither running or retrying and is considered invalid.", new object[2] {
jobRequest?.JobId,
status
});
return flag;
}
private IRelativityTransferHost CreateTransferHost()
{
if (relativityTransferHost == null) {
GlobalSettings.Instance.StatisticsLogEnabled = false;
RelativityConnectionInfo connectionInfo = tapiObjectService.CreateRelativityConnectionInfo(parameters);
relativityTransferHost = tapiObjectService.CreateRelativityTransferHost(connectionInfo, Logger);
}
return relativityTransferHost;
}
private void CreateTransferClient(ClientConfiguration configuration)
{
Logger.LogInformation("TAPI client configuration {Configuration}", new object[1] {
configuration
});
IRelativityTransferHost val = CreateTransferHost();
val.Clear();
transferClient = val.CreateClient(configuration);
UpdateAllTransferListenersClientName();
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Never fail due to retrieving process info.")]
private void CreateTransferJob(bool webModeSwitch, bool isRetry)
{
CheckDispose();
if (TransferJob == null) {
Guid guid = Guid.NewGuid();
Logger.LogInformation("Create job {jobId} started...", new object[1] {
guid
});
CreateTransferClient();
lock (syncRoot) {
transferActivityTimestamp = DateTime.Now;
}
if (!isRetry)
ClearAllTotals();
currentJobNumber++;
currentJobId = guid;
RaisedPermissionIssue = false;
RaisedPermissionIssueMessage = null;
jobRequest = CreateTransferRequestForJob(transferContext);
jobRequest.Application = parameters.Application;
if (string.IsNullOrEmpty(jobRequest.Application))
try {
string fileName = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
jobRequest.Application = Path.GetFileName(fileName);
} catch (Exception) {
jobRequest.Application = System.Diagnostics.Process.GetCurrentProcess().ProcessName;
}
jobRequest.ClientRequestId = parameters.ClientRequestId;
jobRequest.JobId = currentJobId;
jobRequest.Tag = currentJobNumber;
jobRequest.Name = $"""{ClientId}""{currentJobNumber:""}";
jobRequest.RetryStrategy = RetryStrategies.CreateFixedTimeStrategy((double)parameters.WaitTimeBetweenRetryAttempts);
SetupRemotePathResolvers(jobRequest);
jobRequest.SubmitApmMetrics = parameters.SubmitApmMetrics;
try {
Task<ITransferJob> task = transferClient.CreateJobAsync(jobRequest, cancellationToken);
TransferJob = task.GetAwaiter().GetResult();
Logger.LogInformation("Create job ended. {JobId}", new object[1] {
jobRequest.JobId
});
} catch (OperationCanceledException) {
LogCancelRequest();
DestroyTransferJob();
throw;
} catch (Exception ex3) {
Logger.LogError(ex3, "Failed to create the transfer job.", Array.Empty<object>());
if (webModeSwitch)
throw;
SwitchTapiClientMode(ex3, TapiClient.Web);
}
}
}
private void CreateTransferClient<T>() where T : ClientConfiguration, new
{
T val = new T();
val.TransferLogDirectory = parameters.TransferLogDirectory;
val.BadPathErrorsRetry = parameters.BadPathErrorsRetry;
val.CookieContainer = parameters.WebCookieContainer;
val.FileNotFoundErrorsDisabled = parameters.FileNotFoundErrorsDisabled;
val.FileNotFoundErrorsRetry = parameters.FileNotFoundErrorsRetry;
val.MaxJobParallelism = parameters.MaxJobParallelism;
val.MaxJobRetryAttempts = parameters.MaxJobRetryAttempts;
val.MaxHttpRetryAttempts = parameters.HttpErrorNumberOfRetries;
val.PreserveDates = parameters.PreserveFileTimestamps;
val.PermissionErrorsRetry = parameters.PermissionErrorsRetry;
val.SavingMemoryMode = true;
val.UseLegacyWebApi = useLegacyWebApi;
CreateTransferClient(val);
}
private void CreateJobRetryListener()
{
transferListeners.Add(new TapiJobRetryListener(Logger, parameters.MaxJobRetryAttempts, transferContext));
}
private void CreatePathIssueListener()
{
transferListeners.Add(new TapiPathIssueListener(Logger, currentDirection, transferContext));
transferContext.TransferPathIssue += OnTransferPathIssue;
}
private void CreatePathProgressListener()
{
TapiPathProgressListener tapiPathProgressListener = new TapiPathProgressListener(Logger, transferContext);
tapiPathProgressListener.ProgressEvent += delegate(object sender, TapiProgressEventArgs args) {
if (args.Completed) {
batchTotals.IncrementTotalCompletedFileTransfers();
jobTotals.IncrementTotalCompletedFileTransfers();
}
if (args.Successful) {
batchTotals.IncrementTotalSuccessfulFileTransfers();
jobTotals.IncrementTotalSuccessfulFileTransfers();
}
UpdateTransferActivityTimestamp();
this.TapiProgress?.Invoke(sender, args);
};
tapiPathProgressListener.LargeFileProgressEvent += delegate(object sender, TapiLargeFileProgressEventArgs args) {
UpdateTransferActivityTimestamp();
this.TapiLargeFileProgress?.Invoke(sender, args);
};
transferListeners.Add(tapiPathProgressListener);
}
private void CreateRequestListener()
{
transferListeners.Add(new TapiRequestListener(Logger, transferContext));
}
private void CreateStatisticsListener()
{
TapiStatisticsListener tapiStatisticsListener = new TapiStatisticsListener(Logger, transferContext);
tapiStatisticsListener.StatisticsEvent += delegate(object sender, TapiStatisticsEventArgs args) {
this.TapiStatistics?.Invoke(sender, args);
};
transferListeners.Add(tapiStatisticsListener);
}
private void DestroyTransferClient()
{
if (transferClient != null) {
transferClient.Dispose();
transferClient = null;
UpdateAllTransferListenersClientName();
}
}
private void DestroyTransferHost()
{
if (relativityTransferHost != null) {
((IDisposable)relativityTransferHost).Dispose();
relativityTransferHost = null;
}
}
private void DestroyTransferJob()
{
currentJobId = null;
if (TransferJob != null) {
TransferJob.Dispose();
TransferJob = null;
}
}
private void DestroyTransferLog()
{
if (TransferLog != null) {
TransferLog.Dispose();
TransferLog = null;
}
}
private void DestroyTransferListeners()
{
if (transferListeners != null) {
foreach (TapiListenerBase transferListener in transferListeners) {
transferListener.Dispose();
}
transferListeners.Clear();
transferContext.TransferPathIssue -= OnTransferPathIssue;
}
}
private void Dispose(bool disposing)
{
if (!disposed) {
if (disposing) {
DestroyTransferJob();
DestroyTransferLog();
DestroyTransferClient();
DestroyTransferHost();
DestroyTransferListeners();
}
disposed = true;
}
}
private void SwitchTapiClientMode(Exception exception, TapiClient tapiClient)
{
Logger.LogWarning("Switching Tapi Client mode to {tapiClient}. Retrying in the original transfer mode value: {RetryInTheOriginalTransferMode}. With exception: {exception}", new object[3] {
tapiClient,
AppSettings.Instance.RetryInTheOriginalTransferMode,
exception
});
if (!AppSettings.Instance.RetryInTheOriginalTransferMode)
tapiClient = TapiClient.Web;
if (RaisedPermissionIssue) {
Logger.LogError("Destroy transfer job {currentJobId} because of RaisedPermissionIssue=True", new object[1] {
currentJobId
});
DestroyTransferJob();
throw new TransferException(string.Format(CultureInfo.CurrentCulture, Strings.WebModeFallbackPermissionsFatalExceptionMessage, RaisedPermissionIssueMessage), exception, true);
}
if (transferClient?.Id == (Guid?)new Guid("18F9E382-288C-4B6D-8AE8-FBA4D88CD841")) {
Logger.LogError("Destroy transfer job {currentJobId} because it was already WebMode", new object[1] {
currentJobId
});
DestroyTransferJob();
throw new TransferException(Strings.WebModeFallbackAlreadyWebModeFatalExceptionMessage, exception, true);
}
string message = string.Format(CultureInfo.CurrentCulture, Strings.WebModeFallbackNoErrorWarningMessage, ClientDisplayName);
if (tapiClient != TapiClient.Web)
message = string.Format(CultureInfo.CurrentCulture, Strings.TransferFallbackWarningMessage, tapiClient, ClientDisplayName, (exception == null) ? "(null)" : exception.Message);
else if (exception != null) {
message = string.Format(CultureInfo.CurrentCulture, Strings.WebModeFallbackWarningMessage, ClientDisplayName, exception.Message);
}
PublishWarningMessage(message, 0);
IList<TransferPath> retryableTransferPaths = GetRetryableTransferPaths();
DestroyTransferJob();
DestroyTransferClient();
CreateFallbackTapiClient(tapiClient);
CreateTransferJob(tapiClient == TapiClient.Web, true);
if (retryableTransferPaths.Count == 0)
Logger.LogInformation("No retryable paths exist.", Array.Empty<object>());
else {
Logger.LogInformation("Adding {count} retryable paths.", new object[1] {
retryableTransferPaths.Count
});
foreach (TransferPath item in retryableTransferPaths) {
item.RevertPaths();
TransferJob.AddPath(item, cancellationToken);
}
}
}
private void CreateFallbackTapiClient(TapiClient tapiClient)
{
switch (tapiClient) {
case TapiClient.Aspera:
this.CreateTransferClient<AsperaClientConfiguration>();
PublishClientChanged(ClientChangeReason.HttpFallback);
break;
case TapiClient.Direct:
this.CreateTransferClient<FileShareClientConfiguration>();
PublishClientChanged(ClientChangeReason.HttpFallback);
break;
default:
this.CreateTransferClient<HttpClientConfiguration>();
PublishClientChanged(ClientChangeReason.HttpFallback);
break;
}
OptimizeClient();
Logger.LogInformation("Successfully switched to {tapiClient} mode.", new object[1] {
tapiClient
});
}
private bool GetIsTransferPathInJobQueue(TransferPath path)
{
if (TransferJob == null)
return false;
return TransferJob.JobService.GetJobTransferPath(path) != null;
}
private IList<TransferPath> GetRetryableTransferPaths()
{
List<TransferPath> paths = new List<TransferPath>();
if (TransferJob != null) {
IReadOnlyCollection<TransferPath> retryableRequestTransferPaths = TransferJob.JobService.GetRetryableRequestTransferPaths();
paths.AddRange(retryableRequestTransferPaths);
Logger.LogInformation("Number of retryable paths from tapi: {paths}", new object[1] {
retryableRequestTransferPaths.Count
});
TransferPath[] array = (from jobPath in TransferJob.JobService.GetJobTransferPaths().Where(delegate(JobTransferPath x) {
if (x.Status == TransferPathStatus.Fatal)
return !paths.Contains(x.Path);
return false;
})
select jobPath.Path).ToArray();
Logger.LogInformation("Number of failed Fatal paths from tapi: {paths}", new object[1] {
array.Length
});
paths.AddRange(array);
} else
Logger.LogWarning("Trying to get retryable paths but TransferJob is null", Array.Empty<object>());
Logger.LogInformation("Total number of retryable paths: {TotalRetryablePaths:n0}", new object[1] {
paths.Count
});
Logger.LogInformation("Total number of retryable bytes: {TotalRetryableBytes:n0}", new object[1] {
paths.Sum((TransferPath x) => x.Bytes)
});
return paths;
}
private void IncrementTotalFileTransferRequests()
{
batchTotals.IncrementTotalFileTransferRequests();
jobTotals.IncrementTotalFileTransferRequests();
}
private void LogCancelRequest()
{
Logger.LogInformation("The file transfer has been cancelled. ClientId={ClientId}, JobId={JobId} ", new object[2] {
parameters.ClientRequestId,
jobRequest?.JobId
});
}
private void LogTransferTotals(string prefix, bool completed)
{
StringBuilder stringBuilder = new StringBuilder("WaitForTransfers-" + prefix + ": ");
if (!completed) {
stringBuilder.Append("Awaiting {TotalFileTransferRequests:n0} transfer files from {TransferJobId} using {TransferMode} mode.");
Logger.LogInformation(stringBuilder.ToString(), new object[3] {
jobTotals.TotalFileTransferRequests,
jobRequest?.JobId,
ClientDisplayName
});
} else {
stringBuilder.Append("Completed {TotalSuccessfulFileTransfers:n0} of {TotalFileTransferRequests:n0} transfer files from {TransferJobId} using {TransferMode} mode.");
Logger.LogInformation(stringBuilder.ToString(), new object[4] {
jobTotals.TotalSuccessfulFileTransfers,
jobTotals.TotalFileTransferRequests,
jobRequest?.JobId,
ClientDisplayName
});
if (jobTotals.TotalFileTransferRequests == 0)
Logger.LogWarning("Although the {TransferJobId} transfer job completed, the total number of file requests is zero and may suggest a logic issue or unexpected result.", new object[1] {
jobRequest?.JobId
});
}
}
private void OnTransferPathIssue(object sender, TransferPathIssueEventArgs e)
{
if (e.Issue.Attributes.HasFlag(IssueAttributes.ReadWritePermissions)) {
RaisedPermissionIssue = true;
RaisedPermissionIssueMessage = e.Issue.Message;
}
}
private void OptimizeClient()
{
if (transferClient != null) {
string a = transferClient.Id.ToString().ToUpperInvariant();
if (!(a == "0315D6C7-FF07-41E2-9C25-16573FC6B9DE")) {
if (!(a == "18F9E382-288C-4B6D-8AE8-FBA4D88CD841")) {
if (a == "812A70A5-7311-46CC-BE53-07BB8C5F9A7D")
transferClient.Configuration.MaxJobParallelism = 1;
} else
transferClient.Configuration.MaxJobParallelism = 1;
}
}
}
private void PublishClientChanged(ClientChangeReason reason)
{
CheckDispose();
string message;
switch (reason) {
case ClientChangeReason.BestFit:
message = string.Format(CultureInfo.CurrentCulture, Strings.TransferClientChangedBestFitMessage, ClientDisplayName);
break;
case ClientChangeReason.ForceConfig:
message = string.Format(CultureInfo.CurrentCulture, Strings.TransferClientChangedForceConfigMessage, ClientDisplayName);
break;
case ClientChangeReason.HttpFallback:
message = string.Format(CultureInfo.CurrentCulture, Strings.TransferClientChangedHttpFallbackMessage, ClientDisplayName);
break;
default:
message = string.Format(CultureInfo.CurrentCulture, Strings.TransferClientChangedDefaultMessage, ClientDisplayName);
break;
}
PublishStatusMessage(message, 0);
TapiClientEventArgs e = new TapiClientEventArgs(InstanceId, ClientDisplayName, Client);
this.TapiClientChanged?.Invoke(this, e);
UpdateAllTransferListenersClientName();
}
private void PublishFatalError(string message, int lineNumber)
{
CheckDispose();
this.TapiFatalError?.Invoke(this, new TapiMessageEventArgs(message, lineNumber));
}
private void PublishStatusMessage(string message, int lineNumber)
{
CheckDispose();
this.TapiStatusMessage?.Invoke(this, new TapiMessageEventArgs(message, lineNumber));
}
private void PublishWarningMessage(string message, int lineNumber)
{
CheckDispose();
this.TapiWarningMessage?.Invoke(this, new TapiMessageEventArgs(message, lineNumber));
}
private void SetupTransferListeners()
{
CreatePathProgressListener();
CreatePathIssueListener();
CreateRequestListener();
CreateJobRetryListener();
CreateStatisticsListener();
foreach (TapiListenerBase transferListener in transferListeners) {
transferListener.ErrorMessage += delegate(object sender, TapiMessageEventArgs args) {
this.TapiErrorMessage?.Invoke(sender, args);
};
transferListener.FatalError += delegate(object sender, TapiMessageEventArgs args) {
this.TapiFatalError?.Invoke(sender, args);
};
transferListener.StatusMessage += delegate(object sender, TapiMessageEventArgs args) {
this.TapiStatusMessage?.Invoke(sender, args);
};
transferListener.WarningMessage += delegate(object sender, TapiMessageEventArgs args) {
this.TapiWarningMessage?.Invoke(sender, args);
};
}
}
private void UpdateAllTransferListenersClientName()
{
if (!disposed) {
string clientDisplayName = ClientDisplayName;
foreach (TapiListenerBase transferListener in transferListeners) {
transferListener.ClientDisplayName = clientDisplayName;
}
}
}
private void UpdateTransferActivityTimestamp()
{
lock (syncRoot) {
transferActivityTimestamp = DateTime.Now;
}
}
private void ValidateTransferPath(TransferPath path)
{
if (string.IsNullOrWhiteSpace(path.SourcePath) && (!path.SourcePathId.HasValue || path.SourcePathId.Value < 1))
throw new ArgumentException((currentDirection == TransferDirection.Download || path.Direction == TransferDirection.Download) ? Strings.TransferPathArgumentDownloadSourcePathExceptionMessage : Strings.TransferPathArgumentUploadSourcePathExceptionMessage, "path");
if (string.IsNullOrWhiteSpace(path.TargetPath) && string.IsNullOrWhiteSpace(TargetPath))
throw new ArgumentException((currentDirection == TransferDirection.Download || path.Direction == TransferDirection.Download) ? Strings.TransferPathArgumentDownloadTargetPathExceptionMessage : Strings.TransferPathArgumentUploadTargetPathExceptionMessage, "path");
}
private TapiTotals WaitForCompletedTransfers()
{
Logger.LogInformation("Preparing to wait for the {TransferJobId} batched file transfers to complete...", new object[1] {
jobRequest?.JobId
});
try {
int originalClientModeRetryCounter = 0;
RetryTResultSyntax.WaitAndRetryForever<bool>(Policy.HandleResult<bool>(false), (Func<int, TimeSpan>)((int i) => TimeSpan.FromMilliseconds(250)), (Action<DelegateResult<bool>, TimeSpan>)delegate {
originalClientModeRetryCounter++;
}).Execute((Func<bool>)delegate {
try {
if (!CheckValidTransferJobStatus() && originalClientModeRetryCounter < 2)
SwitchTapiClientMode(null, Client);
cancellationToken.ThrowIfCancellationRequested();
return CheckCompletedTransfers() || CheckDataInactivityTimeExceeded() || CheckAbortOnRaisedPermissionIssues();
} catch (OperationCanceledException) {
LogCancelRequest();
DestroyTransferJob();
throw;
} catch (Exception ex2) {
if (ExceptionHelper.IsFatalException(ex2))
throw;
Logger.LogError(ex2, "An exception was thrown waiting for the {TransferJobId} file transfers to complete.", new object[1] {
jobRequest?.JobId
});
SwitchTapiClientMode(ex2, TapiClient.Web);
return true;
}
});
if (!CheckCompletedTransfers()) {
Logger.LogWarning("WaitForCompletedTransfers has exited, not all {TransferJobId} file transfers have completed, and now going to wait for the transfer job to complete.", new object[1] {
jobRequest?.JobId
});
LogTransferTotals("Inactivity", false);
WaitForCompletedTransferJob();
}
return batchTotals.DeepCopy();
} finally {
batchTotals.Clear();
}
}
private TapiTotals WaitForCompletedTransferJob()
{
if (TransferJob != null) {
Logger.LogInformation("Preparing to wait for transfer job {TransferJobId} to complete...", new object[1] {
jobRequest?.JobId
});
try {
Exception handledException = null;
RetrySyntax.Retry(Policy.Handle<TransferException>(), 2, (Action<Exception, int>)delegate(Exception exception, int count) {
handledException = exception;
Logger.LogWarning(exception, "An unexpected error has occurred attempting to wait for transfer job {TransferJobId} to complete.", new object[1] {
jobRequest?.JobId
});
SwitchTapiClientMode(exception, (count >= 2) ? TapiClient.Web : Client);
}).Execute((Action)delegate {
ITransferResult result = TransferJob.CompleteAsync(cancellationToken).GetAwaiter().GetResult();
Logger.LogInformation("Transfer job {TransferJobId} completed. {Name} transfer status: {Status}, elapsed time: {Elapsed}, data rate: {TransferRate:0.00} Mbps", new object[5] {
jobRequest?.JobId,
ClientDisplayName,
result.Status,
result.Elapsed,
result.TransferRateMbps
});
IssueAttributes missingFilesFlag = IssueAttributes.FileNotFound | IssueAttributes.Io | IssueAttributes.Warning;
if (result.Status == TransferStatus.Fatal || (result.Status == TransferStatus.Failed && (result.Issues.Count == 0 || result.Issues.Any((ITransferIssue x) => !x.Attributes.HasFlag(missingFilesFlag))))) {
LogTransferTotals("NotSuccessful", true);
string transferErrorMessage = GetTransferErrorMessage(result);
PublishStatusMessage(transferErrorMessage, 0);
if (handledException == null)
throw new TransferException(transferErrorMessage);
PublishFatalError(transferErrorMessage, 0);
}
});
return jobTotals.DeepCopy();
} catch (OperationCanceledException) {
LogCancelRequest();
throw;
} finally {
DestroyTransferJob();
}
}
return jobTotals.DeepCopy();
}
}
}