From 8cd0659a222483a2ecc6e6bed726a6fa37b64c9b Mon Sep 17 00:00:00 2001 From: dbuechel Date: Tue, 6 Feb 2018 15:12:11 +0100 Subject: [PATCH] SEBWIN-219: Implemented draft of communication operation and runtime communication host. --- SafeExamBrowser.Client/CompositionRoot.cs | 2 +- .../ConfigurationRepository.cs | 87 ++++++++++++++++++ SafeExamBrowser.Configuration/RuntimeInfo.cs | 5 + .../SafeExamBrowser.Configuration.csproj | 2 +- .../Settings/SettingsRepository.cs | 37 -------- .../Behaviour/IRuntimeController.cs | 1 + .../Communication/ICommunicationHost.cs | 28 ++++++ .../Communication/IRuntimeHost.cs | 15 +++ ...ository.cs => IConfigurationRepository.cs} | 20 ++-- .../Configuration/IRuntimeInfo.cs | 31 +++++++ SafeExamBrowser.Contracts/I18n/TextKey.cs | 3 + .../SafeExamBrowser.Contracts.csproj | 4 +- .../Operations/CommunicationOperationTests.cs | 70 ++++++++++++++ .../Operations/OperationSequenceTests.cs | 23 +++++ .../SafeExamBrowser.Core.UnitTests.csproj | 1 + .../Operations/CommunicationOperation.cs | 59 ++++++++++++ .../Behaviour/Operations/OperationSequence.cs | 8 +- .../Communication/BaseHost.cs | 91 +++++++++++++++++++ .../Communication/BaseProxy.cs | 28 ++++-- .../Communication/ServiceProxy.cs | 2 +- SafeExamBrowser.Core/I18n/Text.xml | 9 ++ .../SafeExamBrowser.Core.csproj | 2 + .../Operations/ConfigurationOperationTests.cs | 38 ++++---- .../Operations/KioskModeOperationTests.cs | 2 +- .../Operations/ServiceOperationTests.cs | 37 ++++---- .../Operations/ConfigurationOperation.cs | 10 +- .../Operations/KioskModeOperation.cs | 9 +- .../Behaviour/Operations/ServiceOperation.cs | 9 +- .../Behaviour/RuntimeController.cs | 71 ++++++++++----- .../Communication/RuntimeHost.cs | 42 +++++++++ SafeExamBrowser.Runtime/CompositionRoot.cs | 49 +++------- .../SafeExamBrowser.Runtime.csproj | 1 + 32 files changed, 626 insertions(+), 170 deletions(-) create mode 100644 SafeExamBrowser.Configuration/ConfigurationRepository.cs delete mode 100644 SafeExamBrowser.Configuration/Settings/SettingsRepository.cs create mode 100644 SafeExamBrowser.Contracts/Communication/ICommunicationHost.cs create mode 100644 SafeExamBrowser.Contracts/Communication/IRuntimeHost.cs rename SafeExamBrowser.Contracts/Configuration/{Settings/ISettingsRepository.cs => IConfigurationRepository.cs} (54%) create mode 100644 SafeExamBrowser.Core.UnitTests/Behaviour/Operations/CommunicationOperationTests.cs create mode 100644 SafeExamBrowser.Core/Behaviour/Operations/CommunicationOperation.cs create mode 100644 SafeExamBrowser.Core/Communication/BaseHost.cs create mode 100644 SafeExamBrowser.Runtime/Communication/RuntimeHost.cs diff --git a/SafeExamBrowser.Client/CompositionRoot.cs b/SafeExamBrowser.Client/CompositionRoot.cs index 300b806b..35f94a47 100644 --- a/SafeExamBrowser.Client/CompositionRoot.cs +++ b/SafeExamBrowser.Client/CompositionRoot.cs @@ -58,7 +58,7 @@ namespace SafeExamBrowser.Client browserInfo = new BrowserApplicationInfo(); logger = new Logger(); nativeMethods = new NativeMethods(); - settings = new SettingsRepository().LoadDefaults(); + settings = new ConfigurationRepository().LoadDefaultSettings(); systemInfo = new SystemInfo(); uiFactory = new UserInterfaceFactory(); diff --git a/SafeExamBrowser.Configuration/ConfigurationRepository.cs b/SafeExamBrowser.Configuration/ConfigurationRepository.cs new file mode 100644 index 00000000..84853c52 --- /dev/null +++ b/SafeExamBrowser.Configuration/ConfigurationRepository.cs @@ -0,0 +1,87 @@ +/* + * 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 System.IO; +using System.Reflection; +using SafeExamBrowser.Contracts.Configuration; +using SafeExamBrowser.Contracts.Configuration.Settings; + +namespace SafeExamBrowser.Configuration +{ + public class ConfigurationRepository : IConfigurationRepository + { + private RuntimeInfo runtimeInfo; + + public ISettings CurrentSettings { get; private set; } + + public IRuntimeInfo RuntimeInfo + { + get + { + if (runtimeInfo == null) + { + InitializeRuntimeInfo(); + } + + return runtimeInfo; + } + } + + public ISettings LoadSettings(Uri path) + { + // TODO + + return LoadDefaultSettings(); + } + + public ISettings LoadDefaultSettings() + { + var settings = new Settings.Settings(); + + // TODO + settings.ServicePolicy = ServicePolicy.Optional; + + CurrentSettings = settings; + + return settings; + } + + private void InitializeRuntimeInfo() + { + var appDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), nameof(SafeExamBrowser)); + var baseAddress = "net.pipe://localhost/safeexambrowser"; + var clientId = Guid.NewGuid(); + var executable = Assembly.GetEntryAssembly(); + var runtimeId = Guid.NewGuid(); + var startTime = DateTime.Now; + var logFolder = Path.Combine(appDataFolder, "Logs"); + var logFilePrefix = startTime.ToString("yyyy-MM-dd\\_HH\\hmm\\mss\\s"); + + runtimeInfo = new RuntimeInfo + { + ApplicationStartTime = startTime, + AppDataFolder = appDataFolder, + BrowserCachePath = Path.Combine(appDataFolder, "Cache"), + BrowserLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Browser.txt"), + ClientId = Guid.NewGuid(), + ClientAddress = $"{baseAddress}/client/{clientId}", + ClientLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Client.txt"), + DefaultSettingsFileName = "SebClientSettings.seb", + ProgramCopyright = executable.GetCustomAttribute().Copyright, + ProgramDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), nameof(SafeExamBrowser)), + ProgramTitle = executable.GetCustomAttribute().Title, + ProgramVersion = executable.GetCustomAttribute().InformationalVersion, + RuntimeId = Guid.NewGuid(), + RuntimeAddress = $"{baseAddress}/runtime/{runtimeId}", + RuntimeLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Runtime.txt"), + ServiceAddress = $"{baseAddress}/service" + }; + } + } +} diff --git a/SafeExamBrowser.Configuration/RuntimeInfo.cs b/SafeExamBrowser.Configuration/RuntimeInfo.cs index 06524adb..c0cfbc89 100644 --- a/SafeExamBrowser.Configuration/RuntimeInfo.cs +++ b/SafeExamBrowser.Configuration/RuntimeInfo.cs @@ -18,12 +18,17 @@ namespace SafeExamBrowser.Configuration public DateTime ApplicationStartTime { get; set; } public string BrowserCachePath { get; set; } public string BrowserLogFile { get; set; } + public string ClientAddress { get; set; } + public Guid ClientId { get; set; } public string ClientLogFile { get; set; } public string DefaultSettingsFileName { get; set; } public string ProgramCopyright { get; set; } public string ProgramDataFolder { get; set; } public string ProgramTitle { get; set; } public string ProgramVersion { get; set; } + public string RuntimeAddress { get; set; } + public Guid RuntimeId { get; set; } public string RuntimeLogFile { get; set; } + public string ServiceAddress { get; set; } } } diff --git a/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj b/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj index e17b4eb5..d1e7b694 100644 --- a/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj +++ b/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj @@ -59,7 +59,7 @@ - + diff --git a/SafeExamBrowser.Configuration/Settings/SettingsRepository.cs b/SafeExamBrowser.Configuration/Settings/SettingsRepository.cs deleted file mode 100644 index 6343b131..00000000 --- a/SafeExamBrowser.Configuration/Settings/SettingsRepository.cs +++ /dev/null @@ -1,37 +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 System; -using SafeExamBrowser.Contracts.Configuration.Settings; - -namespace SafeExamBrowser.Configuration.Settings -{ - public class SettingsRepository : ISettingsRepository - { - public ISettings Current { get; private set; } - - public ISettings Load(Uri path) - { - // TODO - - return LoadDefaults(); - } - - public ISettings LoadDefaults() - { - var settings = new Settings(); - - // TODO - settings.ServicePolicy = ServicePolicy.Optional; - - Current = settings; - - return settings; - } - } -} diff --git a/SafeExamBrowser.Contracts/Behaviour/IRuntimeController.cs b/SafeExamBrowser.Contracts/Behaviour/IRuntimeController.cs index e40a3518..500f38b0 100644 --- a/SafeExamBrowser.Contracts/Behaviour/IRuntimeController.cs +++ b/SafeExamBrowser.Contracts/Behaviour/IRuntimeController.cs @@ -6,6 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + namespace SafeExamBrowser.Contracts.Behaviour { public interface IRuntimeController diff --git a/SafeExamBrowser.Contracts/Communication/ICommunicationHost.cs b/SafeExamBrowser.Contracts/Communication/ICommunicationHost.cs new file mode 100644 index 00000000..7004f4a1 --- /dev/null +++ b/SafeExamBrowser.Contracts/Communication/ICommunicationHost.cs @@ -0,0 +1,28 @@ +/* + * 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 ICommunicationHost + { + /// + /// Indicates whether the host is running and ready for communication. + /// + bool IsRunning { get; } + + /// + /// Starts the host and opens it for communication. + /// + void Start(); + + /// + /// Closes and terminates the host. + /// + void Stop(); + } +} diff --git a/SafeExamBrowser.Contracts/Communication/IRuntimeHost.cs b/SafeExamBrowser.Contracts/Communication/IRuntimeHost.cs new file mode 100644 index 00000000..dbcb1463 --- /dev/null +++ b/SafeExamBrowser.Contracts/Communication/IRuntimeHost.cs @@ -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 IRuntimeHost : ICommunicationHost + { + + } +} diff --git a/SafeExamBrowser.Contracts/Configuration/Settings/ISettingsRepository.cs b/SafeExamBrowser.Contracts/Configuration/IConfigurationRepository.cs similarity index 54% rename from SafeExamBrowser.Contracts/Configuration/Settings/ISettingsRepository.cs rename to SafeExamBrowser.Contracts/Configuration/IConfigurationRepository.cs index 8c419131..baa652b2 100644 --- a/SafeExamBrowser.Contracts/Configuration/Settings/ISettingsRepository.cs +++ b/SafeExamBrowser.Contracts/Configuration/IConfigurationRepository.cs @@ -7,26 +7,32 @@ */ using System; +using SafeExamBrowser.Contracts.Configuration.Settings; -namespace SafeExamBrowser.Contracts.Configuration.Settings +namespace SafeExamBrowser.Contracts.Configuration { - public interface ISettingsRepository + public interface IConfigurationRepository { /// /// Retrieves the current settings, i.e. the last ones which were loaded. If no settings have been loaded yet, this property will - /// be null. + /// be null! /// - ISettings Current { get; } + ISettings CurrentSettings { get; } + + /// + /// The runtime information for the currently running application instance. + /// + IRuntimeInfo RuntimeInfo { get; } /// /// Attempts to load settings from the specified path. /// - /// Thrown if the given path cannot be resolved to a settings file. - ISettings Load(Uri path); + /// Thrown if the given path cannot be resolved to a settings file. + ISettings LoadSettings(Uri path); /// /// Loads the default settings. /// - ISettings LoadDefaults(); + ISettings LoadDefaultSettings(); } } diff --git a/SafeExamBrowser.Contracts/Configuration/IRuntimeInfo.cs b/SafeExamBrowser.Contracts/Configuration/IRuntimeInfo.cs index c6ddef4e..da2f96dd 100644 --- a/SafeExamBrowser.Contracts/Configuration/IRuntimeInfo.cs +++ b/SafeExamBrowser.Contracts/Configuration/IRuntimeInfo.cs @@ -32,6 +32,22 @@ namespace SafeExamBrowser.Contracts.Configuration /// string BrowserLogFile { get; } + /// + /// The communication address of the client component. + /// + /// TODO: Will need to be updated for each new client instance! + /// + /// + string ClientAddress { get; } + + /// + /// The unique identifier for the currently running client instance. + /// + /// TODO: Will need to be updated for each new client instance! + /// + /// + Guid ClientId { get; } + /// /// The file path under which the log of the client component is to be stored. /// @@ -62,9 +78,24 @@ namespace SafeExamBrowser.Contracts.Configuration /// string ProgramVersion { get; } + /// + /// The communication address of the runtime component. + /// + string RuntimeAddress { get; } + + /// + /// The unique identifier for the currently running runtime instance. + /// + Guid RuntimeId { get; } + /// /// The file path under which the log of the runtime component is to be stored. /// string RuntimeLogFile { get; } + + /// + /// The communication address of the service component. + /// + string ServiceAddress { get; } } } diff --git a/SafeExamBrowser.Contracts/I18n/TextKey.cs b/SafeExamBrowser.Contracts/I18n/TextKey.cs index d5cf4ee6..6b4e74f5 100644 --- a/SafeExamBrowser.Contracts/I18n/TextKey.cs +++ b/SafeExamBrowser.Contracts/I18n/TextKey.cs @@ -35,12 +35,15 @@ namespace SafeExamBrowser.Contracts.I18n ProgressIndicator_InitializeTaskbar, ProgressIndicator_InitializeWindowMonitoring, ProgressIndicator_InitializeWorkingArea, + ProgressIndicator_RestartCommunicationHost, ProgressIndicator_RestoreWorkingArea, ProgressIndicator_RevertKioskMode, ProgressIndicator_ShutdownProcedure, + ProgressIndicator_StartCommunicationHost, ProgressIndicator_StartEventHandling, ProgressIndicator_StartKeyboardInterception, ProgressIndicator_StartMouseInterception, + ProgressIndicator_StopCommunicationHost, ProgressIndicator_StopEventHandling, ProgressIndicator_StopKeyboardInterception, ProgressIndicator_StopMouseInterception, diff --git a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj index a2abce6c..b851b8a1 100644 --- a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj +++ b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj @@ -58,6 +58,8 @@ + + @@ -78,7 +80,7 @@ - + diff --git a/SafeExamBrowser.Core.UnitTests/Behaviour/Operations/CommunicationOperationTests.cs b/SafeExamBrowser.Core.UnitTests/Behaviour/Operations/CommunicationOperationTests.cs new file mode 100644 index 00000000..14a095a4 --- /dev/null +++ b/SafeExamBrowser.Core.UnitTests/Behaviour/Operations/CommunicationOperationTests.cs @@ -0,0 +1,70 @@ +/* + * 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.Contracts.Communication; +using SafeExamBrowser.Contracts.Logging; +using SafeExamBrowser.Core.Behaviour.Operations; + +namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations +{ + [TestClass] + public class CommunicationOperationTests + { + private Mock hostMock; + private Mock loggerMock; + private CommunicationOperation sut; + + [TestInitialize] + public void Initialize() + { + hostMock = new Mock(); + loggerMock = new Mock(); + + sut = new CommunicationOperation(hostMock.Object, loggerMock.Object); + } + + [TestMethod] + public void MustRestartHostOnRepeat() + { + var order = 0; + var stop = 0; + var start = 0; + + hostMock.Setup(h => h.Stop()).Callback(() => stop = ++order); + hostMock.Setup(h => h.Start()).Callback(() => start = ++order); + + sut.Repeat(); + + hostMock.Verify(h => h.Stop(), Times.Once); + hostMock.Verify(h => h.Start(), Times.Once); + + Assert.AreEqual(stop, 1); + Assert.AreEqual(start, 2); + } + + [TestMethod] + public void MustStartHostOnPerform() + { + sut.Perform(); + + hostMock.Verify(h => h.Start(), Times.Once); + hostMock.Verify(h => h.Stop(), Times.Never); + } + + [TestMethod] + public void MustStopHostOnRevert() + { + sut.Revert(); + + hostMock.Verify(h => h.Stop(), Times.Once); + hostMock.Verify(h => h.Start(), Times.Never); + } + } +} diff --git a/SafeExamBrowser.Core.UnitTests/Behaviour/Operations/OperationSequenceTests.cs b/SafeExamBrowser.Core.UnitTests/Behaviour/Operations/OperationSequenceTests.cs index 2ed608cd..e496af26 100644 --- a/SafeExamBrowser.Core.UnitTests/Behaviour/Operations/OperationSequenceTests.cs +++ b/SafeExamBrowser.Core.UnitTests/Behaviour/Operations/OperationSequenceTests.cs @@ -28,6 +28,29 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations loggerMock = new Mock(); } + [TestMethod] + public void MustCreateCopyOfOperationQueue() + { + var operationA = new Mock(); + var operationB = new Mock(); + var operationC = new Mock(); + var operations = new Queue(); + + operations.Enqueue(operationA.Object); + operations.Enqueue(operationB.Object); + operations.Enqueue(operationC.Object); + + var sut = new OperationSequence(loggerMock.Object, operations); + + operations.Clear(); + + sut.TryPerform(); + + operationA.Verify(o => o.Perform(), Times.Once); + operationB.Verify(o => o.Perform(), Times.Once); + operationC.Verify(o => o.Perform(), Times.Once); + } + #region Perform Tests [TestMethod] diff --git a/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj b/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj index b78f2aad..5a0df3b5 100644 --- a/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj +++ b/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj @@ -78,6 +78,7 @@ + diff --git a/SafeExamBrowser.Core/Behaviour/Operations/CommunicationOperation.cs b/SafeExamBrowser.Core/Behaviour/Operations/CommunicationOperation.cs new file mode 100644 index 00000000..7ae5891b --- /dev/null +++ b/SafeExamBrowser.Core/Behaviour/Operations/CommunicationOperation.cs @@ -0,0 +1,59 @@ +/* + * 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.Operations; +using SafeExamBrowser.Contracts.Communication; +using SafeExamBrowser.Contracts.I18n; +using SafeExamBrowser.Contracts.Logging; +using SafeExamBrowser.Contracts.UserInterface; + +namespace SafeExamBrowser.Core.Behaviour.Operations +{ + public class CommunicationOperation : IOperation + { + private ICommunicationHost host; + private ILogger logger; + + public bool Abort { get; private set; } + public IProgressIndicator ProgressIndicator { private get; set; } + + public CommunicationOperation(ICommunicationHost host, ILogger logger) + { + this.host = host; + this.logger = logger; + } + + public void Perform() + { + logger.Info("Starting communication host..."); + ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_StartCommunicationHost); + + host.Start(); + } + + public void Repeat() + { + if (!host.IsRunning) + { + logger.Info("Restarting communication host..."); + ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_RestartCommunicationHost); + + host.Stop(); + host.Start(); + } + } + + public void Revert() + { + logger.Info("Stopping communication host..."); + ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_StopCommunicationHost); + + host.Stop(); + } + } +} diff --git a/SafeExamBrowser.Core/Behaviour/Operations/OperationSequence.cs b/SafeExamBrowser.Core/Behaviour/Operations/OperationSequence.cs index 37f4cf92..3dff57a9 100644 --- a/SafeExamBrowser.Core/Behaviour/Operations/OperationSequence.cs +++ b/SafeExamBrowser.Core/Behaviour/Operations/OperationSequence.cs @@ -40,7 +40,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations if (!success) { - Revert(); + Revert(true); } } catch (Exception e) @@ -74,8 +74,8 @@ namespace SafeExamBrowser.Core.Behaviour.Operations try { - Initialize(); - success = Revert(false); + Initialize(true); + success = Revert(); } catch (Exception e) { @@ -154,7 +154,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations return true; } - private bool Revert(bool regress = true) + private bool Revert(bool regress = false) { var success = true; diff --git a/SafeExamBrowser.Core/Communication/BaseHost.cs b/SafeExamBrowser.Core/Communication/BaseHost.cs new file mode 100644 index 00000000..4940fb17 --- /dev/null +++ b/SafeExamBrowser.Core/Communication/BaseHost.cs @@ -0,0 +1,91 @@ +/* + * 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 System.ServiceModel; +using SafeExamBrowser.Contracts.Communication; +using SafeExamBrowser.Contracts.Communication.Messages; +using SafeExamBrowser.Contracts.Communication.Responses; +using SafeExamBrowser.Contracts.Logging; + +namespace SafeExamBrowser.Core.Communication +{ + [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single)] + public abstract class BaseHost : ICommunication, ICommunicationHost + { + private string address; + private ILogger logger; + private ServiceHost host; + + public bool IsRunning + { + get { return host?.State == CommunicationState.Opened; } + } + + public BaseHost(string address, ILogger logger) + { + this.address = address; + this.logger = logger; + } + + public abstract IConnectResponse Connect(Guid? token = null); + public abstract void Disconnect(IMessage message); + public abstract IResponse Send(IMessage message); + + public void Start() + { + host = new ServiceHost(this); + host.AddServiceEndpoint(typeof(ICommunication), new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport), address); + host.Closed += Host_Closed; + host.Closing += Host_Closing; + host.Faulted += Host_Faulted; + host.Opened += Host_Opened; + host.Opening += Host_Opening; + host.UnknownMessageReceived += Host_UnknownMessageReceived; + host.Open(); + + logger.Debug($"Successfully started communication host for endpoint '{address}'."); + } + + public void Stop() + { + host?.Close(); + logger.Debug($"Terminated communication host for endpoint '{address}'."); + } + + private void Host_Closed(object sender, EventArgs e) + { + logger.Debug("Communication host has been closed."); + } + + private void Host_Closing(object sender, EventArgs e) + { + logger.Debug("Communication host is closing..."); + } + + private void Host_Faulted(object sender, EventArgs e) + { + logger.Debug("Communication host has faulted!"); + } + + private void Host_Opened(object sender, EventArgs e) + { + logger.Debug("Communication host has been opened."); + } + + private void Host_Opening(object sender, EventArgs e) + { + logger.Debug("Communication host is opening..."); + } + + private void Host_UnknownMessageReceived(object sender, UnknownMessageReceivedEventArgs e) + { + logger.Debug($"Communication host has received an unknown message: {e?.Message}."); + } + } +} diff --git a/SafeExamBrowser.Core/Communication/BaseProxy.cs b/SafeExamBrowser.Core/Communication/BaseProxy.cs index 55aa203b..1b10b338 100644 --- a/SafeExamBrowser.Core/Communication/BaseProxy.cs +++ b/SafeExamBrowser.Core/Communication/BaseProxy.cs @@ -23,7 +23,7 @@ namespace SafeExamBrowser.Core.Communication protected Guid? CommunicationToken { get; private set; } protected ILogger Logger { get; private set; } - public BaseProxy(ILogger logger, string address) + public BaseProxy(string address, ILogger logger) { this.address = address; this.Logger = logger; @@ -34,9 +34,11 @@ namespace SafeExamBrowser.Core.Communication var endpoint = new EndpointAddress(address); channel = ChannelFactory.CreateChannel(new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport), endpoint); - (channel as ICommunicationObject).Closed += CommunicationHostProxy_Closed; - (channel as ICommunicationObject).Closing += CommunicationHostProxy_Closing; - (channel as ICommunicationObject).Faulted += CommunicationHostProxy_Faulted; + (channel as ICommunicationObject).Closed += BaseProxy_Closed; + (channel as ICommunicationObject).Closing += BaseProxy_Closing; + (channel as ICommunicationObject).Faulted += BaseProxy_Faulted; + (channel as ICommunicationObject).Opened += BaseProxy_Opened; + (channel as ICommunicationObject).Opening += BaseProxy_Opening; var response = channel.Connect(token); @@ -84,21 +86,31 @@ namespace SafeExamBrowser.Core.Communication return channel != null && (channel as ICommunicationObject).State == CommunicationState.Opened; } - private void CommunicationHostProxy_Closed(object sender, EventArgs e) + private void BaseProxy_Closed(object sender, EventArgs e) { Logger.Debug("Communication channel has been closed."); } - private void CommunicationHostProxy_Closing(object sender, EventArgs e) + private void BaseProxy_Closing(object sender, EventArgs e) { - Logger.Debug("Communication channel is closing."); + Logger.Debug("Communication channel is closing..."); } - private void CommunicationHostProxy_Faulted(object sender, EventArgs e) + private void BaseProxy_Faulted(object sender, EventArgs e) { Logger.Debug("Communication channel has faulted!"); } + private void BaseProxy_Opened(object sender, EventArgs e) + { + Logger.Debug("Communication channel has been opened."); + } + + private void BaseProxy_Opening(object sender, EventArgs e) + { + Logger.Debug("Communication channel is opening..."); + } + private string GetChannelState() { return channel == null ? "null" : $"in state '{(channel as ICommunicationObject).State}'"; diff --git a/SafeExamBrowser.Core/Communication/ServiceProxy.cs b/SafeExamBrowser.Core/Communication/ServiceProxy.cs index 7af22a74..8654d33c 100644 --- a/SafeExamBrowser.Core/Communication/ServiceProxy.cs +++ b/SafeExamBrowser.Core/Communication/ServiceProxy.cs @@ -16,7 +16,7 @@ namespace SafeExamBrowser.Core.Communication { public bool Ignore { private get; set; } - public ServiceProxy(ILogger logger, string address) : base(logger, address) + public ServiceProxy(string address, ILogger logger) : base(address, logger) { } diff --git a/SafeExamBrowser.Core/I18n/Text.xml b/SafeExamBrowser.Core/I18n/Text.xml index 77ec31b0..2d553a04 100644 --- a/SafeExamBrowser.Core/I18n/Text.xml +++ b/SafeExamBrowser.Core/I18n/Text.xml @@ -60,6 +60,9 @@ Initializing working area + + Restarting communication host + Restoring working area @@ -69,6 +72,9 @@ Initiating shutdown procedure + + Starting communication host + Starting event handling @@ -78,6 +84,9 @@ Starting mouse interception + + Stopping communication host + Stopping event handling diff --git a/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj b/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj index 33514868..31e04463 100644 --- a/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj +++ b/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj @@ -55,9 +55,11 @@ + + diff --git a/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/ConfigurationOperationTests.cs b/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/ConfigurationOperationTests.cs index 8b672df7..0f14dc1f 100644 --- a/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/ConfigurationOperationTests.cs +++ b/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/ConfigurationOperationTests.cs @@ -24,7 +24,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations { private Mock logger; private Mock info; - private Mock repository; + private Mock repository; private Mock settings; private Mock text; private Mock uiFactory; @@ -35,7 +35,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations { logger = new Mock(); info = new Mock(); - repository = new Mock(); + repository = new Mock(); settings = new Mock(); text = new Mock(); uiFactory = new Mock(); @@ -43,24 +43,24 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations info.SetupGet(i => i.AppDataFolder).Returns(@"C:\Not\Really\AppData"); info.SetupGet(i => i.DefaultSettingsFileName).Returns("SettingsDummy.txt"); info.SetupGet(i => i.ProgramDataFolder).Returns(@"C:\Not\Really\ProgramData"); - repository.Setup(r => r.Load(It.IsAny())).Returns(settings.Object); - repository.Setup(r => r.LoadDefaults()).Returns(settings.Object); + repository.Setup(r => r.LoadSettings(It.IsAny())).Returns(settings.Object); + repository.Setup(r => r.LoadDefaultSettings()).Returns(settings.Object); } [TestMethod] public void MustNotFailWithoutCommandLineArgs() { - repository.Setup(r => r.LoadDefaults()); + repository.Setup(r => r.LoadDefaultSettings()); - sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null); + sut = new ConfigurationOperation(repository.Object, logger.Object, info.Object, text.Object, uiFactory.Object, null); sut.Perform(); - sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, new string[] { }); + sut = new ConfigurationOperation(repository.Object, logger.Object, info.Object, text.Object, uiFactory.Object, new string[] { }); sut.Perform(); - repository.Verify(r => r.LoadDefaults(), Times.Exactly(2)); + repository.Verify(r => r.LoadDefaultSettings(), Times.Exactly(2)); } [TestMethod] @@ -68,7 +68,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations { var path = @"an/invalid\path.'*%yolo/()"; - sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, new [] { "blubb.exe", path }); + sut = new ConfigurationOperation(repository.Object, logger.Object, info.Object, text.Object, uiFactory.Object, new [] { "blubb.exe", path }); sut.Perform(); } @@ -82,11 +82,11 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations info.SetupGet(r => r.ProgramDataFolder).Returns(location); info.SetupGet(r => r.AppDataFolder).Returns(location); - sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, new[] { "blubb.exe", path }); + sut = new ConfigurationOperation(repository.Object, logger.Object, info.Object, text.Object, uiFactory.Object, new[] { "blubb.exe", path }); sut.Perform(); - repository.Verify(r => r.Load(It.Is(u => u.Equals(new Uri(path)))), Times.Once); + repository.Verify(r => r.LoadSettings(It.Is(u => u.Equals(new Uri(path)))), Times.Once); } [TestMethod] @@ -97,11 +97,11 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations info.SetupGet(r => r.ProgramDataFolder).Returns(location); info.SetupGet(r => r.AppDataFolder).Returns($@"{location}\WRONG"); - sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null); + sut = new ConfigurationOperation(repository.Object, logger.Object, info.Object, text.Object, uiFactory.Object, null); sut.Perform(); - repository.Verify(r => r.Load(It.Is(u => u.Equals(new Uri(Path.Combine(location, "SettingsDummy.txt"))))), Times.Once); + repository.Verify(r => r.LoadSettings(It.Is(u => u.Equals(new Uri(Path.Combine(location, "SettingsDummy.txt"))))), Times.Once); } [TestMethod] @@ -111,21 +111,21 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations info.SetupGet(r => r.AppDataFolder).Returns(location); - sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null); + sut = new ConfigurationOperation(repository.Object, logger.Object, info.Object, text.Object, uiFactory.Object, null); sut.Perform(); - repository.Verify(r => r.Load(It.Is(u => u.Equals(new Uri(Path.Combine(location, "SettingsDummy.txt"))))), Times.Once); + repository.Verify(r => r.LoadSettings(It.Is(u => u.Equals(new Uri(Path.Combine(location, "SettingsDummy.txt"))))), Times.Once); } [TestMethod] public void MustFallbackToDefaultsAsLastPrio() { - sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null); + sut = new ConfigurationOperation(repository.Object, logger.Object, info.Object, text.Object, uiFactory.Object, null); sut.Perform(); - repository.Verify(r => r.LoadDefaults(), Times.Once); + repository.Verify(r => r.LoadDefaultSettings(), Times.Once); } [TestMethod] @@ -136,7 +136,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations info.SetupGet(r => r.ProgramDataFolder).Returns(location); uiFactory.Setup(u => u.Show(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(MessageBoxResult.Yes); - sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null); + sut = new ConfigurationOperation(repository.Object, logger.Object, info.Object, text.Object, uiFactory.Object, null); sut.Perform(); @@ -148,7 +148,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations { uiFactory.Setup(u => u.Show(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(MessageBoxResult.No); - sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null); + sut = new ConfigurationOperation(repository.Object, logger.Object, info.Object, text.Object, uiFactory.Object, null); sut.Perform(); diff --git a/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/KioskModeOperationTests.cs b/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/KioskModeOperationTests.cs index e32291a9..4872f701 100644 --- a/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/KioskModeOperationTests.cs +++ b/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/KioskModeOperationTests.cs @@ -14,7 +14,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations public class KioskModeOperationTests { [TestMethod] - public void Todo() + public void TODO() { Assert.Fail(); } diff --git a/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/ServiceOperationTests.cs b/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/ServiceOperationTests.cs index 6b1e918a..4b5a925e 100644 --- a/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/ServiceOperationTests.cs +++ b/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/ServiceOperationTests.cs @@ -10,6 +10,7 @@ using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using SafeExamBrowser.Contracts.Communication; +using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration.Settings; using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.Logging; @@ -23,7 +24,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations { private Mock logger; private Mock service; - private Mock settings; + private Mock configuration; private Mock progressIndicator; private Mock text; private ServiceOperation sut; @@ -33,23 +34,23 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations { logger = new Mock(); service = new Mock(); - settings = new Mock(); + configuration = new Mock(); progressIndicator = new Mock(); text = new Mock(); - sut = new ServiceOperation(logger.Object, service.Object, settings.Object, text.Object); + sut = new ServiceOperation(configuration.Object, logger.Object, service.Object, text.Object); } [TestMethod] public void MustConnectToService() { service.Setup(s => s.Connect()).Returns(true); - settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Mandatory); + configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Mandatory); sut.Perform(); service.Setup(s => s.Connect()).Returns(true); - settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Optional); + configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Optional); sut.Perform(); @@ -60,22 +61,22 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations public void MustNotFailIfServiceNotAvailable() { service.Setup(s => s.Connect()).Returns(false); - settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Mandatory); + configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Mandatory); sut.Perform(); service.Setup(s => s.Connect()).Returns(false); - settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Optional); + configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Optional); sut.Perform(); service.Setup(s => s.Connect()).Throws(); - settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Mandatory); + configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Mandatory); sut.Perform(); service.Setup(s => s.Connect()).Throws(); - settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Optional); + configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Optional); sut.Perform(); } @@ -84,7 +85,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations public void MustAbortIfServiceMandatoryAndNotAvailable() { service.Setup(s => s.Connect()).Returns(false); - settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Mandatory); + configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Mandatory); sut.Perform(); @@ -95,7 +96,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations public void MustNotAbortIfServiceOptionalAndNotAvailable() { service.Setup(s => s.Connect()).Returns(false); - settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Optional); + configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Optional); sut.Perform(); @@ -107,13 +108,13 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations public void MustDisconnectWhenReverting() { service.Setup(s => s.Connect()).Returns(true); - settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Mandatory); + configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Mandatory); sut.Perform(); sut.Revert(); service.Setup(s => s.Connect()).Returns(true); - settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Optional); + configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Optional); sut.Perform(); sut.Revert(); @@ -126,7 +127,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations { service.Setup(s => s.Connect()).Returns(true); service.Setup(s => s.Disconnect()).Throws(); - settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Optional); + configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Optional); sut.Perform(); sut.Revert(); @@ -138,25 +139,25 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations public void MustNotDisconnnectIfNotAvailable() { service.Setup(s => s.Connect()).Returns(false); - settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Mandatory); + configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Mandatory); sut.Perform(); sut.Revert(); service.Setup(s => s.Connect()).Returns(false); - settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Optional); + configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Optional); sut.Perform(); sut.Revert(); service.Setup(s => s.Connect()).Throws(); - settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Mandatory); + configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Mandatory); sut.Perform(); sut.Revert(); service.Setup(s => s.Connect()).Throws(); - settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Optional); + configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Optional); sut.Perform(); sut.Revert(); diff --git a/SafeExamBrowser.Runtime/Behaviour/Operations/ConfigurationOperation.cs b/SafeExamBrowser.Runtime/Behaviour/Operations/ConfigurationOperation.cs index a0ffca2c..c492ba24 100644 --- a/SafeExamBrowser.Runtime/Behaviour/Operations/ConfigurationOperation.cs +++ b/SafeExamBrowser.Runtime/Behaviour/Operations/ConfigurationOperation.cs @@ -19,9 +19,9 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations { internal class ConfigurationOperation : IOperation { + private IConfigurationRepository repository; private ILogger logger; private IRuntimeInfo runtimeInfo; - private ISettingsRepository repository; private IText text; private IUserInterfaceFactory uiFactory; private string[] commandLineArgs; @@ -30,16 +30,16 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations public IProgressIndicator ProgressIndicator { private get; set; } public ConfigurationOperation( + IConfigurationRepository repository, ILogger logger, IRuntimeInfo runtimeInfo, - ISettingsRepository repository, IText text, IUserInterfaceFactory uiFactory, string[] commandLineArgs) { + this.repository = repository; this.logger = logger; this.commandLineArgs = commandLineArgs; - this.repository = repository; this.runtimeInfo = runtimeInfo; this.text = text; this.uiFactory = uiFactory; @@ -56,7 +56,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations if (isValidUri) { logger.Info($"Loading configuration from '{uri.AbsolutePath}'..."); - settings = repository.Load(uri); + settings = repository.LoadSettings(uri); if (settings.ConfigurationMode == ConfigurationMode.ConfigureClient && UserWantsToAbortStartup()) { @@ -67,7 +67,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations else { logger.Info("No valid settings file specified nor found in PROGRAMDATA or APPDATA - loading default settings..."); - settings = repository.LoadDefaults(); + settings = repository.LoadDefaultSettings(); } } diff --git a/SafeExamBrowser.Runtime/Behaviour/Operations/KioskModeOperation.cs b/SafeExamBrowser.Runtime/Behaviour/Operations/KioskModeOperation.cs index 04c56dbc..e697ad4a 100644 --- a/SafeExamBrowser.Runtime/Behaviour/Operations/KioskModeOperation.cs +++ b/SafeExamBrowser.Runtime/Behaviour/Operations/KioskModeOperation.cs @@ -7,6 +7,7 @@ */ using SafeExamBrowser.Contracts.Behaviour.Operations; +using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration.Settings; using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.Logging; @@ -17,21 +18,21 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations internal class KioskModeOperation : IOperation { private ILogger logger; - private ISettingsRepository settingsRepository; + private IConfigurationRepository configuration; private KioskMode kioskMode; public bool Abort { get; private set; } public IProgressIndicator ProgressIndicator { private get; set; } - public KioskModeOperation(ILogger logger, ISettingsRepository settingsRepository) + public KioskModeOperation(ILogger logger, IConfigurationRepository configuration) { this.logger = logger; - this.settingsRepository = settingsRepository; + this.configuration = configuration; } public void Perform() { - kioskMode = settingsRepository.Current.KioskMode; + kioskMode = configuration.CurrentSettings.KioskMode; logger.Info($"Initializing kiosk mode '{kioskMode}'..."); ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_InitializeKioskMode); diff --git a/SafeExamBrowser.Runtime/Behaviour/Operations/ServiceOperation.cs b/SafeExamBrowser.Runtime/Behaviour/Operations/ServiceOperation.cs index 453779fa..22a3f0a9 100644 --- a/SafeExamBrowser.Runtime/Behaviour/Operations/ServiceOperation.cs +++ b/SafeExamBrowser.Runtime/Behaviour/Operations/ServiceOperation.cs @@ -9,6 +9,7 @@ using System; using SafeExamBrowser.Contracts.Behaviour.Operations; using SafeExamBrowser.Contracts.Communication; +using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration.Settings; using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.Logging; @@ -20,19 +21,19 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations { private bool serviceAvailable; private bool serviceMandatory; + private IConfigurationRepository configuration; private ILogger logger; private IServiceProxy service; - private ISettingsRepository settingsRepository; private IText text; public bool Abort { get; private set; } public IProgressIndicator ProgressIndicator { private get; set; } - public ServiceOperation(ILogger logger, IServiceProxy service, ISettingsRepository settingsRepository, IText text) + public ServiceOperation(IConfigurationRepository configuration, ILogger logger, IServiceProxy service, IText text) { + this.configuration = configuration; this.service = service; this.logger = logger; - this.settingsRepository = settingsRepository; this.text = text; } @@ -43,7 +44,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations try { - serviceMandatory = settingsRepository.Current.ServicePolicy == ServicePolicy.Mandatory; + serviceMandatory = configuration.CurrentSettings.ServicePolicy == ServicePolicy.Mandatory; serviceAvailable = service.Connect(); } catch (Exception e) diff --git a/SafeExamBrowser.Runtime/Behaviour/RuntimeController.cs b/SafeExamBrowser.Runtime/Behaviour/RuntimeController.cs index d7335342..cd8374aa 100644 --- a/SafeExamBrowser.Runtime/Behaviour/RuntimeController.cs +++ b/SafeExamBrowser.Runtime/Behaviour/RuntimeController.cs @@ -20,36 +20,41 @@ namespace SafeExamBrowser.Runtime.Behaviour { internal class RuntimeController : IRuntimeController { + private bool initialized; + + private IConfigurationRepository configuration; private ILogger logger; private IOperationSequence bootstrapSequence; private IOperationSequence sessionSequence; + private IRuntimeHost runtimeHost; private IRuntimeInfo runtimeInfo; private IRuntimeWindow runtimeWindow; private IServiceProxy serviceProxy; - private ISettingsRepository settingsRepository; private ISplashScreen splashScreen; - private Action terminationCallback; + private Action shutdown; private IText text; private IUserInterfaceFactory uiFactory; - + public RuntimeController( + IConfigurationRepository configuration, ILogger logger, IOperationSequence bootstrapSequence, IOperationSequence sessionSequence, + IRuntimeHost runtimeHost, IRuntimeInfo runtimeInfo, IServiceProxy serviceProxy, - ISettingsRepository settingsRepository, - Action terminationCallback, + Action shutdown, IText text, IUserInterfaceFactory uiFactory) { + this.configuration = configuration; this.logger = logger; this.bootstrapSequence = bootstrapSequence; this.sessionSequence = sessionSequence; + this.runtimeHost = runtimeHost; this.runtimeInfo = runtimeInfo; this.serviceProxy = serviceProxy; - this.settingsRepository = settingsRepository; - this.terminationCallback = terminationCallback; + this.shutdown = shutdown; this.text = text; this.uiFactory = uiFactory; } @@ -58,19 +63,16 @@ namespace SafeExamBrowser.Runtime.Behaviour { logger.Info("--- Initiating startup procedure ---"); + runtimeWindow = uiFactory.CreateRuntimeWindow(runtimeInfo, text); splashScreen = uiFactory.CreateSplashScreen(runtimeInfo, text); splashScreen.Show(); bootstrapSequence.ProgressIndicator = splashScreen; - var success = bootstrapSequence.TryPerform(); + initialized = bootstrapSequence.TryPerform(); - System.Threading.Thread.Sleep(5000); - - if (success) + if (initialized) { - runtimeWindow = uiFactory.CreateRuntimeWindow(runtimeInfo, text); - logger.Info("--- Application successfully initialized! ---"); logger.Log(string.Empty); logger.Subscribe(runtimeWindow); @@ -85,17 +87,19 @@ namespace SafeExamBrowser.Runtime.Behaviour logger.Log(string.Empty); } - return success; + return initialized; } public void Terminate() { - StopSession(); + // TODO: Necessary here? Move to App.cs as private "started" flag if not... + if (!initialized) + { + return; + } - // TODO: - // - Disconnect from service - // - Terminate runtime communication host - // - Revert kiosk mode (or do that when stopping session?) + // TODO: Only if session is running! + StopSession(); logger.Unsubscribe(runtimeWindow); runtimeWindow?.Close(); @@ -104,6 +108,10 @@ namespace SafeExamBrowser.Runtime.Behaviour logger.Log(string.Empty); logger.Info("--- Initiating shutdown procedure ---"); + // TODO: + // - Disconnect from service + // - Terminate runtime communication host + // - Revert kiosk mode (or do that when stopping session?) var success = bootstrapSequence.TryRevert(); if (success) @@ -137,25 +145,28 @@ namespace SafeExamBrowser.Runtime.Behaviour if (success) { - + // TODO } else { - + // TODO } + // TODO: Remove! System.Threading.Thread.Sleep(5000); + runtimeWindow.HideProgressBar(); runtimeWindow.UpdateText(TextKey.RuntimeWindow_ApplicationRunning); - if (settingsRepository.Current.KioskMode == KioskMode.DisableExplorerShell) + if (configuration.CurrentSettings.KioskMode == KioskMode.DisableExplorerShell) { runtimeWindow.Hide(); } + // TODO: Remove! System.Threading.Thread.Sleep(5000); - terminationCallback.Invoke(); + shutdown.Invoke(); } private void StopSession() @@ -163,12 +174,26 @@ namespace SafeExamBrowser.Runtime.Behaviour logger.Info("Stopping current session..."); runtimeWindow.Show(); runtimeWindow.BringToForeground(); + runtimeWindow.ShowProgressBar(); runtimeWindow.UpdateText(TextKey.RuntimeWindow_StopSession, true); // TODO: // - Terminate client (or does it terminate itself?) // - Finalize session with service // - Stop event handling and close session + var success = sessionSequence.TryRevert(); + + // TODO: Remove! + System.Threading.Thread.Sleep(5000); + + if (success) + { + // TODO + } + else + { + // TODO + } } } } diff --git a/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs b/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs new file mode 100644 index 00000000..74e03098 --- /dev/null +++ b/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs @@ -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.Runtime.Communication +{ + internal class RuntimeHost : BaseHost, IRuntimeHost + { + public RuntimeHost(string address, ILogger logger) : base(address, logger) + { + } + + public override IConnectResponse Connect(Guid? token = null) + { + // TODO + throw new NotImplementedException(); + } + + public override void Disconnect(IMessage message) + { + // TODO + throw new NotImplementedException(); + } + + public override IResponse Send(IMessage message) + { + // TODO + throw new NotImplementedException(); + } + } +} diff --git a/SafeExamBrowser.Runtime/CompositionRoot.cs b/SafeExamBrowser.Runtime/CompositionRoot.cs index bca23e0b..23319ed0 100644 --- a/SafeExamBrowser.Runtime/CompositionRoot.cs +++ b/SafeExamBrowser.Runtime/CompositionRoot.cs @@ -8,11 +8,8 @@ using System; using System.Collections.Generic; -using System.IO; -using System.Reflection; using System.Windows; using SafeExamBrowser.Configuration; -using SafeExamBrowser.Configuration.Settings; using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour.Operations; using SafeExamBrowser.Contracts.Configuration; @@ -23,6 +20,7 @@ using SafeExamBrowser.Core.I18n; using SafeExamBrowser.Core.Logging; using SafeExamBrowser.Runtime.Behaviour; using SafeExamBrowser.Runtime.Behaviour.Operations; +using SafeExamBrowser.Runtime.Communication; using SafeExamBrowser.UserInterface.Classic; using SafeExamBrowser.WindowsApi; @@ -31,7 +29,7 @@ namespace SafeExamBrowser.Runtime internal class CompositionRoot { private ILogger logger; - private RuntimeInfo runtimeInfo; + private IRuntimeInfo runtimeInfo; private ISystemInfo systemInfo; internal IRuntimeController RuntimeController { get; private set; } @@ -42,30 +40,30 @@ namespace SafeExamBrowser.Runtime var bootstrapOperations = new Queue(); var sessionOperations = new Queue(); var nativeMethods = new NativeMethods(); - var settingsRepository = new SettingsRepository(); + var configuration = new ConfigurationRepository(); var uiFactory = new UserInterfaceFactory(); logger = new Logger(); - runtimeInfo = new RuntimeInfo(); + runtimeInfo = configuration.RuntimeInfo; systemInfo = new SystemInfo(); - InitializeRuntimeInfo(); InitializeLogging(); var text = new Text(logger); - var serviceProxy = new ServiceProxy(new ModuleLogger(logger, typeof(ServiceProxy)), "net.pipe://localhost/safeexambrowser/service"); + var runtimeHost = new RuntimeHost(runtimeInfo.RuntimeAddress, new ModuleLogger(logger, typeof(RuntimeHost))); + var serviceProxy = new ServiceProxy(runtimeInfo.ServiceAddress, new ModuleLogger(logger, typeof(ServiceProxy))); bootstrapOperations.Enqueue(new I18nOperation(logger, text)); - // TODO: RuntimeHostOperation here (is IBootstrapOperation -> only performed once per runtime!) + bootstrapOperations.Enqueue(new CommunicationOperation(runtimeHost, logger)); - sessionOperations.Enqueue(new ConfigurationOperation(logger, runtimeInfo, settingsRepository, text, uiFactory, args)); - sessionOperations.Enqueue(new ServiceOperation(logger, serviceProxy, settingsRepository, text)); - sessionOperations.Enqueue(new KioskModeOperation(logger, settingsRepository)); + sessionOperations.Enqueue(new ConfigurationOperation(configuration, logger, runtimeInfo, text, uiFactory, args)); + sessionOperations.Enqueue(new ServiceOperation(configuration, logger, serviceProxy, text)); + sessionOperations.Enqueue(new KioskModeOperation(logger, configuration)); - var bootstrapSequence = new OperationSequence(logger, bootstrapOperations); + var boostrapSequence = new OperationSequence(logger, bootstrapOperations); var sessionSequence = new OperationSequence(logger, sessionOperations); - RuntimeController = new RuntimeController(logger, bootstrapSequence, sessionSequence, runtimeInfo, serviceProxy, settingsRepository, Application.Current.Shutdown, text, uiFactory); + RuntimeController = new RuntimeController(configuration, logger, boostrapSequence, sessionSequence, runtimeHost, runtimeInfo, serviceProxy, Application.Current.Shutdown, text, uiFactory); } internal void LogStartupInformation() @@ -84,28 +82,7 @@ namespace SafeExamBrowser.Runtime internal void LogShutdownInformation() { - logger?.Log($"{Environment.NewLine}# Application terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); - } - - private void InitializeRuntimeInfo() - { - var appDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), nameof(SafeExamBrowser)); - var executable = Assembly.GetEntryAssembly(); - var startTime = DateTime.Now; - var logFolder = Path.Combine(appDataFolder, "Logs"); - var logFilePrefix = startTime.ToString("yyyy-MM-dd\\_HH\\hmm\\mss\\s"); - - runtimeInfo.ApplicationStartTime = startTime; - runtimeInfo.AppDataFolder = appDataFolder; - runtimeInfo.BrowserCachePath = Path.Combine(appDataFolder, "Cache"); - runtimeInfo.BrowserLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Browser.txt"); - runtimeInfo.ClientLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Client.txt"); - runtimeInfo.DefaultSettingsFileName = "SebClientSettings.seb"; - runtimeInfo.ProgramCopyright = executable.GetCustomAttribute().Copyright; - runtimeInfo.ProgramDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), nameof(SafeExamBrowser)); - runtimeInfo.ProgramTitle = executable.GetCustomAttribute().Title; - runtimeInfo.ProgramVersion = executable.GetCustomAttribute().InformationalVersion; - runtimeInfo.RuntimeLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Runtime.txt"); + logger?.Log($"# Application terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); } private void InitializeLogging() diff --git a/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj b/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj index 2657b348..6b5764f0 100644 --- a/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj +++ b/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj @@ -90,6 +90,7 @@ + Code