using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; using System.IO; using System.IO.Pipes; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using GameServiceWarden.Core.Module.Exceptions; using SimpleLogger; using GameServiceWarden.ModuleFramework; using System.Net.Sockets; //TODO Update UML namespace GameServiceWarden.Core.Module { public class ServiceDescriptor //entity { public const string MODULE_PROPERTY = "SERVICE_MODULE"; public const string ASSEMBLY_PROPERTY = "SERVICE_ASSEMBLY"; private const int TIMEOUT = 1000; /// /// The name of the service itself, independent of the name of the module this service is using. /// public string ServiceName { get { return serviceName; } } private readonly string serviceName; private volatile ServiceState state; private readonly IService service; private string moduleName; private readonly string assemblyName; /// /// Name of module this service uses. /// private readonly IReadOnlyDictionary configurables; public event EventHandler ServiceStateChangeEvent; public event EventHandler LogUpdateEvent; public ServiceDescriptor(IService service, string serviceName, string moduleName, string assemblyName) { this.service = service ?? throw new ArgumentNullException("service"); this.moduleName = moduleName ?? throw new ArgumentNullException("moduleName"); this.assemblyName = assemblyName ?? throw new ArgumentNullException("assemblyName"); this.serviceName = serviceName ?? throw new ArgumentNullException("serviceName"); this.service.StateChangeEvent += OnServiceStateChange; this.service.UpdateLogEvent += OnLogUpdate; Dictionary tempConfigurables = new Dictionary(); foreach (IServiceConfigurable configurable in service.Configurables) { tempConfigurables.Add(configurable.OptionName, configurable); } this.configurables = new ReadOnlyDictionary(tempConfigurables); } /// /// Starts this service. /// /// Is thrown when the service is already running. public void Start() { Logger.Log($"\"{ServiceName}\" is starting."); if (state == ServiceState.Running) throw new InvalidOperationException("Service instance already running."); CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(TIMEOUT); Task initializationTask = Task.Run(() => service.InitializeService(), cancellationTokenSource.Token); try { initializationTask.Wait(); } catch (AggregateException a) { a.Handle((e) => e is TaskCanceledException); } cancellationTokenSource.Dispose(); } /// /// Stops the service. /// /// Is thrown when the is not running. public void Stop() { if (state != ServiceState.Running) throw new InvalidOperationException("Service instance not running."); Logger.Log($"\"{ServiceName}\" is stopping."); service.ElegantShutdown(); } /// /// Sends a command to this service to execute. /// /// The command to execute. /// Is thrown when the service is not running. public void ExecuteCommand(string command) { Logger.Log($"\"{ServiceName}\" is executing command \"{command}\".", LogLevel.Debug); if (state != ServiceState.Running) throw new InvalidOperationException("Service instance not running."); service.ExecuteCommand(command); } /// /// Gets the possible 's names for this service. /// /// A returned where the string is the option's name. public ISet GetConfigurableOptions() { return new HashSet(this.configurables.Keys); } public bool SetConfigurableValue(string configurationName, string value) { if (!this.configurables.ContainsKey(configurationName)) throw new KeyNotFoundException($"Unable to find option with name \"{configurationName}\"."); return this.configurables[configurationName].SetValue(value); } public string GetConfigurableValue(string configurationName) { if (!this.configurables.ContainsKey(configurationName)) throw new KeyNotFoundException($"Unable to find option with name \"{configurationName}\"."); return this.configurables[configurationName].GetValue(); } public ServiceState GetServiceState() { return state; } public string GetServiceName() { return serviceName; } public string GetModuleName() { return moduleName; } /// The name of assembly this module is contained in. public string GetAssemblyName() { return assemblyName; } public byte[] GetLogBuffer() { if (state == ServiceState.Stopped) throw new InvalidOperationException("Cannot get log of service that is not running."); return service.GetLogBuffer(); } private void OnServiceStateChange(object sender, ServiceState state) { this.state = state; Logger.Log($"The service \"{ServiceName}\" is changing states to {this.state}.", LogLevel.Debug); ServiceStateChangeEvent?.Invoke(this, this.state); } private void OnLogUpdate(object sender, string log) { LogUpdateEvent?.Invoke(this, log); } } }