Removing server init from the app init. Making sure server connection is established before doing a request. Handling Connecting state.
This commit is contained in:
@@ -19,19 +19,13 @@ namespace Wino.Core.Domain.Interfaces
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Launches Full Trust process (Wino Server) and awaits connection completion.
|
/// Launches Full Trust process (Wino Server) and awaits connection completion.
|
||||||
/// If connection is not established in 5 seconds, it will return false.
|
/// If connection is not established in 10 seconds, it will return false.
|
||||||
/// If the server process is already running, it'll connect to existing one.
|
/// If the server process is already running, it'll connect to existing one.
|
||||||
/// If the server process is not running, it'll be launched and connection establishment is awaited.
|
/// If the server process is not running, it'll be launched and connection establishment is awaited.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Whether connection is established or not.</returns>
|
/// <returns>Whether connection is established or not.</returns>
|
||||||
Task<bool> ConnectAsync();
|
Task<bool> ConnectAsync();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Disconnects from existing connection and disposes the connection.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Whether disconnection is succesfull or not.</returns>
|
|
||||||
Task<bool> DisconnectAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queues a new user request to be processed by Wino Server.
|
/// Queues a new user request to be processed by Wino Server.
|
||||||
/// Healthy connection must present before calling this method.
|
/// Healthy connection must present before calling this method.
|
||||||
@@ -48,6 +42,13 @@ namespace Wino.Core.Domain.Interfaces
|
|||||||
/// <param name="clientMessage">Request type.</param>
|
/// <param name="clientMessage">Request type.</param>
|
||||||
/// <returns>Response received from the server for the given TResponse type.</returns>
|
/// <returns>Response received from the server for the given TResponse type.</returns>
|
||||||
Task<WinoServerResponse<TResponse>> GetResponseAsync<TResponse, TRequestType>(TRequestType clientMessage) where TRequestType : IClientMessage;
|
Task<WinoServerResponse<TResponse>> GetResponseAsync<TResponse, TRequestType>(TRequestType clientMessage) where TRequestType : IClientMessage;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handle for connecting to the server.
|
||||||
|
/// If the server is already running, it'll connect to existing one.
|
||||||
|
/// Callers can await this handle to wait for connection establishment.
|
||||||
|
/// </summary>
|
||||||
|
TaskCompletionSource<bool> ConnectingHandle { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IWinoServerConnectionManager<TAppServiceConnection> : IWinoServerConnectionManager, IInitializeAsync
|
public interface IWinoServerConnectionManager<TAppServiceConnection> : IWinoServerConnectionManager, IInitializeAsync
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ namespace Wino.Core.Domain.Models.Server
|
|||||||
public string Message { get; set; }
|
public string Message { get; set; }
|
||||||
public T Data { get; set; }
|
public T Data { get; set; }
|
||||||
|
|
||||||
// protected WinoServerResponse() { }
|
|
||||||
|
|
||||||
public static WinoServerResponse<T> CreateSuccessResponse(T data)
|
public static WinoServerResponse<T> CreateSuccessResponse(T data)
|
||||||
{
|
{
|
||||||
return new WinoServerResponse<T>
|
return new WinoServerResponse<T>
|
||||||
|
|||||||
@@ -26,10 +26,11 @@ namespace Wino.Core.UWP.Services
|
|||||||
IWinoServerConnectionManager<AppServiceConnection>,
|
IWinoServerConnectionManager<AppServiceConnection>,
|
||||||
IRecipient<WinoServerConnectionEstablished>
|
IRecipient<WinoServerConnectionEstablished>
|
||||||
{
|
{
|
||||||
private const int ServerConnectionTimeoutMs = 5000;
|
private const int ServerConnectionTimeoutMs = 10000;
|
||||||
|
|
||||||
public event EventHandler<WinoServerConnectionStatus> StatusChanged;
|
public event EventHandler<WinoServerConnectionStatus> StatusChanged;
|
||||||
private TaskCompletionSource<bool> _connectionTaskCompletionSource;
|
|
||||||
|
public TaskCompletionSource<bool> ConnectingHandle { get; private set; }
|
||||||
|
|
||||||
private ILogger Logger => Logger.ForContext<WinoServerConnectionManager>();
|
private ILogger Logger => Logger.ForContext<WinoServerConnectionManager>();
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@ namespace Wino.Core.UWP.Services
|
|||||||
get { return status; }
|
get { return status; }
|
||||||
private set
|
private set
|
||||||
{
|
{
|
||||||
|
Log.Information("Server connection status changed to {Status}.", value);
|
||||||
status = value;
|
status = value;
|
||||||
StatusChanged?.Invoke(this, value);
|
StatusChanged?.Invoke(this, value);
|
||||||
}
|
}
|
||||||
@@ -85,13 +87,29 @@ namespace Wino.Core.UWP.Services
|
|||||||
|
|
||||||
public async Task<bool> ConnectAsync()
|
public async Task<bool> ConnectAsync()
|
||||||
{
|
{
|
||||||
if (Status == WinoServerConnectionStatus.Connected) return true;
|
if (Status == WinoServerConnectionStatus.Connected)
|
||||||
|
{
|
||||||
|
Log.Information("Server is already connected.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Status == WinoServerConnectionStatus.Connecting)
|
||||||
|
{
|
||||||
|
// A connection is already being established at the moment.
|
||||||
|
// No need to run another connection establishment process.
|
||||||
|
// Await the connecting handler if possible.
|
||||||
|
|
||||||
|
if (ConnectingHandle != null)
|
||||||
|
{
|
||||||
|
return await ConnectingHandle.Task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (ApiInformation.IsApiContractPresent("Windows.ApplicationModel.FullTrustAppContract", 1, 0))
|
if (ApiInformation.IsApiContractPresent("Windows.ApplicationModel.FullTrustAppContract", 1, 0))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_connectionTaskCompletionSource ??= new TaskCompletionSource<bool>();
|
ConnectingHandle ??= new TaskCompletionSource<bool>();
|
||||||
|
|
||||||
var connectionCancellationToken = new CancellationTokenSource(TimeSpan.FromMilliseconds(ServerConnectionTimeoutMs));
|
var connectionCancellationToken = new CancellationTokenSource(TimeSpan.FromMilliseconds(ServerConnectionTimeoutMs));
|
||||||
|
|
||||||
@@ -103,34 +121,40 @@ namespace Wino.Core.UWP.Services
|
|||||||
// Once the connection is established, the handler will set the Connection property
|
// Once the connection is established, the handler will set the Connection property
|
||||||
// and WinoServerConnectionEstablished will be fired by the messenger.
|
// and WinoServerConnectionEstablished will be fired by the messenger.
|
||||||
|
|
||||||
await _connectionTaskCompletionSource.Task.WaitAsync(connectionCancellationToken.Token);
|
await ConnectingHandle.Task.WaitAsync(connectionCancellationToken.Token);
|
||||||
|
|
||||||
|
Log.Information("Server connection established successfully.");
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
Log.Error(ex, "Failed to connect to the server.");
|
||||||
|
|
||||||
Status = WinoServerConnectionStatus.Failed;
|
Status = WinoServerConnectionStatus.Failed;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.Information("FullTrustAppContract is not present in the system. Server connection is not possible.");
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> DisconnectAsync()
|
|
||||||
{
|
|
||||||
if (Connection == null || Status == WinoServerConnectionStatus.Disconnected) return true;
|
|
||||||
|
|
||||||
// TODO: Send disconnect message to the fulltrust process.
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task InitializeAsync()
|
public async Task InitializeAsync()
|
||||||
{
|
{
|
||||||
var isConnectionSuccessfull = await ConnectAsync();
|
var isConnectionSuccessfull = await ConnectAsync();
|
||||||
|
|
||||||
// TODO: Log connection status
|
if (isConnectionSuccessfull)
|
||||||
|
{
|
||||||
|
Log.Information("ServerConnectionManager initialized successfully.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.Error("ServerConnectionManager initialization failed.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ServerMessageReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
|
private void ServerMessageReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
|
||||||
@@ -222,7 +246,7 @@ namespace Wino.Core.UWP.Services
|
|||||||
|
|
||||||
private void ServerDisconnected(AppServiceConnection sender, AppServiceClosedEventArgs args)
|
private void ServerDisconnected(AppServiceConnection sender, AppServiceClosedEventArgs args)
|
||||||
{
|
{
|
||||||
// TODO: Handle server disconnection.
|
Log.Information("Server disconnected.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task QueueRequestAsync(IRequestBase request, Guid accountId)
|
public async Task QueueRequestAsync(IRequestBase request, Guid accountId)
|
||||||
@@ -242,8 +266,8 @@ namespace Wino.Core.UWP.Services
|
|||||||
|
|
||||||
private async Task<WinoServerResponse<TResponse>> GetResponseInternalAsync<TResponse, TRequestType>(TRequestType message, Dictionary<string, object> parameters = null)
|
private async Task<WinoServerResponse<TResponse>> GetResponseInternalAsync<TResponse, TRequestType>(TRequestType message, Dictionary<string, object> parameters = null)
|
||||||
{
|
{
|
||||||
if (Connection == null)
|
if (Status != WinoServerConnectionStatus.Connected)
|
||||||
return WinoServerResponse<TResponse>.CreateErrorResponse("Server connection is not established.");
|
await ConnectAsync();
|
||||||
|
|
||||||
string serializedMessage = string.Empty;
|
string serializedMessage = string.Empty;
|
||||||
|
|
||||||
@@ -306,11 +330,6 @@ namespace Wino.Core.UWP.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Receive(WinoServerConnectionEstablished message)
|
public void Receive(WinoServerConnectionEstablished message)
|
||||||
{
|
=> ConnectingHandle?.TrySetResult(true);
|
||||||
if (_connectionTaskCompletionSource != null)
|
|
||||||
{
|
|
||||||
_connectionTaskCompletionSource.TrySetResult(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ namespace Wino.Core.Services
|
|||||||
await QueueRequestAsync(accountRequest, accountId.Key);
|
await QueueRequestAsync(accountRequest, accountId.Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
QueueSynchronization(accountId.Key);
|
await QueueSynchronizationAsync(accountId.Key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ namespace Wino.Core.Services
|
|||||||
if (request == null) return;
|
if (request == null) return;
|
||||||
|
|
||||||
await QueueRequestAsync(request, accountId);
|
await QueueRequestAsync(request, accountId);
|
||||||
QueueSynchronization(accountId);
|
await QueueSynchronizationAsync(accountId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ExecuteAsync(DraftPreparationRequest draftPreperationRequest)
|
public async Task ExecuteAsync(DraftPreparationRequest draftPreperationRequest)
|
||||||
@@ -116,7 +116,7 @@ namespace Wino.Core.Services
|
|||||||
var request = new CreateDraftRequest(draftPreperationRequest);
|
var request = new CreateDraftRequest(draftPreperationRequest);
|
||||||
|
|
||||||
await QueueRequestAsync(request, draftPreperationRequest.Account.Id);
|
await QueueRequestAsync(request, draftPreperationRequest.Account.Id);
|
||||||
QueueSynchronization(draftPreperationRequest.Account.Id);
|
await QueueSynchronizationAsync(draftPreperationRequest.Account.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ExecuteAsync(SendDraftPreparationRequest sendDraftPreperationRequest)
|
public async Task ExecuteAsync(SendDraftPreparationRequest sendDraftPreperationRequest)
|
||||||
@@ -124,23 +124,26 @@ namespace Wino.Core.Services
|
|||||||
var request = new SendDraftRequest(sendDraftPreperationRequest);
|
var request = new SendDraftRequest(sendDraftPreperationRequest);
|
||||||
|
|
||||||
await QueueRequestAsync(request, sendDraftPreperationRequest.MailItem.AssignedAccount.Id);
|
await QueueRequestAsync(request, sendDraftPreperationRequest.MailItem.AssignedAccount.Id);
|
||||||
QueueSynchronization(sendDraftPreperationRequest.MailItem.AssignedAccount.Id);
|
await QueueSynchronizationAsync(sendDraftPreperationRequest.MailItem.AssignedAccount.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task QueueRequestAsync(IRequestBase request, Guid accountId)
|
private async Task QueueRequestAsync(IRequestBase request, Guid accountId)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
await EnsureServerConnectedAsync();
|
||||||
await _winoServerConnectionManager.QueueRequestAsync(request, accountId);
|
await _winoServerConnectionManager.QueueRequestAsync(request, accountId);
|
||||||
}
|
}
|
||||||
catch (WinoServerException serverException)
|
catch (WinoServerException serverException)
|
||||||
{
|
{
|
||||||
_dialogService.InfoBarMessage("", serverException.Message, InfoBarMessageType.Error);
|
_dialogService.InfoBarMessage("Wino Server Exception", serverException.Message, InfoBarMessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void QueueSynchronization(Guid accountId)
|
private async Task QueueSynchronizationAsync(Guid accountId)
|
||||||
{
|
{
|
||||||
|
await EnsureServerConnectedAsync();
|
||||||
|
|
||||||
var options = new SynchronizationOptions()
|
var options = new SynchronizationOptions()
|
||||||
{
|
{
|
||||||
AccountId = accountId,
|
AccountId = accountId,
|
||||||
@@ -149,5 +152,12 @@ namespace Wino.Core.Services
|
|||||||
|
|
||||||
WeakReferenceMessenger.Default.Send(new NewSynchronizationRequested(options, SynchronizationSource.Client));
|
WeakReferenceMessenger.Default.Send(new NewSynchronizationRequested(options, SynchronizationSource.Client));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task EnsureServerConnectedAsync()
|
||||||
|
{
|
||||||
|
if (_winoServerConnectionManager.Status == WinoServerConnectionStatus.Connected) return;
|
||||||
|
|
||||||
|
await _winoServerConnectionManager.ConnectAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -845,9 +845,6 @@ namespace Wino.Mail.ViewModels
|
|||||||
trackingSynchronizationId = null;
|
trackingSynchronizationId = null;
|
||||||
completedTrackingSynchronizationCount = 0;
|
completedTrackingSynchronizationCount = 0;
|
||||||
|
|
||||||
// Check whether the account synchronizer that this folder belongs to is already in synchronization.
|
|
||||||
await CheckIfAccountIsSynchronizingAsync();
|
|
||||||
|
|
||||||
// Notify change for archive-unarchive app bar button.
|
// Notify change for archive-unarchive app bar button.
|
||||||
OnPropertyChanged(nameof(IsArchiveSpecialFolder));
|
OnPropertyChanged(nameof(IsArchiveSpecialFolder));
|
||||||
|
|
||||||
@@ -865,6 +862,9 @@ namespace Wino.Mail.ViewModels
|
|||||||
await Task.Delay(100);
|
await Task.Delay(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check whether the account synchronizer that this folder belongs to is already in synchronization.
|
||||||
|
await CheckIfAccountIsSynchronizingAsync();
|
||||||
|
|
||||||
// Let awaiters know about the completion of mail init.
|
// Let awaiters know about the completion of mail init.
|
||||||
message.FolderInitLoadAwaitTask?.TrySetResult(true);
|
message.FolderInitLoadAwaitTask?.TrySetResult(true);
|
||||||
|
|
||||||
|
|||||||
@@ -67,7 +67,6 @@ namespace Wino
|
|||||||
private List<IInitializeAsync> initializeServices => new List<IInitializeAsync>()
|
private List<IInitializeAsync> initializeServices => new List<IInitializeAsync>()
|
||||||
{
|
{
|
||||||
_databaseService,
|
_databaseService,
|
||||||
_appServiceConnectionManager,
|
|
||||||
_translationService,
|
_translationService,
|
||||||
_themeService,
|
_themeService,
|
||||||
};
|
};
|
||||||
@@ -77,8 +76,6 @@ namespace Wino
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
UnhandledException += OnAppUnhandledException;
|
UnhandledException += OnAppUnhandledException;
|
||||||
EnteredBackground += OnEnteredBackground;
|
|
||||||
LeavingBackground += OnLeavingBackground;
|
|
||||||
|
|
||||||
Resuming += OnResuming;
|
Resuming += OnResuming;
|
||||||
Suspending += OnSuspending;
|
Suspending += OnSuspending;
|
||||||
@@ -126,8 +123,6 @@ namespace Wino
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void LogActivation(string log) => Log.Information($"{WinoLaunchLogPrefix}{log}");
|
private void LogActivation(string log) => Log.Information($"{WinoLaunchLogPrefix}{log}");
|
||||||
private void OnLeavingBackground(object sender, LeavingBackgroundEventArgs e) => LogActivation($"Wino went foreground.");
|
|
||||||
private void OnEnteredBackground(object sender, EnteredBackgroundEventArgs e) => LogActivation($"Wino went background.");
|
|
||||||
private IServiceProvider ConfigureServices()
|
private IServiceProvider ConfigureServices()
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
@@ -417,13 +412,11 @@ namespace Wino
|
|||||||
yield return Services.GetService<FileActivationHandler>();
|
yield return Services.GetService<FileActivationHandler>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void OnConnectionBackgroundTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
|
public void OnConnectionBackgroundTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
|
||||||
{
|
{
|
||||||
sender.Canceled -= OnConnectionBackgroundTaskCanceled;
|
sender.Canceled -= OnConnectionBackgroundTaskCanceled;
|
||||||
|
|
||||||
Log.Information($"Server connection background task '{sender.Task.Name}' was canceled. Reason: {reason}");
|
Log.Information($"Server connection background task was canceled. Reason: {reason}");
|
||||||
|
|
||||||
await _appServiceConnectionManager.DisconnectAsync();
|
|
||||||
|
|
||||||
connectionBackgroundTaskDeferral?.Complete();
|
connectionBackgroundTaskDeferral?.Complete();
|
||||||
connectionBackgroundTaskDeferral = null;
|
connectionBackgroundTaskDeferral = null;
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ namespace Wino.Server
|
|||||||
|
|
||||||
if (status != AppServiceConnectionStatus.Success)
|
if (status != AppServiceConnectionStatus.Success)
|
||||||
{
|
{
|
||||||
// TODO: Handle connection error
|
Log.Error("Opening server connection failed. Status: {status}", status);
|
||||||
|
|
||||||
DisposeConnection();
|
DisposeConnection();
|
||||||
}
|
}
|
||||||
@@ -224,8 +224,6 @@ namespace Wino.Server
|
|||||||
|
|
||||||
private void OnConnectionClosed(AppServiceConnection sender, AppServiceClosedEventArgs args)
|
private void OnConnectionClosed(AppServiceConnection sender, AppServiceClosedEventArgs args)
|
||||||
{
|
{
|
||||||
// TODO: Handle connection closed.
|
|
||||||
|
|
||||||
// UWP app might've been terminated or suspended.
|
// UWP app might've been terminated or suspended.
|
||||||
// At this point, we must keep active synchronizations going, but connection is lost.
|
// At this point, we must keep active synchronizations going, but connection is lost.
|
||||||
// As long as this process is alive, database will be kept updated, but no messages will be sent.
|
// As long as this process is alive, database will be kept updated, but no messages will be sent.
|
||||||
|
|||||||
Reference in New Issue
Block a user