using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using GameServiceWarden.ClientAPI.Module; using GameServiceWarden.Core.Persistence; using GameServiceWarden.ModuleAPI; using GameServiceWarden.Core.Collection; using System.Text; namespace GameServiceWarden.Core.Module { public class ServiceManager : IServiceManagerActionExecuter { private IServiceManagerMonitor managerMonitor; private readonly ConcurrentDictionary running; private readonly IPersistent> services; private readonly LRUCache descriptorCache; private readonly IReadOnlyPersistent> modules; public ServiceManager(IServiceManagerMonitor actionMonitor, IPersistent> services, IReadOnlyPersistent> modules) { this.services = services ?? throw new ArgumentNullException("services"); this.modules = modules ?? throw new ArgumentNullException("modules"); this.managerMonitor = actionMonitor ?? throw new ArgumentNullException("actionMonitor"); this.running = new ConcurrentDictionary(); this.descriptorCache = new LRUCache(100); } public void CreateService(string serviceName, string assemblyName, string moduleName) { if (!this.modules.ContainsKey(assemblyName)) throw new KeyNotFoundException($"No file \"{assemblyName}\" found."); IReadOnlyDictionary assemblyModules = this.modules[assemblyName]; if (services.ContainsKey(serviceName)) throw new ArgumentException($"Service of Name \"{serviceName}\" already exists."); Dictionary data = new Dictionary(); data[ServiceDescriptor.ASSEMBLY_PROPERTY] = assemblyName; data[ServiceDescriptor.MODULE_PROPERTY] = moduleName; services.AddToPersistence(serviceName, data); ServiceManagerDelta managerState = new ServiceManagerDelta(); managerState.subtract = false; managerState.service = serviceName; managerMonitor.Present(managerState); } public void DeleteService(string serviceName) { if (!services.ContainsKey(serviceName)) throw new KeyNotFoundException($"Service under name \"{serviceName}\" not found."); if (running.ContainsKey(serviceName)) running[serviceName].Stop(); services.Delete(serviceName); ServiceManagerDelta managerState = new ServiceManagerDelta(); managerState.subtract = true; managerState.service = serviceName; managerMonitor.Present(managerState); } public IEnumerable GetModuleNames() { ServiceManagerTotal managerState = new ServiceManagerTotal(); managerState.modules = modules.Keys.ToImmutableArray(); managerMonitor.Present(managerState); return modules.Keys; } public IEnumerable GetServiceNames() { ServiceManagerTotal managerState = new ServiceManagerTotal(); managerState.services = services.Keys.ToImmutableArray(); managerMonitor.Present(managerState); return services.Keys; } public IEnumerable GetRunningServiceNames() { ServiceManagerTotal managerState = new ServiceManagerTotal(); managerState.running = running.Keys.ToImmutableArray(); managerMonitor.Present(managerState); return running.Keys; } private IEnumerable GetServiceOptions(string serviceName) { if (!services.ContainsKey(serviceName)) throw new KeyNotFoundException($"Service under name \"{serviceName}\" not found."); IReadOnlyDictionary info = services[serviceName]; ServiceDescriptor service = descriptorCache.Use(serviceName, () => GenerateDescriptor(serviceName, info[ServiceDescriptor.ASSEMBLY_PROPERTY], info[ServiceDescriptor.MODULE_PROPERTY])); return service.GetConfigurableOptions(); } private string GetServiceOptionValue(string serviceName, string optionName) { if (!services.ContainsKey(serviceName)) throw new KeyNotFoundException($"Service under name \"{serviceName}\" not found."); IReadOnlyDictionary info = services[serviceName]; ServiceDescriptor service = descriptorCache.Use(serviceName, () => GenerateDescriptor(serviceName, info[ServiceDescriptor.ASSEMBLY_PROPERTY], info[ServiceDescriptor.MODULE_PROPERTY])); if (!service.GetConfigurableOptions().Contains(optionName)) throw new KeyNotFoundException($"Option \"{optionName}\" for service \"{serviceName}\" not found."); return service.GetConfigurableValue(optionName); } public IReadOnlyDictionary> GetOptions() { ServiceManagerTotal managerState = new ServiceManagerTotal(); Dictionary> serviceOptions = new Dictionary>(); foreach (string service in GetServiceNames()) { Dictionary optionsOfService = new Dictionary(); foreach (string option in GetServiceOptions(service)) { optionsOfService.Add(option, GetServiceOptionValue(service, option)); } serviceOptions.Add(service, optionsOfService); } managerState.serviceOptions = serviceOptions; managerMonitor.Present(managerState); return serviceOptions; } public bool SetServiceOptionValue(string serviceName, string optionName, string value) { if (!services.ContainsKey(serviceName)) throw new KeyNotFoundException($"Service under name \"{serviceName}\" not found."); IReadOnlyDictionary info = services[serviceName]; ServiceDescriptor service = descriptorCache.Use(serviceName, () => GenerateDescriptor(serviceName, info[ServiceDescriptor.ASSEMBLY_PROPERTY], info[ServiceDescriptor.MODULE_PROPERTY])); if (!service.GetConfigurableOptions().Contains(optionName)) throw new KeyNotFoundException($"Option \"{optionName}\" for service \"{serviceName}\" not found."); ServiceManagerDelta managerState = new ServiceManagerDelta(); if (service.SetConfigurableValue(optionName, value)) { managerState.optionName = optionName; managerState.optionValue = GetServiceOptionValue(serviceName, optionName); managerMonitor.Present(managerState); return true; } return false; } public void StartService(string serviceName) { if (!services.ContainsKey(serviceName)) throw new KeyNotFoundException($"Service under name \"{serviceName}\" not found."); if (running.ContainsKey(serviceName)) throw new InvalidOperationException($"Service under name \"{serviceName}\" is already running."); IReadOnlyDictionary info = services[serviceName]; ServiceDescriptor service = descriptorCache.Use(serviceName, () => GenerateDescriptor(serviceName, info[ServiceDescriptor.ASSEMBLY_PROPERTY], info[ServiceDescriptor.MODULE_PROPERTY])); service.ServiceStateChangeEvent += OnServiceStateChange; service.LogUpdateEvent += OnLogUpdated; service.Start(); } public void StopService(string serviceName) { if (!running.ContainsKey(serviceName)) throw new InvalidOperationException($"Service under name \"{serviceName}\" is not running."); running[serviceName].Stop(); } public void ExecuteCommand(string serviceName, string command) { if (!running.ContainsKey(serviceName)) throw new InvalidOperationException($"Service under name \"{serviceName}\" is not running."); running[serviceName].ExecuteCommand(command); } private byte[] GetServiceLogBuffer(string serviceName) { if (!running.ContainsKey(serviceName)) throw new InvalidOperationException($"Service under name \"{serviceName}\" is not running and therefore, does not have a log."); IReadOnlyDictionary info = services[serviceName]; ServiceDescriptor service = descriptorCache.Use(serviceName, () => GenerateDescriptor(serviceName, info[ServiceDescriptor.ASSEMBLY_PROPERTY], info[ServiceDescriptor.MODULE_PROPERTY])); return service.GetLogBuffer(); } public Dictionary GetLogBuffer() { Dictionary logs = new Dictionary(); foreach (string service in running.Keys) { logs.Add(service, running[service].GetLogBuffer()); } ServiceManagerTotal managerState = new ServiceManagerTotal(); managerState.logs = logs; managerMonitor.Present(managerState); return logs; } private ServiceDescriptor GenerateDescriptor(string name, string assembly, string module) { return new ServiceDescriptor(modules[assembly][module].InstantiateService(services.GetPathForKey(name)), name, module, assembly); } private void OnServiceStateChange(object sender, ServiceState state) { ServiceDescriptor serviceInfo = (ServiceDescriptor)sender; ServiceManagerDelta managerChange = new ServiceManagerDelta(); switch (state) { case ServiceState.Running: if (running.TryAdd(serviceInfo.ServiceName, serviceInfo)) { managerChange.running = serviceInfo.ServiceName; } break; case ServiceState.Stopped: ServiceDescriptor removed; if (running.TryRemove(serviceInfo.ServiceName, out removed)) { removed.ServiceStateChangeEvent -= OnServiceStateChange; removed.LogUpdateEvent -= OnLogUpdated; Dictionary removedInfo = new Dictionary(); removedInfo[ServiceDescriptor.ASSEMBLY_PROPERTY] = removed.GetAssemblyName(); removedInfo[ServiceDescriptor.MODULE_PROPERTY] = removed.GetModuleName(); services[serviceInfo.ServiceName] = removedInfo; managerChange.subtract = true; managerChange.running = serviceInfo.ServiceName; } break; } managerMonitor.Present(managerChange); } void OnLogUpdated(object sender, string update) { ServiceDescriptor service = (ServiceDescriptor)sender; ServiceManagerDelta delta = new ServiceManagerDelta(); delta.logs = Encoding.UTF8.GetBytes(update); managerMonitor.Present(delta); } public void ExecuteAction(ServiceManagerAction action) { switch (action.action) { case ServiceManagerAction.Type.CreateService: CreateService(action.serviceName, action.assemblyName, action.moduleName); break; case ServiceManagerAction.Type.DeleteService: DeleteService(action.serviceName); break; case ServiceManagerAction.Type.ExecuteCommand: ExecuteCommand(action.serviceName, action.command); break; case ServiceManagerAction.Type.SetServiceOption: SetServiceOptionValue(action.serviceName, action.option, action.value); break; case ServiceManagerAction.Type.Start: StartService(action.serviceName); break; case ServiceManagerAction.Type.Stop: StopService(action.serviceName); break; } } public void ViewState() { GetServiceNames(); GetRunningServiceNames(); GetModuleNames(); GetLogBuffer(); GetOptions(); } } }