<PackageReference Include="Relativity.Transfer.Client" Version="7.1.29" />

TransferClientBase

using Relativity.Transfer.Resources; using System; using System.Globalization; using System.Threading; using System.Threading.Tasks; namespace Relativity.Transfer { public abstract class TransferClientBase : ITransferClient, IDisposable { private static readonly object SyncRoot = new object(); private bool disposed; private Workspace workspace; public WellKnownTransferClient Client { get; } public RelativityConnectionInfo ConnectionInfo { get; } public ClientConfiguration Configuration { get; } public string DisplayName { get; } public Guid Id { get; } public string Name { get; } internal IRelativityServiceFactory ServiceFactory { get; } protected IFileSystemService FileSystemService { get; } protected ITransferLog Log { get; } protected IPathValidationProvider PathValidationProvider => ServiceFactory.PathValidationProvider; protected Workspace Workspace { get { lock (SyncRoot) { return workspace; } } private set { lock (SyncRoot) { workspace = value; } } } protected IWorkspaceService WorkspaceService { get; } protected TransferClientBase(RelativityConnectionInfo connectionInfo, ClientConfiguration configuration, IWorkspaceService workspaceService, ITransferLog log, Guid id, WellKnownTransferClient client, string name, string displayName) : this(connectionInfo, configuration, workspaceService, log, ServiceObjectLocator.GetService<IFileSystemService>(), id, client, name, displayName, new DefaultPathValidationFactory()) { } protected TransferClientBase(RelativityConnectionInfo connectionInfo, ClientConfiguration configuration, IWorkspaceService workspaceService, ITransferLog log, Guid id, WellKnownTransferClient client, string name, string displayName, IPathValidationFactory pathValidationFactory) : this(connectionInfo, configuration, workspaceService, log, ServiceObjectLocator.GetService<IFileSystemService>(), id, client, name, displayName, pathValidationFactory) { } protected TransferClientBase(RelativityConnectionInfo connectionInfo, ClientConfiguration configuration, IWorkspaceService workspaceService, ITransferLog log, IFileSystemService fileSystemService, Guid id, WellKnownTransferClient client, string name, string displayName, IPathValidationFactory pathValidationFactory) { if (connectionInfo == null) throw new ArgumentNullException("connectionInfo"); if (connectionInfo.Credential == null) throw new ArgumentException(CoreStrings.RelativityConnectionCredentialsArgumentExceptionMessage, "connectionInfo"); if (connectionInfo.Host == (Uri)null) throw new ArgumentException(CoreStrings.RelativityConnectionHostArgumentExceptionMessage, "connectionInfo"); if (connectionInfo.WorkspaceId < 1 && connectionInfo.WorkspaceId != -1) throw new ArgumentOutOfRangeException("connectionInfo", CoreStrings.RelativityConnectionWorkspaceArgumentExceptionMessage); if (configuration == null) throw new ArgumentNullException("configuration"); if (workspaceService == null) throw new ArgumentNullException("workspaceService"); if (log == null) throw new ArgumentNullException("log"); if (id == Guid.Empty) throw new ArgumentNullException("id"); if (string.IsNullOrEmpty(name)) throw new ArgumentNullException("name"); if (string.IsNullOrEmpty(displayName)) throw new ArgumentNullException("displayName"); if (fileSystemService == null) throw new ArgumentNullException("fileSystemService"); if (pathValidationFactory == null) throw new ArgumentNullException("pathValidationFactory"); Client = client; Configuration = configuration; ConnectionInfo = connectionInfo; Log = log; Id = id; Name = name; DisplayName = displayName; Workspace = null; WorkspaceService = workspaceService; FileSystemService = fileSystemService; ServiceFactory = new ServiceFactory(ConnectionInfo, Log, Configuration.MaxHttpRetryAttempts, Configuration.HttpTimeoutSeconds, pathValidationFactory.CreatePathValidationProvider()); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } public Task<ITransferJob> CreateJobAsync(ITransferRequest request) { if (request == null) throw new ArgumentNullException("request"); return CreateJobAsync(request, CancellationToken.None); } [Obsolete("This enumeration support will be removed in next release. In order to perform file enumeration use EnumerationBuilder and IEnumerationOrchestrator")] public IPathEnumerator CreatePathEnumerator(bool local) { return local ? OnCreateLocalPathEnumerator() : OnCreateRemotePathEnumerator(); } public IBatchSerializer CreateBatchSerializer(string batchDirectory, TransferDirection direction, PathEnumeratorContext context) { return new BatchSerializer(batchDirectory, direction, context, PathValidationProvider, Log); } public virtual async Task<ITransferJob> CreateJobAsync(ITransferRequest request, CancellationToken token) { if (request == null) throw new ArgumentNullException("request"); Log.LogTransferInformation(request, "Received request to create transfer job: {request}", request); VerifyIfTargetPathExists(request); await InitializeClientAsync(token).ConfigureAwait(false); Log.LogTransferInformation(request, "Preparing to create the {Name} transfer job...", Name); ITransferJob job = await OnCreateJobAsync(request, token).ConfigureAwait(false); Log.LogTransferInformation(request, "Successfully created the {Name} transfer job.", Name); return job; } public Task<Workspace> GetWorkspaceAsync() { return GetWorkspaceAsync(CancellationToken.None); } public Task<Workspace> GetWorkspaceAsync(CancellationToken token) { return GetWorkspaceAsync(ConnectionInfo.WorkspaceId, CancellationToken.None); } public async Task<Workspace> GetWorkspaceAsync(int workspaceArtifactId, CancellationToken token) { string key = CoreMemoryCacheKeys.CreateWorkspaceKey(ConnectionInfo.Host, workspaceArtifactId); using (MemoryCacheRepository cacheRepository = new MemoryCacheRepository(TransferCache.Default)) { Workspace currentWorkspace = cacheRepository.SelectByKey<Workspace>(key); if (currentWorkspace == null) { Log.LogDebug("Retrieving the '{WorkspaceId}' workspace information.", workspaceArtifactId); currentWorkspace = await WorkspaceService.GetWorkspaceAsync(workspaceArtifactId, token).ConfigureAwait(false); if (currentWorkspace != null) Log.LogDebug("Successfully retrieved the '{WorkspaceId}' workspace information.", workspaceArtifactId); else Log.LogWarning("The '{WorkspaceId}' workspace does not exist.", workspaceArtifactId); } if (currentWorkspace != null) cacheRepository.Upsert(key, currentWorkspace); return currentWorkspace; } } public virtual Task<ITransferResult> TransferAsync(ITransferRequest request) { if (request == null) throw new ArgumentNullException("request"); return TransferAsync(request, CancellationToken.None); } public virtual async Task<ITransferResult> TransferAsync(ITransferRequest request, CancellationToken token) { if (request == null) throw new ArgumentNullException("request"); if (request.Paths.Count == 0) throw new ArgumentException(CoreStrings.TransferRequestNoTransferPathsArgumentExceptionMessage, "request"); using (ITransferJob job = await CreateJobAsync(request, token).ConfigureAwait(false)) { foreach (TransferPath path in request.Paths) { job.AddPath(path); } return await job.CompleteAsync(TransferConstants.DefaultWaitTimeout, token).ConfigureAwait(false); } } public Task<ISupportCheckResult> SupportCheckAsync() { return SupportCheckAsync(CancellationToken.None); } public async Task<ISupportCheckResult> SupportCheckAsync(CancellationToken token) { Log.LogInformation("The support check for the {Name} client is requested...", Name); Log.LogInformation("Preparing to auto-configure the {Name} client...", Name); string key = CoreMemoryCacheKeys.CreateSupportCheckKey(ConnectionInfo, Id, Configuration.SupportCheckPath); using (MemoryCacheRepository cacheRepository = new MemoryCacheRepository(TransferCache.Default)) { try { await OnAutoConfigAsync(token).ConfigureAwait(false); } catch (OperationCanceledException) { throw; } catch (Exception e2) { Log.LogWarning(e2, "Failed to auto-configure the {Name} support check.", Name); SupportCheckResult failedResult = new SupportCheckResult { ErrorMessage = e2.Message, IsSupported = false }; cacheRepository.Upsert(key, failedResult); return failedResult; } Log.LogInformation("Successfully auto-configured the {Name} client.", Name); Log.LogInformation("Checking the support check cache repository for the {Name} client...", Name); ISupportCheckResult result2 = cacheRepository.SelectByKey<ISupportCheckResult>(key); if (result2 == null) try { Log.LogInformation("Preparing to execute the {Name} support check...", Name); result2 = await OnSupportCheckAsync(token).ConfigureAwait(false); cacheRepository.Upsert(key, result2); Log.LogInformation("Successfully executed the {Name} support check. Result={IsSupported}.", Name, result2.IsSupported); return result2; } catch (Exception e) { Log.LogWarning(e, "Failed to execute the {Name} support check.", Name); throw; } finally { Log.LogInformation("The support check for the {Name} client is completed.", Name); } Log.LogInformation("The support check cache repository contains an entry for the {Name} client. Result={IsSupported}.", Name, result2.IsSupported); return result2; } } public Task<IConnectionCheckResult> ConnectionCheckAsync(DiagnosticsContext context) { if (context == null) throw new ArgumentNullException("context"); return ConnectionCheckAsync(context, CancellationToken.None); } public async Task<IConnectionCheckResult> ConnectionCheckAsync(DiagnosticsContext context, CancellationToken token) { if (context != null) { Log.LogInformation("Executing connection test for the {Name} client.", Name); try { string key = CoreMemoryCacheKeys.CreateWorkspaceKey(ConnectionInfo.Host, ConnectionInfo.WorkspaceId); using (MemoryCacheRepository cacheRepository = new MemoryCacheRepository(TransferCache.Default)) Workspace = cacheRepository.SelectByKey<Workspace>(key); if (ConnectionInfo.WorkspaceId == -1 && Configuration.TargetFileShare != null) { context.PublishStatusMessage(message: string.Format(CoreStrings.ConnectionCheckUsingFileShare, Configuration.TargetFileShare.Name), clientId: Id); if (Workspace == null) await InitializeWorkspaceAsync(token).ConfigureAwait(false); } else { context.PublishStatusMessage(message: string.Format(CultureInfo.CurrentCulture, CoreStrings.ConnectionCheckGetWorkspaceMessage, ConnectionInfo.WorkspaceId), clientId: Id); if (Workspace == null) await InitializeWorkspaceAsync(token).ConfigureAwait(false); context.PublishStatusMessage(Id, CoreStrings.ConnectionCheckGetWorkspaceSuccessMessage); } context.PublishStatusMessage(Id, string.Empty); return await OnExecConnectionCheckAsync(context, token).ConfigureAwait(false); } finally { Log.LogInformation("The connection test for the {Name} client is completed.", Name); } } throw new ArgumentNullException("context"); } public override string ToString() { return $"""{Name}""{Id}"; } protected IRelativityServiceFactory CreateRelativityServiceFactory() { return new ServiceFactory(ConnectionInfo, Log); } protected virtual ITransferJobService CreateTransferJobService(ITransferRequest request, CancellationToken token) { return CreateTransferJobService(request, Configuration, token); } protected virtual ITransferJobService CreateTransferJobService(ITransferRequest request, ClientConfiguration configuration, CancellationToken token) { return new TransferJobService(request, configuration, Log, token); } protected virtual Task OnAutoConfigAsync(CancellationToken token) { return Task.FromResult(true); } [Obsolete("This enumeration support will be removed in next release. In order to perform file enumeration use EnumerationBuilder and IEnumerationOrchestrator")] protected virtual IPathEnumerator OnCreateLocalPathEnumerator() { return new LocalPathEnumerator(FileSystemService, PathValidationProvider, Log); } [Obsolete("This enumeration support will be removed in next release. In order to perform file enumeration use EnumerationBuilder and IEnumerationOrchestrator")] protected virtual IPathEnumerator OnCreateRemotePathEnumerator() { throw new NotSupportedException(CoreStrings.RemotePathEnumeratorNotSupported); } protected abstract Task<ISupportCheckResult> OnSupportCheckAsync(CancellationToken token); protected abstract Task<ITransferJob> OnCreateJobAsync(ITransferRequest request, CancellationToken token); protected abstract Task<IConnectionCheckResult> OnExecConnectionCheckAsync(DiagnosticsContext context, CancellationToken token); protected virtual void Dispose(bool disposing) { if (disposed) return; if (!disposing) goto IL_0015; goto IL_0015; IL_0015: disposed = true; } private async Task InitializeClientAsync(CancellationToken token) { Log.LogInformation("Preparing to initialize the {Name} client...", Name); await InitializeWorkspaceAsync(token).ConfigureAwait(false); Log.LogInformation("Successfully initialized the {Name} client.", Name); Log.LogInformation("Preparing to auto-configure the {Name} client...", Name); await OnAutoConfigAsync(token).ConfigureAwait(false); Log.LogInformation("Successfully auto-configured the {Name} client.", Name); } private async Task InitializeWorkspaceAsync(CancellationToken token) { Workspace = await GetWorkspaceAsync(token).ConfigureAwait(false); if (Workspace == null) { string message = string.Format(CultureInfo.CurrentCulture, CoreStrings.WorkspaceNotFoundExceptionMessage, ConnectionInfo.WorkspaceId); throw new TransferException(message, true); } } private void VerifyIfTargetPathExists(ITransferRequest request) { if (string.IsNullOrEmpty(request.TargetPath)) { foreach (TransferPath path in request.Paths) { if (string.IsNullOrEmpty(path.TargetPath)) Log.LogTransferWarning(request, "Transfer Path (Source: {SourcePath}) has no target path set, neither does transfer job itself. Transfer may fail.", path.SourcePath, LogRedaction.OnPositions(default(int))); } } } } }