diff --git a/Wino.Core.Domain/Interfaces/IWinoServerConnectionManager.cs b/Wino.Core.Domain/Interfaces/IWinoServerConnectionManager.cs
index 4285f128..eff74b95 100644
--- a/Wino.Core.Domain/Interfaces/IWinoServerConnectionManager.cs
+++ b/Wino.Core.Domain/Interfaces/IWinoServerConnectionManager.cs
@@ -19,19 +19,13 @@ namespace Wino.Core.Domain.Interfaces
///
/// 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 not running, it'll be launched and connection establishment is awaited.
///
/// Whether connection is established or not.
Task ConnectAsync();
- ///
- /// Disconnects from existing connection and disposes the connection.
- ///
- /// Whether disconnection is succesfull or not.
- Task DisconnectAsync();
-
///
/// Queues a new user request to be processed by Wino Server.
/// Healthy connection must present before calling this method.
@@ -48,6 +42,13 @@ namespace Wino.Core.Domain.Interfaces
/// Request type.
/// Response received from the server for the given TResponse type.
Task> GetResponseAsync(TRequestType clientMessage) where TRequestType : IClientMessage;
+
+ ///
+ /// 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.
+ ///
+ TaskCompletionSource ConnectingHandle { get; }
}
public interface IWinoServerConnectionManager : IWinoServerConnectionManager, IInitializeAsync
diff --git a/Wino.Core.Domain/Models/Server/WinoServerResponse.cs b/Wino.Core.Domain/Models/Server/WinoServerResponse.cs
index 45b8bb06..d903aa6e 100644
--- a/Wino.Core.Domain/Models/Server/WinoServerResponse.cs
+++ b/Wino.Core.Domain/Models/Server/WinoServerResponse.cs
@@ -13,8 +13,6 @@ namespace Wino.Core.Domain.Models.Server
public string Message { get; set; }
public T Data { get; set; }
- // protected WinoServerResponse() { }
-
public static WinoServerResponse CreateSuccessResponse(T data)
{
return new WinoServerResponse
diff --git a/Wino.Core.UWP/Services/WinoServerConnectionManager.cs b/Wino.Core.UWP/Services/WinoServerConnectionManager.cs
index 9aaca3ed..bb5fd965 100644
--- a/Wino.Core.UWP/Services/WinoServerConnectionManager.cs
+++ b/Wino.Core.UWP/Services/WinoServerConnectionManager.cs
@@ -26,10 +26,11 @@ namespace Wino.Core.UWP.Services
IWinoServerConnectionManager,
IRecipient
{
- private const int ServerConnectionTimeoutMs = 5000;
+ private const int ServerConnectionTimeoutMs = 10000;
public event EventHandler StatusChanged;
- private TaskCompletionSource _connectionTaskCompletionSource;
+
+ public TaskCompletionSource ConnectingHandle { get; private set; }
private ILogger Logger => Logger.ForContext();
@@ -40,6 +41,7 @@ namespace Wino.Core.UWP.Services
get { return status; }
private set
{
+ Log.Information("Server connection status changed to {Status}.", value);
status = value;
StatusChanged?.Invoke(this, value);
}
@@ -85,13 +87,29 @@ namespace Wino.Core.UWP.Services
public async Task 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))
{
try
{
- _connectionTaskCompletionSource ??= new TaskCompletionSource();
+ ConnectingHandle ??= new TaskCompletionSource();
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
// 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;
return false;
}
return true;
}
+ else
+ {
+ Log.Information("FullTrustAppContract is not present in the system. Server connection is not possible.");
+ }
return false;
}
- public async Task DisconnectAsync()
- {
- if (Connection == null || Status == WinoServerConnectionStatus.Disconnected) return true;
-
- // TODO: Send disconnect message to the fulltrust process.
-
- return true;
- }
-
public async Task InitializeAsync()
{
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)
@@ -222,7 +246,7 @@ namespace Wino.Core.UWP.Services
private void ServerDisconnected(AppServiceConnection sender, AppServiceClosedEventArgs args)
{
- // TODO: Handle server disconnection.
+ Log.Information("Server disconnected.");
}
public async Task QueueRequestAsync(IRequestBase request, Guid accountId)
@@ -242,8 +266,8 @@ namespace Wino.Core.UWP.Services
private async Task> GetResponseInternalAsync(TRequestType message, Dictionary parameters = null)
{
- if (Connection == null)
- return WinoServerResponse.CreateErrorResponse("Server connection is not established.");
+ if (Status != WinoServerConnectionStatus.Connected)
+ await ConnectAsync();
string serializedMessage = string.Empty;
@@ -306,11 +330,6 @@ namespace Wino.Core.UWP.Services
}
public void Receive(WinoServerConnectionEstablished message)
- {
- if (_connectionTaskCompletionSource != null)
- {
- _connectionTaskCompletionSource.TrySetResult(true);
- }
- }
+ => ConnectingHandle?.TrySetResult(true);
}
}
diff --git a/Wino.Core/Services/WinoRequestDelegator.cs b/Wino.Core/Services/WinoRequestDelegator.cs
index b47196ba..c4e9b60e 100644
--- a/Wino.Core/Services/WinoRequestDelegator.cs
+++ b/Wino.Core/Services/WinoRequestDelegator.cs
@@ -80,7 +80,7 @@ namespace Wino.Core.Services
await QueueRequestAsync(accountRequest, accountId.Key);
}
- QueueSynchronization(accountId.Key);
+ await QueueSynchronizationAsync(accountId.Key);
}
}
@@ -108,7 +108,7 @@ namespace Wino.Core.Services
if (request == null) return;
await QueueRequestAsync(request, accountId);
- QueueSynchronization(accountId);
+ await QueueSynchronizationAsync(accountId);
}
public async Task ExecuteAsync(DraftPreparationRequest draftPreperationRequest)
@@ -116,7 +116,7 @@ namespace Wino.Core.Services
var request = new CreateDraftRequest(draftPreperationRequest);
await QueueRequestAsync(request, draftPreperationRequest.Account.Id);
- QueueSynchronization(draftPreperationRequest.Account.Id);
+ await QueueSynchronizationAsync(draftPreperationRequest.Account.Id);
}
public async Task ExecuteAsync(SendDraftPreparationRequest sendDraftPreperationRequest)
@@ -124,23 +124,26 @@ namespace Wino.Core.Services
var request = new SendDraftRequest(sendDraftPreperationRequest);
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)
{
try
{
+ await EnsureServerConnectedAsync();
await _winoServerConnectionManager.QueueRequestAsync(request, accountId);
}
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()
{
AccountId = accountId,
@@ -149,5 +152,12 @@ namespace Wino.Core.Services
WeakReferenceMessenger.Default.Send(new NewSynchronizationRequested(options, SynchronizationSource.Client));
}
+
+ private async Task EnsureServerConnectedAsync()
+ {
+ if (_winoServerConnectionManager.Status == WinoServerConnectionStatus.Connected) return;
+
+ await _winoServerConnectionManager.ConnectAsync();
+ }
}
}
diff --git a/Wino.Mail.ViewModels/MailListPageViewModel.cs b/Wino.Mail.ViewModels/MailListPageViewModel.cs
index e3f24b1a..2470bec4 100644
--- a/Wino.Mail.ViewModels/MailListPageViewModel.cs
+++ b/Wino.Mail.ViewModels/MailListPageViewModel.cs
@@ -845,9 +845,6 @@ namespace Wino.Mail.ViewModels
trackingSynchronizationId = null;
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.
OnPropertyChanged(nameof(IsArchiveSpecialFolder));
@@ -865,6 +862,9 @@ namespace Wino.Mail.ViewModels
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.
message.FolderInitLoadAwaitTask?.TrySetResult(true);
diff --git a/Wino.Mail/App.xaml.cs b/Wino.Mail/App.xaml.cs
index c4e2d1ad..42d658f9 100644
--- a/Wino.Mail/App.xaml.cs
+++ b/Wino.Mail/App.xaml.cs
@@ -67,7 +67,6 @@ namespace Wino
private List initializeServices => new List()
{
_databaseService,
- _appServiceConnectionManager,
_translationService,
_themeService,
};
@@ -77,8 +76,6 @@ namespace Wino
InitializeComponent();
UnhandledException += OnAppUnhandledException;
- EnteredBackground += OnEnteredBackground;
- LeavingBackground += OnLeavingBackground;
Resuming += OnResuming;
Suspending += OnSuspending;
@@ -126,8 +123,6 @@ namespace Wino
}
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()
{
var services = new ServiceCollection();
@@ -417,13 +412,11 @@ namespace Wino
yield return Services.GetService();
}
- public async void OnConnectionBackgroundTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
+ public void OnConnectionBackgroundTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
sender.Canceled -= OnConnectionBackgroundTaskCanceled;
- Log.Information($"Server connection background task '{sender.Task.Name}' was canceled. Reason: {reason}");
-
- await _appServiceConnectionManager.DisconnectAsync();
+ Log.Information($"Server connection background task was canceled. Reason: {reason}");
connectionBackgroundTaskDeferral?.Complete();
connectionBackgroundTaskDeferral = null;
diff --git a/Wino.Server/ServerContext.cs b/Wino.Server/ServerContext.cs
index 9a60c63d..cf2ce174 100644
--- a/Wino.Server/ServerContext.cs
+++ b/Wino.Server/ServerContext.cs
@@ -173,7 +173,7 @@ namespace Wino.Server
if (status != AppServiceConnectionStatus.Success)
{
- // TODO: Handle connection error
+ Log.Error("Opening server connection failed. Status: {status}", status);
DisposeConnection();
}
@@ -224,8 +224,6 @@ namespace Wino.Server
private void OnConnectionClosed(AppServiceConnection sender, AppServiceClosedEventArgs args)
{
- // TODO: Handle connection closed.
-
// UWP app might've been terminated or suspended.
// 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.