aboutsummaryrefslogtreecommitdiff
path: root/lib/VNLib.Plugins.Extensions.Loading
diff options
context:
space:
mode:
Diffstat (limited to 'lib/VNLib.Plugins.Extensions.Loading')
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/ConfigurationExtensions.cs35
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs72
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/Users/UserManager.cs4
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/>