2021-04-08 21:36:08 -05:00
using System ;
using System.Collections.Concurrent ;
using System.Collections.Generic ;
using System.Collections.Immutable ;
2021-04-20 23:55:03 -05:00
using GameServiceWarden.ClientAPI.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-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 ;
private readonly LRUCache < string , ServiceDescriptor > descriptorCache ;
2021-04-08 21:36:08 -05:00
private readonly IReadOnlyPersistent < IReadOnlyDictionary < string , IServiceModule > > modules ;
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-08 21:36:08 -05:00
ServiceManagerState managerState = new ServiceManagerState ( ) ;
managerState . delta = true ;
managerState . subtract = false ;
managerState . services = new List < string > ( ) ;
managerState . services . Add ( serviceName ) ;
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 ) ;
ServiceManagerState managerState = new ServiceManagerState ( ) ;
managerState . delta = true ;
managerState . subtract = true ;
managerState . services = new List < string > ( ) ;
managerState . services . Add ( serviceName ) ;
managerMonitor . Present ( managerState ) ;
}
public IEnumerable < string > GetModuleNames ( )
{
ServiceManagerState managerState = new ServiceManagerState ( ) ;
managerState . modules = modules . Keys . ToImmutableArray ( ) ;
managerMonitor . Present ( managerState ) ;
return modules . Keys ;
}
public IEnumerable < string > GetServiceNames ( )
{
ServiceManagerState managerState = new ServiceManagerState ( ) ;
managerState . services = services . Keys . ToImmutableArray ( ) ;
managerMonitor . Present ( managerState ) ;
return services . Keys ;
}
public IEnumerable < string > GetRunningServiceNames ( ) {
ServiceManagerState managerState = new ServiceManagerState ( ) ;
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 ( ) {
ServiceManagerState managerState = new ServiceManagerState ( ) ;
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-08 21:36:08 -05:00
ServiceManagerState managerState = new ServiceManagerState ( ) ;
2021-04-19 01:34:45 -05:00
if ( service . SetConfigurableValue ( optionName , value ) ) {
2021-04-08 21:36:08 -05:00
managerState . delta = true ;
Dictionary < string , IReadOnlyDictionary < string , string > > changedOption = new Dictionary < string , IReadOnlyDictionary < string , string > > ( ) ;
Dictionary < string , string > options = new Dictionary < string , string > ( ) ;
options [ optionName ] = GetServiceOptionValue ( serviceName , optionName ) ;
changedOption [ serviceName ] = options ;
managerState . serviceOptions = changedOption ;
}
managerMonitor . Present ( managerState ) ;
return managerState . delta ;
}
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-19 01:34:45 -05:00
public Dictionary < string , byte [ ] > GetLogBuffer ( ) {
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-19 01:34:45 -05:00
ServiceManagerState managerState = new ServiceManagerState ( ) ;
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 ;
ServiceManagerState managerChange = new ServiceManagerState ( ) ;
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 ) ) {
managerChange . delta = true ;
managerChange . running = new List < string > ( ) ;
managerChange . running . Add ( serviceInfo . ServiceName ) ;
}
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 . delta = true ;
managerChange . subtract = true ;
managerChange . running = new List < string > ( ) ;
managerChange . running . Add ( serviceInfo . ServiceName ) ;
}
break ;
}
managerMonitor . Present ( managerChange ) ;
}
2021-04-19 01:34:45 -05:00
void OnLogUpdated ( object sender , string update ) {
ServiceDescriptor service = ( ServiceDescriptor ) sender ;
ServiceManagerState state = new ServiceManagerState ( ) ;
state . delta = true ;
Dictionary < string , string > logUpdate = new Dictionary < string , string > ( ) ;
logUpdate [ service . ServiceName ] = update ;
managerMonitor . Present ( state ) ;
}
2021-04-08 21:36:08 -05:00
public void ExecuteAction ( ServiceManagerAction action )
{
switch ( action . action )
{
case ServiceManagerAction . Type . View :
GetServiceNames ( ) ;
GetRunningServiceNames ( ) ;
GetModuleNames ( ) ;
2021-04-19 01:34:45 -05:00
GetLogBuffer ( ) ;
2021-04-08 21:36:08 -05:00
GetOptions ( ) ;
break ;
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 ;
}
}
}
}