From 27d2bbe4eaddec5956c3e10672eac0b206feb700 Mon Sep 17 00:00:00 2001 From: DarkDerte <160210611+DarkDerte@users.noreply.github.com> Date: Wed, 21 Jan 2026 22:18:58 +0100 Subject: [PATCH] - Refactor ConfigContext - Add load context from json --- .../StaticWebModule.cs | 24 ++++-- .../Classes/ConfigNode.cs | 28 +++++++ .../Interfaces/IConfigContext.cs | 13 +++ .../Interfaces/ILogContext.cs | 9 ++ .../Interfaces/IServiceContext.cs | 7 -- .../Interfaces/IServiceModule.cs | 2 +- .../ServiceHub.Core/Classes/ConfigNode.cs | 32 +++++++ .../ServiceHub.Core/Classes/SimpleContext.cs | 84 +++++++++++++++++++ .../ServiceHub.Core/Model/ServiceInstance.cs | 32 ++++--- ServiceHub/ServiceHub.Core/ServiceHub.cs | 17 ++-- ServiceHub/ServiceHub.Host/LogContext.cs | 21 +++++ ServiceHub/ServiceHub.Host/Program.cs | 42 ++++++++-- ServiceHub/ServiceHub.Host/SimpleContext.cs | 12 --- 13 files changed, 273 insertions(+), 50 deletions(-) create mode 100644 ServiceHub/ServiceHub.Contracts/Classes/ConfigNode.cs create mode 100644 ServiceHub/ServiceHub.Contracts/Interfaces/IConfigContext.cs create mode 100644 ServiceHub/ServiceHub.Contracts/Interfaces/ILogContext.cs delete mode 100644 ServiceHub/ServiceHub.Contracts/Interfaces/IServiceContext.cs create mode 100644 ServiceHub/ServiceHub.Core/Classes/ConfigNode.cs create mode 100644 ServiceHub/ServiceHub.Core/Classes/SimpleContext.cs create mode 100644 ServiceHub/ServiceHub.Host/LogContext.cs delete mode 100644 ServiceHub/ServiceHub.Host/SimpleContext.cs diff --git a/ServiceHub/Modules/ServiceHub.Modules.StaticWeb/StaticWebModule.cs b/ServiceHub/Modules/ServiceHub.Modules.StaticWeb/StaticWebModule.cs index 40d1781..122808e 100644 --- a/ServiceHub/Modules/ServiceHub.Modules.StaticWeb/StaticWebModule.cs +++ b/ServiceHub/Modules/ServiceHub.Modules.StaticWeb/StaticWebModule.cs @@ -8,24 +8,38 @@ namespace ServiceHub.Modules.StaticWeb { public class StaticWebModule : IServiceModule { - private WebServer _server; + private int _port = 8080; + private string _directory = "wwwroot"; + + private WebServer? _server; private CancellationToken _token; + private ILogContext? _logContext; + private IConfigContext? _configContext; public string Name => "StaticWebModule"; - public void Initialize(IServiceContext context) + public void Initialize(ILogContext log, IConfigContext config) { - context.Log($"{Name} initialized."); + _logContext = log; + _configContext = config; + + var port = _configContext.Get("port") ?? "8080"; + var root = _configContext.Get("directory") ?? "wwwroot"; + + _port = int.Parse(port); + _directory = root; + + _logContext.Info($"{Name} initialized."); } public async Task StartAsync(CancellationToken token) { _token = token; - var fileProvider = new PhysicalFileProvider(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules", "WebModule", "wwwroot")); + var fileProvider = new PhysicalFileProvider(_directory); _server = new WebServer(o => o - .WithUrlPrefix("http://localhost:8080/") + .WithUrlPrefix($"http://localhost:{_port}/") .WithMode(HttpListenerMode.EmbedIO)) .WithLocalSessionManager() .WithModule(new FileModule("wwwroot", (EmbedIO.Files.IFileProvider)fileProvider)); diff --git a/ServiceHub/ServiceHub.Contracts/Classes/ConfigNode.cs b/ServiceHub/ServiceHub.Contracts/Classes/ConfigNode.cs new file mode 100644 index 0000000..ed6d1ba --- /dev/null +++ b/ServiceHub/ServiceHub.Contracts/Classes/ConfigNode.cs @@ -0,0 +1,28 @@ +namespace ServiceHub.Contracts.Classes +{ + public sealed class ConfigNode + { + private readonly Dictionary _children = new(); + private string? _value; + + public string? Value + { + get => _value; + set => _value = value; + } + + public IReadOnlyDictionary Children => _children; + + public ConfigNode GetOrAdd(string key) + { + if (!_children.TryGetValue(key, out var node)) + { + node = new ConfigNode(); + _children[key] = node; + } + return node; + } + + public bool TryGet(string key, out ConfigNode node) => _children.TryGetValue(key, out node!); + } +} diff --git a/ServiceHub/ServiceHub.Contracts/Interfaces/IConfigContext.cs b/ServiceHub/ServiceHub.Contracts/Interfaces/IConfigContext.cs new file mode 100644 index 0000000..f9b6f38 --- /dev/null +++ b/ServiceHub/ServiceHub.Contracts/Interfaces/IConfigContext.cs @@ -0,0 +1,13 @@ +namespace ServiceHub.Contracts.Interfaces +{ + public interface IConfigContext + { + string? Get(string path); + + IConfigContext? GetConfig(string path); + + string? Value { get; } + + IConfigContext[] Items { get; } + } +} diff --git a/ServiceHub/ServiceHub.Contracts/Interfaces/ILogContext.cs b/ServiceHub/ServiceHub.Contracts/Interfaces/ILogContext.cs new file mode 100644 index 0000000..c1eb0df --- /dev/null +++ b/ServiceHub/ServiceHub.Contracts/Interfaces/ILogContext.cs @@ -0,0 +1,9 @@ +namespace ServiceHub.Contracts.Interfaces +{ + public interface ILogContext + { + void Info(string message); + void Warning(string message); + void Error(string message); + } +} diff --git a/ServiceHub/ServiceHub.Contracts/Interfaces/IServiceContext.cs b/ServiceHub/ServiceHub.Contracts/Interfaces/IServiceContext.cs deleted file mode 100644 index d2726af..0000000 --- a/ServiceHub/ServiceHub.Contracts/Interfaces/IServiceContext.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ServiceHub.Contracts.Interfaces -{ - public interface IServiceContext - { - void Log(string message); - } -} diff --git a/ServiceHub/ServiceHub.Contracts/Interfaces/IServiceModule.cs b/ServiceHub/ServiceHub.Contracts/Interfaces/IServiceModule.cs index 277602f..3320819 100644 --- a/ServiceHub/ServiceHub.Contracts/Interfaces/IServiceModule.cs +++ b/ServiceHub/ServiceHub.Contracts/Interfaces/IServiceModule.cs @@ -5,7 +5,7 @@ namespace ServiceHub.Contracts.Interfaces public interface IServiceModule { string Name { get; } - void Initialize(IServiceContext context); + void Initialize(ILogContext log, IConfigContext config); Task StartAsync(CancellationToken token); void Stop(); ServiceState State(); diff --git a/ServiceHub/ServiceHub.Core/Classes/ConfigNode.cs b/ServiceHub/ServiceHub.Core/Classes/ConfigNode.cs new file mode 100644 index 0000000..2ca7e78 --- /dev/null +++ b/ServiceHub/ServiceHub.Core/Classes/ConfigNode.cs @@ -0,0 +1,32 @@ +namespace ServiceHub.Core.Classes +{ + internal class ConfigNode + { + private readonly Dictionary _children = new(); + private readonly List _items = new(); + + public string? Value { get; set; } + + public IReadOnlyDictionary Children => _children; + public IReadOnlyList Items => _items; + + public bool IsArray => _items.Count > 0; + + public ConfigNode GetOrAdd(string key) + { + if (!_children.TryGetValue(key, out var node)) + { + node = new ConfigNode(); + _children[key] = node; + } + return node; + } + + public ConfigNode AddItem() + { + var node = new ConfigNode(); + _items.Add(node); + return node; + } + } +} diff --git a/ServiceHub/ServiceHub.Core/Classes/SimpleContext.cs b/ServiceHub/ServiceHub.Core/Classes/SimpleContext.cs new file mode 100644 index 0000000..e40492f --- /dev/null +++ b/ServiceHub/ServiceHub.Core/Classes/SimpleContext.cs @@ -0,0 +1,84 @@ +using ServiceHub.Contracts.Interfaces; +using System.Text.Json; + +namespace ServiceHub.Core.Classes +{ + public class SimpleContext : IConfigContext + { + internal ConfigNode Root { get; } = new(); + + public string? Value + { + get + { + if (Root == null) + return null; + return Root.Value; + } + } + + public IConfigContext[] Items { get => Root.Items.Select(x => new SimpleContext(x)).ToArray(); } + + public SimpleContext() { } + internal SimpleContext(ConfigNode root) { Root = root; } + + public void LoadJson(string json) + { + using var doc = JsonDocument.Parse(json); + LoadElement(Root, doc.RootElement); + } + + private void LoadElement(ConfigNode node, JsonElement element) + { + switch (element.ValueKind) + { + case JsonValueKind.Object: + foreach (var prop in element.EnumerateObject()) + { + var child = node.GetOrAdd(prop.Name); + LoadElement(child, prop.Value); + } + break; + + case JsonValueKind.Array: + foreach (var item in element.EnumerateArray()) + { + var child = node.AddItem(); + LoadElement(child, item); + } + break; + + case JsonValueKind.String: + case JsonValueKind.Number: + case JsonValueKind.True: + case JsonValueKind.False: + node.Value = element.ToString(); + break; + } + } + + public string? Get(string path) => Traverse(path)?.Value; + + public IConfigContext? GetConfig(string path) => Traverse(path); + + private IConfigContext? Traverse(string path) + { + var parts = path.Split(':', StringSplitOptions.RemoveEmptyEntries); + ConfigNode current = Root; + + foreach (var part in parts) + { + if (int.TryParse(part, out var index)) + { + if (current.Items.Count <= index) + return null; + current = current.Items[index]; + } + else if (!current.Children.TryGetValue(part, out current)) + return null; + } + + return new SimpleContext(current); + } + } +} diff --git a/ServiceHub/ServiceHub.Core/Model/ServiceInstance.cs b/ServiceHub/ServiceHub.Core/Model/ServiceInstance.cs index 81e3ef7..3a68b04 100644 --- a/ServiceHub/ServiceHub.Core/Model/ServiceInstance.cs +++ b/ServiceHub/ServiceHub.Core/Model/ServiceInstance.cs @@ -6,31 +6,41 @@ namespace ServiceHub.Core.Model internal sealed class ServiceInstance { private string _serviceName; - private IServiceModule? ServiceModule { get; set; } - private IServiceContext Context { get; set; } - public ServiceState State => ServiceModule?.State() ?? ServiceState.Error; + private IServiceModule? _serviceModule { get; set; } + private IConfigContext _config { get; set; } + private ILogContext _log { get; set; } + public ServiceState State => _serviceModule?.State() ?? ServiceState.Error; public string Name => _serviceName; - public void StartAsync(CancellationToken ct) => ServiceModule?.StartAsync(ct); + public void StartAsync(CancellationToken ct) => _serviceModule?.StartAsync(ct); - public void Stop(CancellationToken ct) => ServiceModule?.Stop(); + public void Stop(CancellationToken ct) => _serviceModule?.Stop(); - public ServiceInstance(string name, Type type, IServiceContext context) + public ServiceInstance(ILogContext log, IConfigContext config, Type type) { - Context = context; - _serviceName = name; + _config = config; + _log = log; + + _serviceName = config.Get("name") ?? string.Empty; + if (string.IsNullOrEmpty(_serviceName)) + throw new ArgumentNullException("name"); + + IConfigContext? contextValue = config.GetConfig("parameters"); + if (contextValue == null) + throw new ArgumentNullException("parameters"); + try { if (!typeof(IServiceModule).IsAssignableFrom(type) || type.IsAbstract) throw new Exception($"The {type.Name} Module is not valid" ); - ServiceModule = (IServiceModule)Activator.CreateInstance(type); - ServiceModule?.Initialize(context); + _serviceModule = (IServiceModule)Activator.CreateInstance(type); + _serviceModule?.Initialize(log, contextValue); } catch (Exception ex) { - Context.Log($"Error : {ex.Message}"); + _log.Error($"Error : {ex.Message}"); throw; } } diff --git a/ServiceHub/ServiceHub.Core/ServiceHub.cs b/ServiceHub/ServiceHub.Core/ServiceHub.cs index 532cae5..43beffe 100644 --- a/ServiceHub/ServiceHub.Core/ServiceHub.cs +++ b/ServiceHub/ServiceHub.Core/ServiceHub.cs @@ -28,14 +28,19 @@ private ServiceHub() { } #region Public Methods - public bool InitModule(string typeService, string name, IServiceContext context) + public bool InitModule(ILogContext log, IConfigContext config) { + var typeService = config.Get("type") ?? string.Empty; + + if (string.IsNullOrEmpty(typeService)) + throw new ArgumentNullException("type"); + if (!_availableModules.TryGetValue(typeService, out Type? type)) return false; try { - var instance = new ServiceInstance(name, type, context); - _modulesLoaded.Add(name.ToLower().Trim(), instance); + var instance = new ServiceInstance(log, config, type); + _modulesLoaded.Add(instance.Name.ToLower().Trim(), instance); } catch { return false; @@ -65,7 +70,7 @@ public void StopAll() #endregion - public void LoadModules(string folderPath, IServiceContext context) + public void LoadModules(ILogContext log, string folderPath) { if (!Directory.Exists(folderPath)) Directory.CreateDirectory(folderPath); @@ -86,13 +91,13 @@ public void LoadModules(string folderPath, IServiceContext context) if (!string.IsNullOrWhiteSpace(name)) { _availableModules.Add(name ?? string.Empty, type); - context.Log($"Loaded module: {name}"); + log.Info($"Loaded module: {name}"); } } } catch (Exception ex) { - context.Log($"Failed to load {file}: {ex.Message}"); + log.Warning($"Failed to load {file}: {ex.Message}"); } } } diff --git a/ServiceHub/ServiceHub.Host/LogContext.cs b/ServiceHub/ServiceHub.Host/LogContext.cs new file mode 100644 index 0000000..3f10d40 --- /dev/null +++ b/ServiceHub/ServiceHub.Host/LogContext.cs @@ -0,0 +1,21 @@ +using ServiceHub.Contracts.Classes; +using ServiceHub.Contracts.Interfaces; + +namespace ServiceHub.Host +{ + internal class LogContext : ILogContext + { + public void Info(string message) => Write(ConsoleColor.Blue, message); + + public void Warning(string message) => Write(ConsoleColor.Yellow, message); + + public void Error(string message) => Write(ConsoleColor.Red,message); + + private void Write(ConsoleColor Color, string message) + { + Console.ForegroundColor = Color; + Console.WriteLine(message); + Console.ForegroundColor = ConsoleColor.White; + } + } +} diff --git a/ServiceHub/ServiceHub.Host/Program.cs b/ServiceHub/ServiceHub.Host/Program.cs index 4419d87..8fb3a5d 100644 --- a/ServiceHub/ServiceHub.Host/Program.cs +++ b/ServiceHub/ServiceHub.Host/Program.cs @@ -1,25 +1,51 @@ -namespace ServiceHub.Host +using ServiceHub.Core.Classes; + +namespace ServiceHub.Host { internal class Program { static void Main(string[] args) { var context = new SimpleContext(); - Core.ServiceHub.Instance.LoadModules("Modules", context); - Core.ServiceHub.Instance.InitModule(Core.ServiceHub.Instance.Modules.First(), "test", context); - Core.ServiceHub.Instance.Start("test"); + var log = new LogContext(); + + context.LoadJson(@"{ + ""services"": [ + { + ""name"": ""WebStatic"", + ""type"": ""StaticWebModule"", + ""parameters"": { + ""port"": ""8080"", + ""root"": ""wwwroot"" + } + } + ] +}"); + + Core.ServiceHub.Instance.LoadModules(log, "Modules"); + + if (context.GetConfig("services") == null) + throw new Exception("Json Config is not valid"); + + log.Info("ServiceHub running..."); + foreach (var localContext in context.GetConfig("services").Items) + { + Core.ServiceHub.Instance.InitModule(log, localContext); + Core.ServiceHub.Instance.Start(localContext.Get("name")); - Console.WriteLine("ServiceHub running. Press any key to stop..."); + } + log.Info("Press any key to stop..."); Console.ReadKey(); - Core.ServiceHub.Instance.Stop("test"); + foreach (var item in Core.ServiceHub.Instance.ServicesInitialized) + Core.ServiceHub.Instance.Stop(item); - Console.WriteLine("ServiceHub stopping."); + log.Info("ServiceHub stopping..."); while (Core.ServiceHub.Instance.ServicesInitialized.Count > 0) Thread.Sleep(100); - Console.WriteLine("ServiceHub stopped."); + log.Info("ServiceHub stopped."); } } } diff --git a/ServiceHub/ServiceHub.Host/SimpleContext.cs b/ServiceHub/ServiceHub.Host/SimpleContext.cs deleted file mode 100644 index 6e51348..0000000 --- a/ServiceHub/ServiceHub.Host/SimpleContext.cs +++ /dev/null @@ -1,12 +0,0 @@ -using ServiceHub.Contracts.Interfaces; - -namespace ServiceHub.Host -{ - internal class SimpleContext : IServiceContext - { - public void Log(string message) - { - Console.WriteLine($"[LOG] {message}"); - } - } -}