aboutsummaryrefslogtreecommitdiff
path: root/lib/Plugins.Essentials.ServiceStack/src/Plugins
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2024-07-28 19:15:04 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2024-07-28 19:15:04 -0400
commit7be5d6648e633ba46a270ca5784de6f4a5a4e0a9 (patch)
treea6b53de82f12e6778ede2b3974073a6c3b51aace /lib/Plugins.Essentials.ServiceStack/src/Plugins
parent1b590c2517fef110564943ed8a10edd11fa758b0 (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')
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Plugins/IHttpPluginManager.cs63
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Plugins/IManagedPlugin.cs63
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Plugins/IManualPlugin.cs71
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Plugins/IPluginInitializer.cs40
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginExtensions.cs57
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginManager.cs118
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginRutimeEventHandler.cs80
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginStackInitializer.cs327
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;
+ }
+
+ }
+}