aboutsummaryrefslogtreecommitdiff
path: root/lib/Plugins.Essentials.ServiceStack
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2023-04-15 01:58:55 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2023-04-15 01:58:55 -0400
commit3ce61cf38727db2f37a0e478182d2a73222c8a7c (patch)
treea03bf1bdde44961183b7bb34dea9c3381050dc27 /lib/Plugins.Essentials.ServiceStack
parentbaf859f45cf1f00e79508954517ed4b6fb446103 (diff)
Managed assembly loading overhaul
Diffstat (limited to 'lib/Plugins.Essentials.ServiceStack')
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/HttpServiceStackBuilder.cs5
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/IPluginAssemblyLoaderFactory.cs43
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/IPluginController.cs5
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/ManagedPlugin.cs35
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/PluginLoadConfiguration.cs25
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs34
6 files changed, 80 insertions, 67 deletions
diff --git a/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStackBuilder.cs b/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStackBuilder.cs
index 25b6d5f..ae3b736 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStackBuilder.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStackBuilder.cs
@@ -23,7 +23,6 @@
*/
using System;
-using System.Threading.Tasks;
using System.Collections.Generic;
using VNLib.Net.Http;
@@ -117,7 +116,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack
/// </summary>
/// <returns>The newly constructed <see cref="HttpServiceStack"/> that may be used to manage your http services</returns>
/// <exception cref="ArgumentNullException"></exception>
- public async Task<HttpServiceStack> BuildAsync(PluginLoadConfiguration config, ILogProvider appLog)
+ public HttpServiceStack Build(IPluginLoadConfiguration config, ILogProvider appLog)
{
_ = _hostBuilder ?? throw new ArgumentNullException("WithDomainBuilder", "You have not configured a service domain configuration callback");
_ = _getServers ?? throw new ArgumentNullException("WithHttp", "You have not configured a IHttpServer configuration callback");
@@ -132,7 +131,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack
}
//Load plugins async
- await sd.PluginManager.LoadPluginsAsync(config, appLog);
+ sd.PluginManager.LoadPlugins(config, appLog);
LinkedList<IHttpServer> servers = new();
diff --git a/lib/Plugins.Essentials.ServiceStack/src/IPluginAssemblyLoaderFactory.cs b/lib/Plugins.Essentials.ServiceStack/src/IPluginAssemblyLoaderFactory.cs
new file mode 100644
index 0000000..4cb5741
--- /dev/null
+++ b/lib/Plugins.Essentials.ServiceStack/src/IPluginAssemblyLoaderFactory.cs
@@ -0,0 +1,43 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Plugins.Essentials.ServiceStack
+* File: IPluginAssemblyLoaderFactory.cs
+*
+* IPluginAssemblyLoaderFactory.cs is part of VNLib.Plugins.Essentials.ServiceStack
+* which is part of the larger VNLib collection of libraries and utilities.
+*
+* VNLib.Plugins.Essentials.ServiceStack 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 2 of the
+* License, or (at your option) any later version.
+*
+* VNLib.Plugins.Essentials.ServiceStack 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 VNLib.Plugins.Runtime;
+
+namespace VNLib.Plugins.Essentials.ServiceStack
+{
+ /// <summary>
+ /// Represents a provider that creates a unique <see cref="IPluginAssemblyLoader"/> for the the
+ /// loaded assembly file.
+ /// </summary>
+ public interface IPluginAssemblyLoaderFactory
+ {
+ /// <summary>
+ /// Gets a new and unique <see cref="IPluginAssemblyLoader"/> instance for a given plugin assembly
+ /// file path
+ /// </summary>
+ /// <param name="pluginFile">The file path to the requested plugin assembly file</param>
+ /// <returns>The new <see cref="IPluginAssemblyLoader"/> to recover the plugin assembly manifest</returns>
+ IPluginAssemblyLoader GetLoaderForPluginFile(string pluginFile);
+ }
+}
diff --git a/lib/Plugins.Essentials.ServiceStack/src/IPluginController.cs b/lib/Plugins.Essentials.ServiceStack/src/IPluginController.cs
index fb9c340..3394208 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/IPluginController.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/IPluginController.cs
@@ -23,7 +23,6 @@
*/
using System;
-using System.Threading.Tasks;
using System.Collections.Generic;
using VNLib.Utils.Logging;
@@ -45,10 +44,10 @@ namespace VNLib.Plugins.Essentials.ServiceStack
/// Loads all plugins specified by the host config to the service manager,
/// or attempts to load plugins by the default
/// </summary>
- /// <param name="config">The configuration instance to pass to plugins</param>
+ /// <param name="config">The plugin loading configuration</param>
/// <param name="appLog">A log provider to write message and errors to</param>
/// <returns>A task that resolves when all plugins are loaded</returns>
- Task LoadPluginsAsync(PluginLoadConfiguration config, ILogProvider appLog);
+ void LoadPlugins(IPluginLoadConfiguration config, ILogProvider appLog);
/// <summary>
/// Sends a message to a plugin identified by it's name.
diff --git a/lib/Plugins.Essentials.ServiceStack/src/ManagedPlugin.cs b/lib/Plugins.Essentials.ServiceStack/src/ManagedPlugin.cs
index 3bf99cb..54f0090 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/ManagedPlugin.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/ManagedPlugin.cs
@@ -27,17 +27,12 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Reflection;
-using System.Runtime.Loader;
-using System.Threading.Tasks;
using System.ComponentModel.Design;
-using McMaster.NETCore.Plugins;
-
using VNLib.Utils;
using VNLib.Plugins.Runtime;
using VNLib.Plugins.Attributes;
-
namespace VNLib.Plugins.Essentials.ServiceStack
{
@@ -48,39 +43,21 @@ namespace VNLib.Plugins.Essentials.ServiceStack
private UnloadableServiceContainer? _services;
- public ManagedPlugin(string pluginPath, in PluginLoadConfiguration config, IPluginEventListener listener)
+ public ManagedPlugin(RuntimePluginLoader loader, IPluginEventListener listener)
{
- PluginPath = pluginPath;
-
- //Get the plugin config for the assembly
- PluginConfig pConfig = GetConfigForAssemblyPath(pluginPath, in config);
-
//configure the loader
- _plugin = new(pConfig, config.HostConfig, config.PluginErrorLog);
+ _plugin = loader;
- //Register listener before loading occurs
+ //Register loading event listener before loading occurs
_plugin.Controller.Register(this, this);
//Store listener to raise events
_serviceDomainListener = listener;
}
- private static PluginConfig GetConfigForAssemblyPath(string asmPath, in PluginLoadConfiguration loadConfig)
- {
- PluginConfig config = new(asmPath)
- {
- IsUnloadable = loadConfig.HotReload,
- EnableHotReload = loadConfig.HotReload,
- IsLazyLoaded = false,
- PreferSharedTypes = true,
- DefaultContext = AssemblyLoadContext.Default,
- ReloadDelay = loadConfig.ReloadDelay
- };
- return config;
- }
///<inheritdoc/>
- public string PluginPath { get; }
+ public string PluginPath => _plugin.Config.AssemblyFile;
///<inheritdoc/>
public IUnloadableServiceProvider Services
@@ -104,10 +81,10 @@ namespace VNLib.Plugins.Essentials.ServiceStack
internal string PluginFileName => Path.GetFileName(PluginPath);
- internal Task InitializePluginsAsync()
+ internal void InitializePlugins()
{
Check();
- return _plugin.InitializeController();
+ _plugin.InitializeController();
}
internal void LoadPlugins()
diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginLoadConfiguration.cs b/lib/Plugins.Essentials.ServiceStack/src/PluginLoadConfiguration.cs
index 894ae55..9ab9a82 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/PluginLoadConfiguration.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/PluginLoadConfiguration.cs
@@ -3,9 +3,9 @@
*
* Library: VNLib
* Package: VNLib.Plugins.Essentials.ServiceStack
-* File: PluginLoadConfiguration.cs
+* File: IPluginLoadConfiguration.cs
*
-* PluginLoadConfiguration.cs is part of VNLib.Plugins.Essentials.ServiceStack
+* IPluginLoadConfiguration.cs is part of VNLib.Plugins.Essentials.ServiceStack
* which is part of the larger VNLib collection of libraries and utilities.
*
* VNLib.Plugins.Essentials.ServiceStack is free software: you can redistribute it and/or modify
@@ -30,38 +30,33 @@ using VNLib.Plugins.Runtime;
namespace VNLib.Plugins.Essentials.ServiceStack
{
+
/// <summary>
/// Plugin loading configuration variables
/// </summary>
- public readonly record struct PluginLoadConfiguration
+ public interface IPluginLoadConfiguration
{
/// <summary>
/// The directory containing the dynamic plugin assemblies to load
/// </summary>
- public readonly string PluginDir { get; init; }
-
- /// <summary>
- /// A value that indicates if the internal <see cref="PluginController"/>
- /// allows for hot-reload/unloadable plugin assemblies.
- /// </summary>
- public readonly bool HotReload { get; init; }
+ string PluginDir { get; }
/// <summary>
/// The optional host configuration file to merge with plugin config
/// to pass to the loading plugin.
/// </summary>
- public readonly JsonElement? HostConfig { get; init; }
+ JsonElement? HostConfig { get; }
/// <summary>
/// Passed to the underlying <see cref="RuntimePluginLoader"/>
/// holding plugins
/// </summary>
- public readonly ILogProvider? PluginErrorLog { get; init; }
+ ILogProvider? PluginErrorLog { get; }
/// <summary>
- /// If hot-reload is enabled, sets a time delay the file watcher waits when
- /// a plugin assembly has changed.
+ /// A factory instance the provides new <see cref="IPluginAssemblyLoader"/> instances
+ /// on demand from its plugin assembly path
/// </summary>
- public readonly TimeSpan ReloadDelay { get; init; }
+ public IPluginAssemblyLoaderFactory AssemblyLoaderFactory { get; init; }
}
}
diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs b/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs
index e4e5670..2013a58 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs
@@ -66,7 +66,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack
/// <inheritdoc/>
/// <exception cref="ObjectDisposedException"></exception>
- public Task LoadPluginsAsync(PluginLoadConfiguration config, ILogProvider appLog)
+ public void LoadPlugins(IPluginLoadConfiguration config, ILogProvider appLog)
{
Check();
@@ -76,10 +76,10 @@ namespace VNLib.Plugins.Essentials.ServiceStack
if (!dir.Exists)
{
appLog.Warn("Plugin directory {dir} does not exist. No plugins were loaded", config.PluginDir);
- return Task.CompletedTask;
+ return;
}
- appLog.Information("Loading plugins. Hot-reload: {en}", config.HotReload);
+ appLog.Information("Loading managed plugins");
//Enumerate all dll files within this dir
IEnumerable<DirectoryInfo> dirs = dir.EnumerateDirectories("*", SearchOption.TopDirectoryOnly);
@@ -91,14 +91,21 @@ namespace VNLib.Plugins.Essentials.ServiceStack
appLog.Debug("Found plugin files: \n{files}", string.Concat(pluginFileNames));
- //Initialze plugin managers
- ManagedPlugin[] wrappers = pluginPaths.Select(pw => new ManagedPlugin(pw, config, this)).ToArray();
+ /*
+ * We need to get the assembly loader for the plugin file, then create its
+ * RuntimePluginLoader which will be passed to the Managed plugin instance
+ */
+
+ ManagedPlugin[] wrappers = pluginPaths.Select(pw => config.AssemblyLoaderFactory.GetLoaderForPluginFile(pw))
+ .Select(l => new RuntimePluginLoader(l, config.HostConfig, config.PluginErrorLog))
+ .Select(loader => new ManagedPlugin(loader, this))
+ .ToArray();
//Add to loaded plugins
_plugins.AddRange(wrappers);
//Load plugins
- return InitiailzeAndLoadAsync(appLog);
+ InitiailzeAndLoad(appLog);
}
private static IEnumerable<string> GetPluginPaths(IEnumerable<DirectoryInfo> dirs)
@@ -118,13 +125,10 @@ namespace VNLib.Plugins.Essentials.ServiceStack
});
}
- private async Task InitiailzeAndLoadAsync(ILogProvider debugLog)
+ private void InitiailzeAndLoad(ILogProvider debugLog)
{
//Load all async
- Task[] initAll = _plugins.Select(p => InitializePlugin(p, debugLog)).ToArray();
-
- //Wait for initalization
- await Task.WhenAll(initAll).ConfigureAwait(false);
+ _plugins.ToArray().TryForeach(p => InitializePlugin(p, debugLog));
//Load stage, load all multithreaded
Parallel.ForEach(_plugins, p => LoadPlugin(p, debugLog));
@@ -132,7 +136,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack
debugLog.Information("Plugin loading completed");
}
- private async Task InitializePlugin(ManagedPlugin plugin, ILogProvider debugLog)
+ private void InitializePlugin(ManagedPlugin plugin, ILogProvider debugLog)
{
void LogAndRemovePlugin(Exception ex)
{
@@ -151,11 +155,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack
try
{
//Load wrapper
- await plugin.InitializePluginsAsync().ConfigureAwait(true);
- }
- catch(AggregateException ae) when (ae.InnerException != null)
- {
- LogAndRemovePlugin(ae.InnerException);
+ plugin.InitializePlugins();
}
catch (Exception ex)
{