247 lines
13 KiB
C#
247 lines
13 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using GameServiceWarden.InteractionAPI.Module;
|
|
using GameServiceWarden.Core.Persistence;
|
|
using GameServiceWarden.ModuleFramework;
|
|
using GameServiceWarden.Core.Collection;
|
|
using System.Text;
|
|
|
|
namespace GameServiceWarden.Core.Module
|
|
{
|
|
public class ServiceManager : IServiceManagerActionExecuter
|
|
{
|
|
private IServiceManagerMonitor managerMonitor;
|
|
private readonly ConcurrentDictionary<string, ServiceDescriptor> running;
|
|
private readonly IPersistent<IReadOnlyDictionary<string, string>> services;
|
|
private readonly IReadOnlyPersistent<IReadOnlyDictionary<string, IServiceModule>> modules;
|
|
private readonly LRUCache<string, ServiceDescriptor> descriptorCache;
|
|
|
|
public ServiceManager(IServiceManagerMonitor actionMonitor, IPersistent<IReadOnlyDictionary<string, string>> services, IReadOnlyPersistent<IReadOnlyDictionary<string, IServiceModule>> 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<string, ServiceDescriptor>();
|
|
this.descriptorCache = new LRUCache<string, ServiceDescriptor>(100);
|
|
}
|
|
|
|
public void CreateService(string serviceName, string assemblyName, string moduleName)
|
|
{
|
|
if (!this.modules.ContainsKey(assemblyName)) throw new KeyNotFoundException($"No file \"{assemblyName}\" found.");
|
|
IReadOnlyDictionary<string, IServiceModule> assemblyModules = this.modules[assemblyName];
|
|
if (services.ContainsKey(serviceName)) throw new ArgumentException($"Service of Name \"{serviceName}\" already exists.");
|
|
Dictionary<string, string> data = new Dictionary<string, string>();
|
|
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<string> GetModuleNames()
|
|
{
|
|
ServiceManagerTotal managerState = new ServiceManagerTotal();
|
|
managerState.modules = modules.Keys.ToImmutableArray();
|
|
managerMonitor.Present(managerState);
|
|
return modules.Keys;
|
|
}
|
|
|
|
public IEnumerable<string> GetServiceNames()
|
|
{
|
|
ServiceManagerTotal managerState = new ServiceManagerTotal();
|
|
managerState.services = services.Keys.ToImmutableArray();
|
|
managerMonitor.Present(managerState);
|
|
return services.Keys;
|
|
}
|
|
|
|
public IEnumerable<string> GetRunningServiceNames() {
|
|
ServiceManagerTotal managerState = new ServiceManagerTotal();
|
|
managerState.running = running.Keys.ToImmutableArray();
|
|
managerMonitor.Present(managerState);
|
|
return running.Keys;
|
|
}
|
|
|
|
private IEnumerable<string> GetServiceOptions(string serviceName)
|
|
{
|
|
if (!services.ContainsKey(serviceName)) throw new KeyNotFoundException($"Service under name \"{serviceName}\" not found.");
|
|
IReadOnlyDictionary<string, string> 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<string, string> 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<string, IReadOnlyDictionary<string, string>> GetOptions() {
|
|
ServiceManagerTotal managerState = new ServiceManagerTotal();
|
|
Dictionary<string, IReadOnlyDictionary<string, string>> serviceOptions = new Dictionary<string, IReadOnlyDictionary<string, string>>();
|
|
foreach (string service in GetServiceNames())
|
|
{
|
|
Dictionary<string, string> optionsOfService = new Dictionary<string, string>();
|
|
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<string, string> 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<string, string> 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<string, string> info = services[serviceName];
|
|
ServiceDescriptor service = descriptorCache.Use(serviceName, () => GenerateDescriptor(serviceName, info[ServiceDescriptor.ASSEMBLY_PROPERTY], info[ServiceDescriptor.MODULE_PROPERTY]));
|
|
return service.GetLogBuffer();
|
|
}
|
|
|
|
public IReadOnlyDictionary<string, byte[]> GetLogBuffers() {
|
|
Dictionary<string, byte[]> logs = new Dictionary<string, byte[]>();
|
|
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<string, string> removedInfo = new Dictionary<string, string>();
|
|
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 View()
|
|
{
|
|
GetServiceNames();
|
|
GetRunningServiceNames();
|
|
GetModuleNames();
|
|
GetLogBuffers();
|
|
GetOptions();
|
|
}
|
|
}
|
|
} |