SEBWIN-405: Implemented mechanism to retrieve server failure action via client.

This commit is contained in:
Damian Büchel 2020-07-31 20:35:18 +02:00
parent 4d59ee399d
commit 4af0cc0d48
16 changed files with 289 additions and 1 deletions

View file

@ -192,6 +192,7 @@ namespace SafeExamBrowser.Client
ClientHost.PasswordRequested += ClientHost_PasswordRequested;
ClientHost.ReconfigurationAborted += ClientHost_ReconfigurationAborted;
ClientHost.ReconfigurationDenied += ClientHost_ReconfigurationDenied;
ClientHost.ServerFailureActionRequested += ClientHost_ServerFailureActionRequested;
ClientHost.Shutdown += ClientHost_Shutdown;
displayMonitor.DisplayChanged += DisplayMonitor_DisplaySettingsChanged;
runtime.ConnectionLost += Runtime_ConnectionLost;
@ -233,6 +234,7 @@ namespace SafeExamBrowser.Client
ClientHost.PasswordRequested -= ClientHost_PasswordRequested;
ClientHost.ReconfigurationAborted -= ClientHost_ReconfigurationAborted;
ClientHost.ReconfigurationDenied -= ClientHost_ReconfigurationDenied;
ClientHost.ServerFailureActionRequested -= ClientHost_ServerFailureActionRequested;
ClientHost.Shutdown -= ClientHost_Shutdown;
}
@ -463,6 +465,17 @@ namespace SafeExamBrowser.Client
splashScreen.Hide();
}
private void ClientHost_ServerFailureActionRequested(ServerFailureActionRequestEventArgs args)
{
logger.Info($"Received server failure action request with id '{args.RequestId}'.");
var dialog = uiFactory.CreateServerFailureDialog(args.Message, args.ShowFallback);
var result = dialog.Show(splashScreen);
runtime.SubmitServerFailureActionResult(args.RequestId, result.Abort, result.Fallback, result.Retry);
logger.Info($"Server failure action request with id '{args.RequestId}' is complete.");
}
private void ClientHost_Shutdown()
{
shutdown.Invoke();

View file

@ -30,6 +30,7 @@ namespace SafeExamBrowser.Client.Communication
public event CommunicationEventHandler ReconfigurationAborted;
public event CommunicationEventHandler<ReconfigurationEventArgs> ReconfigurationDenied;
public event CommunicationEventHandler RuntimeDisconnected;
public event CommunicationEventHandler<ServerFailureActionRequestEventArgs> ServerFailureActionRequested;
public event CommunicationEventHandler Shutdown;
public ClientHost(
@ -82,6 +83,9 @@ namespace SafeExamBrowser.Client.Communication
case ReconfigurationDeniedMessage m:
ReconfigurationDenied?.InvokeAsync(new ReconfigurationEventArgs { ConfigurationPath = m.FilePath });
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
case ServerFailureActionRequestMessage m:
ServerFailureActionRequested?.InvokeAsync(new ServerFailureActionRequestEventArgs { Message = m.Message, RequestId = m.RequestId, ShowFallback = m.ShowFallback });
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
}
return new SimpleResponse(SimpleResponsePurport.UnknownMessage);

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
namespace SafeExamBrowser.Communication.Contracts.Data
{
/// <summary>
/// The reply to a <see cref="ServerFailureActionRequestMessage"/>.
/// </summary>
[Serializable]
public class ServerFailureActionReplyMessage : Message
{
/// <summary>
/// The user chose to abort the operation.
/// </summary>
public bool Abort { get; set; }
/// <summary>
/// The user chose to perform a fallback.
/// </summary>
public bool Fallback { get; set; }
/// <summary>
/// The user chose to retry the operation.
/// </summary>
public bool Retry { get; set; }
/// <summary>
/// Identifies the server failure action request.
/// </summary>
public Guid RequestId { get; set; }
public ServerFailureActionReplyMessage(bool abort, bool fallback, bool retry, Guid requestId)
{
Abort = abort;
Fallback = fallback;
Retry = retry;
RequestId = requestId;
}
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
namespace SafeExamBrowser.Communication.Contracts.Data
{
/// <summary>
/// This message is transmitted to the client to request a server failure action selection by the user.
/// </summary>
[Serializable]
public class ServerFailureActionRequestMessage : Message
{
/// <summary>
/// The server failure message, if available.
/// </summary>
public string Message { get; set; }
/// <summary>
/// Indicates whether the fallback option should be shown to the user.
/// </summary>
public bool ShowFallback { get; set; }
/// <summary>
/// The unique identifier for the server failure action selection request.
/// </summary>
public Guid RequestId { get; }
public ServerFailureActionRequestMessage(string message, bool showFallback, Guid requestId)
{
Message = message;
ShowFallback = showFallback;
RequestId = requestId;
}
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
namespace SafeExamBrowser.Communication.Contracts.Events
{
/// <summary>
/// The event arguments used for the server failure action event fired by the <see cref="Hosts.IRuntimeHost"/>.
/// </summary>
public class ServerFailureActionReplyEventArgs : CommunicationEventArgs
{
/// <summary>
/// The user chose to abort the operation.
/// </summary>
public bool Abort { get; set; }
/// <summary>
/// The user chose to perform a fallback.
/// </summary>
public bool Fallback { get; set; }
/// <summary>
/// The user chose to retry the operation.
/// </summary>
public bool Retry { get; set; }
/// <summary>
/// Identifies the server failure action request.
/// </summary>
public Guid RequestId { get; set; }
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
namespace SafeExamBrowser.Communication.Contracts.Events
{
/// <summary>
/// The event arguments used for the server failure action request event fired by the <see cref="Hosts.IClientHost"/>.
/// </summary>
public class ServerFailureActionRequestEventArgs : CommunicationEventArgs
{
/// <summary>
/// The server failure message, if available.
/// </summary>
public string Message { get; set; }
/// <summary>
/// Indicates whether the fallback option should be shown to the user.
/// </summary>
public bool ShowFallback { get; set; }
/// <summary>
/// Identifies the server failure action selection request.
/// </summary>
public Guid RequestId { get; set; }
}
}

View file

@ -56,6 +56,11 @@ namespace SafeExamBrowser.Communication.Contracts.Hosts
/// </summary>
event CommunicationEventHandler RuntimeDisconnected;
/// <summary>
/// Event fired when the runtime requests a server failure action selection from the user.
/// </summary>
event CommunicationEventHandler<ServerFailureActionRequestEventArgs> ServerFailureActionRequested;
/// <summary>
/// Event fired when the runtime commands the client to shutdown.
/// </summary>

View file

@ -61,6 +61,11 @@ namespace SafeExamBrowser.Communication.Contracts.Hosts
/// </summary>
event CommunicationEventHandler<ReconfigurationEventArgs> ReconfigurationRequested;
/// <summary>
/// Event fired when the client submitted a server failure action chosen by the user.
/// </summary>
event CommunicationEventHandler<ServerFailureActionReplyEventArgs> ServerFailureActionReceived;
/// <summary>
/// Event fired when the client requests to shut down the application.
/// </summary>

View file

@ -29,6 +29,8 @@ namespace SafeExamBrowser.Communication.Contracts
[ServiceKnownType(typeof(PasswordRequestMessage))]
[ServiceKnownType(typeof(ReconfigurationMessage))]
[ServiceKnownType(typeof(ReconfigurationDeniedMessage))]
[ServiceKnownType(typeof(ServerFailureActionReplyMessage))]
[ServiceKnownType(typeof(ServerFailureActionRequestMessage))]
[ServiceKnownType(typeof(ServiceConfiguration))]
[ServiceKnownType(typeof(SessionStartMessage))]
[ServiceKnownType(typeof(SessionStopMessage))]

View file

@ -47,6 +47,11 @@ namespace SafeExamBrowser.Communication.Contracts.Proxies
/// </summary>
CommunicationResult RequestPassword(PasswordRequestPurpose purpose, Guid requestId);
/// <summary>
/// Requests the client to render a server failure action dialog and subsequently return the interaction result as separate message.
/// </summary>
CommunicationResult RequestServerFailureAction(string message, bool showFallback, Guid requestId);
/// <summary>
/// Requests the client to render a message box and subsequently return the interaction result as separate message.
/// </summary>

View file

@ -52,5 +52,10 @@ namespace SafeExamBrowser.Communication.Contracts.Proxies
/// the password parameter will be <see cref="default(string)"/>!
/// </summary>
CommunicationResult SubmitPassword(Guid requestId, bool success, string password = default(string));
/// <summary>
/// Submits the result of a server failure action selection previously requested by the runtime.
/// </summary>
CommunicationResult SubmitServerFailureActionResult(Guid requestId, bool abort, bool fallback, bool retry);
}
}

View file

@ -71,6 +71,8 @@
<Compile Include="Data\ReconfigurationDeniedMessage.cs" />
<Compile Include="Data\ReconfigurationMessage.cs" />
<Compile Include="Data\Response.cs" />
<Compile Include="Data\ServerFailureActionReplyMessage.cs" />
<Compile Include="Data\ServerFailureActionRequestMessage.cs" />
<Compile Include="Data\SessionStartMessage.cs" />
<Compile Include="Data\SessionStopMessage.cs" />
<Compile Include="Data\SimpleMessage.cs" />
@ -87,6 +89,8 @@
<Compile Include="Events\PasswordReplyEventArgs.cs" />
<Compile Include="Events\PasswordRequestEventArgs.cs" />
<Compile Include="Events\ReconfigurationEventArgs.cs" />
<Compile Include="Events\ServerFailureActionReplyEventArgs.cs" />
<Compile Include="Events\ServerFailureActionRequestEventArgs.cs" />
<Compile Include="Events\SessionStartEventArgs.cs" />
<Compile Include="Events\SessionStopEventArgs.cs" />
<Compile Include="Hosts\IClientHost.cs" />

View file

@ -180,6 +180,32 @@ namespace SafeExamBrowser.Communication.Proxies
}
}
public CommunicationResult RequestServerFailureAction(string message, bool showFallback, Guid requestId)
{
try
{
var response = Send(new ServerFailureActionRequestMessage(message, showFallback, requestId));
var success = IsAcknowledged(response);
if (success)
{
Logger.Debug("Client acknowledged server failure action request.");
}
else
{
Logger.Error($"Client did not acknowledge server failure action request! Received: {ToString(response)}.");
}
return new CommunicationResult(success);
}
catch (Exception e)
{
Logger.Error($"Failed to perform '{nameof(RequestServerFailureAction)}'", e);
return new CommunicationResult(false);
}
}
public CommunicationResult ShowMessage(string message, string title, int action, int icon, Guid requestId)
{
try

View file

@ -204,5 +204,31 @@ namespace SafeExamBrowser.Communication.Proxies
return new CommunicationResult(false);
}
}
public CommunicationResult SubmitServerFailureActionResult(Guid requestId, bool abort, bool fallback, bool retry)
{
try
{
var response = Send(new ServerFailureActionReplyMessage(abort, fallback, retry, requestId));
var acknowledged = IsAcknowledged(response);
if (acknowledged)
{
Logger.Debug("Runtime acknowledged server failure action transmission.");
}
else
{
Logger.Error($"Runtime did not acknowledge server failure action transmission! Response: {ToString(response)}.");
}
return new CommunicationResult(acknowledged);
}
catch (Exception e)
{
Logger.Error($"Failed to perform '{nameof(SubmitServerFailureActionResult)}'", e);
return new CommunicationResult(false);
}
}
}
}

