LotSizingDataModel.Solver 2.0.1
Solver-independent modeling, execution, monitoring and adapter infrastructure.
Loading...
Searching...
No Matches
SolverAdapterLoader.cs
Go to the documentation of this file.
1using System;
2using System.IO;
3using System.Reflection;
4using System.Runtime.Loader;
7
9
10/// <summary>
11/// Loads solver adapter plugins from managed assemblies.
12/// </summary>
13/// <remarks>
14/// Adapter-loading failures are converted into
15/// <see cref="SolverAdapterLoadResult"/> instances so that a
16/// missing or incompatible solver plugin does not prevent other
17/// adapters from being discovered and used.
18/// </remarks>
19public sealed class SolverAdapterLoader
20{
21 /// <summary>
22 /// Initializes a new solver-adapter loader.
23 /// </summary>
25 {
26 }
27
28 /// <summary>
29 /// Loads a solver adapter described by the supplied
30 /// descriptor.
31 /// </summary>
32 /// <param name="descriptor">
33 /// Solver-adapter descriptor.
34 /// </param>
35 /// <returns>
36 /// Adapter-loading result.
37 /// </returns>
38 /// <exception cref="ArgumentNullException">
39 /// Thrown when <paramref name="descriptor"/> is
40 /// <see langword="null"/>.
41 /// </exception>
43 SolverAdapterDescriptor descriptor)
44 {
45 ArgumentNullException.ThrowIfNull(
46 descriptor);
47
48 var result =
50 {
51 Descriptor =
52 descriptor,
53
54 AssemblyPath =
55 descriptor.AssemblyPath,
56
57 TypeName =
58 descriptor.TypeName,
59
60 Status =
62 };
63
64 try
65 {
66 descriptor.EnsureValid();
67
68 if (string.IsNullOrWhiteSpace(
69 descriptor.AssemblyPath))
70 {
71 result.Status =
72 SolverAdapterLoadStatus.AssemblyNotFound;
73
74 result.AddDiagnostic(
75 "The adapter assembly path is empty.");
76
77 return result;
78 }
79
80 string assemblyPath =
81 Path.GetFullPath(
82 descriptor.AssemblyPath);
83
84 result.AssemblyPath =
85 assemblyPath;
86
87 if (!File.Exists(
88 assemblyPath))
89 {
90 result.Status =
91 SolverAdapterLoadStatus.AssemblyNotFound;
92
93 result.AddDiagnostic(
94 $"The adapter assembly was not found at " +
95 $"'{assemblyPath}'.");
96
97 return result;
98 }
99
100 Assembly assembly;
101
102 try
103 {
104 assembly =
105 AssemblyLoadContext.Default
106 .LoadFromAssemblyPath(
107 assemblyPath);
108 }
109 catch (Exception exception)
110 when (
111 exception is
112 FileLoadException or
113 FileNotFoundException or
114 BadImageFormatException)
115 {
116 result.Status =
117 SolverAdapterLoadStatus.AssemblyLoadFailure;
118
119 result.SetException(
120 exception);
121
122 result.AddDiagnostic(
123 "The adapter assembly could not be loaded.");
124
125 return result;
126 }
127
128 Type? adapterType =
129 ResolveAdapterType(
130 assembly,
131 descriptor.TypeName);
132
133 if (adapterType is null)
134 {
135 result.Status =
136 SolverAdapterLoadStatus.AdapterTypeNotFound;
137
138 result.AddDiagnostic(
139 string.IsNullOrWhiteSpace(
140 descriptor.TypeName)
141 ? "No public concrete solver-adapter " +
142 "type was found in the assembly."
143 : $"Adapter type " +
144 $"'{descriptor.TypeName}' was not " +
145 "found in the assembly.");
146
147 return result;
148 }
149
150 result.TypeName =
151 adapterType.AssemblyQualifiedName ??
152 adapterType.FullName ??
153 adapterType.Name;
154
155 if (!typeof(ISolverAdapter).IsAssignableFrom(
156 adapterType))
157 {
158 result.Status =
159 SolverAdapterLoadStatus.InvalidAdapterType;
160
161 result.AddDiagnostic(
162 $"Type '{adapterType.FullName}' does not " +
163 $"implement {nameof(ISolverAdapter)}.");
164
165 return result;
166 }
167
168 if (adapterType.IsAbstract ||
169 adapterType.IsInterface)
170 {
171 result.Status =
172 SolverAdapterLoadStatus.InvalidAdapterType;
173
174 result.AddDiagnostic(
175 $"Type '{adapterType.FullName}' is not a " +
176 "concrete adapter type.");
177
178 return result;
179 }
180
181 if (adapterType.GetConstructor(
182 Type.EmptyTypes) is null)
183 {
184 result.Status =
185 SolverAdapterLoadStatus.InvalidAdapterType;
186
187 result.AddDiagnostic(
188 $"Type '{adapterType.FullName}' does not " +
189 "provide a public parameterless " +
190 "constructor.");
191
192 return result;
193 }
194
195 try
196 {
197 result.Adapter =
198 (ISolverAdapter?)Activator.CreateInstance(
199 adapterType);
200 }
201 catch (Exception exception)
202 when (
203 exception is
204 TargetInvocationException or
205 MemberAccessException or
206 MissingMethodException or
207 TypeInitializationException)
208 {
209 result.Status =
210 IsNativeDependencyFailure(
211 exception)
213 .NativeDependencyFailure
215 .InstantiationFailure;
216
217 result.SetException(
218 GetInnermostException(
219 exception));
220
221 result.AddDiagnostic(
222 "The solver adapter could not be " +
223 "instantiated.");
224
225 return result;
226 }
227
228 if (result.Adapter is null)
229 {
230 result.Status =
232 .InstantiationFailure;
233
234 result.AddDiagnostic(
235 "The adapter constructor returned no " +
236 "instance.");
237
238 return result;
239 }
240
241 if (result.Adapter.SolverKind !=
242 descriptor.SolverKind)
243 {
244 result.Status =
245 SolverAdapterLoadStatus.InvalidAdapterType;
246
247 result.AddDiagnostic(
248 $"The adapter reports solver kind " +
249 $"'{result.Adapter.SolverKind}', whereas " +
250 $"the descriptor declares " +
251 $"'{descriptor.SolverKind}'.");
252
253 result.Adapter =
254 null;
255
256 return result;
257 }
258
259 result.Status =
261
262 result.AddDiagnostic(
263 $"Adapter '{result.Adapter.AdapterName}' was " +
264 "loaded successfully.");
265
266 return result;
267 }
268 catch (Exception exception)
269 {
270 result.Status =
271 IsNativeDependencyFailure(
272 exception)
274 .NativeDependencyFailure
276 .AssemblyLoadFailure;
277
278 result.SetException(
279 GetInnermostException(
280 exception));
281
282 result.AddDiagnostic(
283 "An unexpected error occurred while loading " +
284 "the solver adapter.");
285
286 return result;
287 }
288 }
289
290 private static Type? ResolveAdapterType(
291 Assembly assembly,
292 string typeName)
293 {
294 if (!string.IsNullOrWhiteSpace(
295 typeName))
296 {
297 return assembly.GetType(
298 typeName.Trim(),
299 throwOnError:
300 false,
301 ignoreCase:
302 false);
303 }
304
305 try
306 {
307 return Array.Find(
308 assembly.GetExportedTypes(),
309 type =>
310 typeof(ISolverAdapter)
311 .IsAssignableFrom(type) &&
312 !type.IsAbstract &&
313 !type.IsInterface);
314 }
315 catch (ReflectionTypeLoadException exception)
316 {
317 return Array.Find(
318 exception.Types,
319 type =>
320 type is not null &&
321 typeof(ISolverAdapter)
322 .IsAssignableFrom(type) &&
323 !type.IsAbstract &&
324 !type.IsInterface);
325 }
326 }
327
328 private static bool IsNativeDependencyFailure(
329 Exception exception)
330 {
331 Exception innermostException =
332 GetInnermostException(
333 exception);
334
335 return innermostException is
336 DllNotFoundException or
337 BadImageFormatException or
338 FileNotFoundException;
339 }
340
341 private static Exception GetInnermostException(
342 Exception exception)
343 {
344 Exception currentException =
345 exception;
346
347 while (currentException.InnerException is not null)
348 {
349 currentException =
350 currentException.InnerException;
351 }
352
353 return currentException;
354 }
355}
Describes a solver adapter without loading or executing its native optimization engine.
SolverKind SolverKind
Gets or sets the solver kind implemented by the adapter.
string TypeName
Gets or sets the assembly-qualified adapter type name.
string AssemblyPath
Gets or sets the full path of the adapter assembly.
Represents the result of attempting to load a solver adapter plugin.
SolverAdapterLoadResult Load(SolverAdapterDescriptor descriptor)
Loads a solver adapter described by the supplied descriptor.
SolverAdapterLoader()
Initializes a new solver-adapter loader.
Defines the metadata and capabilities exposed by a solver adapter plugin.
SolverAdapterLoadStatus
Describes the result of loading a solver adapter plugin.
@ Discovered
The adapter was discovered but has not been loaded yet.