SEBWIN-219: Adapted startup procedure for client component by introducing the DelayedInitializationOperation.

This commit is contained in:
dbuechel 2018-02-12 12:21:55 +01:00
parent 66e9078a4c
commit 10202a807f
35 changed files with 591 additions and 218 deletions

View file

@ -1,50 +0,0 @@
/*
* Copyright (c) 2018 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 Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Client.Behaviour.Operations;
using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Logging;
namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
{
[TestClass]
public class ClientControllerOperationTests
{
private Mock<ILogger> loggerMock;
private Mock<IClientController> clientControllerMock;
private ClientControllerOperation sut;
[TestInitialize]
public void Initialize()
{
loggerMock = new Mock<ILogger>();
clientControllerMock = new Mock<IClientController>();
sut = new ClientControllerOperation(clientControllerMock.Object, loggerMock.Object);
}
[TestMethod]
public void MustPerformCorrectly()
{
sut.Perform();
clientControllerMock.Verify(r => r.Start(), Times.Once);
}
[TestMethod]
public void MustRevertCorrectly()
{
sut.Revert();
clientControllerMock.Verify(r => r.Stop(), Times.Once);
}
}
}

View file

@ -9,7 +9,6 @@
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Client.Behaviour;
using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring;
@ -37,14 +36,16 @@ namespace SafeExamBrowser.Client.UnitTests
taskbarMock = new Mock<ITaskbar>();
windowMonitorMock= new Mock<IWindowMonitor>();
sut = new ClientController(
displayMonitorMock.Object,
loggerMock.Object,
processMonitorMock.Object,
taskbarMock.Object,
windowMonitorMock.Object);
// TODO
sut.Start();
//sut = new ClientController(
// displayMonitorMock.Object,
// loggerMock.Object,
// processMonitorMock.Object,
// taskbarMock.Object,
// windowMonitorMock.Object);
// sut.Start();
}
[TestMethod]
@ -151,7 +152,7 @@ namespace SafeExamBrowser.Client.UnitTests
[TestCleanup]
public void Cleanup()
{
sut.Stop();
// TODO sut.Stop();
}
}
}

View file

@ -79,7 +79,6 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Behaviour\Operations\BrowserOperationTests.cs" />
<Compile Include="Behaviour\Operations\ClientControllerOperationTests.cs" />
<Compile Include="Behaviour\Operations\ClipboardOperationTests.cs" />
<Compile Include="Behaviour\Operations\DisplayMonitorOperationTests.cs" />
<Compile Include="Behaviour\Operations\KeyboardInterceptorOperationTests.cs" />

View file

@ -7,12 +7,9 @@
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Windows;
using SafeExamBrowser.Contracts.Behaviour.Operations;
namespace SafeExamBrowser.Client
{
@ -61,26 +58,24 @@ namespace SafeExamBrowser.Client
instances.BuildObjectGraph();
//var success = instances.StartupController.TryInitializeApplication(instances.StartupOperations);
var success = instances.ClientController.TryStart();
//if (success)
//{
// MainWindow = instances.Taskbar;
// MainWindow.Closing += MainWindow_Closing;
// MainWindow.Show();
//}
//else
//{
// Shutdown();
//}
if (success)
{
MainWindow = instances.Taskbar;
MainWindow.Closing += MainWindow_Closing;
MainWindow.Show();
}
else
{
Shutdown();
}
}
private void MainWindow_Closing(object sender, CancelEventArgs e)
{
var operations = new Queue<IOperation>(instances.StartupOperations.Reverse());
MainWindow.Hide();
//instances.ShutdownController.FinalizeApplication(operations);
MainWindow?.Hide();
instances.ClientController.Terminate();
}
}
}

View file

@ -8,6 +8,7 @@
using System;
using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
@ -18,6 +19,7 @@ namespace SafeExamBrowser.Client.Behaviour
{
private IDisplayMonitor displayMonitor;
private ILogger logger;
private IOperationSequence operations;
private IProcessMonitor processMonitor;
private ITaskbar taskbar;
private IWindowMonitor windowMonitor;
@ -25,31 +27,35 @@ namespace SafeExamBrowser.Client.Behaviour
public ClientController(
IDisplayMonitor displayMonitor,
ILogger logger,
IOperationSequence operations,
IProcessMonitor processMonitor,
ITaskbar taskbar,
IWindowMonitor windowMonitor)
{
this.displayMonitor = displayMonitor;
this.logger = logger;
this.operations = operations;
this.processMonitor = processMonitor;
this.taskbar = taskbar;
this.windowMonitor = windowMonitor;
}
public void Start()
{
displayMonitor.DisplayChanged += DisplayMonitor_DisplaySettingsChanged;
processMonitor.ExplorerStarted += ProcessMonitor_ExplorerStarted;
windowMonitor.WindowChanged += WindowMonitor_WindowChanged;
}
public void Stop()
public void Terminate()
{
displayMonitor.DisplayChanged -= DisplayMonitor_DisplaySettingsChanged;
processMonitor.ExplorerStarted -= ProcessMonitor_ExplorerStarted;
windowMonitor.WindowChanged -= WindowMonitor_WindowChanged;
}
public bool TryStart()
{
displayMonitor.DisplayChanged += DisplayMonitor_DisplaySettingsChanged;
processMonitor.ExplorerStarted += ProcessMonitor_ExplorerStarted;
windowMonitor.WindowChanged += WindowMonitor_WindowChanged;
return true;
}
private void DisplayMonitor_DisplaySettingsChanged()
{
logger.Info("Reinitializing working area...");

View file

@ -1,52 +0,0 @@
/*
* Copyright (c) 2018 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 SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Client.Behaviour.Operations
{
internal class ClientControllerOperation : IOperation
{
private ILogger logger;
private IClientController controller;
public bool Abort { get; private set; }
public IProgressIndicator ProgressIndicator { private get; set; }
public ClientControllerOperation(IClientController controller, ILogger logger)
{
this.controller = controller;
this.logger = logger;
}
public void Perform()
{
logger.Info("Starting event handling...");
ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_StartEventHandling);
controller.Start();
}
public void Repeat()
{
// Nothing to do here...
}
public void Revert()
{
logger.Info("Stopping event handling...");
ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_StopEventHandling);
controller.Stop();
}
}
}

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2018 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;
using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Client.Behaviour.Operations
{
internal class ConfigurationOperation : IOperation
{
private IClientConfiguration configuration;
private ILogger logger;
private IRuntimeProxy runtime;
public bool Abort { get; private set; }
public IProgressIndicator ProgressIndicator { private get; set; }
public ConfigurationOperation(IClientConfiguration configuration, ILogger logger, IRuntimeProxy runtime)
{
this.configuration = configuration;
this.logger = logger;
this.runtime = runtime;
}
public void Perform()
{
logger.Info("Initializing application configuration...");
ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_InitializeConfiguration);
try
{
var config = runtime.GetConfiguration();
configuration.RuntimeInfo = config.RuntimeInfo;
configuration.SessionData = config.SessionData;
configuration.Settings = config.Settings;
logger.Info("Successfully retrieved the application configuration from the runtime.");
}
catch (Exception e)
{
logger.Error("An unexpected error occurred while trying to retrieve the application configuration!", e);
}
}
public void Repeat()
{
// Nothing to do here...
}
public void Revert()
{
// Nothing to do here...
}
}
}

View file

@ -0,0 +1,81 @@
/*
* Copyright (c) 2018 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;
using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Client.Behaviour.Operations
{
internal class RuntimeConnectionOperation : IOperation
{
private bool connected;
private ILogger logger;
private IRuntimeProxy runtime;
private Guid token;
public bool Abort { get; private set; }
public IProgressIndicator ProgressIndicator { private get; set; }
public RuntimeConnectionOperation(ILogger logger, IRuntimeProxy runtime, Guid token)
{
this.logger = logger;
this.runtime = runtime;
this.token = token;
}
public void Perform()
{
logger.Info("Initializing runtime connection...");
ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_InitializeRuntimeConnection);
try
{
connected = runtime.Connect(token);
logger.Info("Successfully connected to the runtime host.");
}
catch (Exception e)
{
logger.Error("An unexpected error occurred while trying to connect to the runtime host!", e);
}
if (!connected)
{
Abort = true;
logger.Info("Failed to connect to the runtime. Aborting startup...");
}
}
public void Repeat()
{
// Nothing to do here...
}
public void Revert()
{
logger.Info("Closing runtime connection...");
ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_CloseRuntimeConnection);
if (connected)
{
try
{
runtime.Disconnect();
}
catch (Exception e)
{
logger.Error("Failed to disconnect from runtime host!", e);
}
}
}
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2018 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;
using SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.Communication.Messages;
using SafeExamBrowser.Contracts.Communication.Responses;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Core.Communication;
namespace SafeExamBrowser.Client.Communication
{
internal class ClientHost : BaseHost, IClientHost
{
public ClientHost(string address, ILogger logger) : base(address, logger)
{
}
protected override IConnectResponse OnConnect(Guid? token)
{
// TODO
throw new NotImplementedException();
}
protected override void OnDisconnect(IMessage message)
{
// TODO
throw new NotImplementedException();
}
protected override IResponse OnReceive(IMessage message)
{
// TODO
throw new NotImplementedException();
}
}
}

View file

@ -6,22 +6,30 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Collections.Generic;
using SafeExamBrowser.Browser;
using SafeExamBrowser.Client.Behaviour;
using SafeExamBrowser.Client.Behaviour.Operations;
using SafeExamBrowser.Client.Communication;
using SafeExamBrowser.Configuration;
using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
using SafeExamBrowser.Contracts.WindowsApi;
using SafeExamBrowser.Core.Behaviour.Operations;
using SafeExamBrowser.Core.Communication;
using SafeExamBrowser.Core.I18n;
using SafeExamBrowser.Core.Logging;
using SafeExamBrowser.Monitoring.Display;
using SafeExamBrowser.Monitoring.Keyboard;
using SafeExamBrowser.Monitoring.Mouse;
using SafeExamBrowser.Monitoring.Processes;
using SafeExamBrowser.Monitoring.Windows;
using SafeExamBrowser.SystemComponents;
using SafeExamBrowser.UserInterface.Classic;
using SafeExamBrowser.WindowsApi;
@ -29,77 +37,127 @@ namespace SafeExamBrowser.Client
{
internal class CompositionRoot
{
private IApplicationController browserController;
private IApplicationInfo browserInfo;
private IClientController clientController;
private IDisplayMonitor displayMonitor;
private IKeyboardInterceptor keyboardInterceptor;
private IClientConfiguration configuration;
private ILogger logger;
private IMouseInterceptor mouseInterceptor;
private IProcessMonitor processMonitor;
private INativeMethods nativeMethods;
private ISettings settings;
private ISystemComponent<ISystemKeyboardLayoutControl> keyboardLayout;
private ISystemComponent<ISystemPowerSupplyControl> powerSupply;
private ISystemComponent<ISystemWirelessNetworkControl> wirelessNetwork;
private ISystemInfo systemInfo;
private IText text;
private IUserInterfaceFactory uiFactory;
private IWindowMonitor windowMonitor;
//internal IShutdownController ShutdownController { get; private set; }
//internal IStartupController StartupController { get; private set; }
internal Queue<IOperation> StartupOperations { get; private set; }
internal IClientController ClientController { get; private set; }
internal Taskbar Taskbar { get; private set; }
internal void BuildObjectGraph()
{
browserInfo = new BrowserApplicationInfo();
var args = Environment.GetCommandLineArgs();
Validate(args);
configuration = new ClientConfiguration();
logger = new Logger();
nativeMethods = new NativeMethods();
settings = new ConfigurationRepository().LoadDefaultSettings();
systemInfo = new SystemInfo();
InitializeLogging();
InitializeLogging(args[1]);
text = new Text(logger);
uiFactory = new UserInterfaceFactory(text);
// TODO
//Taskbar = new Taskbar(new ModuleLogger(logger, typeof(Taskbar)));
//browserController = new BrowserApplicationController(settings.Browser, text, uiFactory);
//displayMonitor = new DisplayMonitor(new ModuleLogger(logger, typeof(DisplayMonitor)), nativeMethods);
//keyboardInterceptor = new KeyboardInterceptor(settings.Keyboard, new ModuleLogger(logger, typeof(KeyboardInterceptor)));
//keyboardLayout = new KeyboardLayout(new ModuleLogger(logger, typeof(KeyboardLayout)), text);
//mouseInterceptor = new MouseInterceptor(new ModuleLogger(logger, typeof(MouseInterceptor)), settings.Mouse);
//powerSupply = new PowerSupply(new ModuleLogger(logger, typeof(PowerSupply)), text);
//processMonitor = new ProcessMonitor(new ModuleLogger(logger, typeof(ProcessMonitor)), nativeMethods);
//windowMonitor = new WindowMonitor(new ModuleLogger(logger, typeof(WindowMonitor)), nativeMethods);
//wirelessNetwork = new WirelessNetwork(new ModuleLogger(logger, typeof(WirelessNetwork)), text);
//clientController = new ClientController(displayMonitor, new ModuleLogger(logger, typeof(ClientController)), processMonitor, Taskbar, windowMonitor);
//ShutdownController = new ShutdownController(logger, settings, text, uiFactory);
//StartupController = new StartupController(logger, settings, systemInfo, text, uiFactory);
var runtimeProxy = new RuntimeProxy(args[2], new ModuleLogger(logger, typeof(RuntimeProxy)));
var displayMonitor = new DisplayMonitor(new ModuleLogger(logger, typeof(DisplayMonitor)), nativeMethods);
var processMonitor = new ProcessMonitor(new ModuleLogger(logger, typeof(ProcessMonitor)), nativeMethods);
var windowMonitor = new WindowMonitor(new ModuleLogger(logger, typeof(WindowMonitor)), nativeMethods);
//StartupOperations = new Queue<IOperation>();
//StartupOperations.Enqueue(new I18nOperation(logger, text));
//StartupOperations.Enqueue(new KeyboardInterceptorOperation(keyboardInterceptor, logger, nativeMethods));
//StartupOperations.Enqueue(new WindowMonitorOperation(logger, windowMonitor));
//StartupOperations.Enqueue(new ProcessMonitorOperation(logger, processMonitor));
//StartupOperations.Enqueue(new DisplayMonitorOperation(displayMonitor, logger, Taskbar));
//StartupOperations.Enqueue(new TaskbarOperation(logger, settings.Taskbar, keyboardLayout, powerSupply, wirelessNetwork, systemInfo, Taskbar, text, uiFactory));
//StartupOperations.Enqueue(new BrowserOperation(browserController, browserInfo, logger, Taskbar, uiFactory));
//StartupOperations.Enqueue(new ClientControllerOperation(clientController, logger));
//StartupOperations.Enqueue(new ClipboardOperation(logger, nativeMethods));
//StartupOperations.Enqueue(new MouseInterceptorOperation(logger, mouseInterceptor, nativeMethods));
Taskbar = new Taskbar(new ModuleLogger(logger, typeof(Taskbar)));
var operations = new Queue<IOperation>();
operations.Enqueue(new I18nOperation(logger, text));
operations.Enqueue(new RuntimeConnectionOperation(logger, runtimeProxy, Guid.Parse(args[3])));
operations.Enqueue(new ConfigurationOperation(configuration, logger, runtimeProxy));
operations.Enqueue(new DelayedInitializationOperation(BuildCommunicationHostOperation));
operations.Enqueue(new DelayedInitializationOperation(BuildKeyboardInterceptorOperation));
operations.Enqueue(new WindowMonitorOperation(logger, windowMonitor));
operations.Enqueue(new ProcessMonitorOperation(logger, processMonitor));
operations.Enqueue(new DisplayMonitorOperation(displayMonitor, logger, Taskbar));
operations.Enqueue(new DelayedInitializationOperation(BuildTaskbarOperation));
operations.Enqueue(new DelayedInitializationOperation(BuildBrowserOperation));
operations.Enqueue(new ClipboardOperation(logger, nativeMethods));
operations.Enqueue(new DelayedInitializationOperation(BuildMouseInterceptorOperation));
var sequence = new OperationSequence(logger, operations);
ClientController = new ClientController(displayMonitor, logger, sequence, processMonitor, Taskbar, windowMonitor);
}
private void InitializeLogging()
private void Validate(string[] args)
{
// TODO
//var logFileWriter = new LogFileWriter(new DefaultLogFormatter(), settings.Logging.ClientLogFile);
var hasFourParameters = args?.Length == 4;
//logFileWriter.Initialize();
//logger.Subscribe(logFileWriter);
if (hasFourParameters)
{
var hasLogfilePath = Uri.TryCreate(args?[1], UriKind.Absolute, out Uri filePath) && filePath.IsFile;
var hasHostUri = Uri.TryCreate(args?[2], UriKind.Absolute, out Uri hostUri) && hostUri.IsWellFormedOriginalString();
var hasToken = Guid.TryParse(args?[3], out Guid token);
if (hasLogfilePath && hasHostUri && hasToken)
{
return;
}
}
throw new ArgumentException("Invalid parameters! Required: SafeExamBrowser.Client.exe <logfile path> <host URI> <token>");
}
private void InitializeLogging(string filePath)
{
var logFileWriter = new LogFileWriter(new DefaultLogFormatter(), filePath);
logFileWriter.Initialize();
logger.Subscribe(logFileWriter);
}
private IOperation BuildBrowserOperation()
{
var browserController = new BrowserApplicationController(configuration.Settings.Browser, configuration.RuntimeInfo, text, uiFactory);
var browserInfo = new BrowserApplicationInfo();
var operation = new BrowserOperation(browserController, browserInfo, logger, Taskbar, uiFactory);
return operation;
}
private IOperation BuildCommunicationHostOperation()
{
var host = new ClientHost(configuration.RuntimeInfo.ClientAddress, new ModuleLogger(logger, typeof(ClientHost)));
var operation = new CommunicationOperation(host, logger);
return operation;
}
private IOperation BuildKeyboardInterceptorOperation()
{
var keyboardInterceptor = new KeyboardInterceptor(configuration.Settings.Keyboard, new ModuleLogger(logger, typeof(KeyboardInterceptor)));
var operation = new KeyboardInterceptorOperation(keyboardInterceptor, logger, nativeMethods);
return operation;
}
private IOperation BuildMouseInterceptorOperation()
{
var mouseInterceptor = new MouseInterceptor(new ModuleLogger(logger, typeof(MouseInterceptor)), configuration.Settings.Mouse);
var operation = new MouseInterceptorOperation(logger, mouseInterceptor, nativeMethods);
return operation;
}
private IOperation BuildTaskbarOperation()
{
var keyboardLayout = new KeyboardLayout(new ModuleLogger(logger, typeof(KeyboardLayout)), text);
var powerSupply = new PowerSupply(new ModuleLogger(logger, typeof(PowerSupply)), text);
var wirelessNetwork = new WirelessNetwork(new ModuleLogger(logger, typeof(WirelessNetwork)), text);
var operation = new TaskbarOperation(logger, configuration.Settings.Taskbar, keyboardLayout, powerSupply, wirelessNetwork, systemInfo, Taskbar, text, uiFactory);
return operation;
}
}
}

View file

@ -70,6 +70,9 @@
<ItemGroup>
<Compile Include="App.cs" />
<Compile Include="Behaviour\ClientController.cs" />
<Compile Include="Behaviour\Operations\ConfigurationOperation.cs" />
<Compile Include="Behaviour\Operations\RuntimeConnectionOperation.cs" />
<Compile Include="Communication\ClientHost.cs" />
<Compile Include="CompositionRoot.cs" />
<Compile Include="Notifications\AboutNotificationController.cs" />
<Compile Include="Notifications\AboutNotificationIconResource.cs" />
@ -78,7 +81,6 @@
<Compile Include="Notifications\LogNotificationIconResource.cs" />
<Compile Include="Notifications\LogNotificationInfo.cs" />
<Compile Include="Behaviour\Operations\BrowserOperation.cs" />
<Compile Include="Behaviour\Operations\ClientControllerOperation.cs" />
<Compile Include="Behaviour\Operations\ClipboardOperation.cs" />
<Compile Include="Behaviour\Operations\DisplayMonitorOperation.cs" />
<Compile Include="Behaviour\Operations\KeyboardInterceptorOperation.cs" />

View file

@ -0,0 +1,22 @@
/*
* Copyright (c) 2018 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;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings;
namespace SafeExamBrowser.Configuration
{
[Serializable]
public class ClientConfiguration : IClientConfiguration
{
public ISessionData SessionData { get; set; }
public ISettings Settings { get; set; }
public IRuntimeInfo RuntimeInfo { get; set; }
}
}

View file

@ -45,6 +45,16 @@ namespace SafeExamBrowser.Configuration
return sessionData;
}
public IClientConfiguration BuildClientConfiguration()
{
return new ClientConfiguration
{
RuntimeInfo = RuntimeInfo,
SessionData = CurrentSessionData,
Settings = CurrentSettings
};
}
public ISettings LoadSettings(Uri path)
{
// TODO

View file

@ -53,6 +53,7 @@
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="ClientConfiguration.cs" />
<Compile Include="RuntimeInfo.cs" />
<Compile Include="SessionData.cs" />
<Compile Include="Settings\BrowserSettings.cs" />

View file

@ -8,16 +8,17 @@
namespace SafeExamBrowser.Contracts.Behaviour
{
// TODO: Interface really needed?!
public interface IClientController
{
/// <summary>
/// Wires up and starts the application event handling.
/// Reverts any changes, releases all used resources and terminates the client.
/// </summary>
void Start();
void Terminate();
/// <summary>
/// Stops the event handling and removes all event subscriptions.
/// Tries to start the client. Returns <c>true</c> if successful, otherwise <c>false</c>.
/// </summary>
void Stop();
bool TryStart();
}
}

View file

@ -9,6 +9,7 @@
namespace SafeExamBrowser.Contracts.Behaviour
{
// TODO: Interface really needed?!
public interface IRuntimeController
{
/// <summary>

View file

@ -0,0 +1,15 @@
/*
* Copyright (c) 2018 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/.
*/
namespace SafeExamBrowser.Contracts.Communication
{
public interface IClientHost : ICommunicationHost
{
// TODO: Necessary?
}
}

