From 766e179d110db4f955fffce55f2b0ad41c139179 Mon Sep 17 00:00:00 2001 From: vnugent Date: Wed, 6 Mar 2024 21:35:35 -0500 Subject: refactor: changed how service constructors are invoked, moved routing --- .../src/LoadingExtensions.cs | 77 +++++++++------ .../src/Routing/RoutingExtensions.cs | 110 +++++++++++++++++++++ .../src/RoutingExtensions.cs | 110 --------------------- 3 files changed, 155 insertions(+), 142 deletions(-) create mode 100644 lib/VNLib.Plugins.Extensions.Loading/src/Routing/RoutingExtensions.cs delete mode 100644 lib/VNLib.Plugins.Extensions.Loading/src/RoutingExtensions.cs diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs b/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs index 3538337..b65c5e6 100644 --- a/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs +++ b/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs @@ -376,7 +376,7 @@ namespace VNLib.Plugins.Extensions.Loading string assemblyDllName, SearchOption search = SearchOption.AllDirectories, AssemblyLoadContext? defaultCtx = null - ) + ) where T : class { /* * Get or create the library for the assembly path, but only load it once @@ -636,42 +636,16 @@ namespace VNLib.Plugins.Extensions.Loading } object service; - ConstructorInfo? constructor; try { - //Determin configuration requirments - if (ConfigurationExtensions.ConfigurationRequired(serviceType) || config != null) - { - if (config == null) - { - ConfigurationExtensions.ThrowConfigNotFoundForType(serviceType); - } - - //Get the constructor for required or available config - constructor = serviceType.GetConstructor([typeof(PluginBase), typeof(IConfigScope)]); - - //Make sure the constructor exists - _ = constructor ?? throw new MissingMemberException($"No constructor found for {serviceType.Name}"); - - //Call constructore - service = constructor.Invoke(new object[2] { plugin, config }); - } - else if((constructor = serviceType.GetConstructor([typeof(PluginBase)])) != null) - { - //Call constructor - service = constructor.Invoke(new object[1] { plugin }); - } - //try to get empty constructor - else if ((constructor = serviceType.GetConstructor([])) != null) + //Determine configuration requirments + if (ConfigurationExtensions.ConfigurationRequired(serviceType) && config == null) { - //Invoked empty constructor - service = constructor.Invoke(null); - } - else - { - throw new MissingMemberException($"No constructor found for {serviceType.Name}"); + ConfigurationExtensions.ThrowConfigNotFoundForType(serviceType); } + + service = InvokeServiceConstructor(serviceType, plugin, config); } catch(TargetInvocationException te) when (te.InnerException != null) { @@ -720,6 +694,45 @@ namespace VNLib.Plugins.Extensions.Loading return service; } + /* + * Attempts to find the most appropriate constructor for the service type + * if found, then invokes it to create the service instance + */ + + private static object InvokeServiceConstructor(Type serviceSType, PluginBase plugin, IConfigScope? config) + { + ConstructorInfo? constructor; + + /* + * First try to load a constructor with the plugin and config scope + */ + if (config != null) + { + constructor = serviceSType.GetConstructor([typeof(PluginBase), typeof(IConfigScope)]); + + if(constructor is not null) + { + return constructor.Invoke([plugin, config]); + } + } + + //Try to get plugin only constructor + constructor = serviceSType.GetConstructor([typeof(PluginBase)]); + if (constructor is not null) + { + return constructor.Invoke([plugin]); + } + + //Finally fall back to the empty constructor + constructor = serviceSType.GetConstructor([]); + if (constructor is not null) + { + return constructor.Invoke(null); + } + + throw new MissingMemberException($"No constructor found for {serviceSType.Name}"); + } + [DoesNotReturn] internal static void FindAndThrowInnerException(Exception ex) { diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/Routing/RoutingExtensions.cs b/lib/VNLib.Plugins.Extensions.Loading/src/Routing/RoutingExtensions.cs new file mode 100644 index 0000000..a1817a8 --- /dev/null +++ b/lib/VNLib.Plugins.Extensions.Loading/src/Routing/RoutingExtensions.cs @@ -0,0 +1,110 @@ +/* +* Copyright (c) 2024 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Extensions.Loading +* File: RoutingExtensions.cs +* +* RoutingExtensions.cs is part of VNLib.Plugins.Extensions.Loading which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Plugins.Extensions.Loading is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* VNLib.Plugins.Extensions.Loading is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +using VNLib.Plugins.Essentials.Runtime; + +namespace VNLib.Plugins.Extensions.Loading.Routing +{ + + /// + /// Provides advanced QOL features to plugin loading + /// + public static class RoutingExtensions + { + private static readonly ConditionalWeakTable _pluginRefs = new(); + private static readonly ConditionalWeakTable _pluginEndpoints = new(); + + /// + /// Constructs and routes the specific endpoint type for the current plugin + /// + /// The type + /// + /// + public static T Route(this PluginBase plugin) where T : IEndpoint + { + //Create the endpoint service, then route it + T endpoint = plugin.CreateService(); + + //Route the endpoint + plugin.Route(endpoint); + + //Store ref to plugin for endpoint + _pluginRefs.Add(endpoint, plugin); + + return endpoint; + } + + /// + /// Routes a single endpoint for the current plugin and exports the collection to the + /// service pool + /// + /// + /// The endpoint to add to the collection + public static void Route(this PluginBase plugin, IEndpoint endpoint) + { + /* + * Export the new collection to the service pool in the constructor + * function to ensure it's only export once per plugin + */ + static EndpointCollection OnCreate(PluginBase plugin) + { + EndpointCollection collection = new(); + plugin.ExportService(collection); + return collection; + } + + //Get the endpoint collection for the current plugin + EndpointCollection endpoints = _pluginEndpoints.GetValue(plugin, OnCreate); + + //Add the endpoint to the collection + endpoints.Endpoints.Add(endpoint); + } + + /// + /// Gets the plugin that loaded the current endpoint + /// + /// + /// The plugin that loaded the current endpoint + /// + public static PluginBase GetPlugin(this IEndpoint ep) + { + _ = _pluginRefs.TryGetValue(ep, out PluginBase? pBase); + return pBase ?? throw new InvalidOperationException("Endpoint was not dynamically routed"); + } + + + private sealed class EndpointCollection : IVirtualEndpointDefinition + { + public List Endpoints { get; } = new(); + + /// + IEnumerable IVirtualEndpointDefinition.GetEndpoints() => Endpoints; + } + } +} diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/RoutingExtensions.cs b/lib/VNLib.Plugins.Extensions.Loading/src/RoutingExtensions.cs deleted file mode 100644 index a1817a8..0000000 --- a/lib/VNLib.Plugins.Extensions.Loading/src/RoutingExtensions.cs +++ /dev/null @@ -1,110 +0,0 @@ -/* -* Copyright (c) 2024 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Extensions.Loading -* File: RoutingExtensions.cs -* -* RoutingExtensions.cs is part of VNLib.Plugins.Extensions.Loading which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Extensions.Loading is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Extensions.Loading is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System; -using System.Reflection; -using System.Collections.Generic; -using System.Runtime.CompilerServices; - -using VNLib.Plugins.Essentials.Runtime; - -namespace VNLib.Plugins.Extensions.Loading.Routing -{ - - /// - /// Provides advanced QOL features to plugin loading - /// - public static class RoutingExtensions - { - private static readonly ConditionalWeakTable _pluginRefs = new(); - private static readonly ConditionalWeakTable _pluginEndpoints = new(); - - /// - /// Constructs and routes the specific endpoint type for the current plugin - /// - /// The type - /// - /// - public static T Route(this PluginBase plugin) where T : IEndpoint - { - //Create the endpoint service, then route it - T endpoint = plugin.CreateService(); - - //Route the endpoint - plugin.Route(endpoint); - - //Store ref to plugin for endpoint - _pluginRefs.Add(endpoint, plugin); - - return endpoint; - } - - /// - /// Routes a single endpoint for the current plugin and exports the collection to the - /// service pool - /// - /// - /// The endpoint to add to the collection - public static void Route(this PluginBase plugin, IEndpoint endpoint) - { - /* - * Export the new collection to the service pool in the constructor - * function to ensure it's only export once per plugin - */ - static EndpointCollection OnCreate(PluginBase plugin) - { - EndpointCollection collection = new(); - plugin.ExportService(collection); - return collection; - } - - //Get the endpoint collection for the current plugin - EndpointCollection endpoints = _pluginEndpoints.GetValue(plugin, OnCreate); - - //Add the endpoint to the collection - endpoints.Endpoints.Add(endpoint); - } - - /// - /// Gets the plugin that loaded the current endpoint - /// - /// - /// The plugin that loaded the current endpoint - /// - public static PluginBase GetPlugin(this IEndpoint ep) - { - _ = _pluginRefs.TryGetValue(ep, out PluginBase? pBase); - return pBase ?? throw new InvalidOperationException("Endpoint was not dynamically routed"); - } - - - private sealed class EndpointCollection : IVirtualEndpointDefinition - { - public List Endpoints { get; } = new(); - - /// - IEnumerable IVirtualEndpointDefinition.GetEndpoints() => Endpoints; - } - } -} -- cgit