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 {
get {
ITransferClient obj = transferClient;
if (obj == null)
return Guid.Empty;
return obj.get_Id();
}
}
public Guid InstanceId { get; }
public TapiTotals JobTotals => jobTotals;
public TapiBridgeParameters2 Parameters => parameters;
public string TargetPath { get; set; }
public bool TransfersPending {
get {
if ((object)TransferJob != null)
return TransferJob.get_JobService().get_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 {
get {
ITransferClient obj = transferClient;
return ((obj != null) ? obj.get_DisplayName() : null) ?? 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 == null)
throw new ArgumentNullException("path");
ValidateTransferPath(path);
CheckDispose();
CreateTransferJob(false, false);
if ((object)TransferJob != null)
try {
Exception transferException = null;
return RetrySyntax.Retry(Policy.Handle<TransferException>(), 3, (Action<Exception, int>)delegate(Exception exception, int count) {
transferException = exception;
object obj4 = (object)TransferJob;
if ((object)((obj4 != null) ? obj4.get_JobService().get_Statistics() : null) != null && TransferJob.get_JobService().get_Statistics().get_JobError()) {
object logger2 = (object)Logger;
object[] obj5 = new object[2];
ITransferRequest obj6 = jobRequest;
obj5[0] = ((obj6 != null) ? obj6.get_JobId() : null);
obj5[1] = TransferJob.get_JobService().get_Statistics().get_JobErrorMessage();
logger2.LogError(exception, "Failed to add a path to the {TransferJobId} transfer job due to a job-level error. Job error: {JobErrorMessage}", obj5);
} else {
object logger3 = (object)Logger;
object[] obj7 = new object[1];
ITransferRequest obj8 = jobRequest;
obj7[0] = ((obj8 != null) ? obj8.get_JobId() : null);
logger3.LogError(exception, "Failed to add a path to the {TransferJobId} transfer job.", obj7);
}
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.get_TargetFileName()))
return FileSystem.Instance.Path.GetFileName(path.get_SourcePath());
return path.get_TargetFileName();
});
} catch (ArgumentException ex) {
object logger = (object)Logger;
ArgumentException ex2 = ex;
object[] obj = new object[2] {
path.get_SourcePath().Secure(),
null
};
ITransferRequest obj2 = jobRequest;
obj[1] = ((obj2 != null) ? obj2.get_JobId() : null);
logger.LogWarning((Exception)ex2, "There was a problem adding the '{SourceFile}' source file to the {TransferJobId} transfer job.", obj);
throw new FileNotFoundException(ex.Message, path.get_SourcePath());
} catch (FileNotFoundException ex3) {
Logger.LogWarning((Exception)ex3, "The '{SourceFile}' source file doesn't exist.", new object[1] {
path.get_SourcePath().Secure()
});
throw;
} catch (OperationCanceledException) {
LogCancelRequest();
return (!string.IsNullOrEmpty(path.get_TargetFileName())) ? path.get_TargetFileName() : FileSystem.Instance.Path.GetFileName(path.get_SourcePath());
}
throw new InvalidOperationException(Strings.TransferJobNullExceptionMessage);
}
public virtual void CreateTransferClient()
{
CheckDispose();
if (transferClient == null) {
ClientConfiguration val = 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) {
val.set_ClientId(clientId);
CreateTransferClient(val);
PublishClientChanged(ClientChangeReason.ForceConfig);
} else {
val.set_ClientId(Guid.Empty);
TransferClientStrategy val2;
if (!string.IsNullOrEmpty(parameters.ForceClientCandidates)) {
val2 = new TransferClientStrategy(parameters.ForceClientCandidates);
Logger.LogInformation("Overriding the default transfer client strategy. Candidates={ForceClientCandidates}", new object[1] {
parameters.ForceClientCandidates
});
} else {
val2 = new TransferClientStrategy();
Logger.LogInformation("Using the default transfer client strategy.", Array.Empty<object>());
}
Logger.LogInformation("TAPI client configuration {Configuration}", new object[1] {
val
});
IRelativityTransferHost val3 = CreateTransferHost();
transferClient = val3.CreateClientAsync(val, val2, cancellationToken).GetAwaiter().GetResult();
Logger.LogInformation("TAPI created the {Client} client via best-fit strategy.", new object[1] {
transferClient.get_DisplayName()
});
PublishClientChanged(ClientChangeReason.BestFit);
}
} catch (Exception ex) {
Logger.LogError(ex, "The transfer client construction failed.", Array.Empty<object>());
val.set_ClientId(new Guid("18F9E382-288C-4B6D-8AE8-FBA4D88CD841"));
CreateTransferClient(val);
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 = ((object)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");
TransferContext val = new TransferContext();
val.set_StatisticsRateSeconds(1);
val.set_LargeFileProgressEnabled(true);
val.set_LargeFileProgressRateSeconds(1);
return val;
}
protected virtual ClientConfiguration CreateClientConfiguration()
{
parameters.HttpErrorNumberOfRetries = 1;
ClientConfiguration val = new ClientConfiguration();
val.set_BadPathErrorsRetry(parameters.BadPathErrorsRetry);
val.set_BcpRootFolder(parameters.AsperaBcpRootFolder);
val.set_CookieContainer(parameters.WebCookieContainer);
val.set_Credential(parameters.TransferCredential);
val.set_FileTransferHint(0);
val.set_FileNotFoundErrorsDisabled(parameters.FileNotFoundErrorsDisabled);
val.set_FileNotFoundErrorsRetry(parameters.FileNotFoundErrorsRetry);
val.set_HttpTimeoutSeconds((double)parameters.TimeoutSeconds);
val.set_MaxJobParallelism(parameters.MaxJobParallelism);
val.set_MaxJobRetryAttempts(parameters.MaxJobRetryAttempts);
val.set_MaxHttpRetryAttempts(parameters.HttpErrorNumberOfRetries);
val.set_MinDataRateMbps(parameters.MinDataRateMbps);
val.set_PermissionErrorsRetry(parameters.PermissionErrorsRetry);
val.set_PreserveDates(parameters.PreserveFileTimestamps);
val.set_SupportCheckPath(parameters.SupportCheckPath);
val.set_TargetDataRateMbps(parameters.TargetDataRateMbps);
val.set_TransferLogDirectory(parameters.TransferLogDirectory);
val.set_ValidateSourcePaths(false);
val.set_SavingMemoryMode(true);
val.set_UseLegacyWebApi(useLegacyWebApi);
ClientConfiguration val2 = val;
if (parameters.AsperaDatagramSize != 0)
((Dictionary<string, object>)val2)["aspera-datagram-size"] = parameters.AsperaDatagramSize;
return val2;
}
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 ((int)result.get_TransferError() != 0)
text = result.get_TransferError().get_Message();
if (string.IsNullOrEmpty(text) && result.get_Issues().Count > 0) {
List<ITransferIssue> list = (from x in result.get_Issues()
orderby x.get_Index()
select x).ToList();
ITransferIssue val = list.FindLast((ITransferIssue x) => x.get_Path() != null) ?? list.LastOrDefault();
if (val != null)
text = val.get_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) {
object logger = (object)Logger;
object[] obj = new object[1];
ITransferRequest obj2 = jobRequest;
obj[0] = ((obj2 != null) ? obj2.get_JobId() : null);
logger.LogInformation("Successfully waited for all {TransferJobId} file transfers to complete.", obj);
}
return flag;
}
private bool CheckDataInactivityTimeExceeded()
{
lock (syncRoot) {
DateTime dateTime = transferActivityTimestamp;
bool flag = (DateTime.Now - dateTime).TotalSeconds > maxInactivitySeconds;
if (flag) {
object logger = (object)Logger;
object[] obj = new object[3] {
maxInactivitySeconds,
dateTime,
null
};
ITransferRequest obj2 = jobRequest;
obj[2] = ((obj2 != null) ? obj2.get_JobId() : null);
logger.LogInformation("Exceeded the max inactivity time of {MaxInactivitySeconds} seconds since the previous {LastTransferActivityTimestamp} timestamp update for the {TransferJobId} transfer job.", obj);
}
return flag;
}
}
private bool CheckAbortOnRaisedPermissionIssues()
{
if ((object)TransferJob == null)
return false;
bool flag = RaisedPermissionIssue && !parameters.PermissionErrorsRetry;
if (flag) {
object logger = (object)Logger;
object[] obj = new object[2];
ITransferRequest obj2 = jobRequest;
obj[0] = ((obj2 != null) ? obj2.get_JobId() : null);
obj[1] = RaisedPermissionIssueMessage;
logger.LogInformation("The transfer job {TransferJobId} will abort because a file permission issue was raised. {Error}", obj);
}
return flag;
}
private bool CheckValidTransferJobStatus()
{
if ((object)TransferJob == null)
return false;
TransferJobStatus status = TransferJob.get_Status();
bool flag = (int)status == 3 || (int)status == 4 || (int)status == 2 || (int)status == 8;
if (!flag) {
object logger = (object)Logger;
object[] obj = new object[2];
ITransferRequest obj2 = jobRequest;
obj[0] = ((obj2 != null) ? obj2.get_JobId() : null);
obj[1] = status;
logger.LogWarning("The transfer job {TransferJobId} status {TransferJobStatus} is neither running or retrying and is considered invalid.", obj);
}
return flag;
}
private IRelativityTransferHost CreateTransferHost()
{
if (relativityTransferHost == null) {
GlobalSettings.get_Instance().set_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 ((object)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.set_Application(parameters.Application);
if (string.IsNullOrEmpty(jobRequest.get_Application()))
try {
string fileName = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
jobRequest.set_Application(Path.GetFileName(fileName));
} catch (Exception) {
jobRequest.set_Application(System.Diagnostics.Process.GetCurrentProcess().ProcessName);
}
jobRequest.set_ClientRequestId((Guid?)parameters.ClientRequestId);
jobRequest.set_JobId(currentJobId);
jobRequest.set_Tag((object)currentJobNumber);
jobRequest.set_Name($"""{ClientId}""{currentJobNumber:""}");
jobRequest.set_RetryStrategy(RetryStrategies.CreateFixedTimeStrategy((double)parameters.WaitTimeBetweenRetryAttempts));
SetupRemotePathResolvers(jobRequest);
jobRequest.set_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.get_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.set_TransferLogDirectory(parameters.TransferLogDirectory);
val.set_BadPathErrorsRetry(parameters.BadPathErrorsRetry);
val.set_CookieContainer(parameters.WebCookieContainer);
val.set_FileNotFoundErrorsDisabled(parameters.FileNotFoundErrorsDisabled);
val.set_FileNotFoundErrorsRetry(parameters.FileNotFoundErrorsRetry);
val.set_MaxJobParallelism(parameters.MaxJobParallelism);
val.set_MaxJobRetryAttempts(parameters.MaxJobRetryAttempts);
val.set_MaxHttpRetryAttempts(parameters.HttpErrorNumberOfRetries);
val.set_PreserveDates(parameters.PreserveFileTimestamps);
val.set_PermissionErrorsRetry(parameters.PermissionErrorsRetry);
val.set_SavingMemoryMode(true);
val.set_UseLegacyWebApi(useLegacyWebApi);
CreateTransferClient((object)val);
}
private void CreateJobRetryListener()
{
transferListeners.Add(new TapiJobRetryListener(Logger, parameters.MaxJobRetryAttempts, transferContext));
}
private void CreatePathIssueListener()
{
transferListeners.Add(new TapiPathIssueListener(Logger, currentDirection, transferContext));
transferContext.add_TransferPathIssue((EventHandler<TransferPathIssueEventArgs>)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) {
((IDisposable)transferClient).Dispose();
transferClient = null;
UpdateAllTransferListenersClientName();
}
}
private void DestroyTransferHost()
{
if (relativityTransferHost != null) {
((IDisposable)relativityTransferHost).Dispose();
relativityTransferHost = null;
}
}
private void DestroyTransferJob()
{
currentJobId = null;
if ((object)TransferJob != null) {
((IDisposable)TransferJob).Dispose();
TransferJob = null;
}
}
private void DestroyTransferLog()
{
if ((object)TransferLog != null) {
((IDisposable)TransferLog).Dispose();
TransferLog = null;
}
}
private void DestroyTransferListeners()
{
if (transferListeners != null) {
foreach (TapiListenerBase transferListener in transferListeners) {
transferListener.Dispose();
}
transferListeners.Clear();
transferContext.remove_TransferPathIssue((EventHandler<TransferPathIssueEventArgs>)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);
}
ITransferClient obj = transferClient;
if (((obj != null) ? new Guid?(obj.get_Id()) : null) == (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 ((object)TransferJob == null)
return false;
return (object)TransferJob.get_JobService().GetJobTransferPath(path) != null;
}
private IList<TransferPath> GetRetryableTransferPaths()
{
List<TransferPath> paths = new List<TransferPath>();
if ((object)TransferJob != null) {
IReadOnlyCollection<TransferPath> retryableRequestTransferPaths = TransferJob.get_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.get_JobService().GetJobTransferPaths().Where(delegate(JobTransferPath x) {
if ((int)x.get_Status() == 2)
return !paths.Contains(x.get_Path());
return false;
})
select jobPath.get_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.get_Bytes())
});
return paths;
}
private void IncrementTotalFileTransferRequests()
{
batchTotals.IncrementTotalFileTransferRequests();
jobTotals.IncrementTotalFileTransferRequests();
}
private void LogCancelRequest()
{
object logger = (object)Logger;
object[] obj = new object[2] {
parameters.ClientRequestId,
null
};
ITransferRequest obj2 = jobRequest;
obj[1] = ((obj2 != null) ? obj2.get_JobId() : null);
logger.LogInformation("The file transfer has been cancelled. ClientId={ClientId}, JobId={JobId} ", obj);
}
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.");
object logger = (object)Logger;
string text = stringBuilder.ToString();
object[] obj = new object[3] {
jobTotals.TotalFileTransferRequests,
null,
null
};
ITransferRequest obj2 = jobRequest;
obj[1] = ((obj2 != null) ? obj2.get_JobId() : null);
obj[2] = ClientDisplayName;
logger.LogInformation(text, obj);
} else {
stringBuilder.Append("Completed {TotalSuccessfulFileTransfers:n0} of {TotalFileTransferRequests:n0} transfer files from {TransferJobId} using {TransferMode} mode.");
object logger2 = (object)Logger;
string text2 = stringBuilder.ToString();
object[] obj3 = new object[4] {
jobTotals.TotalSuccessfulFileTransfers,
jobTotals.TotalFileTransferRequests,
null,
null
};
ITransferRequest obj4 = jobRequest;
obj3[2] = ((obj4 != null) ? obj4.get_JobId() : null);
obj3[3] = ClientDisplayName;
logger2.LogInformation(text2, obj3);
if (jobTotals.TotalFileTransferRequests == 0) {
object logger3 = (object)Logger;
object[] obj5 = new object[1];
ITransferRequest obj6 = jobRequest;
obj5[0] = ((obj6 != null) ? obj6.get_JobId() : null);
logger3.LogWarning("Although the {TransferJobId} transfer job completed, the total number of file requests is zero and may suggest a logic issue or unexpected result.", obj5);
}
}
}
private void OnTransferPathIssue(object sender, TransferPathIssueEventArgs e)
{
if (((Enum)e.get_Issue().get_Attributes()).HasFlag((Enum)(object)8192)) {
RaisedPermissionIssue = true;
RaisedPermissionIssueMessage = e.get_Issue().get_Message();
}
}
private void OptimizeClient()
{
if (transferClient != null) {
string a = transferClient.get_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.get_Configuration().set_MaxJobParallelism(1);
} else
transferClient.get_Configuration().set_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.get_SourcePath()) && (!path.get_SourcePathId().HasValue || path.get_SourcePathId().Value < 1))
throw new ArgumentException(((int)currentDirection == 2 || (int)path.get_Direction() == 2) ? Strings.TransferPathArgumentDownloadSourcePathExceptionMessage : Strings.TransferPathArgumentUploadSourcePathExceptionMessage, "path");
if (string.IsNullOrWhiteSpace(path.get_TargetPath()) && string.IsNullOrWhiteSpace(TargetPath))
throw new ArgumentException(((int)currentDirection == 2 || (int)path.get_Direction() == 2) ? Strings.TransferPathArgumentDownloadTargetPathExceptionMessage : Strings.TransferPathArgumentUploadTargetPathExceptionMessage, "path");
}
private TapiTotals WaitForCompletedTransfers()
{
object logger = (object)Logger;
object[] obj = new object[1];
ITransferRequest obj2 = jobRequest;
obj[0] = ((obj2 != null) ? obj2.get_JobId() : null);
logger.LogInformation("Preparing to wait for the {TransferJobId} batched file transfers to complete...", obj);
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;
object logger3 = (object)Logger;
Exception ex3 = ex2;
object[] obj5 = new object[1];
ITransferRequest obj6 = jobRequest;
obj5[0] = ((obj6 != null) ? obj6.get_JobId() : null);
logger3.LogError(ex3, "An exception was thrown waiting for the {TransferJobId} file transfers to complete.", obj5);
SwitchTapiClientMode(ex2, TapiClient.Web);
return true;
}
});
if (!CheckCompletedTransfers()) {
object logger2 = (object)Logger;
object[] obj3 = new object[1];
ITransferRequest obj4 = jobRequest;
obj3[0] = ((obj4 != null) ? obj4.get_JobId() : null);
logger2.LogWarning("WaitForCompletedTransfers has exited, not all {TransferJobId} file transfers have completed, and now going to wait for the transfer job to complete.", obj3);
LogTransferTotals("Inactivity", false);
WaitForCompletedTransferJob();
}
return batchTotals.DeepCopy();
} finally {
batchTotals.Clear();
}
}
private TapiTotals WaitForCompletedTransferJob()
{
if ((object)TransferJob != null) {
object logger = (object)Logger;
object[] obj = new object[1];
ITransferRequest obj2 = jobRequest;
obj[0] = ((obj2 != null) ? obj2.get_JobId() : null);
logger.LogInformation("Preparing to wait for transfer job {TransferJobId} to complete...", obj);
try {
Exception handledException = null;
RetrySyntax.Retry(Policy.Handle<TransferException>(), 2, (Action<Exception, int>)delegate(Exception exception, int count) {
handledException = exception;
object logger3 = (object)Logger;
object[] obj5 = new object[1];
ITransferRequest obj6 = jobRequest;
obj5[0] = ((obj6 != null) ? obj6.get_JobId() : null);
logger3.LogWarning(exception, "An unexpected error has occurred attempting to wait for transfer job {TransferJobId} to complete.", obj5);
SwitchTapiClientMode(exception, (count >= 2) ? TapiClient.Web : Client);
}).Execute((Action)delegate {
ITransferResult result = TransferJob.CompleteAsync(cancellationToken).GetAwaiter().GetResult();
object logger2 = (object)Logger;
object[] obj3 = new object[5];
ITransferRequest obj4 = jobRequest;
obj3[0] = ((obj4 != null) ? obj4.get_JobId() : null);
obj3[1] = ClientDisplayName;
obj3[2] = result.get_Status();
obj3[3] = result.get_Elapsed();
obj3[4] = result.get_TransferRateMbps();
logger2.LogInformation("Transfer job {TransferJobId} completed. {Name} transfer status: {Status}, elapsed time: {Elapsed}, data rate: {TransferRate:0.00} Mbps", obj3);
IssueAttributes missingFilesFlag = 131168;
if ((int)result.get_Status() == 2 || ((int)result.get_Status() == 1 && (result.get_Issues().Count == 0 || result.get_Issues().Any((ITransferIssue x) => !((Enum)x.get_Attributes()).HasFlag((Enum)(object)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();
}
}
}