1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
using System.Reflection;
namespace PagerParser;
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class PagerHandlerAttribute : Attribute {}
public interface IPagerHandler {
public void OnConfiguring(
ILogger logger,
IConfiguration config,
IServiceProvider serviceProvider);
public Task StartAsync(CancellationToken ct);
public Task StopAsync(CancellationToken ct);
public 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<RootPagerHandler> logger;
public RootPagerHandler(IServiceProvider serviceProvider) {
handlers = typeof(RootPagerHandler).Assembly.GetTypes()
.Where(t => t.IsClass)
.Where(t => t.IsAssignableTo(typeof(IPagerHandler)))
.Where(t => t.GetCustomAttributes<PagerHandlerAttribute>().Any())
.Select(t => Activator.CreateInstance(t))
.Cast<IPagerHandler>()
.Select(h => new HandlerEntry(h))
.ToArray();
var config = serviceProvider.GetRequiredService<IConfiguration>();
logger = serviceProvider.GetRequiredService<ILogger<RootPagerHandler>>();
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
}
}
|