SEBWIN-219: Implemented logging of component and session IDs and introduced IProxyFactory for client proxy instantiation during session startup.

This commit is contained in:
dbuechel 2018-03-14 11:04:28 +01:00
parent e3b8bb8cc8
commit be761fd72c
13 changed files with 150 additions and 68 deletions

View file

@ -45,6 +45,9 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
configuration.Settings = config.Settings;
logger.Info("Successfully retrieved the application configuration from the runtime.");
logger.Info($" -> Client-ID: {configuration.RuntimeInfo.ClientId}");
logger.Info($" -> Runtime-ID: {configuration.RuntimeInfo.RuntimeId}");
logger.Info($" -> Session-ID: {configuration.SessionId}");
}
catch (Exception e)
{

View file

@ -16,6 +16,9 @@ namespace SafeExamBrowser.Configuration
{
public class ConfigurationRepository : IConfigurationRepository
{
private const string BASE_ADDRESS = "net.pipe://localhost/safeexambrowser";
private bool firstSession = true;
private RuntimeInfo runtimeInfo;
public ISessionData CurrentSession { get; private set; }
@ -35,19 +38,6 @@ namespace SafeExamBrowser.Configuration
}
}
public ISessionData InitializeSessionData()
{
var session = new SessionData
{
Id = Guid.NewGuid(),
StartupToken = Guid.NewGuid()
};
CurrentSession = session;
return session;
}
public ClientConfiguration BuildClientConfiguration()
{
return new ClientConfiguration
@ -58,6 +48,24 @@ namespace SafeExamBrowser.Configuration
};
}
public void InitializeSessionConfiguration()
{
CurrentSession = new SessionData
{
Id = Guid.NewGuid(),
StartupToken = Guid.NewGuid()
};
if (!firstSession)
{
UpdateRuntimeInfo();
}
else
{
firstSession = false;
}
}
public Settings LoadSettings(Uri path)
{
// TODO: Implement loading mechanism
@ -92,10 +100,7 @@ namespace SafeExamBrowser.Configuration
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");
@ -107,7 +112,7 @@ namespace SafeExamBrowser.Configuration
BrowserCachePath = Path.Combine(appDataFolder, "Cache"),
BrowserLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Browser.txt"),
ClientId = Guid.NewGuid(),
ClientAddress = $"{baseAddress}/client/{clientId}",
ClientAddress = $"{BASE_ADDRESS}/client/{Guid.NewGuid()}",
ClientExecutablePath = Path.Combine(Path.GetDirectoryName(executable.Location), $"{nameof(SafeExamBrowser)}.Client.exe"),
ClientLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Client.txt"),
DefaultSettingsFileName = "SebClientSettings.seb",
@ -116,10 +121,16 @@ namespace SafeExamBrowser.Configuration
ProgramTitle = executable.GetCustomAttribute<AssemblyTitleAttribute>().Title,
ProgramVersion = executable.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion,
RuntimeId = Guid.NewGuid(),
RuntimeAddress = $"{baseAddress}/runtime/{runtimeId}",
RuntimeAddress = $"{BASE_ADDRESS}/runtime/{Guid.NewGuid()}",
RuntimeLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Runtime.txt"),
ServiceAddress = $"{baseAddress}/service"
ServiceAddress = $"{BASE_ADDRESS}/service"
};
}
private void UpdateRuntimeInfo()
{
RuntimeInfo.ClientId = Guid.NewGuid();
RuntimeInfo.ClientAddress = $"{BASE_ADDRESS}/client/{Guid.NewGuid()}";
}
}
}

View file

