diff options
author | vnugent <public@vaughnnugent.com> | 2024-07-28 19:15:04 -0400 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2024-07-28 19:15:04 -0400 |
commit | 7be5d6648e633ba46a270ca5784de6f4a5a4e0a9 (patch) | |
tree | a6b53de82f12e6778ede2b3974073a6c3b51aace /lib/Plugins.Essentials.ServiceStack/src/Plugins | |
parent | 1b590c2517fef110564943ed8a10edd11fa758b0 (diff) |
Squashed commit of the following:
commit a4dacd2909426bf628c1eee1253cc5c8a01e2691
Author: vnugent <public@vaughnnugent.com>
Date: Sat Jul 27 22:41:04 2024 -0400
package updates
commit f836e09981866f5c9f2ae46990d11b186a7d12bb
Author: vnugent <public@vaughnnugent.com>
Date: Wed Jul 24 19:15:54 2024 -0400
chore: Remove argon2 docs & optional tcp resuse
commit b9b892ab2143b0ab92e4dcf0a8b043c5c6c17271
Author: vnugent <public@vaughnnugent.com>
Date: Sun Jul 21 20:57:01 2024 -0400
fix spelling Enqueue and deprecate mispelled version
commit 21ffa816f18be4b765ad740ed5d93346ec3b1fda
Author: vnugent <public@vaughnnugent.com>
Date: Sat Jul 20 19:44:31 2024 -0400
static arugment list parsing functions
commit 85cd6793818a3edd0a963bb4829a960ee6b0e022
Author: vnugent <public@vaughnnugent.com>
Date: Mon Jul 15 18:58:06 2024 -0400
chore: Just some minor checks and adjustments
commit abfb5761ee381b7e1e5342a5525ceca8c8fd81dd
Author: vnugent <public@vaughnnugent.com>
Date: Thu Jul 4 23:57:37 2024 -0400
analyzer pass
commit 4a96dbb924f2b5bf80293e4054f221efe67151dd
Author: vnugent <public@vaughnnugent.com>
Date: Thu Jul 4 22:45:28 2024 -0400
package updates
commit 38ad7d923fa8d9e463d4aaa8e35f021086a03f2d
Author: vnugent <public@vaughnnugent.com>
Date: Thu Jul 4 16:20:48 2024 -0400
mimalloc merge upstream upgrades
commit 981ba286e4793de95bf65e6588313411344c4d53
Author: vnugent <public@vaughnnugent.com>
Date: Thu Jul 4 16:04:03 2024 -0400
refactor: Refactor extensions with perf updates
commit 6b8c67888731f7dd210acdb2b1160cdbdbe30d47
Author: vnugent <public@vaughnnugent.com>
Date: Fri Jun 28 15:48:22 2024 -0400
refactor: Update service stack to reflect new loading patterns
commit 12391e9a207b60b41a074600fc2373ad3eb1c3ab
Author: vnugent <public@vaughnnugent.com>
Date: Wed Jun 26 21:01:15 2024 -0400
feat(server): Server arch update, Memory struct access
commit 92e182ceaf843f8d859d38faa8b2c0ff53207ff6
Author: vnugent <public@vaughnnugent.com>
Date: Fri Jun 21 16:02:34 2024 -0400
feat: Multi transport listeners
commit ee3620b8168a42c8e571e853c751ad5999a9b907
Author: vnugent <public@vaughnnugent.com>
Date: Tue Jun 18 21:17:28 2024 -0400
feat: Add file path caching support
commit ff0926be56fc6eafdce36411847d73bf4ce9f183
Author: vnugent <public@vaughnnugent.com>
Date: Sun Jun 16 13:08:31 2024 -0400
feat: Allow multiple plugin loading directories
commit 07ddf6738d32127926d07b1366e56d2a2308b53b
Author: vnugent <public@vaughnnugent.com>
Date: Sun Jun 16 01:12:07 2024 -0400
perf: Absolutely yuge perf boosts
commit ff15c05a9c3e632c39f3889820fb7d889342b452
Author: vnugent <public@vaughnnugent.com>
Date: Fri Jun 14 14:16:24 2024 -0400
fix: Improper request buffer property assignment
commit 7d2987f1d4048c30808a85798e32c99747f6cfe3
Author: vnugent <public@vaughnnugent.com>
Date: Thu Jun 13 21:57:34 2024 -0400
perf: Async pre-buffer to avoid sync buffer
commit 75c1d0cbf9a5a7856c544671a45f1b4312ffe7ce
Author: vnugent <public@vaughnnugent.com>
Date: Tue Jun 11 22:11:45 2024 -0400
feat: Add a default site adapater and interceptors
commit a7c739b7db9a17622cee751fe0e8a10e4b84b48b
Author: vnugent <public@vaughnnugent.com>
Date: Sun Jun 9 13:05:12 2024 -0400
chore: Package updated
commit b4b506a4b6c7c1e90b5b0980e4cfe0460e7546a2
Author: vnugent <public@vaughnnugent.com>
Date: Sat Jun 8 21:54:52 2024 -0400
some minor touchups
commit 2160510fcc22a8574b0090fd91ca29072f45ab59
Author: vnugent <public@vaughnnugent.com>
Date: Fri May 31 15:12:20 2024 -0400
refactor: Immutable tcp listeners
commit 51cb4eb93e4f1b4c47d35b105e72af1fe771abcc
Author: vnugent <public@vaughnnugent.com>
Date: Thu May 30 17:31:16 2024 -0400
refactor: minor non-breaking changes to VNEncoding
commit 768ddc1eb949266d693f96c67d734e881bd59374
Merge: 9a835fe 1b590c2
Author: vnugent <public@vaughnnugent.com>
Date: Wed May 22 17:50:57 2024 -0400
Merge branch 'main' into develop
commit 9a835fe12c9586ab8dd44d7c96fef4a2d6017e4b
Author: vnugent <public@vaughnnugent.com>
Date: Fri May 17 18:27:03 2024 -0400
chore: Update mimmaloc v2.1.6, update fPIC & cleanup
commit 3b7004b88acfc7f7baa3a8857a5a2f7cf3dd560e
Author: vnugent <public@vaughnnugent.com>
Date: Fri May 17 16:03:28 2024 -0400
feat: Added ReadFileDataAsync function
commit 9a964795757bf0da4dd7fcab15ad304f4ea3fdf1
Author: vnugent <public@vaughnnugent.com>
Date: Wed May 15 21:57:39 2024 -0400
refactor: Harden some argon2 password hashing
commit 4035c838c1508af0aa7e767a97431a692958ce1c
Author: vnugent <public@vaughnnugent.com>
Date: Sun May 12 16:55:32 2024 -0400
perf: Utils + http perf mods
commit f4f0d4f74250257991c57bfae74c4852c7e1ae46
Author: vnugent <public@vaughnnugent.com>
Date: Thu May 2 15:22:53 2024 -0400
feat: Buff middleware handlers
|
| Added implicit support for middleware post processing of files before the filehandler closes the connection. Also cleaned up some project file stuff
commit f0b7dca107659df1d7d4631fdbd2aae9d716d053
Merge: 8c4a45e 107b058
Author: vnugent <public@vaughnnugent.com>
Date: Sat Apr 20 12:24:05 2024 -0400
Merge branch 'main' into develop
commit 8c4a45e384accf92b1b6d748530e8d46f7de40d6
Author: vnugent <public@vaughnnugent.com>
Date: Sat Apr 20 11:10:30 2024 -0400
refactor: Overhaul C libraries and fix builds
commit 42ff77080d10b0fc9fecbbc46141e8e23a1d066a
Author: vnugent <public@vaughnnugent.com>
Date: Sat Apr 20 00:45:57 2024 -0400
fix!: Middlware array, multiple cookie set, and cookie check
commit 97e82b9d66f387f9e6d21d88ddc7a8ab8693149c
Merge: 4ca5791 e07537a
Author: vnugent <public@vaughnnugent.com>
Date: Tue Apr 2 13:34:22 2024 -0400
Merge branch 'main' into develop
commit 4ca5791ed67b9834bdbd010206b30373e4705e9b
Author: vnugent <public@vaughnnugent.com>
Date: Tue Apr 2 13:32:12 2024 -0400
fix: Missed ! on null pointer check
commit 9b4036377c52200c6488c98180d69a0e63321f97
Author: vnugent <public@vaughnnugent.com>
Date: Tue Apr 2 13:22:29 2024 -0400
fix: Fix _In_ macro for compression public api
commit 53a7b4b5c5b67b4a4e06e1d9098cac4bcd6afd7c
Merge: 448a93b 21130c8
Author: vnugent <public@vaughnnugent.com>
Date: Sun Mar 31 17:01:15 2024 -0400
Merge branch 'main' into develop
commit 448a93bb1d18d032087afe2476ffccb98634a54c
Author: vnugent <public@vaughnnugent.com>
Date: Sun Mar 31 16:56:51 2024 -0400
ci: fix third-party dir cleanup
commit 9afed1427472da1ea13079f98dbe27339e55ee7d
Author: vnugent <public@vaughnnugent.com>
Date: Sun Mar 31 16:43:15 2024 -0400
perf: Deprecate unsafememoryhandle span extensions
commit 3ff90da4f02af47ea6d233fdd4445337ebe36452
Author: vnugent <public@vaughnnugent.com>
Date: Sat Mar 30 21:36:18 2024 -0400
refactor: Updates, advanced tracing, http optimizations
commit 8d6b79b5ae309b36f265ba81529bcef8bfcd7414
Merge: 6c1667b 5585915
Author: vnugent <public@vaughnnugent.com>
Date: Sun Mar 24 21:01:31 2024 -0400
Merge branch 'main' into develop
commit 6c1667be23597513537f8190e2f55d65eb9b7c7a
Author: vnugent <public@vaughnnugent.com>
Date: Fri Mar 22 12:01:53 2024 -0400
refactor: Overhauled native library loading and lazy init
commit ebf688f2f974295beabf7b5def7e6f6f150551d0
Author: vnugent <public@vaughnnugent.com>
Date: Wed Mar 20 22:16:17 2024 -0400
refactor: Update compression header files and macros + Ci build
commit 9c7b564911080ccd5cbbb9851a0757b05e1e9047
Author: vnugent <public@vaughnnugent.com>
Date: Tue Mar 19 21:54:49 2024 -0400
refactor: JWK overhaul & add length getter to FileUpload
commit 6d8c3444e09561e5957491b3cc1ae858e0abdd14
Author: vnugent <public@vaughnnugent.com>
Date: Mon Mar 18 16:13:20 2024 -0400
feat: Add FNV1a software checksum and basic correction tests
commit 00d182088cecefc08ca80b1faee9bed3f215f40b
Author: vnugent <public@vaughnnugent.com>
Date: Fri Mar 15 01:05:27 2024 -0400
chore: #6 Use utils filewatcher instead of built-in
commit d513c10d9895c6693519ef1d459c6a5a76929436
Author: vnugent <public@vaughnnugent.com>
Date: Sun Mar 10 21:58:14 2024 -0400
source tree project location updated
Diffstat (limited to 'lib/Plugins.Essentials.ServiceStack/src/Plugins')
8 files changed, 819 insertions, 0 deletions
diff --git a/lib/Plugins.Essentials.ServiceStack/src/Plugins/IHttpPluginManager.cs b/lib/Plugins.Essentials.ServiceStack/src/Plugins/IHttpPluginManager.cs new file mode 100644 index 0000000..23e436b --- /dev/null +++ b/lib/Plugins.Essentials.ServiceStack/src/Plugins/IHttpPluginManager.cs @@ -0,0 +1,63 @@ +/* +* Copyright (c) 2024 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials.ServiceStack +* File: IHttpPluginManager.cs +* +* IHttpPluginManager.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 System; +using System.Collections.Generic; + +namespace VNLib.Plugins.Essentials.ServiceStack.Plugins +{ + /// <summary> + /// Represents a live plugin controller that manages all + /// plugins loaded in a <see cref="ServiceDomain"/> + /// </summary> + public interface IHttpPluginManager + { + /// <summary> + /// The the plugins managed by this <see cref="IHttpPluginManager"/> + /// </summary> + public IEnumerable<IManagedPlugin> Plugins { get; } + + /// <summary> + /// Sends a message to a plugin identified by it's name. + /// </summary> + /// <param name="pluginName">The name of the plugin to pass the message to</param> + /// <param name="message">The message to pass to the plugin</param> + /// <param name="nameComparison">The name string comparison type</param> + /// <returns>True if the plugin was found and it has a message handler loaded</returns> + /// <exception cref="ObjectDisposedException"></exception> + bool SendCommandToPlugin(string pluginName, string message, StringComparison nameComparison = StringComparison.Ordinal); + + /// <summary> + /// Manually reloads all plugins loaded to the current service manager + /// </summary> + /// <exception cref="AggregateException"></exception> + /// <exception cref="ObjectDisposedException"></exception> + void ForceReloadAllPlugins(); + + /// <summary> + /// Unloads all loaded plugins and calls thier event handlers + /// </summary> + void UnloadPlugins(); + } +} diff --git a/lib/Plugins.Essentials.ServiceStack/src/Plugins/IManagedPlugin.cs b/lib/Plugins.Essentials.ServiceStack/src/Plugins/IManagedPlugin.cs new file mode 100644 index 0000000..b2506c5 --- /dev/null +++ b/lib/Plugins.Essentials.ServiceStack/src/Plugins/IManagedPlugin.cs @@ -0,0 +1,63 @@ +/* +* Copyright (c) 2024 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials.ServiceStack +* File: IManagedPlugin.cs +* +* IManagedPlugin.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 System; +using System.ComponentModel.Design; + +namespace VNLib.Plugins.Essentials.ServiceStack.Plugins +{ + /// <summary> + /// Represents a plugin managed by a <see cref="IHttpPluginManager"/> that includes dynamically loaded plugins + /// </summary> + public interface IManagedPlugin + { + /// <summary> + /// The exposed services the inernal plugin provides + /// </summary> + /// <remarks> + /// WARNING: Services exposed by the plugin will abide by the plugin lifecycle, so consumers + /// must listen for plugin load/unload events to respect lifecycles properly. + /// </remarks> + IServiceContainer Services { get; } + + /// <summary> + /// Internal notification that the plugin is loaded + /// </summary> + internal void OnPluginLoaded(); + + /// <summary> + /// Internal notification that the plugin is unloaded + /// </summary> + internal void OnPluginUnloaded(); + + /// <summary> + /// Sends the specified command to the desired plugin by it's name + /// </summary> + /// <param name="pluginName">The name of the plugin to find</param> + /// <param name="command">The command text to send to the plugin</param> + /// <param name="comp">The string name comparison type</param> + /// <returns>True if the command was sent successfully</returns> + internal bool SendCommandToPlugin(string pluginName, string command, StringComparison comp); + } +} diff --git a/lib/Plugins.Essentials.ServiceStack/src/Plugins/IManualPlugin.cs b/lib/Plugins.Essentials.ServiceStack/src/Plugins/IManualPlugin.cs new file mode 100644 index 0000000..57fd631 --- /dev/null +++ b/lib/Plugins.Essentials.ServiceStack/src/Plugins/IManualPlugin.cs @@ -0,0 +1,71 @@ +/* +* Copyright (c) 2024 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials.ServiceStack +* File: IManualPlugin.cs +* +* IManualPlugin.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 System; +using System.ComponentModel.Design; + +namespace VNLib.Plugins.Essentials.ServiceStack.Plugins +{ + /// <summary> + /// Represents a plugin that may be added to a service stack in user-code + /// instead of the conventional runtime plugin loading system + /// </summary> + public interface IManualPlugin : IDisposable + { + /// <summary> + /// The name of the plugin + /// </summary> + string Name { get; } + + /// <summary> + /// Collects all exported services for use within the service stack + /// </summary> + /// <param name="container">The container to add services to</param> + void GetAllExportedServices(IServiceContainer container); + + /// <summary> + /// Initializes the plugin, called before accessing any other methods + /// </summary> + void Initialize(); + + /// <summary> + /// Loads the plugin, called after initialization but before getting + /// endpoints or services to allow for the plugin to configure itself + /// and perform initial setup + /// </summary> + void Load(); + + /// <summary> + /// Called when an unload was requested, either manually by the plugin controller + /// or when the service stack is unloading + /// </summary> + void Unload(); + + /// <summary> + /// Passes a console command to the plugin + /// </summary> + /// <param name="command">The raw command text to pass to the plugin from the console</param> + void OnConsoleCommand(string command); + } +} diff --git a/lib/Plugins.Essentials.ServiceStack/src/Plugins/IPluginInitializer.cs b/lib/Plugins.Essentials.ServiceStack/src/Plugins/IPluginInitializer.cs new file mode 100644 index 0000000..9435d9b --- /dev/null +++ b/lib/Plugins.Essentials.ServiceStack/src/Plugins/IPluginInitializer.cs @@ -0,0 +1,40 @@ +/* +* Copyright (c) 2024 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials.ServiceStack +* File: IPluginInitializer.cs +* +* IPluginInitializer.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.Utils.Logging; + +namespace VNLib.Plugins.Essentials.ServiceStack.Plugins +{ + internal interface IPluginInitializer + { + IManagedPlugin[] InitializePluginStack(ILogProvider eventLogger); + + void UnloadPlugins(); + + void ReloadPlugins(); + + void Dispose(); + } +} diff --git a/lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginExtensions.cs b/lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginExtensions.cs new file mode 100644 index 0000000..4d04c54 --- /dev/null +++ b/lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginExtensions.cs @@ -0,0 +1,57 @@ +/* +* Copyright (c) 2024 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials.ServiceStack +* File: PluginExtensions.cs +* +* PluginExtensions.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 System.Linq; +using System.Collections.Generic; + +using VNLib.Plugins.Essentials.Runtime; + +namespace VNLib.Plugins.Essentials.ServiceStack.Plugins +{ + /// <summary> + /// Internal and service stack specific extensions for plugins + /// </summary> + public static class PluginExtensions + { + /// <summary> + /// Gets the endpoints exposed by the plugin + /// </summary> + /// <param name="plugin"></param> + /// <returns>The enumeration of web endpoints</returns> + internal static IEnumerable<IEndpoint> GetEndpoints(this IManagedPlugin plugin) + { + //Try to get the endpoint defintion + if (plugin.Services.GetService(typeof(IVirtualEndpointDefinition)) is IVirtualEndpointDefinition defintion) + { + //Return the endpoints from the definition + return defintion.GetEndpoints(); + } + + //If the plugin does not have an endpoint definition, return an empty enumeration + return Enumerable.Empty<IEndpoint>(); + } + + internal static PluginRutimeEventHandler GetListener(this ServiceDomain domain) => new(domain); + } +} diff --git a/lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginManager.cs b/lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginManager.cs new file mode 100644 index 0000000..ce80a9e --- /dev/null +++ b/lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginManager.cs @@ -0,0 +1,118 @@ +/* +* Copyright (c) 2024 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials.ServiceStack +* File: PluginManager.cs +* +* PluginManager.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 System; +using System.Collections.Generic; + +using VNLib.Utils; +using VNLib.Utils.Logging; + +namespace VNLib.Plugins.Essentials.ServiceStack.Plugins +{ + + /// <summary> + /// A sealed type that manages the plugin interaction layer. Manages the lifetime of plugin + /// instances, exposes controls, and relays stateful plugin events. + /// </summary> + internal sealed class PluginManager(IPluginInitializer stack) : VnDisposeable, IHttpPluginManager + { + + /// <summary> + /// The collection of internal controllers + /// </summary> + public IEnumerable<IManagedPlugin> Plugins => _loadedPlugins; + + private IManagedPlugin[] _loadedPlugins = []; + + /// <summary> + /// Configures the manager to capture and manage plugins within a plugin stack + /// </summary> + /// <param name="debugLog"></param> + /// <exception cref="InvalidOperationException"></exception> + /// <exception cref="AggregateException"></exception> + public void LoadPlugins(ILogProvider debugLog) + { + _ = stack ?? throw new InvalidOperationException("Plugin stack has not been set."); + + Check(); + + //Initialize the plugin stack and store the loaded plugins + _loadedPlugins = stack.InitializePluginStack(debugLog); + + debugLog.Information("Plugin loading completed"); + } + + + /// <inheritdoc/> + public bool SendCommandToPlugin(string pluginName, string message, StringComparison nameComparison = StringComparison.Ordinal) + { + Check(); + + foreach (IManagedPlugin plugin in _loadedPlugins) + { + if (plugin.SendCommandToPlugin(pluginName, message, nameComparison)) + { + return true; + } + } + + return false; + } + + /// <inheritdoc/> + public void ForceReloadAllPlugins() + { + Check(); + + //Reload all plugins, causing an event cascade + stack.ReloadPlugins(); + } + + /// <inheritdoc/> + public void UnloadPlugins() + { + Check(); + + //Unload all plugin controllers + stack.UnloadPlugins(); + + /* + * All plugin instances must be destroyed because the + * only way they will be loaded is from their files + * again, so they must be released + */ + Free(); + } + + protected override void Free() + { + //Clear plugin table + _loadedPlugins = []; + + //Dispose the plugin stack + stack.Dispose(); + } + } +} diff --git a/lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginRutimeEventHandler.cs b/lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginRutimeEventHandler.cs new file mode 100644 index 0000000..c5b094f --- /dev/null +++ b/lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginRutimeEventHandler.cs @@ -0,0 +1,80 @@ +/* +* Copyright (c) 2024 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials.ServiceStack +* File: PluginRutimeEventHandler.cs +* +* PluginRutimeEventHandler.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 System.Linq; + +using VNLib.Utils.Extensions; +using VNLib.Plugins.Runtime; + +namespace VNLib.Plugins.Essentials.ServiceStack.Plugins +{ + internal sealed class PluginRutimeEventHandler(ServiceDomain Domain) : IPluginEventListener + { + ///<inheritdoc/> + void IPluginEventListener.OnPluginLoaded(PluginController controller, object? state) => OnPluginLoaded((state as IManagedPlugin)!); + + ///<inheritdoc/> + void IPluginEventListener.OnPluginUnloaded(PluginController controller, object? state) => OnPluginUnloaded((state as IManagedPlugin)!); + + /// <summary> + /// Called when a plugin has been successfully loaded and + /// should be put into service + /// </summary> + /// <param name="plugin">The plugin that was loaded</param> + internal void OnPluginLoaded(IManagedPlugin plugin) + { + //Run onload method before invoking other handlers + plugin.OnPluginLoaded(); + + //Get event listeners at event time because deps may be modified by the domain + ServiceGroup[] deps = Domain.ServiceGroups.Select(static d => d).ToArray(); + + //run onload method + deps.TryForeach(d => d.OnPluginLoaded(plugin)); + } + + /// <summary> + /// Called when a plugin is about to be unloaded and should + /// be removed from service. + /// </summary> + /// <param name="plugin">The plugin instance to unload</param> + internal void OnPluginUnloaded(IManagedPlugin plugin) + { + try + { + //Get event listeners at event time because deps may be modified by the domain + ServiceGroup[] deps = Domain.ServiceGroups.Select(static d => d).ToArray(); + + //Run unloaded method + deps.TryForeach(d => d.OnPluginUnloaded(plugin)); + } + finally + { + //always unload the plugin wrapper + plugin.OnPluginUnloaded(); + } + } + } +} diff --git a/lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginStackInitializer.cs b/lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginStackInitializer.cs new file mode 100644 index 0000000..fb685f7 --- /dev/null +++ b/lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginStackInitializer.cs @@ -0,0 +1,327 @@ +/* +* Copyright (c) 2024 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials.ServiceStack +* File: PluginStackInitializer.cs +* +* PluginStackInitializer.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 System; +using System.IO; +using System.Linq; +using System.Diagnostics; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.ComponentModel.Design; + +using VNLib.Utils.Logging; +using VNLib.Plugins.Runtime; +using VNLib.Utils.Extensions; +using VNLib.Plugins.Runtime.Services; + +namespace VNLib.Plugins.Essentials.ServiceStack.Plugins +{ + + internal sealed class PluginStackInitializer(PluginRutimeEventHandler Listener, IPluginStack Stack, IManualPlugin[] ManualPlugins, bool ConcurrentLoad) + : IPluginInitializer + { + private readonly LinkedList<IManagedPlugin> _managedPlugins = new(); + private readonly LinkedList<ManualPluginWrapper> _manualPlugins = new(); + + private void PrepareStack() + { + /* + * Since we own the plugin stack, it is safe to build it here. + * This method is not public and should not be called more than + * once. Otherwise it can cause issues with the plugin stack. + */ + Stack.BuildStack(); + + //Create plugin wrappers from loaded plugins + ManagedPlugin[] wrapper = Stack.Plugins.Select(static p => new ManagedPlugin(p)).ToArray(); + + //Add wrappers to list of managed plugins + Array.ForEach(wrapper, p => _managedPlugins.AddLast(p)); + + //Register for all plugins and pass the plugin instance as the state object + Array.ForEach(wrapper, p => p.Plugin.Controller.Register(Listener, p)); + + //Add manual plugins to list of managed plugins + Array.ForEach(ManualPlugins, p => _manualPlugins.AddLast(new ManualPluginWrapper(Listener, p))); + } + + ///<inheritdoc/> + public IManagedPlugin[] InitializePluginStack(ILogProvider debugLog) + { + //Prepare the plugin stack before initializing + PrepareStack(); + + //single thread initialziation + LinkedList<IManagedPlugin> _loadedPlugins = new(); + + //Combine all managed plugins and initialize them individually + IEnumerable<IManagedPlugin> plugins = _managedPlugins.Union(_manualPlugins); + + foreach (IManagedPlugin p in plugins) + { + //Try init plugin and add it to the list of loaded plugins + if (InitializePluginCore(p, debugLog)) + { + _loadedPlugins.AddLast(p); + } + } + + /* + * Load stage, load only initialized plugins. + * + * Optionally single-threaded or parallel + */ + + if (ConcurrentLoad) + { + Parallel.ForEach(_loadedPlugins, wp => LoadPlugin(wp, debugLog)); + } + else + { + _loadedPlugins.TryForeach(_loadedPlugins => LoadPlugin(_loadedPlugins, debugLog)); + } + + return [.. _loadedPlugins]; + } + + ///<inheritdoc/> + public void UnloadPlugins() + { + Stack.UnloadAll(); + + //Unload manual plugins in listener + _manualPlugins.TryForeach(static mp => mp.Unload()); + } + + ///<inheritdoc/> + public void ReloadPlugins() + { + Stack.ReloadAll(); + + //Unload manual plugins in listener + _manualPlugins.TryForeach(static mp => mp.Unload()); + + //Load, then invoke on-loaded events + _manualPlugins.TryForeach(static mp => mp.Load()); + } + + ///<inheritdoc/> + public void Dispose() + { + Stack.Dispose(); + _manualPlugins.TryForeach(static mp => mp.Dispose()); + _manualPlugins.Clear(); + } + + private static bool InitializePluginCore(IManagedPlugin plugin, ILogProvider debugLog) + { + try + { + if (plugin is ManagedPlugin mp) + { + //Initialzie plugin wrapper + mp.Plugin.InitializeController(); + + /* + * If the plugin assembly does not expose any plugin types or there is an issue loading the assembly, + * its types my not unify, then we should give the user feedback insead of a silent fail. + */ + if (!mp.Plugin.Controller.Plugins.Any()) + { + debugLog.Warn("No plugin instances were exposed via {asm} assembly. This may be due to an assebmly mismatch", plugin.ToString()); + } + } + else if (plugin is ManualPluginWrapper mpw) + { + //Initialzie plugin wrapper + mpw.Plugin.Initialize(); + } + else + { + Debug.Fail("Missed managed plugin wrapper type"); + } + + return true; + } + catch (Exception ex) + { + debugLog.Error("Exception raised during initialzation of {asm}. It has been removed from the collection\n{ex}", plugin.ToString(), ex); + } + + return false; + } + + private void LoadPlugin(IManagedPlugin plugin, ILogProvider debugLog) + { + Stopwatch sw = new(); + try + { + sw.Start(); + + //Recover the base class used to load instances + if (plugin is ManagedPlugin mp) + { + mp.Plugin.LoadPlugins(); + } + else if (plugin is ManualPluginWrapper mpw) + { + mpw.Load(); + } + else + { + Debug.Fail("Missed managed plugin wrapper type"); + } + + sw.Stop(); + + debugLog.Verbose("Loaded {pl} in {tm} ms", plugin.ToString(), sw.ElapsedMilliseconds); + } + catch (Exception ex) + { + debugLog.Error("Exception raised during loading {asf}. Failed to load plugin \n{ex}", plugin.ToString(), ex); + } + finally + { + sw.Stop(); + } + } + + + private sealed record class ManagedPlugin(RuntimePluginLoader Plugin) : IManagedPlugin + { + private ServiceContainer? _services; + + ///<inheritdoc/> + public IServiceContainer Services => _services ?? throw new InvalidOperationException("The service container is not currently loaded"); + + /* + * Automatically called after the plugin has successfully loaded + * by event handlers below + */ + + ///<inheritdoc/> + void IManagedPlugin.OnPluginLoaded() + { + //If the service container is defined, dispose + _services?.Dispose(); + + //Init new service container + _services = new(); + + //Get all exported services and add them to the container + PluginServiceExport[] exports = Plugin.Controller.GetExportedServices(); + Array.ForEach(exports, e => _services.AddService(e.ServiceType, e.Service, true)); + } + + ///<inheritdoc/> + void IManagedPlugin.OnPluginUnloaded() + { + //Cleanup services no longer in use. Plugin is still valid until this method returns + _services?.Dispose(); + + //Remove ref to services + _services = null; + } + + ///<inheritdoc/> + bool IManagedPlugin.SendCommandToPlugin(string pluginName, string command, StringComparison comp) + { + //Get plugin + LivePlugin? plugin = Plugin.Controller.Plugins.FirstOrDefault(p => p.PluginName.Equals(pluginName, comp)); + + //If plugin is null, return false + if (plugin == null) + { + return false; + } + + return plugin.SendConsoleMessage(command); + } + + public override string ToString() => Path.GetFileName(Plugin.Config.AssemblyFile); + } + + private sealed record class ManualPluginWrapper(PluginRutimeEventHandler Listener, IManualPlugin Plugin) : IManagedPlugin, IDisposable + { + private ServiceContainer _container = new(); + + ///<inheritdoc/> + public IServiceContainer Services => _container; + + public void Load() + { + Plugin.Load(); + Plugin.GetAllExportedServices(_container); + + //Finally notify of load + Listener.OnPluginLoaded(this); + } + + public void Unload() + { + //Notify of unload + Listener.OnPluginUnloaded(this); + + Plugin.Unload(); + + //Unload and re-init container + _container.Dispose(); + _container = new(); + } + + public void Dispose() + { + //Dispose container + _container.Dispose(); + + //Dispose plugin + Plugin.Dispose(); + } + + ///<inheritdoc/> + bool IManagedPlugin.SendCommandToPlugin(string pluginName, string command, StringComparison comp) + { + + if (Plugin.Name.Equals(pluginName, comp)) + { + Plugin.OnConsoleCommand(command); + return true; + } + + return false; + } + + void IManagedPlugin.OnPluginLoaded() + { } + + void IManagedPlugin.OnPluginUnloaded() + { } + + ///<inheritdoc/> + public override string ToString() => Plugin.Name; + } + + } +} |