View file

@ -28,6 +28,7 @@ namespace SafeExamBrowser.Runtime.Communication
public event CommunicationEventHandler<MessageBoxReplyEventArgs> MessageBoxReplyReceived;
public event CommunicationEventHandler<PasswordReplyEventArgs> PasswordReceived;
public event CommunicationEventHandler<ReconfigurationEventArgs> ReconfigurationRequested;
public event CommunicationEventHandler<ServerFailureActionReplyEventArgs> ServerFailureActionReceived;
public event CommunicationEventHandler ShutdownRequested;
public RuntimeHost(string address, IHostObjectFactory factory, ILogger logger, int timeout_ms) : base(address, factory, logger, timeout_ms)
@ -71,6 +72,9 @@ namespace SafeExamBrowser.Runtime.Communication
case ReconfigurationMessage m:
ReconfigurationRequested?.InvokeAsync(new ReconfigurationEventArgs { ConfigurationPath = m.ConfigurationPath });
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
case ServerFailureActionReplyMessage m:
ServerFailureActionReceived?.InvokeAsync(new ServerFailureActionReplyEventArgs { Abort = m.Abort, Fallback = m.Fallback, RequestId = m.RequestId, Retry = m.Retry });
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
}
return new SimpleResponse(SimpleResponsePurport.UnknownMessage);

View file

@ -567,7 +567,37 @@ namespace SafeExamBrowser.Runtime
private void TryAskForServerFailureActionViaClient(ServerFailureEventArgs args)
{
// TODO: Implement communication mechanism!
var requestId = Guid.NewGuid();
var response = default(ServerFailureActionReplyEventArgs);
var responseEvent = new AutoResetEvent(false);
var responseEventHandler = new CommunicationEventHandler<ServerFailureActionReplyEventArgs>((a) =>
{
if (a.RequestId == requestId)
{
response = a;
responseEvent.Set();
}
});
runtimeHost.ServerFailureActionReceived += responseEventHandler;
var communication = sessionContext.ClientProxy.RequestServerFailureAction(args.Message, args.ShowFallback, requestId);
if (communication.Success)
{
responseEvent.WaitOne();
args.Abort = response.Abort;
args.Fallback = response.Fallback;
args.Retry = response.Retry;
}
else
{
args.Abort = true;
args.Fallback = false;
args.Retry = false;
}
runtimeHost.ServerFailureActionReceived -= responseEventHandler;
}
private void TryGetPasswordViaDialog(PasswordRequiredEventArgs args)