18
18
using System . Reflection ;
19
19
using Ardalis . GuardClauses ;
20
20
using Microsoft . Extensions . DependencyInjection ;
21
+ using Microsoft . Extensions . Diagnostics . HealthChecks ;
21
22
using Monai . Deploy . Messaging . API ;
22
23
using Monai . Deploy . Messaging . Configuration ;
23
24
24
25
namespace Monai . Deploy . Messaging
25
26
{
26
27
public static class IServiceCollectionExtensions
27
28
{
28
- private static IFileSystem ? s_fileSystem ;
29
-
30
29
/// <summary>
31
30
/// Configures all dependencies required for the MONAI Deploy Message Broker Subscriber Service.
32
31
/// </summary>
33
32
/// <param name="services">Instance of <see cref="IServiceCollection"/>.</param>
34
33
/// <param name="fullyQualifiedTypeName">Fully qualified type name of the service to use.</param>
35
34
/// <returns>Instance of <see cref="IServiceCollection"/>.</returns>
36
35
/// <exception cref="ConfigurationException"></exception>
37
- public static IServiceCollection AddMonaiDeployMessageBrokerSubscriberService ( this IServiceCollection services , string fullyQualifiedTypeName )
38
- => AddMonaiDeployMessageBrokerSubscriberService ( services , fullyQualifiedTypeName , new FileSystem ( ) ) ;
36
+ public static IServiceCollection AddMonaiDeployMessageBrokerSubscriberService (
37
+ this IServiceCollection services ,
38
+ string fullyQualifiedTypeName ,
39
+ bool registerHealthCheck = true ,
40
+ HealthStatus ? failureStatus = null ,
41
+ IEnumerable < string > ? tags = null ,
42
+ TimeSpan ? timeout = null )
43
+ => AddMonaiDeployMessageBrokerSubscriberService ( services , fullyQualifiedTypeName , new FileSystem ( ) , registerHealthCheck , failureStatus , tags , timeout ) ;
39
44
40
45
/// <summary>
41
46
/// Configures all dependencies required for the MONAI Deploy Message Broker Subscriber Service.
@@ -45,8 +50,15 @@ public static IServiceCollection AddMonaiDeployMessageBrokerSubscriberService(th
45
50
/// <param name="fileSystem">Instance of <see cref="IFileSystem"/>.</param>
46
51
/// <returns>Instance of <see cref="IServiceCollection"/>.</returns>
47
52
/// <exception cref="ConfigurationException"></exception>
48
- public static IServiceCollection AddMonaiDeployMessageBrokerSubscriberService ( this IServiceCollection services , string fullyQualifiedTypeName , IFileSystem fileSystem )
49
- => Add < IMessageBrokerSubscriberService , SubscriberServiceRegistrationBase > ( services , fullyQualifiedTypeName , fileSystem ) ;
53
+ public static IServiceCollection AddMonaiDeployMessageBrokerSubscriberService (
54
+ this IServiceCollection services ,
55
+ string fullyQualifiedTypeName ,
56
+ IFileSystem fileSystem ,
57
+ bool registerHealthCheck = true ,
58
+ HealthStatus ? failureStatus = null ,
59
+ IEnumerable < string > ? tags = null ,
60
+ TimeSpan ? timeout = null )
61
+ => Add < IMessageBrokerSubscriberService , SubscriberServiceRegistrationBase , SubscriberServiceHealthCheckRegistrationBase > ( services , fullyQualifiedTypeName , fileSystem , registerHealthCheck , failureStatus , tags , timeout ) ;
50
62
51
63
/// <summary>
52
64
/// Configures all dependencies required for the MONAI Deploy Message Broker Publisher Service.
@@ -55,8 +67,14 @@ public static IServiceCollection AddMonaiDeployMessageBrokerSubscriberService(th
55
67
/// <param name="fullyQualifiedTypeName">Fully qualified type name of the service to use.</param>
56
68
/// <returns>Instance of <see cref="IServiceCollection"/>.</returns>
57
69
/// <exception cref="ConfigurationException"></exception>
58
- public static IServiceCollection AddMonaiDeployMessageBrokerPublisherService ( this IServiceCollection services , string fullyQualifiedTypeName )
59
- => AddMonaiDeployMessageBrokerPublisherService ( services , fullyQualifiedTypeName , new FileSystem ( ) ) ;
70
+ public static IServiceCollection AddMonaiDeployMessageBrokerPublisherService (
71
+ this IServiceCollection services ,
72
+ string fullyQualifiedTypeName ,
73
+ bool registerHealthCheck = true ,
74
+ HealthStatus ? failureStatus = null ,
75
+ IEnumerable < string > ? tags = null ,
76
+ TimeSpan ? timeout = null )
77
+ => AddMonaiDeployMessageBrokerPublisherService ( services , fullyQualifiedTypeName , new FileSystem ( ) , registerHealthCheck , failureStatus , tags , timeout ) ;
60
78
61
79
/// <summary>
62
80
/// Configures all dependencies required for the MONAI Deploy Message Broker Publisher Service.
@@ -66,42 +84,96 @@ public static IServiceCollection AddMonaiDeployMessageBrokerPublisherService(thi
66
84
/// <param name="fileSystem">Instance of <see cref="IFileSystem"/>.</param>
67
85
/// <returns>Instance of <see cref="IServiceCollection"/>.</returns>
68
86
/// <exception cref="ConfigurationException"></exception>
69
- public static IServiceCollection AddMonaiDeployMessageBrokerPublisherService ( this IServiceCollection services , string fullyQualifiedTypeName , IFileSystem fileSystem )
70
- => Add < IMessageBrokerPublisherService , PublisherServiceRegistrationBase > ( services , fullyQualifiedTypeName , fileSystem ) ;
71
-
72
- private static IServiceCollection Add < T , U > ( this IServiceCollection services , string fullyQualifiedTypeName , IFileSystem fileSystem ) where U : ServiceRegistrationBase
87
+ public static IServiceCollection AddMonaiDeployMessageBrokerPublisherService (
88
+ this IServiceCollection services ,
89
+ string fullyQualifiedTypeName ,
90
+ IFileSystem fileSystem ,
91
+ bool registerHealthCheck = true ,
92
+ HealthStatus ? failureStatus = null ,
93
+ IEnumerable < string > ? tags = null ,
94
+ TimeSpan ? timeout = null )
95
+ => Add < IMessageBrokerPublisherService , PublisherServiceRegistrationBase , PublisherServiceHealthCheckRegistrationBase > ( services , fullyQualifiedTypeName , fileSystem , registerHealthCheck , failureStatus , tags , timeout ) ;
96
+
97
+ private static IServiceCollection Add < T , U , V > (
98
+ this IServiceCollection services ,
99
+ string fullyQualifiedTypeName ,
100
+ IFileSystem fileSystem ,
101
+ bool registerHealthCheck = true ,
102
+ HealthStatus ? failureStatus = null ,
103
+ IEnumerable < string > ? tags = null ,
104
+ TimeSpan ? timeout = null )
105
+ where U : ServiceRegistrationBase
106
+ where V : HealthCheckRegistrationBase
73
107
{
74
108
Guard . Against . NullOrWhiteSpace ( fullyQualifiedTypeName , nameof ( fullyQualifiedTypeName ) ) ;
75
109
Guard . Against . Null ( fileSystem , nameof ( fileSystem ) ) ;
76
110
77
- s_fileSystem = fileSystem ;
111
+ ResolveEventHandler resolveEventHandler = ( sender , args ) =>
112
+ {
113
+ return CurrentDomain_AssemblyResolve ( args , fileSystem ) ;
114
+ } ;
78
115
79
- AppDomain . CurrentDomain . AssemblyResolve += CurrentDomain_AssemblyResolve ;
116
+ AppDomain . CurrentDomain . AssemblyResolve += resolveEventHandler ;
80
117
81
118
try
82
119
{
83
- var serviceAssembly = LoadAssemblyFromDisk ( GetAssemblyName ( fullyQualifiedTypeName ) ) ;
84
- var serviceRegistrationType = serviceAssembly . GetTypes ( ) . FirstOrDefault ( p => p . BaseType == typeof ( U ) ) ;
120
+ var serviceAssembly = LoadAssemblyFromDisk ( GetAssemblyName ( fullyQualifiedTypeName ) , fileSystem ) ;
85
121
86
- if ( serviceRegistrationType is null || Activator . CreateInstance ( serviceRegistrationType , fullyQualifiedTypeName ) is not U serviceRegistrar )
122
+ if ( ! IsSupportedType < T > ( fullyQualifiedTypeName , serviceAssembly ) )
87
123
{
88
- throw new ConfigurationException ( $ "Service registrar cannot be found for the configured plug-in ' { fullyQualifiedTypeName } ' .") ;
124
+ throw new ConfigurationException ( $ "The configured type ' { fullyQualifiedTypeName } ' does not implement the { typeof ( T ) . Name } interface .") ;
89
125
}
90
126
91
- if ( ! IsSupportedType < T > ( fullyQualifiedTypeName , serviceAssembly ) )
127
+ RegisterServices < U > ( services , fullyQualifiedTypeName , serviceAssembly ) ;
128
+
129
+ if ( registerHealthCheck )
92
130
{
93
- throw new ConfigurationException ( $ "The configured type ' { fullyQualifiedTypeName } ' does not implement the { typeof ( T ) . Name } interface." ) ;
131
+ RegisterHealtChecks < V > ( services , fullyQualifiedTypeName , serviceAssembly , failureStatus , tags , timeout ) ;
94
132
}
95
133
96
- return serviceRegistrar . Configure ( services ) ;
134
+ return services ;
97
135
}
98
136
finally
99
137
{
100
- AppDomain . CurrentDomain . AssemblyResolve -= CurrentDomain_AssemblyResolve ;
138
+ AppDomain . CurrentDomain . AssemblyResolve -= resolveEventHandler ;
139
+ }
140
+ }
141
+
142
+ private static void RegisterHealtChecks < V > (
143
+ IServiceCollection services ,
144
+ string fullyQualifiedTypeName ,
145
+ Assembly serviceAssembly ,
146
+ HealthStatus ? failureStatus ,
147
+ IEnumerable < string > ? tags ,
148
+ TimeSpan ? timeout ) where V : HealthCheckRegistrationBase
149
+ {
150
+ var healthCheckBaseType = serviceAssembly . GetTypes ( ) . FirstOrDefault ( p => p . BaseType == typeof ( V ) ) ;
151
+
152
+ if ( healthCheckBaseType is null || Activator . CreateInstance ( healthCheckBaseType ) is not V healthCheckBuilderBase )
153
+ {
154
+ throw new ConfigurationException ( $ "Health check registrar cannot be found for the configured plug-in '{ fullyQualifiedTypeName } '.") ;
155
+ }
156
+
157
+ var healthCheckBuilder = services . AddHealthChecks ( ) ;
158
+ healthCheckBuilderBase . Configure ( healthCheckBuilder , failureStatus , tags , timeout ) ;
159
+ }
160
+
161
+ private static void RegisterServices < U > (
162
+ IServiceCollection services ,
163
+ string fullyQualifiedTypeName ,
164
+ Assembly serviceAssembly ) where U : ServiceRegistrationBase
165
+ {
166
+ var serviceRegistrationType = serviceAssembly . GetTypes ( ) . FirstOrDefault ( p => p . BaseType == typeof ( U ) ) ;
167
+
168
+ if ( serviceRegistrationType is null || Activator . CreateInstance ( serviceRegistrationType ) is not U serviceRegistrar )
169
+ {
170
+ throw new ConfigurationException ( $ "Service registrar cannot be found for the configured plug-in '{ fullyQualifiedTypeName } '.") ;
101
171
}
172
+
173
+ serviceRegistrar . Configure ( services ) ;
102
174
}
103
175
104
- private static bool IsSupportedType < T > ( string fullyQualifiedTypeName , Assembly storageServiceAssembly )
176
+ internal static bool IsSupportedType < T > ( string fullyQualifiedTypeName , Assembly storageServiceAssembly )
105
177
{
106
178
Guard . Against . NullOrWhiteSpace ( fullyQualifiedTypeName , nameof ( fullyQualifiedTypeName ) ) ;
107
179
Guard . Against . Null ( storageServiceAssembly , nameof ( storageServiceAssembly ) ) ;
@@ -112,7 +184,7 @@ private static bool IsSupportedType<T>(string fullyQualifiedTypeName, Assembly s
112
184
storageServiceType . GetInterfaces ( ) . Contains ( typeof ( T ) ) ;
113
185
}
114
186
115
- private static string GetAssemblyName ( string fullyQualifiedTypeName )
187
+ internal static string GetAssemblyName ( string fullyQualifiedTypeName )
116
188
{
117
189
var assemblyNameParts = fullyQualifiedTypeName . Split ( ',' , StringSplitOptions . None ) ;
118
190
if ( assemblyNameParts . Length < 2 || string . IsNullOrWhiteSpace ( assemblyNameParts [ 1 ] ) )
@@ -126,31 +198,32 @@ private static string GetAssemblyName(string fullyQualifiedTypeName)
126
198
return assemblyNameParts [ 1 ] . Trim ( ) ;
127
199
}
128
200
129
- private static Assembly CurrentDomain_AssemblyResolve ( object sender , ResolveEventArgs args )
201
+ internal static Assembly CurrentDomain_AssemblyResolve ( ResolveEventArgs args , IFileSystem fileSystem )
130
202
{
131
203
Guard . Against . Null ( args , nameof ( args ) ) ;
204
+ Guard . Against . Null ( fileSystem , nameof ( fileSystem ) ) ;
132
205
133
206
var requestedAssemblyName = new AssemblyName ( args . Name ) ;
134
- return LoadAssemblyFromDisk ( requestedAssemblyName . Name ) ;
207
+ return LoadAssemblyFromDisk ( requestedAssemblyName . Name ! , fileSystem ) ;
135
208
}
136
209
137
- private static Assembly LoadAssemblyFromDisk ( string assemblyName )
210
+ internal static Assembly LoadAssemblyFromDisk ( string assemblyName , IFileSystem fileSystem )
138
211
{
139
212
Guard . Against . NullOrWhiteSpace ( assemblyName , nameof ( assemblyName ) ) ;
140
- Guard . Against . Null ( s_fileSystem , nameof ( s_fileSystem ) ) ;
213
+ Guard . Against . Null ( fileSystem , nameof ( fileSystem ) ) ;
141
214
142
- if ( ! s_fileSystem . Directory . Exists ( SR . PlugInDirectoryPath ) )
215
+ if ( ! fileSystem . Directory . Exists ( SR . PlugInDirectoryPath ) )
143
216
{
144
217
throw new ConfigurationException ( $ "Plug-in directory '{ SR . PlugInDirectoryPath } ' cannot be found.") ;
145
218
}
146
219
147
- var assemblyFilePath = s_fileSystem . Path . Combine ( SR . PlugInDirectoryPath , $ "{ assemblyName } .dll") ;
148
- if ( ! s_fileSystem . File . Exists ( assemblyFilePath ) )
220
+ var assemblyFilePath = fileSystem . Path . Combine ( SR . PlugInDirectoryPath , $ "{ assemblyName } .dll") ;
221
+ if ( ! fileSystem . File . Exists ( assemblyFilePath ) )
149
222
{
150
223
throw new ConfigurationException ( $ "The configured plug-in '{ assemblyFilePath } ' cannot be found.") ;
151
224
}
152
225
153
- var asesmblyeData = s_fileSystem . File . ReadAllBytes ( assemblyFilePath ) ;
226
+ var asesmblyeData = fileSystem . File . ReadAllBytes ( assemblyFilePath ) ;
154
227
return Assembly . Load ( asesmblyeData ) ;
155
228
}
156
229
}
0 commit comments