Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,16 @@
namespace MorganStanley.ComposeUI.Fdc3.DesktopAgent.Client.Infrastructure;

/// <summary>
/// Desktop Agent client implementation.
/// Desktop Agent client implementation for native apps.
/// </summary>
public class DesktopAgentClient : IDesktopAgent
public class DesktopAgentClient : IDesktopAgent, IAsyncDisposable
{
private readonly IMessaging _messaging;
private readonly ILoggerFactory _loggerFactory;
private readonly ILogger<DesktopAgentClient> _logger;
private readonly string _appId;
private readonly string _instanceId;
private IChannelFactory _channelFactory;
private IChannelHandler _channelHandler;
private IMetadataClient _metadataClient;
private IIntentsClient _intentsClient;
private IOpenClient _openClient;
Expand All @@ -48,12 +48,7 @@ public class DesktopAgentClient : IDesktopAgent

private readonly SemaphoreSlim _currentChannelLock = new(1, 1);

private readonly ConcurrentDictionary<string, IChannel> _userChannels = new();
private readonly ConcurrentDictionary<string, IChannel> _appChannels = new();
private readonly SemaphoreSlim _appChannelsLock = new(1, 1);

private readonly TaskCompletionSource<string> _initializationTaskCompletionSource = new(TaskCreationOptions.RunContinuationsAsynchronously);
private string? _openedAppContextId;

public DesktopAgentClient(
IMessaging messaging,
Expand All @@ -74,7 +69,7 @@ public DesktopAgentClient(
_metadataClient = new MetadataClient(_appId, _instanceId, _messaging, _loggerFactory.CreateLogger<MetadataClient>());
_openClient = new OpenClient(_instanceId, _messaging, this, _loggerFactory.CreateLogger<OpenClient>());

_ = Task.Run(() => InitializeAsync());
_ = Task.Run(() => InitializeAsync().ConfigureAwait(false));
}

/// <summary>
Expand Down Expand Up @@ -105,12 +100,13 @@ private async Task InitializeAsync()

if (!string.IsNullOrEmpty(openedAppContextId))
{
_openedAppContextId = openedAppContextId;
await GetOpenedAppContextAsync(openedAppContextId!).ConfigureAwait(false);
}

_channelFactory = new ChannelFactory(_messaging, _instanceId, _openedAppContext, _loggerFactory);
_intentsClient = new IntentsClient(_messaging, _channelFactory, _instanceId, _loggerFactory);
_channelHandler = new ChannelHandler(_messaging, _instanceId, this, _openedAppContext, _loggerFactory);
_intentsClient = new IntentsClient(_messaging, _channelHandler, _instanceId, _loggerFactory);

await _channelHandler.ConfigureChannelSelectorAsync(CancellationToken.None).ConfigureAwait(false);

_initializationTaskCompletionSource.SetResult(_instanceId);
}
Expand Down Expand Up @@ -142,21 +138,21 @@ public async Task<IListener> AddContextListener<T>(string? contextType, ContextH
"Checking if the app was opened via fdc3.open. OpenedAppContext exists: {IsNotNull}, current context listener's context type: {ContextType}, received app context's type via open call: {OpenedAppContextType}.", _openedAppContext != null, contextType, _openedAppContext?.Type);
}

listener = await _channelFactory.CreateContextListenerAsync<T>(handler, _currentChannel, contextType);
listener = await _channelHandler.CreateContextListenerAsync<T>(handler, _currentChannel, contextType).ConfigureAwait(false);

_contextListeners.Add(
listener,
async (channelId, channelType, cancellationToken) =>
{
await listener.SubscribeAsync(channelId, channelType, cancellationToken);
await HandleLastContextAsync(listener);
await listener.SubscribeAsync(channelId, channelType, cancellationToken).ConfigureAwait(false);
await HandleLastContextAsync(listener).ConfigureAwait(false);
});

return listener;
}
finally
{
await HandleLastContextAsync(listener);
await HandleLastContextAsync(listener).ConfigureAwait(false);

_currentChannelLock.Release();
}
Expand All @@ -178,7 +174,7 @@ public async Task<IListener> AddIntentListener<T>(string intent, IntentHandler<T
return existingListener;
}

var listener = await _intentsClient.AddIntentListenerAsync<T>(intent, handler);
var listener = await _intentsClient.AddIntentListenerAsync<T>(intent, handler).ConfigureAwait(false);