View file

@ -10,6 +10,6 @@ namespace SafeExamBrowser.Contracts.Communication
{
public interface IRuntimeHost : ICommunicationHost
{
// TODO: Necessary?
}
}

View file

@ -7,11 +7,25 @@
*/
using System;
using SafeExamBrowser.Contracts.Configuration;
namespace SafeExamBrowser.Contracts.Communication
{
public interface IRuntimeProxy
{
/// <summary>
/// Tries to establish a connection with the runtime host, utilizing the specified authentication token.
/// </summary>
bool Connect(Guid token);
/// <summary>
/// Disconnects from the runtime host.
/// </summary>
void Disconnect();
/// <summary>
/// Retrieves the application configuration from the runtime host.
/// </summary>
IClientConfiguration GetConfiguration();
}
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2018 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 SafeExamBrowser.Contracts.Configuration.Settings;
namespace SafeExamBrowser.Contracts.Configuration
{
public interface IClientConfiguration
{
/// <summary>
/// The session data to be used by the client.
/// </summary>
ISessionData SessionData { get; set; }
/// <summary>
/// The application settings to be used by the client.
/// </summary>
ISettings Settings { get; set; }
/// <summary>
/// The information about the current runtime.
/// </summary>
IRuntimeInfo RuntimeInfo { get; set; }
}
}

View file

@ -30,6 +30,11 @@ namespace SafeExamBrowser.Contracts.Configuration
/// </summary>
IRuntimeInfo RuntimeInfo { get; }
/// <summary>
/// Builds a configuration for the client component, given the currently loaded settings, session data and runtime information.
/// </summary>
IClientConfiguration BuildClientConfiguration();
/// <summary>
/// Initializes all relevant data for a new session.
/// </summary>

View file

@ -27,12 +27,14 @@ namespace SafeExamBrowser.Contracts.I18n
MessageBox_StartupErrorTitle,
Notification_AboutTooltip,
Notification_LogTooltip,
ProgressIndicator_CloseRuntimeConnection,
ProgressIndicator_CloseServiceConnection,
ProgressIndicator_EmptyClipboard,
ProgressIndicator_InitializeBrowser,
ProgressIndicator_InitializeConfiguration,
ProgressIndicator_InitializeKioskMode,
ProgressIndicator_InitializeProcessMonitoring,
ProgressIndicator_InitializeRuntimeConnection,
ProgressIndicator_InitializeServiceConnection,
ProgressIndicator_InitializeTaskbar,
ProgressIndicator_InitializeWindowMonitoring,

View file

@ -56,6 +56,7 @@
<Compile Include="Behaviour\IApplicationController.cs" />
<Compile Include="Behaviour\IRuntimeController.cs" />
<Compile Include="Behaviour\Operations\IOperationSequence.cs" />
<Compile Include="Communication\IClientHost.cs" />
<Compile Include="Communication\ICommunication.cs" />
<Compile Include="Communication\IClientProxy.cs" />
<Compile Include="Communication\ICommunicationHost.cs" />
@ -65,6 +66,7 @@
<Compile Include="Communication\Messages\IMessage.cs" />
<Compile Include="Communication\Responses\IResponse.cs" />
<Compile Include="Communication\Responses\IConnectResponse.cs" />
<Compile Include="Configuration\IClientConfiguration.cs" />
<Compile Include="Configuration\IRuntimeInfo.cs" />
<Compile Include="Configuration\ISessionData.cs" />
<Compile Include="Configuration\Settings\ConfigurationMode.cs" />

View file

@ -0,0 +1,22 @@
/*
* Copyright (c) 2018 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 Microsoft.VisualStudio.TestTools.UnitTesting;
namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations
{
[TestClass]
public class DelayedInitializationOperationTests
{
[TestMethod]
public void Todo()
{
Assert.Fail();
}
}
}

View file

@ -79,6 +79,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Behaviour\Operations\CommunicationOperationTests.cs" />
<Compile Include="Behaviour\Operations\DelayedInitializationOperationTests.cs" />
<Compile Include="Behaviour\Operations\I18nOperationTests.cs" />
<Compile Include="Behaviour\Operations\OperationSequenceTests.cs" />
<Compile Include="I18n\TextTests.cs" />

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2018 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;
using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Core.Behaviour.Operations
{
public class DelayedInitializationOperation : IOperation
{
private Func<IOperation> initialize;
private IOperation operation;
public bool Abort { get; set; }
public IProgressIndicator ProgressIndicator { get; set; }
public DelayedInitializationOperation(Func<IOperation> initialize)
{
this.initialize = initialize;
}
public void Perform()
{
operation = initialize.Invoke();
operation.ProgressIndicator = ProgressIndicator;
operation.Perform();
Abort = operation.Abort;
}
public void Repeat()
{
operation.ProgressIndicator = ProgressIndicator;
operation.Repeat();
Abort = operation.Abort;
}
public void Revert()
{
operation.ProgressIndicator = ProgressIndicator;
operation.Revert();
}
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2018 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;
using SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Core.Communication.Messages;
namespace SafeExamBrowser.Core.Communication
{
public class RuntimeProxy : BaseProxy, IRuntimeProxy
{
public RuntimeProxy(string address, ILogger logger) : base(address, logger)
{
}
public bool Connect(Guid token)
{
return base.Connect(token).ConnectionEstablished;
}
public void Disconnect()
{
FailIfNotConnected(nameof(Disconnect));
base.Disconnect(new Message { CommunicationToken = CommunicationToken.Value });
}
public IClientConfiguration GetConfiguration()
{
// TODO
throw new NotImplementedException();
}
}
}

View file

@ -36,6 +36,9 @@
<Entry key="Notification_LogTooltip">
Application Log
</Entry>
<Entry key="ProgressIndicator_CloseRuntimeConnection">
Closing runtime connection
</Entry>
<Entry key="ProgressIndicator_CloseServiceConnection">
Closing service connection
</Entry>
@ -54,6 +57,9 @@
<Entry key="ProgressIndicator_InitializeProcessMonitoring">
Initializing process monitoring
</Entry>
<Entry key="ProgressIndicator_InitializeRuntimeConnection">
Initializing runtime connection
</Entry>
<Entry key="ProgressIndicator_InitializeServiceConnection">
Initializing service connection
</Entry>

View file

@ -56,11 +56,13 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Behaviour\Operations\CommunicationOperation.cs" />
<Compile Include="Behaviour\Operations\DelayedInitializationOperation.cs" />
<Compile Include="Behaviour\Operations\I18nOperation.cs" />
<Compile Include="Behaviour\Operations\OperationSequence.cs" />
<Compile Include="Communication\BaseProxy.cs" />
<Compile Include="Communication\BaseHost.cs" />
<Compile Include="Communication\Messages\Message.cs" />
<Compile Include="Communication\RuntimeProxy.cs" />
<Compile Include="Communication\ServiceProxy.cs" />
<Compile Include="Logging\DefaultLogFormatter.cs" />
<Compile Include="Logging\LogFileWriter.cs" />

View file

@ -20,14 +20,14 @@ using SafeExamBrowser.Runtime.Behaviour.Operations;
namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
{
[TestClass]
public class ServiceOperationTests
public class ServiceConnectionOperationTests
{
private Mock<ILogger> logger;
private Mock<IServiceProxy> service;
private Mock<IConfigurationRepository> configuration;
private Mock<IProgressIndicator> progressIndicator;
private Mock<IText> text;
private ServiceOperation sut;
private ServiceConnectionOperation sut;
[TestInitialize]
public void Initialize()
@ -38,7 +38,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
progressIndicator = new Mock<IProgressIndicator>();
text = new Mock<IText>();
sut = new ServiceOperation(configuration.Object, logger.Object, service.Object, text.Object);
sut = new ServiceConnectionOperation(configuration.Object, logger.Object, service.Object, text.Object);
}
[TestMethod]

View file

@ -82,7 +82,7 @@
<ItemGroup>
<Compile Include="Behaviour\Operations\ConfigurationOperationTests.cs" />
<Compile Include="Behaviour\Operations\KioskModeOperationTests.cs" />
<Compile Include="Behaviour\Operations\ServiceOperationTests.cs" />
<Compile Include="Behaviour\Operations\ServiceConnectionOperationTests.cs" />
<Compile Include="Behaviour\RuntimeControllerTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>

View file

@ -17,10 +17,9 @@ using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Runtime.Behaviour.Operations
{
internal class ServiceOperation : IOperation
internal class ServiceConnectionOperation : IOperation
{
private bool serviceAvailable;
private bool serviceMandatory;
private bool connected, mandatory;
private IConfigurationRepository configuration;
private ILogger logger;
private IServiceProxy service;
@ -29,7 +28,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
public bool Abort { get; private set; }
public IProgressIndicator ProgressIndicator { private get; set; }
public ServiceOperation(IConfigurationRepository configuration, ILogger logger, IServiceProxy service, IText text)
public ServiceConnectionOperation(IConfigurationRepository configuration, ILogger logger, IServiceProxy service, IText text)
{
this.configuration = configuration;
this.service = service;
@ -44,23 +43,23 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
try
{
serviceMandatory = configuration.CurrentSettings.ServicePolicy == ServicePolicy.Mandatory;
serviceAvailable = service.Connect();
mandatory = configuration.CurrentSettings.ServicePolicy == ServicePolicy.Mandatory;
connected = service.Connect();
}
catch (Exception e)
{
LogException(e);
}
if (serviceMandatory && !serviceAvailable)
if (mandatory && !connected)
{
Abort = true;
logger.Info("Aborting startup because the service is mandatory but not available!");
}
else
{
service.Ignore = !serviceAvailable;
logger.Info($"The service is {(serviceMandatory ? "mandatory" : "optional")} and {(serviceAvailable ? "available." : "not available. All service-related operations will be ignored!")}");
service.Ignore = !connected;
logger.Info($"The service is {(mandatory ? "mandatory" : "optional")} and {(connected ? "available." : "not available. All service-related operations will be ignored!")}");
}
}
@ -74,7 +73,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
logger.Info("Closing service connection...");
ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_CloseServiceConnection);
if (serviceAvailable)
if (connected)
{
try
{
@ -82,7 +81,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
}
catch (Exception e)
{
logger.Error("Failed to disconnect from service component!", e);
logger.Error("Failed to disconnect from service host!", e);
}
}
}
@ -91,7 +90,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
{
var message = "Failed to connect to the service component!";
if (serviceMandatory)
if (mandatory)
{
logger.Error(message, e);
}

View file

@ -17,7 +17,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
{
internal abstract class SessionSequenceOperation : IOperation
{
private bool sessionInitialized;
private bool sessionRunning;
private IConfigurationRepository configuration;
private ILogger logger;
private IServiceProxy serviceProxy;
@ -50,13 +50,13 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
// - Verify session integrity and start event handling -> in runtime controller?
System.Threading.Thread.Sleep(5000);
sessionInitialized = true;
sessionRunning = true;
logger.Info($"Successfully started new session with identifier '{sessionData.Id}'.");
}
protected void StopSession()
{
if (sessionInitialized)
if (sessionRunning)
{
logger.Info($"Stopping session with identifier '{sessionData.Id}'...");
ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_StopSession, true);
@ -68,7 +68,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
// - Stop event handling and verify session termination -> in runtime controller?
System.Threading.Thread.Sleep(5000);
sessionInitialized = false;
sessionRunning = false;
logger.Info($"Successfully stopped session with identifier '{sessionData.Id}'.");
}
}

View file

@ -59,7 +59,7 @@ namespace SafeExamBrowser.Runtime
sessionOperations.Enqueue(new SessionSequenceStartOperation(configuration, logger, serviceProxy));
sessionOperations.Enqueue(new ConfigurationOperation(configuration, logger, runtimeInfo, text, uiFactory, args));
sessionOperations.Enqueue(new ServiceOperation(configuration, logger, serviceProxy, text));
sessionOperations.Enqueue(new ServiceConnectionOperation(configuration, logger, serviceProxy, text));
sessionOperations.Enqueue(new KioskModeOperation(logger, configuration));
sessionOperations.Enqueue(new SessionSequenceEndOperation(configuration, logger, serviceProxy));

View file

@ -89,7 +89,7 @@
<Compile Include="App.cs" />
<Compile Include="Behaviour\Operations\ConfigurationOperation.cs" />
<Compile Include="Behaviour\Operations\KioskModeOperation.cs" />
<Compile Include="Behaviour\Operations\ServiceOperation.cs" />
<Compile Include="Behaviour\Operations\ServiceConnectionOperation.cs" />
<Compile Include="Behaviour\Operations\SessionSequenceEndOperation.cs" />
<Compile Include="Behaviour\Operations\SessionSequenceOperation.cs" />
<Compile Include="Behaviour\Operations\SessionSequenceStartOperation.cs" />