aboutsummaryrefslogtreecommitdiff
path: root/VNLib.Plugins.Extensions.Loading/RoutingExtensions.cs
diff options
context:
space:
mode:
authorLibravatar vman <public@vaughnnugent.com>2022-11-16 14:07:28 -0500
committerLibravatar vman <public@vaughnnugent.com>2022-11-16 14:07:28 -0500
commit3fb601d14354c867e1ead94b027c99c4a2fc15b5 (patch)
tree5bf01312166d97eff255d1fdcd26bf314cebcf76 /VNLib.Plugins.Extensions.Loading/RoutingExtensions.cs
parentc3419e3e43f773ba9ee1e4854e15da873829fbd7 (diff)
Add project files.
Diffstat (limited to 'VNLib.Plugins.Extensions.Loading/RoutingExtensions.cs')
-rw-r--r--VNLib.Plugins.Extensions.Loading/RoutingExtensions.cs135
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;
+ }
+ }
+ }
+}