if (!_intentListeners.TryAdd(intent, listener))
{
Expand Down Expand Up @@ -209,7 +205,7 @@ public async Task Broadcast(IContext context)
throw ThrowHelper.ClientNotConnectedToUserChannel();
}

await _currentChannel.Broadcast(context);
await _currentChannel.Broadcast(context).ConfigureAwait(false);
}
finally
{
Expand All @@ -224,7 +220,7 @@ public async Task Broadcast(IContext context)
public async Task<IPrivateChannel> CreatePrivateChannel()
{
await _initializationTaskCompletionSource.Task.ConfigureAwait(false);
var privateChannel = await _channelFactory.CreatePrivateChannelAsync();
var privateChannel = await _channelHandler.CreatePrivateChannelAsync().ConfigureAwait(false);

return privateChannel;
}
Expand All @@ -238,7 +234,7 @@ public async Task<IEnumerable<IAppIdentifier>> FindInstances(IAppIdentifier app)
{
await _initializationTaskCompletionSource.Task.ConfigureAwait(false);

var instances = await _metadataClient.FindInstancesAsync(app);
var instances = await _metadataClient.FindInstancesAsync(app).ConfigureAwait(false);
return instances;
}

Expand All @@ -253,7 +249,7 @@ public async Task<IAppIntent> FindIntent(string intent, IContext? context = null
{
await _initializationTaskCompletionSource.Task.ConfigureAwait(false);

var result = await _intentsClient.FindIntentAsync(intent, context, resultType);
var result = await _intentsClient.FindIntentAsync(intent, context, resultType).ConfigureAwait(false);
return result;
}

Expand All @@ -267,7 +263,7 @@ public async Task<IEnumerable<IAppIntent>> FindIntentsByContext(IContext context
{
await _initializationTaskCompletionSource.Task.ConfigureAwait(false);

var appIntents = await _intentsClient.FindIntentsByContextAsync(context, resultType);
var appIntents = await _intentsClient.FindIntentsByContextAsync(context, resultType).ConfigureAwait(false);
return appIntents;
}

Expand All @@ -280,7 +276,7 @@ public async Task<IAppMetadata> GetAppMetadata(IAppIdentifier app)
{
await _initializationTaskCompletionSource.Task.ConfigureAwait(false);

var appMetadata = await _metadataClient.GetAppMetadataAsync(app);
var appMetadata = await _metadataClient.GetAppMetadataAsync(app).ConfigureAwait(false);
return appMetadata;
}

Expand All @@ -303,7 +299,7 @@ public async Task<IImplementationMetadata> GetInfo()
{
await _initializationTaskCompletionSource.Task.ConfigureAwait(false);

var implementationMetadata = await _metadataClient.GetInfoAsync();
var implementationMetadata = await _metadataClient.GetInfoAsync().ConfigureAwait(false);
return implementationMetadata;
}

Expand All @@ -314,32 +310,9 @@ public async Task<IImplementationMetadata> GetInfo()
/// <returns></returns>
public async Task<IChannel> GetOrCreateChannel(string channelId)
{
try
{
await _initializationTaskCompletionSource.Task.ConfigureAwait(false);
await _appChannelsLock.WaitAsync().ConfigureAwait(false);

if (_appChannels.TryGetValue(channelId, out var existingChannel))
{
return existingChannel;
}

var channel = await _channelFactory.CreateAppChannelAsync(channelId);

if (!_appChannels.TryAdd(channelId, channel))
{
if (_logger.IsEnabled(LogLevel.Warning))
{
_logger.LogWarning("Failed to add app channel to the internal collection: {ChannelId}.", channelId);
}
}

return channel;
}
finally
{
_appChannelsLock.Release();
}
await _initializationTaskCompletionSource.Task.ConfigureAwait(false);
var channel = await _channelHandler.CreateAppChannelAsync(channelId).ConfigureAwait(false);
return channel;
}

/// <summary>
Expand All @@ -349,8 +322,7 @@ public async Task<IChannel> GetOrCreateChannel(string channelId)
public async Task<IEnumerable<IChannel>> GetUserChannels()
{
await _initializationTaskCompletionSource.Task.ConfigureAwait(false);

var channels = await _channelFactory.GetUserChannelsAsync();
var channels = await _channelHandler.GetUserChannelsAsync().ConfigureAwait(false);
return channels;
}

Expand All @@ -373,12 +345,7 @@ public async Task JoinUserChannel(string channelId)
await LeaveCurrentChannel().ConfigureAwait(false);
}

if (!_userChannels.TryGetValue(channelId, out var channel))
{
channel = await _channelFactory.JoinUserChannelAsync(channelId).ConfigureAwait(false);
_userChannels[channelId] = channel;
}

var channel = await _channelHandler.JoinUserChannelAsync(channelId).ConfigureAwait(false);
_currentChannel = channel;
}
finally
Expand Down Expand Up @@ -422,7 +389,10 @@ public async Task LeaveCurrentChannel()
contextListener.Key.Unsubscribe();
}

