using Connected.Collections; using Connected.Configuration.Environment; using Connected.Interop; using Connected.Middleware.Annotations; using Connected.ServiceModel; using System.Collections.Concurrent; using System.Collections.Immutable; using System.Reflection; namespace Connected.Middleware; internal class MiddlewareService : IMiddlewareService { private static readonly object _lock = new(); static MiddlewareService() { Endpoints = new(); } public MiddlewareService(IEnvironmentService environmentService, IContext context) { EnvironmentService = environmentService; Context = context; if (!IsInitialized) { lock (_lock) { if (!IsInitialized) Initialize(); } } } private static bool IsInitialized { get; set; } private IEnvironmentService EnvironmentService { get; } private IContext Context { get; } private static ConcurrentDictionary> Endpoints { get; set; } public async Task> Query() where TEndpoint : IMiddleware { return await Query(null); } public async Task> Query(Type type) { return await Query(type, null); } public async Task> Query(ICallerContext? context) where TEndpoint : IMiddleware { var key = typeof(TEndpoint).FullName; if (key is null || Endpoints is null) return ImmutableList.Empty; if (!Endpoints.TryGetValue(key, out List? items) || items is null) return ImmutableList.Empty; var result = new List(); foreach (var type in items) { if (!Validate(context, type)) continue; if (Context.GetService(type) is object service) result.Add((TEndpoint)service); } result.SortByPriority(); foreach (var r in result) await r.Initialize(); return result.ToImmutableList(); } public async Task> Query(Type type, ICallerContext? context) { var key = type.FullName; if (key is null || Endpoints is null) return ImmutableList.Empty; if (!Endpoints.TryGetValue(key, out List? items) || items is null) return ImmutableList.Empty; var result = new List(); foreach (var t in items) { if (!Validate(context, t)) continue; if (Context.GetService(type) is IMiddleware service) result.Add(service); } result.SortByPriority(); foreach (var r in result) await r.Initialize(); return result.ToImmutableList(); } public async Task First() where TEndpoint : IMiddleware { var key = typeof(TEndpoint).FullName; if (key is null || Endpoints is null) return default; if (!Endpoints.TryGetValue(key, out List? items) || items is null) return default; var types = new List(); foreach (var type in items) types.Add(type); if (!types.Any()) return default; types.SortByPriority(); foreach (var type in types) { if (!Validate(null, type)) continue; if (Context.GetService(type) is object service) { var r = (TEndpoint)service; await r.Initialize(); return r; } } return default; } public async Task First(Type type) { var key = type.FullName; if (key is null || Endpoints is null) return default; if (!Endpoints.TryGetValue(key, out List? items) || items is null) return default; var types = new List(); foreach (var t in items) types.Add(t); if (!types.Any()) return default; types.SortByPriority(); foreach (var t in types) { if (!Validate(null, t)) continue; if (Context.GetService(t) is IMiddleware service) { await service.Initialize(); return service; } } return default; } private static bool Validate(ICallerContext? context, Type type) { if (context is null) return true; var attributes = type.GetCustomAttributes(); foreach (var attribute in attributes) { if (attribute.GetType() != typeof(MiddlewareAttribute<>)) continue; var method = attribute.GetType().GetProperty(nameof(MiddlewareAttribute.ComponentMethod)); if (method is null) continue; if (!string.Equals(TypeConversion.Convert(method.GetValue(attribute)), context.Method, StringComparison.Ordinal)) continue; var argument = attribute.GetType().GetGenericArguments()[0]; if (argument != context.Sender?.GetType()) continue; return true; } return false; } private void Initialize() { IsInitialized = true; foreach (var endpoint in EnvironmentService.Services.IoCEndpoints) { var endpoints = endpoint.GetImplementedMiddleware(); foreach (var ep in endpoints) { if (string.IsNullOrWhiteSpace(ep.FullName)) continue; if (Endpoints.TryGetValue(ep.FullName, out List? list)) list.Add(endpoint); else Endpoints.TryAdd(ep.FullName, new List { endpoint }); } } } }