2021-04-08 21:36:08 -05:00
using System ;
using System.Collections.Concurrent ;
using System.Collections.Generic ;
using System.Collections.Immutable ;
2021-04-21 02:00:50 -05:00
using GameServiceWarden.InteractionAPI.Module ;
2021-04-08 21:36:08 -05:00
using GameServiceWarden.Core.Persistence ;
2021-04-20 23:55:03 -05:00
using GameServiceWarden.ModuleAPI ;
2021-04-19 14:40:11 -05:00
using GameServiceWarden.Core.Collection ;
2021-04-21 01:14:05 -05:00
using System.Text ;
2021-04-08 21:36:08 -05:00
2021-04-19 01:34:45 -05:00
namespace GameServiceWarden.Core.Module
2021-04-08 21:36:08 -05:00
{
public class ServiceManager : IServiceManagerActionExecuter
{
private IServiceManagerMonitor managerMonitor ;
private readonly ConcurrentDictionary < string , ServiceDescriptor > running ;
2021-04-19 01:34:45 -05:00
private readonly IPersistent < IReadOnlyDictionary < string , string > > services ;
2021-04-08 21:36:08 -05:00
private readonly IReadOnlyPersistent < IReadOnlyDictionary < string , IServiceModule > > modules ;
2021-04-21 01:44:16 -05:00
private readonly LRUCache < string , ServiceDescriptor > descriptorCache ;
2021-04-08 21:36:08 -05:00
2021-04-19 01:34:45 -05:00
public ServiceManager ( IServiceManagerMonitor actionMonitor , IPersistent < IReadOnlyDictionary < string , string > > services , IReadOnlyPersistent < IReadOnlyDictionary < string , IServiceModule > > modules )
2021-04-08 21:36:08 -05:00
{
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 > ( ) ;
2021-04-19 01:34:45 -05:00
this . descriptorCache = new LRUCache < string , ServiceDescriptor > ( 100 ) ;
2021-04-08 21:36:08 -05:00
}
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." ) ;
2021-04-19 01:34:45 -05:00
Dictionary < string , string > data = new Dictionary < string , string > ( ) ;
data [ ServiceDescriptor . ASSEMBLY_PROPERTY ] = assemblyName ;
data [ ServiceDescriptor . MODULE_PROPERTY ] = moduleName ;
services . AddToPersistence ( serviceName , data ) ;
2021-04-21 01:14:05 -05:00
ServiceManagerDelta managerState = new ServiceManagerDelta ( ) ;
2021-04-08 21:36:08 -05:00
managerState . subtract = false ;
2021-04-21 01:14:05 -05:00
managerState . service = serviceName ;
2021-04-08 21:36:08 -05:00
managerMonitor . Present ( managerState ) ;
}
public void DeleteService ( string serviceName )
{
if ( ! services . ContainsKey ( serviceName ) ) throw new KeyNotFoundException ( $"Service under name \" { serviceName } \ " not found." ) ;
2021-04-19 01:34:45 -05:00
if ( running . ContainsKey ( serviceName ) ) running [ serviceName ] . Stop ( ) ;
2021-04-08 21:36:08 -05:00
services . Delete ( serviceName ) ;
2021-04-21 01:14:05 -05:00
ServiceManagerDelta managerState = new ServiceManagerDelta ( ) ;
2021-04-08 21:36:08 -05:00
managerState . subtract = true ;
2021-04-21 01:14:05 -05:00
managerState . service = serviceName ;
2021-04-08 21:36:08 -05:00
managerMonitor . Present ( managerState ) ;
}
public IEnumerable < string > GetModuleNames ( )
{
2021-04-21 01:14:05 -05:00
ServiceManagerTotal managerState = new ServiceManagerTotal ( ) ;
2021-04-08 21:36:08 -05:00
managerState . modules = modules . Keys . ToImmutableArray ( ) ;
managerMonitor . Present ( managerState ) ;
return modules . Keys ;
}
public IEnumerable < string > GetServiceNames ( )
{
2021-04-21 01:14:05 -05:00
ServiceManagerTotal managerState = new ServiceManagerTotal ( ) ;
2021-04-08 21:36:08 -05:00
managerState . services = services . Keys . ToImmutableArray ( ) ;
managerMonitor . Present ( managerState ) ;
return services . Keys ;
}
public IEnumerable < string > GetRunningServiceNames ( ) {
2021-04-21 01:14:05 -05:00
ServiceManagerTotal managerState = new ServiceManagerTotal ( ) ;
2021-04-08 21:36:08 -05:00
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." ) ;
2021-04-19 01:34:45 -05:00
IReadOnlyDictionary < string , string > info = services [ serviceName ] ;
ServiceDescriptor service = descriptorCache . Use ( serviceName , ( ) = > GenerateDescriptor ( serviceName , info [ ServiceDescriptor . ASSEMBLY_PROPERTY ] , info [ ServiceDescriptor . MODULE_PROPERTY ] ) ) ;
return service . GetConfigurableOptions ( ) ;
2021-04-08 21:36:08 -05:00
}
private string GetServiceOptionValue ( string serviceName , string optionName )
{
if ( ! services . ContainsKey ( serviceName ) ) throw new KeyNotFoundException ( $"Service under name \" { serviceName } \ " not found." ) ;
2021-04-19 01:34:45 -05:00
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 ) ;
2021-04-08 21:36:08 -05:00
}
public IReadOnlyDictionary < string , IReadOnlyDictionary < string , string > > GetOptions ( ) {
2021-04-21 01:14:05 -05:00
ServiceManagerTotal managerState = new ServiceManagerTotal ( ) ;
2021-04-08 21:36:08 -05:00
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." ) ;
2021-04-19 01:34:45 -05:00
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." ) ;
2021-04-21 01:14:05 -05:00
ServiceManagerDelta managerState = new ServiceManagerDelta ( ) ;
2021-04-19 01:34:45 -05:00
if ( service . SetConfigurableValue ( optionName , value ) ) {
2021-04-21 01:14:05 -05:00
managerState . optionName = optionName ;
managerState . optionValue = GetServiceOptionValue ( serviceName , optionName ) ;
managerMonitor . Present ( managerState ) ;
return true ;
2021-04-08 21:36:08 -05:00
}
2021-04-21 01:14:05 -05:00
return false ;
2021-04-08 21:36:08 -05:00
}
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." ) ;
2021-04-19 01:34:45 -05:00
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 ( ) ;
2021-04-08 21:36:08 -05:00
}
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 ) ;
}
2021-04-19 01:34:45 -05:00
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 ( ) ;
2021-04-08 21:36:08 -05:00
}
2021-04-21 01:44:16 -05:00
public IReadOnlyDictionary < string , byte [ ] > GetLogBuffers ( ) {
2021-04-19 01:34:45 -05:00
Dictionary < string , byte [ ] > logs = new Dictionary < string , byte [ ] > ( ) ;
foreach ( string service in running . Keys )
2021-04-08 21:36:08 -05:00
{
2021-04-19 01:34:45 -05:00
logs . Add ( service , running [ service ] . GetLogBuffer ( ) ) ;
2021-04-08 21:36:08 -05:00
}
2021-04-21 01:14:05 -05:00
ServiceManagerTotal managerState = new ServiceManagerTotal ( ) ;
2021-04-19 01:34:45 -05:00
managerState . logs = logs ;
2021-04-08 21:36:08 -05:00
managerMonitor . Present ( managerState ) ;
2021-04-19 01:34:45 -05:00
return logs ;
2021-04-08 21:36:08 -05:00
}
2021-04-19 01:34:45 -05:00
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 ) {
2021-04-08 21:36:08 -05:00
ServiceDescriptor serviceInfo = ( ServiceDescriptor ) sender ;
2021-04-21 01:14:05 -05:00
ServiceManagerDelta managerChange = new ServiceManagerDelta ( ) ;
2021-04-08 21:36:08 -05:00
switch ( state )
{
2021-04-19 01:34:45 -05:00
case ServiceState . Running :
2021-04-08 21:36:08 -05:00
if ( running . TryAdd ( serviceInfo . ServiceName , serviceInfo ) ) {
2021-04-21 01:14:05 -05:00
managerChange . running = serviceInfo . ServiceName ;
2021-04-08 21:36:08 -05:00
}
break ;
2021-04-19 01:34:45 -05:00
case ServiceState . Stopped :
2021-04-08 21:36:08 -05:00
ServiceDescriptor removed ;
2021-04-08 22:25:59 -05:00
if ( running . TryRemove ( serviceInfo . ServiceName , out removed ) ) {
2021-04-08 21:36:08 -05:00
removed . ServiceStateChangeEvent - = OnServiceStateChange ;
2021-04-19 01:34:45 -05:00
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 ;
2021-04-08 21:36:08 -05:00
managerChange . subtract = true ;
2021-04-21 01:14:05 -05:00
managerChange . running = serviceInfo . ServiceName ;
2021-04-08 21:36:08 -05:00
}
break ;
}
managerMonitor . Present ( managerChange ) ;
}
2021-04-19 01:34:45 -05:00
void OnLogUpdated ( object sender , string update ) {
ServiceDescriptor service = ( ServiceDescriptor ) sender ;
2021-04-21 01:14:05 -05:00
ServiceManagerDelta delta = new ServiceManagerDelta ( ) ;
delta . logs = Encoding . UTF8 . GetBytes ( update ) ;
managerMonitor . Present ( delta ) ;
2021-04-19 01:34:45 -05:00
}
2021-04-08 21:36:08 -05:00
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 ;
}
}
2021-04-21 01:14:05 -05:00
2021-04-21 01:44:16 -05:00
public void View ( )
2021-04-21 01:14:05 -05:00
{
GetServiceNames ( ) ;
GetRunningServiceNames ( ) ;
GetModuleNames ( ) ;
2021-04-21 01:44:16 -05:00
GetLogBuffers ( ) ;
2021-04-21 01:14:05 -05:00
GetOptions ( ) ;
}
2021-04-08 21:36:08 -05:00
}
}