_currentChannel = null;
if (_currentChannel != null)
{
_currentChannel = null;
}
}
finally
{
Expand All @@ -440,7 +410,7 @@ public async Task<IAppIdentifier> Open(IAppIdentifier app, IContext? context = n
{
await _initializationTaskCompletionSource.Task.ConfigureAwait(false);

var appIdentifier = await _openClient.OpenAsync(app, context);
var appIdentifier = await _openClient.OpenAsync(app, context).ConfigureAwait(false);
return appIdentifier;
}

Expand All @@ -455,7 +425,7 @@ public async Task<IIntentResolution> RaiseIntent(string intent, IContext context
{
await _initializationTaskCompletionSource.Task.ConfigureAwait(false);

var intentResolution = await _intentsClient.RaiseIntentAsync(intent, context, app);
var intentResolution = await _intentsClient.RaiseIntentAsync(intent, context, app).ConfigureAwait(false);
return intentResolution;
}

Expand All @@ -469,7 +439,7 @@ public async Task<IIntentResolution> RaiseIntentForContext(IContext context, IAp
{
await _initializationTaskCompletionSource.Task.ConfigureAwait(false);

var intentResolution = await _intentsClient.RaiseIntentForContextAsync(context, app);
var intentResolution = await _intentsClient.RaiseIntentForContextAsync(context, app).ConfigureAwait(false);
return intentResolution;
}

Expand All @@ -482,7 +452,7 @@ internal async ValueTask GetOpenedAppContextAsync(string openedAppContextId)
{
try
{
_openedAppContext = await _openClient.GetOpenAppContextAsync(openedAppContextId);
_openedAppContext = await _openClient.GetOpenAppContextAsync(openedAppContextId).ConfigureAwait(false);
}
catch (Fdc3DesktopAgentException exception)
{
Expand Down Expand Up @@ -513,7 +483,7 @@ private async Task HandleLastContextAsync<T>(
return;
}

var lastContext = await _currentChannel.GetCurrentContext(listener.ContextType);
var lastContext = await _currentChannel.GetCurrentContext(listener.ContextType).ConfigureAwait(false);

if (lastContext == null)
{
Expand All @@ -526,6 +496,26 @@ private async Task HandleLastContextAsync<T>(
_logger.LogDebug("Invoking context handler for the last context of type: {ContextType}.", listener.ContextType ?? "null");
}

await listener.HandleContextAsync(lastContext);
await listener.HandleContextAsync(lastContext).ConfigureAwait(false);
}

public ValueTask DisposeAsync()
{
if (_channelHandler != null)
{
return _channelHandler.DisposeAsync();
}

foreach (var intentListener in _intentListeners.Values)
{
intentListener.Unsubscribe();
}

foreach (var contextListener in _contextListeners.Keys)
{
contextListener.Unsubscribe();
}

return new ValueTask();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace MorganStanley.ComposeUI.Fdc3.DesktopAgent.Client.Infrastructure;
/// <summary>
/// Provides methods for creating context listeners and joining channels and doing other channel operations.
/// </summary>
internal interface IChannelFactory
internal interface IChannelHandler : IAsyncDisposable
{
/// <summary>
/// Creates a context listener for the specified context type.
Expand Down Expand Up @@ -70,4 +70,11 @@ public ValueTask<ContextListener<T>> CreateContextListenerAsync<T>(
/// </summary>
/// <returns></returns>
public ValueTask<IPrivateChannel> CreatePrivateChannelAsync();
}

/// <summary>
/// Sets the handler when the user choose a user channel to join to from the UI.
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public ValueTask ConfigureChannelSelectorAsync(CancellationToken cancellationToken = default);
}
Loading
Loading