diff options
author | vman <public@vaughnnugent.com> | 2022-11-16 14:07:28 -0500 |
---|---|---|
committer | vman <public@vaughnnugent.com> | 2022-11-16 14:07:28 -0500 |
commit | 3fb601d14354c867e1ead94b027c99c4a2fc15b5 (patch) | |
tree | 5bf01312166d97eff255d1fdcd26bf314cebcf76 /VNLib.Plugins.Extensions.Loading/RoutingExtensions.cs | |
parent | c3419e3e43f773ba9ee1e4854e15da873829fbd7 (diff) |
Add project files.
Diffstat (limited to 'VNLib.Plugins.Extensions.Loading/RoutingExtensions.cs')
-rw-r--r-- | VNLib.Plugins.Extensions.Loading/RoutingExtensions.cs | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/VNLib.Plugins.Extensions.Loading/RoutingExtensions.cs b/VNLib.Plugins.Extensions.Loading/RoutingExtensions.cs new file mode 100644 index 0000000..3e539e3 --- /dev/null +++ b/VNLib.Plugins.Extensions.Loading/RoutingExtensions.cs @@ -0,0 +1,135 @@ +using System; +using System.Linq; +using System.Text.Json; +using System.Reflection; +using System.Collections.Generic; + +using VNLib.Plugins.Extensions.Loading.Events; +using VNLib.Plugins.Extensions.Loading.Configuration; + +namespace VNLib.Plugins.Extensions.Loading.Routing +{ + /// <summary> + /// Provides advanced QOL features to plugin loading + /// </summary> + public static class RoutingExtensions + { + /// <summary> + /// Constructs and routes the specific endpoint type for the current plugin + /// </summary> + /// <typeparam name="T">The <see cref="IEndpoint"/> type</typeparam> + /// <param name="plugin"></param> + /// <param name="pluginConfigPathName">The path to the plugin sepcific configuration property</param> + /// <exception cref="TargetInvocationException"></exception> + public static T Route<T>(this PluginBase plugin, string? pluginConfigPathName) where T : IEndpoint + { + Type endpointType = typeof(T); + try + { + //If the config attribute is not set, then ignore the config variables + if (string.IsNullOrWhiteSpace(pluginConfigPathName)) + { + ConstructorInfo? constructor = endpointType.GetConstructor(new Type[] { typeof(PluginBase) }); + _ = constructor ?? throw new EntryPointNotFoundException($"No constructor found for {endpointType.Name}"); + //Create the new endpoint and pass the plugin instance + T endpoint = (T)constructor.Invoke(new object[] { plugin }); + //Register event handlers for the endpoint + ScheduleIntervals(plugin, endpoint, endpointType, null); + //Route the endpoint + plugin.Route(endpoint); + return endpoint; + } + else + { + ConstructorInfo? constructor = endpointType.GetConstructor(new Type[] { typeof(PluginBase), typeof(Dictionary<string, JsonElement>) }); + //Make sure the constructor exists + _ = constructor ?? throw new EntryPointNotFoundException($"No constructor found for {endpointType.Name}"); + //Get config variables for the endpoint + IReadOnlyDictionary<string, JsonElement> conf = plugin.GetConfig(pluginConfigPathName); + //Create the new endpoint and pass the plugin instance along with the configuration object + T endpoint = (T)constructor.Invoke(new object[] { plugin, conf }); + //Register event handlers for the endpoint + ScheduleIntervals(plugin, endpoint, endpointType, conf); + //Route the endpoint + plugin.Route(endpoint); + return endpoint; + } + } + catch (TargetInvocationException te) when (te.InnerException != null) + { + throw te.InnerException; + } + } + + /// <summary> + /// Constructs and routes the specific endpoint type for the current plugin + /// </summary> + /// <typeparam name="T">The <see cref="IEndpoint"/> type</typeparam> + /// <param name="plugin"></param> + /// <exception cref="TargetInvocationException"></exception> + public static T Route<T>(this PluginBase plugin) where T : IEndpoint + { + Type endpointType = typeof(T); + //Get config name attribute + ConfigurationNameAttribute? configAttr = endpointType.GetCustomAttribute<ConfigurationNameAttribute>(); + //Route using attribute + return plugin.Route<T>(configAttr?.ConfigVarName); + } + + private static void ScheduleIntervals<T>(PluginBase plugin, T endpointInstance, Type epType, IReadOnlyDictionary<string, JsonElement>? endpointLocalConfig) where T: IEndpoint + { + List<EventHandle> registered = new(); + try + { + //Get all methods that have the configureable async interval attribute specified + IEnumerable<Tuple<ConfigurableAsyncIntervalAttribute, AsyncSchedulableCallback>> confIntervals = epType.GetMethods() + .Where(m => m.GetCustomAttribute<ConfigurableAsyncIntervalAttribute>() != null) + .Select(m => new Tuple<ConfigurableAsyncIntervalAttribute, AsyncSchedulableCallback> + (m.GetCustomAttribute<ConfigurableAsyncIntervalAttribute>()!, m.CreateDelegate<AsyncSchedulableCallback>(endpointInstance))); + + //If the endpoint has a local config, then use it to find the interval + if (endpointLocalConfig != null) + { + + //Schedule event handlers on the current plugin + foreach (Tuple<ConfigurableAsyncIntervalAttribute, AsyncSchedulableCallback> interval in confIntervals) + { + int value = endpointLocalConfig[interval.Item1.IntervalPropertyName].GetInt32(); + //Get the timeout from its resolution variable + TimeSpan timeout = interval.Item1.Resolution switch + { + IntervalResultionType.Seconds => TimeSpan.FromSeconds(value), + IntervalResultionType.Minutes => TimeSpan.FromMinutes(value), + IntervalResultionType.Hours => TimeSpan.FromHours(value), + _ => TimeSpan.FromMilliseconds(value), + }; + //Schedule + registered.Add(plugin.ScheduleInterval(interval.Item2, timeout)); + } + } + + //Get all methods that have the async interval attribute specified + IEnumerable<Tuple<AsyncIntervalAttribute, AsyncSchedulableCallback>> intervals = epType.GetMethods() + .Where(m => m.GetCustomAttribute<AsyncIntervalAttribute>() != null) + .Select(m => new Tuple<AsyncIntervalAttribute, AsyncSchedulableCallback>( + m.GetCustomAttribute<AsyncIntervalAttribute>()!, m.CreateDelegate<AsyncSchedulableCallback>(endpointInstance)) + ); + + //Schedule event handlers on the current plugin + foreach (Tuple<AsyncIntervalAttribute, AsyncSchedulableCallback> interval in intervals) + { + registered.Add(plugin.ScheduleInterval(interval.Item2, interval.Item1.Interval)); + } + } + catch + { + //Stop all event handles + foreach(EventHandle evh in registered) + { + evh.Dispose(); + } + throw; + } + } + } +} |