using System.Reflection; namespace PagerParser; [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] internal class PagerHandlerAttribute : Attribute {} internal interface IPagerHandler { internal void OnConfiguring( ILogger logger, IConfiguration config, IServiceProvider serviceProvider); internal Task StartAsync(CancellationToken ct); internal Task StopAsync(CancellationToken ct); internal Task HandleMessageAsync( PagerMessage message, ParsedPagerMessage? parsedMessage); } public interface IRootPagerHandler { public Task HandleMessageAsync( PagerMessage message, ParsedPagerMessage? parsedMessage); } public class RootPagerHandler : IRootPagerHandler, IHostedService { private readonly HandlerEntry[] handlers; private readonly ILogger logger; public RootPagerHandler(IServiceProvider serviceProvider) { handlers = typeof(RootPagerHandler).Assembly.GetTypes() .Where(t => t.IsClass) .Where(t => t.IsAssignableTo(typeof(IPagerHandler))) .Where(t => t.GetCustomAttributes().Any()) .Select(Activator.CreateInstance) .Cast() .Select(h => new HandlerEntry(h)) .ToArray(); var config = serviceProvider.GetRequiredService(); logger = serviceProvider.GetRequiredService>(); foreach(var entry in handlers) { var name = entry.Handler.GetType().Name; var loggerType = typeof(ILogger<>).MakeGenericType(entry.GetType()); var logger = serviceProvider.GetRequiredService(loggerType); try { this.logger.LogDebug($"Configuring page handler: {name}"); entry.Handler.OnConfiguring((ILogger) logger, config, serviceProvider); } catch { // TODO: include exceptions in log on failure this.logger.LogWarning($"Failed to configure page handler: {name}"); entry.State = HandlerState.Failed; } } } public async Task StartAsync(CancellationToken ct) { logger.LogInformation("Root pager handler service starting..."); await Task.WhenAll( handlers.Select(async e => { var name = e.Handler.GetType().Name; if(e.State != HandlerState.Stopped) return; logger.LogDebug($"Starting page handler: {name}"); try { await e.Handler.StartAsync(ct); e.State = HandlerState.Started; } catch { logger.LogWarning($"Failed to start page handler: {name}"); e.State = HandlerState.Failed; } }).ToArray()); } public async Task StopAsync(CancellationToken ct) { logger.LogInformation("Root pager handler service stopping..."); await Task.WhenAll( handlers.Select(async e => { var name = e.Handler.GetType().Name; if(e.State != HandlerState.Started) return; logger.LogDebug($"Stopping page handler: {name}"); try { await e.Handler.StopAsync(ct); e.State = HandlerState.Stopped; } catch { logger.LogWarning($"Failed to stop page handler: {name}"); e.State = HandlerState.Failed; } }).ToArray()); } public async Task HandleMessageAsync(PagerMessage m, ParsedPagerMessage? pm) { await Task.WhenAll( handlers .Where(e => e.State == HandlerState.Started) .Select(async e => { var name = e.Handler.GetType().Name; try { await e.Handler.HandleMessageAsync(m, pm); } catch { logger.LogWarning($"Page handler {name} failed to handle pager message"); } }).ToArray()); } private record HandlerEntry { public IPagerHandler Handler { get; set; } public HandlerState State { get; set; } = HandlerState.Stopped; public HandlerEntry(IPagerHandler handler) => Handler = handler; } private enum HandlerState { Stopped, Started, Failed } }