@ -7,6 +7,7 @@
*/
using System;
using SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.WindowsApi;
@ -14,6 +15,7 @@ namespace SafeExamBrowser.Configuration
{
public class SessionData : ISessionData
{
public IClientProxy ClientProxy { get; set; }
public IProcess ClientProcess { get; set; }
public Guid Id { get; set; }
public Guid StartupToken { get; set; }

View file

@ -0,0 +1,21 @@
/*
* 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
{
/// <summary>
/// A factory to create communication proxies during application runtime.
/// </summary>
public interface IProxyFactory
{
/// <summary>
/// Creates a new <see cref="IClientProxy"/> for the given endpoint address.
/// </summary>
IClientProxy CreateClientProxy(string address);
}
}

View file

@ -45,7 +45,7 @@ namespace SafeExamBrowser.Contracts.Configuration
/// <summary>
/// Initializes all relevant data for a new session.
/// </summary>
ISessionData InitializeSessionData();
void InitializeSessionConfiguration();
/// <summary>
/// Attempts to load settings from the specified path.

View file

@ -7,6 +7,7 @@
*/
using System;
using SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.WindowsApi;
namespace SafeExamBrowser.Contracts.Configuration
@ -16,6 +17,11 @@ namespace SafeExamBrowser.Contracts.Configuration
/// </summary>
public interface ISessionData
{
/// <summary>
/// The communication proxy for the client instance associated to this session.
/// </summary>
IClientProxy ClientProxy { get; set; }
/// <summary>
/// The process information of the client instance associated to this session.
/// </summary>

View file

@ -38,9 +38,6 @@ namespace SafeExamBrowser.Contracts.Configuration
/// <summary>
/// The communication address of the client component.
///
/// TODO: Will need to be updated for each new client instance!
///
/// </summary>
public string ClientAddress { get; set; }
@ -51,9 +48,6 @@ namespace SafeExamBrowser.Contracts.Configuration
/// <summary>
/// The unique identifier for the currently running client instance.
///
/// TODO: Will need to be updated for each new client instance! -> Remove if unused!
///
/// </summary>
public Guid ClientId { get; set; }
@ -94,9 +88,6 @@ namespace SafeExamBrowser.Contracts.Configuration
/// <summary>
/// The unique identifier for the currently running runtime instance.
///
/// TODO: Remove if unused!
///
/// </summary>
public Guid RuntimeId { get; set; }

View file

@ -62,6 +62,7 @@
<Compile Include="Communication\IClientProxy.cs" />
<Compile Include="Communication\ICommunicationHost.cs" />
<Compile Include="Communication\ICommunicationProxy.cs" />
<Compile Include="Communication\IProxyFactory.cs" />
<Compile Include="Communication\IRuntimeHost.cs" />
<Compile Include="Communication\IRuntimeProxy.cs" />
<Compile Include="Communication\IServiceProxy.cs" />

View file

@ -22,7 +22,6 @@ namespace SafeExamBrowser.Runtime.Behaviour
{
private bool sessionRunning;
private IClientProxy client;
private IConfigurationRepository configuration;
private ILogger logger;
private IOperationSequence bootstrapSequence;
@ -36,7 +35,6 @@ namespace SafeExamBrowser.Runtime.Behaviour
private IUserInterfaceFactory uiFactory;
public RuntimeController(
IClientProxy client,
IConfigurationRepository configuration,
ILogger logger,
IOperationSequence bootstrapSequence,
@ -47,7 +45,6 @@ namespace SafeExamBrowser.Runtime.Behaviour
Action shutdown,
IUserInterfaceFactory uiFactory)
{
this.client = client;
this.configuration = configuration;
this.logger = logger;
this.bootstrapSequence = bootstrapSequence;
@ -202,7 +199,6 @@ namespace SafeExamBrowser.Runtime.Behaviour
private void RegisterEvents()
{
client.ConnectionLost += Client_ConnectionLost;
runtimeHost.ReconfigurationRequested += RuntimeHost_ReconfigurationRequested;
runtimeHost.ShutdownRequested += RuntimeHost_ShutdownRequested;
}
@ -210,11 +206,11 @@ namespace SafeExamBrowser.Runtime.Behaviour
private void RegisterSessionEvents()
{
configuration.CurrentSession.ClientProcess.Terminated += ClientProcess_Terminated;
configuration.CurrentSession.ClientProxy.ConnectionLost += Client_ConnectionLost;
}
private void DeregisterEvents()
{
client.ConnectionLost -= Client_ConnectionLost;
runtimeHost.ReconfigurationRequested -= RuntimeHost_ReconfigurationRequested;
runtimeHost.ShutdownRequested -= RuntimeHost_ShutdownRequested;
}
@ -222,6 +218,7 @@ namespace SafeExamBrowser.Runtime.Behaviour
private void DeregisterSessionEvents()
{
configuration.CurrentSession.ClientProcess.Terminated -= ClientProcess_Terminated;
configuration.CurrentSession.ClientProxy.ConnectionLost -= Client_ConnectionLost;
}
private void ClientProcess_Terminated(int exitCode)

View file

@ -22,28 +22,27 @@ namespace SafeExamBrowser.Runtime.Behaviour
private const int TEN_SECONDS = 10000;
private bool sessionRunning;
private IClientProxy client;
private IConfigurationRepository configuration;
private ILogger logger;
private IProcessFactory processFactory;
private IProxyFactory proxyFactory;
private IRuntimeHost runtimeHost;
private IServiceProxy service;
private ISessionData session;
internal IProgressIndicator ProgressIndicator { private get; set; }
internal SessionController(
IClientProxy client,
IConfigurationRepository configuration,
ILogger logger,
IProcessFactory processFactory,
IProxyFactory proxyFactory,
IRuntimeHost runtimeHost,
IServiceProxy service)
{
this.client = client;
this.configuration = configuration;
this.logger = logger;
this.processFactory = processFactory;
this.proxyFactory = proxyFactory;
this.runtimeHost = runtimeHost;
this.service = service;
}
@ -53,23 +52,19 @@ namespace SafeExamBrowser.Runtime.Behaviour
logger.Info("Starting new session...");
ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_StartSession, true);
session = configuration.InitializeSessionData();
runtimeHost.StartupToken = session.StartupToken;
logger.Info("Initializing service session...");
service.StartSession(session.Id, configuration.CurrentSettings);
InitializeSessionConfiguration();
StartServiceSession();
sessionRunning = TryStartClient();
if (!sessionRunning)
{
logger.Info($"Failed to start new session! Reverting service session and aborting procedure...");
service.StopSession(session.Id);
service.StopSession(configuration.CurrentSession.Id);
return OperationResult.Failed;
}
logger.Info($"Successfully started new session with identifier '{session.Id}'.");
logger.Info($"Successfully started new session.");
return OperationResult.Success;
}
@ -78,24 +73,45 @@ namespace SafeExamBrowser.Runtime.Behaviour
{
if (sessionRunning)
{
logger.Info($"Stopping session with identifier '{session.Id}'...");
logger.Info($"Stopping session with identifier '{configuration.CurrentSession.Id}'...");
ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_StopSession, true);
logger.Info("Stopping service session...");
service.StopSession(session.Id);
StopServiceSession();
if (!session.ClientProcess.HasTerminated)
if (!configuration.CurrentSession.ClientProcess.HasTerminated)
{
StopClient();
}
sessionRunning = false;
logger.Info($"Successfully stopped session with identifier '{session.Id}'.");
logger.Info($"Successfully stopped session.");
}
return OperationResult.Success;
}
private void InitializeSessionConfiguration()
{
configuration.InitializeSessionConfiguration();
runtimeHost.StartupToken = configuration.CurrentSession.StartupToken;
logger.Info($" -> Client-ID: {configuration.RuntimeInfo.ClientId}");
logger.Info($" -> Runtime-ID: {configuration.RuntimeInfo.RuntimeId} (as reference, does not change)");
logger.Info($" -> Session-ID: {configuration.CurrentSession.Id}");
}
private void StartServiceSession()
{
logger.Info("Initializing service session...");
service.StartSession(configuration.CurrentSession.Id, configuration.CurrentSettings);
}
private void StopServiceSession()
{
logger.Info("Stopping service session...");
service.StopSession(configuration.CurrentSession.Id);
}
private bool TryStartClient()
{
var clientReady = false;
@ -104,11 +120,11 @@ namespace SafeExamBrowser.Runtime.Behaviour
var clientExecutable = configuration.RuntimeInfo.ClientExecutablePath;
var clientLogFile = $"{'"' + configuration.RuntimeInfo.ClientLogFile + '"'}";
var hostUri = configuration.RuntimeInfo.RuntimeAddress;
var token = session.StartupToken.ToString("D");
var token = configuration.CurrentSession.StartupToken.ToString("D");
logger.Info("Starting new client process.");
logger.Info("Starting new client process...");
runtimeHost.ClientReady += clientReadyEventHandler;
session.ClientProcess = processFactory.StartNew(clientExecutable, clientLogFile, hostUri, token);
configuration.CurrentSession.ClientProcess = processFactory.StartNew(clientExecutable, clientLogFile, hostUri, token);
logger.Info("Waiting for client to complete initialization...");
clientReady = clientReadyEvent.WaitOne(TEN_SECONDS);
@ -122,8 +138,10 @@ namespace SafeExamBrowser.Runtime.Behaviour
}
logger.Info("Client has been successfully started and initialized.");
logger.Info("Creating communication proxy for client host...");
configuration.CurrentSession.ClientProxy = proxyFactory.CreateClientProxy(configuration.RuntimeInfo.ClientAddress);
if (!client.Connect(session.StartupToken))
if (!configuration.CurrentSession.ClientProxy.Connect(configuration.CurrentSession.StartupToken))
{
logger.Error("Failed to connect to client!");
@ -132,16 +150,16 @@ namespace SafeExamBrowser.Runtime.Behaviour
logger.Info("Connection with client has been established.");
var response = client.RequestAuthentication();
var response = configuration.CurrentSession.ClientProxy.RequestAuthentication();
if (session.ClientProcess.Id != response?.ProcessId)
if (configuration.CurrentSession.ClientProcess.Id != response?.ProcessId)
{
logger.Error("Failed to verify client integrity!");
return false;
}
logger.Info("Authentication of client has been successful.");
logger.Info("Authentication of client has been successful, client is ready to operate.");
return true;
}
@ -157,13 +175,13 @@ namespace SafeExamBrowser.Runtime.Behaviour
var terminatedEventHandler = new ProcessTerminatedEventHandler((_) => terminatedEvent.Set());
runtimeHost.ClientDisconnected += disconnectedEventHandler;
session.ClientProcess.Terminated += terminatedEventHandler;
configuration.CurrentSession.ClientProcess.Terminated += terminatedEventHandler;
logger.Info("Instructing client to initiate shutdown procedure.");
client.InitiateShutdown();
configuration.CurrentSession.ClientProxy.InitiateShutdown();
logger.Info("Disconnecting from client communication host.");
client.Disconnect();
configuration.CurrentSession.ClientProxy.Disconnect();
logger.Info("Waiting for client to disconnect from runtime communication host...");
disconnected = disconnectedEvent.WaitOne(TEN_SECONDS);
@ -182,7 +200,7 @@ namespace SafeExamBrowser.Runtime.Behaviour
}
runtimeHost.ClientDisconnected -= disconnectedEventHandler;
session.ClientProcess.Terminated -= terminatedEventHandler;
configuration.CurrentSession.ClientProcess.Terminated -= terminatedEventHandler;
if (disconnected && terminated)
{
@ -206,10 +224,10 @@ namespace SafeExamBrowser.Runtime.Behaviour
return;
}
logger.Info($"Killing client process with ID = {session.ClientProcess.Id}.");
session.ClientProcess.Kill();
logger.Info($"Killing client process with ID = {configuration.CurrentSession.ClientProcess.Id}.");
configuration.CurrentSession.ClientProcess.Kill();
if (session.ClientProcess.HasTerminated)
if (configuration.CurrentSession.ClientProcess.HasTerminated)
{
logger.Info("Client process has terminated.");
}

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.Communication;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Core.Communication;
using SafeExamBrowser.Core.Logging;
namespace SafeExamBrowser.Runtime.Communication
{
internal class ProxyFactory : IProxyFactory
{
private ILogger logger;
public ProxyFactory(ILogger logger)
{
this.logger = logger;
}
public IClientProxy CreateClientProxy(string address)
{
return new ClientProxy(address, new ModuleLogger(logger, typeof(ClientProxy)));
}
}
}

View file

@ -49,10 +49,10 @@ namespace SafeExamBrowser.Runtime
var uiFactory = new UserInterfaceFactory(text);
var desktop = new Desktop(new ModuleLogger(logger, typeof(Desktop)));
var processFactory = new ProcessFactory(desktop, new ModuleLogger(logger, typeof(ProcessFactory)));
var clientProxy = new ClientProxy(runtimeInfo.ClientAddress, new ModuleLogger(logger, typeof(ClientProxy)));
var proxyFactory = new ProxyFactory(logger);
var runtimeHost = new RuntimeHost(runtimeInfo.RuntimeAddress, configuration, new ModuleLogger(logger, typeof(RuntimeHost)));
var serviceProxy = new ServiceProxy(runtimeInfo.ServiceAddress, new ModuleLogger(logger, typeof(ServiceProxy)));
var sessionController = new SessionController(clientProxy, configuration, logger, processFactory, runtimeHost, serviceProxy);
var sessionController = new SessionController(configuration, logger, processFactory, proxyFactory, runtimeHost, serviceProxy);
var bootstrapOperations = new Queue<IOperation>();
var sessionOperations = new Queue<IOperation>();
@ -69,7 +69,7 @@ namespace SafeExamBrowser.Runtime
var boostrapSequence = new OperationSequence(logger, bootstrapOperations);
var sessionSequence = new OperationSequence(logger, sessionOperations);
RuntimeController = new RuntimeController(clientProxy, configuration, logger, boostrapSequence, sessionSequence, runtimeHost, runtimeInfo, serviceProxy, shutdown, uiFactory);
RuntimeController = new RuntimeController(configuration, logger, boostrapSequence, sessionSequence, runtimeHost, runtimeInfo, serviceProxy, shutdown, uiFactory);
}
internal void LogStartupInformation()
@ -81,6 +81,7 @@ namespace SafeExamBrowser.Runtime
logger.Log(string.Empty);
logger.Log($"# Application started at {runtimeInfo.ApplicationStartTime.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
logger.Log($"# Running on {systemInfo.OperatingSystemInfo}");
logger.Log($"# Runtime-ID: {runtimeInfo.RuntimeId}");
logger.Log(string.Empty);
}

View file

@ -93,6 +93,7 @@
<Compile Include="Behaviour\Operations\SessionSequenceEndOperation.cs" />
<Compile Include="Behaviour\SessionController.cs" />
<Compile Include="Behaviour\Operations\SessionSequenceStartOperation.cs" />
<Compile Include="Communication\ProxyFactory.cs" />
<Compile Include="Communication\RuntimeHost.cs" />
<Compile Include="CompositionRoot.cs" />
<Compile Include="Properties\AssemblyInfo.cs">