diff options
Diffstat (limited to 'lib/VNLib.Plugins.Extensions.Loading/src')
3 files changed, 62 insertions, 49 deletions
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/ConfigurationExtensions.cs b/lib/VNLib.Plugins.Extensions.Loading/src/ConfigurationExtensions.cs index bbd6c10..0337fbd 100644 --- a/lib/VNLib.Plugins.Extensions.Loading/src/ConfigurationExtensions.cs +++ b/lib/VNLib.Plugins.Extensions.Loading/src/ConfigurationExtensions.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023 Vaughn Nugent +* Copyright (c) 2024 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Extensions.Loading @@ -198,12 +198,12 @@ namespace VNLib.Plugins.Extensions.Loading public static T GetRequiredProperty<T>(this IConfigScope config, string property, Func<JsonElement, T> getter) { //Check null - _ = config ?? throw new ArgumentNullException(nameof(config)); - _ = property ?? throw new ArgumentNullException(nameof(property)); - _ = getter ?? throw new ArgumentNullException(nameof(getter)); + ArgumentNullException.ThrowIfNull(config); + ArgumentNullException.ThrowIfNull(property); + ArgumentNullException.ThrowIfNull(getter); //Get the property - if(!config.TryGetValue(property, out JsonElement el)) + if (!config.TryGetValue(property, out JsonElement el)) { throw new KeyNotFoundException($"Missing required configuration property '{property}' in config {config.ScopeName}"); } @@ -223,12 +223,13 @@ namespace VNLib.Plugins.Extensions.Loading /// <param name="getter">The function used to set the desired value from the config element</param> /// <param name="value">The output value to set</param> /// <returns>A value that indicates if the property was found</returns> + /// <exception cref="ArgumentNullException"></exception> public static bool TryGetProperty<T>(this IConfigScope config, string property, Func<JsonElement, T> getter, out T? value) { //Check null - ArgumentNullException.ThrowIfNull(config, nameof(config)); - ArgumentNullException.ThrowIfNull(property, nameof(property)); - ArgumentNullException.ThrowIfNull(getter, nameof(getter)); + ArgumentNullException.ThrowIfNull(config); + ArgumentNullException.ThrowIfNull(property); + ArgumentNullException.ThrowIfNull(getter); //Get the property if (config.TryGetValue(property, out JsonElement el)) @@ -242,6 +243,24 @@ namespace VNLib.Plugins.Extensions.Loading } /// <summary> + /// Gets a configuration property from the specified configuration scope + /// and invokes your callback function on the element if found to transform the + /// output value, or returns the default value if the property is not found. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="config"></param> + /// <param name="property">The name of the configuration element to get</param> + /// <param name="getter">The function used to set the desired value from the config element</param> + /// <param name="defaultValue">The default value to return</param> + /// <returns>The property value returned from your getter callback, or the default value if not found</returns> + /// <exception cref="ArgumentNullException"></exception> + [return:NotNullIfNotNull(nameof(defaultValue))] + public static T? GetValueOrDefault<T>(this IConfigScope config, string property, Func<JsonElement, T> getter, T defaultValue) + { + return TryGetProperty(config, property, getter, out T? value) ? value : defaultValue; + } + + /// <summary> /// Gets the configuration property name for the type /// </summary> /// <param name="type">The type to get the configuration name for</param> diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs b/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs index 470d954..3538337 100644 --- a/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs +++ b/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023 Vaughn Nugent +* Copyright (c) 2024 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Extensions.Loading @@ -30,7 +30,6 @@ using System.Reflection; using System.Runtime.Loader; using System.Threading.Tasks; using System.Collections.Generic; -using System.ComponentModel.Design; using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; @@ -74,19 +73,9 @@ namespace VNLib.Plugins.Extensions.Loading /// <returns>The cached or newly created singleton</returns> public static object GetOrCreateSingleton(PluginBase plugin, Type serviceType, Func<PluginBase, object> serviceFactory) { - Lazy<object>? service; //Get local cache PluginLocalCache pc = _localCache.GetValue(plugin, PluginLocalCache.Create); - //Hold lock while get/set the singleton - lock (pc.SyncRoot) - { - //Check if service already exists - service = pc.GetService(serviceType); - //publish the service if it isnt loaded yet - service ??= pc.AddService(serviceType, serviceFactory); - } - //Deferred load of the service - return service.Value; + return pc.GetOrCreateService(serviceType, serviceFactory); } /// <summary> @@ -113,7 +102,7 @@ namespace VNLib.Plugins.Extensions.Loading public static string? GetAssetFilePath(this PluginBase plugin, string assemblyName, SearchOption searchOption) { plugin.ThrowIfUnloaded(); - _ = assemblyName ?? throw new ArgumentNullException(nameof(assemblyName)); + ArgumentNullException.ThrowIfNull(assemblyName); /* * Allow an assets directory to limit the scope of the search for the desired @@ -127,7 +116,7 @@ namespace VNLib.Plugins.Extensions.Loading * This should never happen since this method can only be called from a * plugin context, which means this path was used to load the current plugin */ - _ = assetDir ?? throw new ArgumentNullException(ConfigurationExtensions.PLUGIN_ASSET_KEY, "No plugin path is defined for the current host configuration, this is likely a bug"); + ArgumentNullException.ThrowIfNull(assetDir, "No plugin asset directory is defined for the current host configuration, this is likely a bug"); //Get the first file that matches the search file return Directory.EnumerateFiles(assetDir, assemblyName, searchOption).FirstOrDefault(); @@ -274,10 +263,7 @@ namespace VNLib.Plugins.Extensions.Loading public static void ThrowIfUnloaded(this PluginBase plugin) { //See if the plugin was unlaoded - if (plugin.UnloadToken.IsCancellationRequested) - { - throw new ObjectDisposedException("The plugin has been unloaded"); - } + ObjectDisposedException.ThrowIf(plugin.UnloadToken.IsCancellationRequested, plugin); } /// <summary> @@ -356,7 +342,7 @@ namespace VNLib.Plugins.Extensions.Loading { //Test status plugin.ThrowIfUnloaded(); - _ = callback ?? throw new ArgumentNullException(nameof(callback)); + ArgumentNullException.ThrowIfNull(callback); //Wait method static async Task WaitForUnload(PluginBase pb, Action callback) @@ -637,8 +623,8 @@ namespace VNLib.Plugins.Extensions.Loading /// <exception cref="ConcreteTypeAmbiguousMatchException"></exception> public static object CreateService(this PluginBase plugin, Type serviceType, IConfigScope? config) { - _ = plugin ?? throw new ArgumentNullException(nameof(plugin)); - _ = serviceType ?? throw new ArgumentNullException(nameof(serviceType)); + ArgumentNullException.ThrowIfNull(plugin); + ArgumentNullException.ThrowIfNull(serviceType); plugin.ThrowIfUnloaded(); @@ -752,11 +738,8 @@ namespace VNLib.Plugins.Extensions.Loading private sealed class PluginLocalCache { private readonly PluginBase _plugin; - private readonly Dictionary<Type, Lazy<object>> _store; - public object SyncRoot { get; } = new(); - private PluginLocalCache(PluginBase plugin) { _plugin = plugin; @@ -767,23 +750,34 @@ namespace VNLib.Plugins.Extensions.Loading public static PluginLocalCache Create(PluginBase plugin) => new(plugin); + /* + * Service code should not be executed in multiple threads, so no need to lock + * + * However if a service is added because it does not exist, the second call to + * get service, will invoke the creation callback. Which may be "recursive" + * as child dependencies required more services. + */ - public Lazy<object>? GetService(Type serviceType) + public object GetOrCreateService(Type serviceType, Func<PluginBase, object> ctor) { - Lazy<object>? t = _store.Where(t => t.Key.IsAssignableTo(serviceType)) - .Select(static tk => tk.Value) - .FirstOrDefault(); - return t; - } + Lazy<object>? lazyService; - public Lazy<object> AddService(Type serviceType, Func<PluginBase, object> factory) - { - //Get lazy loader to invoke factory outside of cache lock - Lazy<object> lazyFactory = new(() => factory(_plugin), true); - //Store lazy factory - _store.Add(serviceType, lazyFactory); - //Pass the lazy factory back - return lazyFactory; + lock (_store) + { + lazyService = _store.Where(t => t.Key.IsAssignableTo(serviceType)) + .Select(static tk => tk.Value) + .FirstOrDefault(); + + if (lazyService is null) + { + lazyService = new Lazy<object>(() => ctor(_plugin)); + //add to pool + _store.Add(serviceType, lazyService); + } + } + + //Return the service instance + return lazyService.Value; } } } diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/Users/UserManager.cs b/lib/VNLib.Plugins.Extensions.Loading/src/Users/UserManager.cs index 3e9dbde..6374b18 100644 --- a/lib/VNLib.Plugins.Extensions.Loading/src/Users/UserManager.cs +++ b/lib/VNLib.Plugins.Extensions.Loading/src/Users/UserManager.cs @@ -99,9 +99,9 @@ namespace VNLib.Plugins.Extensions.Loading.Users } ///<inheritdoc/> - public Task<IUser?> GetUserFromEmailAsync(string emailAddress, CancellationToken cancellationToken = default) + public Task<IUser?> GetUserFromUsernameAsync(string emailAddress, CancellationToken cancellationToken = default) { - return _dynamicLoader.GetUserFromEmailAsync(emailAddress, cancellationToken); + return _dynamicLoader.GetUserFromUsernameAsync(emailAddress, cancellationToken); } ///<inheritdoc/> |