aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2024-01-02 02:33:05 -0500
committerLibravatar vnugent <public@vaughnnugent.com>2024-01-02 02:33:05 -0500
commit4ef0142747c4a0dd0c4cb71d8e7359c03b3a2942 (patch)
tree2991941aec6a0b981411a4f4bb83d8d2ad900aba /lib
parenta6b628c8653485a803bdbe322e2ecd2a69fc8e5a (diff)
breaking changes: plugin service pools & move plugin api away from web related
Diffstat (limited to 'lib')
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs6
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/IManagedPlugin.cs8
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/IManualPlugin.cs8
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/PluginExtensions.cs22
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/PluginLoadEventListener.cs2
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs2
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/PluginStackInitializer.cs27
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/ServiceGroup.cs6
-rw-r--r--lib/Plugins.Essentials/src/IEndpoint.cs (renamed from lib/Plugins/src/Web/IEndpoint.cs)0
-rw-r--r--lib/Plugins.Essentials/src/Runtime/IVirtualEndpointDefinition.cs41
-rw-r--r--lib/Plugins.PluginBase/src/PluginBase.cs61
-rw-r--r--lib/Plugins.PluginBase/src/ServiceExport.cs37
-rw-r--r--lib/Plugins.Runtime/src/LivePlugin.cs19
-rw-r--r--lib/Plugins.Runtime/src/LoaderExtensions.cs90
-rw-r--r--lib/Plugins.Runtime/src/PluginController.cs21
-rw-r--r--lib/Plugins.Runtime/src/RuntimePluginLoader.cs4
-rw-r--r--lib/Plugins.Runtime/src/Services/PluginServiceExport.cs49
-rw-r--r--lib/Plugins.Runtime/src/Services/PluginServicePool.cs53
-rw-r--r--lib/Plugins.Runtime/src/SharedPluginServiceProvider.cs115
-rw-r--r--lib/Plugins/src/Attributes/ServiceConfiguratorAttribute.cs58
-rw-r--r--lib/Plugins/src/ExportFlags.cs (renamed from lib/Plugins/src/Web/IWebPlugin.cs)23
-rw-r--r--lib/Plugins/src/IPlugin.cs13
-rw-r--r--lib/Plugins/src/IPluginServicePool.cs42
23 files changed, 542 insertions, 165 deletions
diff --git a/lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs b/lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs
index ee49f99..d96809b 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Essentials.ServiceStack
@@ -185,9 +185,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction
private static void OnPluginServiceEvent<T>(this IManagedPlugin plugin, Action<T> loader)
{
- object? service = plugin.Services.GetService(typeof(T));
-
- if (service is T s)
+ if (plugin.Services.GetService(typeof(T)) is T s)
{
loader(s);
}
diff --git a/lib/Plugins.Essentials.ServiceStack/src/IManagedPlugin.cs b/lib/Plugins.Essentials.ServiceStack/src/IManagedPlugin.cs
index 21c8e91..8332f7e 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/IManagedPlugin.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/IManagedPlugin.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Essentials.ServiceStack
@@ -42,12 +42,6 @@ namespace VNLib.Plugins.Essentials.ServiceStack
IServiceContainer Services { get; }
/// <summary>
- /// Internal call to get all exported plugin endpoints
- /// </summary>
- /// <returns></returns>
- internal IEndpoint[] GetEndpoints();
-
- /// <summary>
/// Internal notification that the plugin is loaded
/// </summary>
internal void OnPluginLoaded();
diff --git a/lib/Plugins.Essentials.ServiceStack/src/IManualPlugin.cs b/lib/Plugins.Essentials.ServiceStack/src/IManualPlugin.cs
index e58067f..cecd481 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/IManualPlugin.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/IManualPlugin.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Essentials.ServiceStack
@@ -45,12 +45,6 @@ namespace VNLib.Plugins.Essentials.ServiceStack
void GetAllExportedServices(IServiceContainer container);
/// <summary>
- /// Collects all exported endpoints to put into service
- /// </summary>
- /// <returns>The collection of endpoints</returns>
- IEndpoint[] GetEndpoints();
-
- /// <summary>
/// Initializes the plugin, called before accessing any other methods
/// </summary>
void Initialize();
diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginExtensions.cs b/lib/Plugins.Essentials.ServiceStack/src/PluginExtensions.cs
index d93df6d..d8cdf75 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/PluginExtensions.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/PluginExtensions.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Essentials.ServiceStack
@@ -26,7 +26,7 @@ using System.Linq;
using System.Collections.Generic;
using VNLib.Utils.Logging;
-using VNLib.Plugins.Runtime;
+using VNLib.Plugins.Essentials.Runtime;
namespace VNLib.Plugins.Essentials.ServiceStack
{
@@ -40,14 +40,18 @@ namespace VNLib.Plugins.Essentials.ServiceStack
/// </summary>
/// <param name="plugin"></param>
/// <returns>The enumeration of web endpoints</returns>
- internal static IEnumerable<IEndpoint> GetEndpoints(this IPlugin plugin) => ((IWebPlugin)plugin).GetEndpoints();
+ 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();
+ }
- /// <summary>
- /// Gets only plugins that implement <see cref="IWebPlugin"/> interface
- /// </summary>
- /// <param name="controller"></param>
- /// <returns></returns>
- internal static IEnumerable<LivePlugin> GetOnlyWebPlugins(this PluginController controller) => controller.Plugins.Where(static p => p.Plugin is IWebPlugin);
+ //If the plugin does not have an endpoint definition, return an empty enumeration
+ return Enumerable.Empty<IEndpoint>();
+ }
/// <summary>
/// Loads all plugins that implement <see cref="IWebPlugin"/> interface into the
diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginLoadEventListener.cs b/lib/Plugins.Essentials.ServiceStack/src/PluginLoadEventListener.cs
index 0eaa3a8..b24019b 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/PluginLoadEventListener.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/PluginLoadEventListener.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Essentials.ServiceStack
diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs b/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs
index ba3b91a..f5f2abc 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Essentials.ServiceStack
diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginStackInitializer.cs b/lib/Plugins.Essentials.ServiceStack/src/PluginStackInitializer.cs
index c2ff1e4..e6489c9 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/PluginStackInitializer.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/PluginStackInitializer.cs
@@ -26,7 +26,6 @@
using System;
using System.IO;
using System.Linq;
-using System.Reflection;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Collections.Generic;
@@ -35,7 +34,7 @@ using System.ComponentModel.Design;
using VNLib.Utils.Logging;
using VNLib.Plugins.Runtime;
using VNLib.Utils.Extensions;
-using VNLib.Plugins.Attributes;
+using VNLib.Plugins.Runtime.Services;
namespace VNLib.Plugins.Essentials.ServiceStack
{
@@ -229,9 +228,6 @@ namespace VNLib.Plugins.Essentials.ServiceStack
}
}
- ///<inheritdoc/>
- IEndpoint[] IManagedPlugin.GetEndpoints() => Plugin.Controller.GetOnlyWebPlugins().SelectMany(static pl => pl.Plugin!.GetEndpoints()).ToArray();
-
/*
* Automatically called after the plugin has successfully loaded
* by event handlers below
@@ -246,21 +242,9 @@ namespace VNLib.Plugins.Essentials.ServiceStack
//Init new service container
_services = new();
- //Get types from plugin
- foreach (LivePlugin plugin in Plugin.Controller.Plugins)
- {
- /*
- * Get the exposed configurator method if declared,
- * it may not be defined.
- */
- ServiceConfigurator? callback = plugin.PluginType.GetMethods()
- .Where(static m => m.GetCustomAttribute<ServiceConfiguratorAttribute>() != null && !m.IsAbstract)
- .Select(m => m.CreateDelegate<ServiceConfigurator>(plugin.Plugin))
- .FirstOrDefault();
-
- //Invoke if defined to expose services
- callback?.Invoke(_services);
- }
+ //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/>
@@ -298,9 +282,6 @@ namespace VNLib.Plugins.Essentials.ServiceStack
///<inheritdoc/>
public IServiceContainer Services => _container;
- ///<inheritdoc/>
- IEndpoint[] IManagedPlugin.GetEndpoints() => Plugin.GetEndpoints();
-
public void Load()
{
Plugin.Load();
diff --git a/lib/Plugins.Essentials.ServiceStack/src/ServiceGroup.cs b/lib/Plugins.Essentials.ServiceStack/src/ServiceGroup.cs
index e504013..da34d54 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/ServiceGroup.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/ServiceGroup.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Essentials.ServiceStack
@@ -23,6 +23,7 @@
*/
using System.Net;
+using System.Linq;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
@@ -82,7 +83,8 @@ namespace VNLib.Plugins.Essentials.ServiceStack
internal void OnPluginLoaded(IManagedPlugin plugin)
{
//Get all new endpoints for plugin
- IEndpoint[] newEndpoints = plugin.GetEndpoints();
+ IEndpoint[] newEndpoints = plugin.GetEndpoints()
+ .ToArray();
//Add endpoints to dict
_endpointsForPlugins.AddOrUpdate(plugin, newEndpoints);
diff --git a/lib/Plugins/src/Web/IEndpoint.cs b/lib/Plugins.Essentials/src/IEndpoint.cs
index 33d49df..33d49df 100644
--- a/lib/Plugins/src/Web/IEndpoint.cs
+++ b/lib/Plugins.Essentials/src/IEndpoint.cs
diff --git a/lib/Plugins.Essentials/src/Runtime/IVirtualEndpointDefinition.cs b/lib/Plugins.Essentials/src/Runtime/IVirtualEndpointDefinition.cs
new file mode 100644
index 0000000..e505902
--- /dev/null
+++ b/lib/Plugins.Essentials/src/Runtime/IVirtualEndpointDefinition.cs
@@ -0,0 +1,41 @@
+/*
+* Copyright (c) 2024 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Plugins.Essentials
+* File: IVirtualEndpointDefinition.cs
+*
+* IVirtualEndpointDefinition.cs is part of VNLib.Plugins.Essentials which is part of the larger
+* VNLib collection of libraries and utilities.
+*
+* VNLib.Plugins.Essentials 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 3 of the
+* License, or (at your option) any later version.
+*
+* VNLib.Plugins.Essentials 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.Collections.Generic;
+
+namespace VNLib.Plugins.Essentials.Runtime
+{
+ /// <summary>
+ /// Represents a runtime factory for exporting
+ /// <see cref="IEndpoint"/> instances
+ /// </summary>
+ public interface IVirtualEndpointDefinition
+ {
+ /// <summary>
+ /// Gets all routable endpoints
+ /// </summary>
+ /// <returns>The endpoint enumeration</returns>
+ IEnumerable<IEndpoint> GetEndpoints();
+ }
+} \ No newline at end of file
diff --git a/lib/Plugins.PluginBase/src/PluginBase.cs b/lib/Plugins.PluginBase/src/PluginBase.cs
index a667a8f..d8b0973 100644
--- a/lib/Plugins.PluginBase/src/PluginBase.cs
+++ b/lib/Plugins.PluginBase/src/PluginBase.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.PluginBase
@@ -44,7 +44,7 @@ namespace VNLib.Plugins
/// Provides a concrete base class for <see cref="IPlugin"/> instances using the Serilog logging provider.
/// Accepts the standard plugin <see cref="JsonDocument"/> configuration constructors
/// </summary>
- public abstract class PluginBase : MarshalByRefObject, IWebPlugin, IPluginTaskObserver
+ public abstract class PluginBase : MarshalByRefObject, IPluginTaskObserver, IPlugin
{
/*
* CTS exists for the life of the plugin, its resources are never disposed
@@ -53,6 +53,7 @@ namespace VNLib.Plugins
private readonly CancellationTokenSource Cts = new();
private readonly LinkedList<Task> DeferredTasks = new();
+ private readonly LinkedList<ServiceExport> _services = new();
/// <summary>
/// A cancellation token that is cancelled when the plugin has been unloaded
@@ -72,13 +73,6 @@ namespace VNLib.Plugins
protected virtual string PluginConfigDomPropertyName => "plugin";
/// <summary>
- /// A list of all currently prepared <see cref="IEndpoint"/> endpoints.
- /// Endpoints must be added to this list before <see cref="IWebPlugin.GetEndpoints"/> is called
- /// by the host app
- /// </summary>
- public ICollection<IEndpoint> Endpoints { get; } = new List<IEndpoint>();
-
- /// <summary>
/// The logging instance
/// </summary>
public ILogProvider Log { get; private set; }
@@ -98,6 +92,12 @@ namespace VNLib.Plugins
/// </summary>
public JsonElement PluginConfig => Configuration.RootElement.GetProperty(PluginConfigDomPropertyName);
+ /// <summary>
+ /// The collection of exported services that will be published to the host
+ /// application
+ /// </summary>
+ public ICollection<ServiceExport> Services => _services;
+
/// <inheritdoc/>
public abstract string PluginName { get; }
@@ -257,18 +257,17 @@ namespace VNLib.Plugins
Log.Error(ex);
}
}
+
/// <summary>
/// Invoked when the host process has a command message to send
/// </summary>
/// <param name="cmd">The command message</param>
protected abstract void ProcessHostCommand(string cmd);
- IEnumerable<IEndpoint> IWebPlugin.GetEndpoints()
- {
- OnGetEndpoints();
- return Endpoints;
- }
-
+ ///<inheritdoc/>
+ void IPlugin.PublishServices(IPluginServicePool pool) => OnPublishServices(pool);
+
+ ///<inheritdoc/>
void IPlugin.Load()
{
//Setup empty log if not specified
@@ -291,7 +290,8 @@ namespace VNLib.Plugins
throw;
}
}
-
+
+ ///<inheritdoc/>
void IPlugin.Unload()
{
try
@@ -317,8 +317,8 @@ namespace VNLib.Plugins
Configuration?.Dispose();
//dispose the log
(Log as IDisposable)?.Dispose();
- //Clear endpoints list
- Endpoints.Clear();
+ //Remove any services
+ _services.Clear();
//empty deffered array
DeferredTasks.Clear();
}
@@ -372,12 +372,6 @@ namespace VNLib.Plugins
}
/// <summary>
- /// Adds the specified endpoint to be routed when loading is complete
- /// </summary>
- /// <param name="endpoint">The <see cref="IEndpoint"/> to present to the application when loaded</param>
- public void Route(IEndpoint endpoint) => Endpoints.Add(endpoint);
-
- /// <summary>
/// <para>
/// Invoked when the host loads the plugin instance
/// </para>
@@ -391,11 +385,22 @@ namespace VNLib.Plugins
/// Invoked when all endpoints have been removed from service. All managed and unmanged resources should be released.
/// </summary>
protected abstract void OnUnLoad();
-
+
/// <summary>
- /// Invoked before <see cref="IWebPlugin.GetEndpoints"/> called by the host app to get all endpoints
- /// for the current plugin
+ /// Invoked when the host requests the plugin to publish services
+ /// <para>
+ /// If overriden, the base implementation must be called to
+ /// publish all services in the internal service collection
+ /// </para>
/// </summary>
- protected virtual void OnGetEndpoints() { }
+ /// <param name="pool">The pool to publish services to</param>
+ protected virtual void OnPublishServices(IPluginServicePool pool)
+ {
+ //Publish all services then cleanup
+ foreach (ServiceExport export in _services)
+ {
+ pool.ExportService(export.ServiceType, export.Service, export.Flags);
+ }
+ }
}
} \ No newline at end of file
diff --git a/lib/Plugins.PluginBase/src/ServiceExport.cs b/lib/Plugins.PluginBase/src/ServiceExport.cs
new file mode 100644
index 0000000..960f286
--- /dev/null
+++ b/lib/Plugins.PluginBase/src/ServiceExport.cs
@@ -0,0 +1,37 @@
+/*
+* Copyright (c) 2024 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Plugins.PluginBase
+* File: ServiceExport.cs
+*
+* ServiceExport.cs is part of VNLib.Plugins.PluginBase which is part of the larger
+* VNLib collection of libraries and utilities.
+*
+* VNLib.Plugins.PluginBase is free software: you can redistribute it and/or modify
+* it under the terms of the GNU 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.PluginBase 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
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Plugins.PluginBase. If not, see http://www.gnu.org/licenses/.
+*/
+
+using System;
+
+namespace VNLib.Plugins
+{
+ /// <summary>
+ /// A service export that will be published to the
+ /// host application after the plugin has been loaded
+ /// </summary>
+ /// <param name="Service"> The exported service instance </param>
+ /// <param name="ServiceType"> The exported service type </param>
+ /// <param name="Flags"> The name of the service </param>
+ public sealed record ServiceExport(Type ServiceType, object Service, ExportFlags Flags);
+} \ No newline at end of file
diff --git a/lib/Plugins.Runtime/src/LivePlugin.cs b/lib/Plugins.Runtime/src/LivePlugin.cs
index 573b520..3ed2ad6 100644
--- a/lib/Plugins.Runtime/src/LivePlugin.cs
+++ b/lib/Plugins.Runtime/src/LivePlugin.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Runtime
@@ -130,6 +130,23 @@ namespace VNLib.Plugins.Runtime
}
/// <summary>
+ /// Gets services from the plugin if it is loaded and
+ /// publishes them to the pool
+ /// </summary>
+ /// <param name="pool">The service pool to collect services into</param>
+ /// <exception cref="InvalidOperationException"></exception>
+ internal void GetServices(IPluginServicePool pool)
+ {
+ if (!_loaded)
+ {
+ throw new InvalidOperationException("Plugin is not loaded");
+ }
+
+ //Load services into pool
+ Plugin?.PublishServices(pool);
+ }
+
+ /// <summary>
/// Invokes the plugins console event handler if the type has one
/// and the plugin is loaded.
/// </summary>
diff --git a/lib/Plugins.Runtime/src/LoaderExtensions.cs b/lib/Plugins.Runtime/src/LoaderExtensions.cs
index d167488..b892213 100644
--- a/lib/Plugins.Runtime/src/LoaderExtensions.cs
+++ b/lib/Plugins.Runtime/src/LoaderExtensions.cs
@@ -27,6 +27,7 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
+using System.Threading.Tasks;
using System.Collections.Generic;
using VNLib.Utils.IO;
@@ -34,6 +35,15 @@ using VNLib.Utils.Extensions;
namespace VNLib.Plugins.Runtime
{
+
+ /// <summary>
+ /// A callback function signature for plugin plugin loading errors on plugin
+ /// stacks.
+ /// </summary>
+ /// <param name="Loader">The loader that the exception occured on</param>
+ /// <param name="exception">The exception cause of the error</param>
+ public delegate void PluginLoadErrorHandler(RuntimePluginLoader Loader, Exception exception);
+
/// <summary>
/// Contains extension methods for PluginLoader library
/// </summary>
@@ -169,14 +179,68 @@ namespace VNLib.Plugins.Runtime
/// Invokes the load method for all plugin instances
/// </summary>
/// <param name="runtime"></param>
+ /// <param name="concurrent">A value that indicates if plugins should be loaded concurrently or sequentially</param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="AggregateException"></exception>
- public static void InvokeLoad(this IPluginStack runtime)
+ public static void InvokeLoad(this IPluginStack runtime, bool concurrent)
+ {
+ List<Exception> exceptions = new ();
+
+ //Add load exceptions into the list
+ void onError(RuntimePluginLoader loader, Exception ex) => exceptions.Add(ex);
+
+ //Invoke load with onError callback
+ InvokeLoad(runtime, concurrent, onError);
+
+ //If any exceptions occured, throw them now
+ if(exceptions.Count > 0)
+ {
+ throw new AggregateException(exceptions);
+ }
+ }
+
+ /// <summary>
+ /// Invokes the load method for all plugin instances, and captures exceptions
+ /// into the specified callback function.
+ /// </summary>
+ /// <param name="runtime"></param>
+ /// <param name="concurrent">A value that indicates if plugins should be loaded concurrently or sequentially</param>
+ /// <param name="onError">A callback function to handle error conditions instead of raising exceptions</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ public static void InvokeLoad(this IPluginStack runtime, bool concurrent, PluginLoadErrorHandler onError)
{
ArgumentNullException.ThrowIfNull(runtime, nameof(runtime));
- //try loading all plugins
- runtime.Plugins.TryForeach(static p => p.LoadPlugins());
+ if (concurrent)
+ {
+ //Invoke load in parallel
+ Parallel.ForEach(runtime.Plugins, p =>
+ {
+ try
+ {
+ p.LoadPlugins();
+ }
+ catch (Exception ex)
+ {
+ onError(p, ex);
+ }
+ });
+ }
+ else
+ {
+ //Load sequentially
+ foreach(RuntimePluginLoader loader in runtime.Plugins)
+ {
+ try
+ {
+ loader.LoadPlugins();
+ }
+ catch (Exception ex)
+ {
+ onError(loader, ex);
+ }
+ }
+ }
}
/// <summary>
@@ -398,6 +462,24 @@ namespace VNLib.Plugins.Runtime
}
/// <summary>
+ /// Registers a new <see cref="SharedPluginServiceProvider"/> for the current plugin stack
+ /// that will listen for plugin events and capture the exported services into a
+ /// single pool.
+ /// </summary>
+ /// <param name="stack"></param>
+ /// <returns>A new <see cref="SharedPluginServiceProvider"/> that will capture all exported services when loaded</returns>
+ public static SharedPluginServiceProvider RegisterServiceProvider(this IPluginStack stack)
+ {
+ //Init new service provider
+ SharedPluginServiceProvider provider = new();
+
+ //Register for all plugins
+ RegsiterListener(stack, provider);
+
+ return provider;
+ }
+
+ /// <summary>
/// Gets the current collection of loaded plugins for the plugin stack
/// </summary>
/// <param name="stack"></param>
@@ -499,6 +581,6 @@ namespace VNLib.Plugins.Runtime
{
//Do nothing
}
- }
+ }
}
}
diff --git a/lib/Plugins.Runtime/src/PluginController.cs b/lib/Plugins.Runtime/src/PluginController.cs
index adb8ff9..7f82c13 100644
--- a/lib/Plugins.Runtime/src/PluginController.cs
+++ b/lib/Plugins.Runtime/src/PluginController.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Runtime
@@ -29,9 +29,11 @@ using System.Collections.Generic;
using VNLib.Utils.IO;
using VNLib.Utils.Extensions;
+using VNLib.Plugins.Runtime.Services;
namespace VNLib.Plugins.Runtime
{
+
/// <summary>
/// Manages the lifetime of a collection of <see cref="IPlugin"/> instances,
/// and their dependent event listeners
@@ -52,11 +54,14 @@ namespace VNLib.Plugins.Runtime
private readonly List<LivePlugin> _plugins;
private readonly List<KeyValuePair<IPluginEventListener, object?>> _listeners;
+ private readonly PluginServicePool _servicePool;
+
internal PluginController()
{
_plugins = new ();
_listeners = new ();
+ _servicePool = new ();
}
/// <summary>
@@ -86,6 +91,10 @@ namespace VNLib.Plugins.Runtime
}
}
+ /// <summary>
+ /// Populates the given <see cref="IServiceContainer"/> with all services
+ /// </summary>
+ public PluginServiceExport[] GetExportedServices() => _servicePool.GetServices();
internal void InitializePlugins(Assembly asm)
{
@@ -121,6 +130,9 @@ namespace VNLib.Plugins.Runtime
//Load all plugins
_plugins.TryForeach(static p => p.LoadPlugin());
+ //Load all services into the service pool
+ _plugins.TryForeach(p => p.GetServices(_servicePool));
+
//Notify event handlers
_listeners.TryForeach(l => l.Key.OnPluginLoaded(this, l.Value));
}
@@ -140,8 +152,10 @@ namespace VNLib.Plugins.Runtime
}
finally
{
- //Always
+ //Always clear plugins
_plugins.Clear();
+ //always make sure service pool is clear
+ _servicePool.Clear();
}
}
}
@@ -150,7 +164,8 @@ namespace VNLib.Plugins.Runtime
{
_plugins.Clear();
_listeners.Clear();
+ _servicePool.Clear();
}
-
+
}
}
diff --git a/lib/Plugins.Runtime/src/RuntimePluginLoader.cs b/lib/Plugins.Runtime/src/RuntimePluginLoader.cs
index cef4d47..40cf5ed 100644
--- a/lib/Plugins.Runtime/src/RuntimePluginLoader.cs
+++ b/lib/Plugins.Runtime/src/RuntimePluginLoader.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Runtime
@@ -199,4 +199,4 @@ namespace VNLib.Plugins.Runtime
Loader.Dispose();
}
}
-} \ No newline at end of file
+}
diff --git a/lib/Plugins.Runtime/src/Services/PluginServiceExport.cs b/lib/Plugins.Runtime/src/Services/PluginServiceExport.cs
new file mode 100644
index 0000000..0053a70
--- /dev/null
+++ b/lib/Plugins.Runtime/src/Services/PluginServiceExport.cs
@@ -0,0 +1,49 @@
+/*
+* Copyright (c) 2024 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Plugins.Runtime
+* File: PluginServiceExport.cs
+*
+* PluginServiceExport.cs is part of VNLib.Plugins.Runtime which is part of the larger
+* VNLib collection of libraries and utilities.
+*
+* VNLib.Plugins.Runtime is free software: you can redistribute it and/or modify
+* it under the terms of the GNU 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.Runtime 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
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Plugins.Runtime. If not, see http://www.gnu.org/licenses/.
+*/
+
+using System;
+
+namespace VNLib.Plugins.Runtime.Services
+{
+ /// <summary>
+ /// An immutable wrapper for an exported service by an <see cref="IPlugin"/>
+ /// </summary>
+ public readonly record struct PluginServiceExport
+ {
+ /// <summary>
+ /// The exported service type
+ /// </summary>
+ public readonly Type ServiceType { get; init; }
+
+ /// <summary>
+ /// The exported service instance
+ /// </summary>
+ public readonly object Service { get; init; }
+
+ /// <summary>
+ /// The export flags
+ /// </summary>
+ public readonly ExportFlags Flags { get; init; }
+ }
+}
diff --git a/lib/Plugins.Runtime/src/Services/PluginServicePool.cs b/lib/Plugins.Runtime/src/Services/PluginServicePool.cs
new file mode 100644
index 0000000..fc8a371
--- /dev/null
+++ b/lib/Plugins.Runtime/src/Services/PluginServicePool.cs
@@ -0,0 +1,53 @@
+/*
+* Copyright (c) 2024 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Plugins.Runtime
+* File: PluginServicePool.cs
+*
+* PluginServicePool.cs is part of VNLib.Plugins.Runtime which is part of the larger
+* VNLib collection of libraries and utilities.
+*
+* VNLib.Plugins.Runtime is free software: you can redistribute it and/or modify
+* it under the terms of the GNU 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.Runtime 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
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Plugins.Runtime. If not, see http://www.gnu.org/licenses/.
+*/
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+namespace VNLib.Plugins.Runtime.Services
+{
+ internal sealed class PluginServicePool : IPluginServicePool
+ {
+ private readonly LinkedList<PluginServiceExport> _services = new();
+
+ ///<inheritdoc/>
+ public void ExportService(Type serviceType, object service, ExportFlags flags = ExportFlags.None)
+ {
+ PluginServiceExport wrapper = new()
+ {
+ Service = service,
+ ServiceType = serviceType,
+ Flags = flags
+ };
+
+ //Add service to container
+ _services.AddLast(wrapper);
+ }
+
+ public PluginServiceExport[] GetServices() => _services.ToArray();
+
+ public void Clear() => _services.Clear();
+ }
+}
diff --git a/lib/Plugins.Runtime/src/SharedPluginServiceProvider.cs b/lib/Plugins.Runtime/src/SharedPluginServiceProvider.cs
new file mode 100644
index 0000000..a64ede3
--- /dev/null
+++ b/lib/Plugins.Runtime/src/SharedPluginServiceProvider.cs
@@ -0,0 +1,115 @@
+/*
+* Copyright (c) 2024 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Plugins.Runtime
+* File: SharedPluginServiceProvider.cs
+*
+* SharedPluginServiceProvider.cs is part of VNLib.Plugins.Runtime which
+* is part of the larger VNLib collection of libraries and utilities.
+*
+* VNLib.Plugins.Runtime is free software: you can redistribute it and/or modify
+* it under the terms of the GNU 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.Runtime 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
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Plugins.Runtime. If not, see http://www.gnu.org/licenses/.
+*/
+
+using System;
+using System.ComponentModel.Design;
+
+using VNLib.Utils;
+using VNLib.Plugins.Runtime.Services;
+
+namespace VNLib.Plugins.Runtime
+{
+ /// <summary>
+ /// Represents a single shared pool for a collection of plugins to
+ /// export services to.
+ /// </summary>
+ public sealed class SharedPluginServiceProvider :
+ VnDisposeable,
+ IServiceProvider,
+ IPluginEventListener
+ {
+ private readonly ServiceContainer _serviceContainer = new();
+ private readonly object _syncRoot = new();
+
+ ///<inheritdoc/>
+ public object? GetService(Type serviceType) => _serviceContainer.GetService(serviceType);
+
+ /// <summary>
+ /// Gets the service object of the specified type.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <returns>
+ /// A service object of type serviceType. -or- null if
+ /// there is no service object of type serviceType.
+ /// </returns>
+ public T? GetService<T>() where T : class => GetService(typeof(T)) as T;
+
+ void IPluginEventListener.OnPluginLoaded(PluginController controller, object? state)
+ {
+ //Add services
+ AddOrRemoveServices(controller, true);
+ }
+
+ void IPluginEventListener.OnPluginUnloaded(PluginController controller, object? state)
+ {
+ //Remove services
+ AddOrRemoveServices(controller, false);
+ }
+
+ private void AddOrRemoveServices(PluginController controller, bool add)
+ {
+ /*
+ * Depending on when services are loaded/unloaded, this instances
+ * may be disposeed so avoid raising an exception for a condition
+ * that doenst matter. If disposed, we dont need to clean anything up
+ */
+ if (Disposed)
+ {
+ return;
+ }
+
+ //Get all exported services
+ PluginServiceExport[] exports = controller.GetExportedServices();
+
+ //We need to hold a lock to synchronize access to the service container
+ lock (_syncRoot)
+ {
+ //if add flag is set, add the serivces, otherwise remove them
+ if (add)
+ {
+ Array.ForEach(exports, e => _serviceContainer.AddService(e.ServiceType, e.Service));
+ }
+ else
+ {
+ Array.ForEach(exports, e => _serviceContainer.RemoveService(e.ServiceType));
+ }
+ }
+
+ //cleanup any disposable services when removing
+ if (!add)
+ {
+ foreach(PluginServiceExport export in exports)
+ {
+ if(export.Service is IDisposable disposable)
+ {
+ disposable.Dispose();
+ }
+ }
+ }
+ }
+
+ ///<inheritdoc/>
+ protected override void Free() => _serviceContainer.Dispose();
+ }
+}
diff --git a/lib/Plugins/src/Attributes/ServiceConfiguratorAttribute.cs b/lib/Plugins/src/Attributes/ServiceConfiguratorAttribute.cs
deleted file mode 100644
index 123fb66..0000000
--- a/lib/Plugins/src/Attributes/ServiceConfiguratorAttribute.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
-* Copyright (c) 2023 Vaughn Nugent
-*
-* Library: VNLib
-* Package: VNLib.Plugins
-* File: ServiceConfiguratorAttribute.cs
-*
-* ServiceConfiguratorAttribute.cs is part of VNLib.Plugins which is part of the larger
-* VNLib collection of libraries and utilities.
-*
-* VNLib.Plugins is free software: you can redistribute it and/or modify
-* it under the terms of the GNU 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 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
-* General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with VNLib.Plugins. If not, see http://www.gnu.org/licenses/.
-*/
-
-using System;
-using System.ComponentModel.Design;
-
-namespace VNLib.Plugins.Attributes
-{
- /// <summary>
- /// <para>
- /// Declare this attribute on an <see cref="IPlugin"/> instance method to define the service configuration
- /// method. When declared, allows the plugin to expose shared types to the host
- /// </para>
- /// <para>
- /// This method may be runtime dependant, it may not be called on all platforms, and it
- /// may not be required.
- /// </para>
- /// <para>
- /// Lifecycle: This method may be called by the runtime anytime after the <see cref="IPlugin.Load"/>
- /// method, its exposed services are considered invalid after the <see cref="IPlugin.Unload"/>
- /// method is called.
- /// </para>
- /// <para>
- /// Method signature: <code>void OnServiceConfiguration(<see cref="IServiceContainer"/> container)</code>
- /// </para>
- /// </summary>
- [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
- public sealed class ServiceConfiguratorAttribute : Attribute
- { }
-
- /// <summary>
- /// A safe delegate that matches the signature of the <see cref="ServiceConfiguratorAttribute"/>
- /// method exposed
- /// </summary>
- /// <param name="sender"></param>
- public delegate void ServiceConfigurator(IServiceContainer sender);
-}
diff --git a/lib/Plugins/src/Web/IWebPlugin.cs b/lib/Plugins/src/ExportFlags.cs
index ed080d0..25c1f9f 100644
--- a/lib/Plugins/src/Web/IWebPlugin.cs
+++ b/lib/Plugins/src/ExportFlags.cs
@@ -1,11 +1,11 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins
-* File: IWebPlugin.cs
+* File: ExportFlags.cs
*
-* IWebPlugin.cs is part of VNLib.Plugins which is part of the larger
+* ExportFlags.cs is part of VNLib.Plugins which is part of the larger
* VNLib collection of libraries and utilities.
*
* VNLib.Plugins is free software: you can redistribute it and/or modify
@@ -22,24 +22,19 @@
* along with VNLib.Plugins. If not, see http://www.gnu.org/licenses/.
*/
-using System.Collections.Generic;
+using System;
namespace VNLib.Plugins
{
/// <summary>
- /// Represents a plugin that is expected to perform web application based operations
+ /// Service export flags
/// </summary>
- public interface IWebPlugin : IPlugin
+ [Flags]
+ public enum ExportFlags
{
/// <summary>
- /// Returns all endpoints within the plugin to load into the current root
+ /// No flags
/// </summary>
- /// <returns>An enumeration of endpoints to load</returns>
- /// <remarks>
- /// Lifecycle: Results returned from this method should be consistant (although its only
- /// likely to be called once) anytime after the <see cref="IPlugin.Load"/> method, and undefined
- /// after the <see cref="IPlugin.Unload"/> method is called.
- /// </remarks>
- IEnumerable<IEndpoint> GetEndpoints();
+ None = 0,
}
} \ No newline at end of file
diff --git a/lib/Plugins/src/IPlugin.cs b/lib/Plugins/src/IPlugin.cs
index c1fa6a6..cf581d6 100644
--- a/lib/Plugins/src/IPlugin.cs
+++ b/lib/Plugins/src/IPlugin.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins
@@ -43,5 +43,16 @@ namespace VNLib.Plugins
/// Invoked when the plugin is unloaded from the runtime
/// </summary>
void Unload();
+
+ /// <summary>
+ /// Called directly after the plugin is loaded to allow for service
+ /// publishing for the host application to use
+ /// <para>
+ /// NOTE: A service pool is shared for each assembly, if there are multiple
+ /// plugins per assembly, they will share the same service pool.
+ /// </para>
+ /// </summary>
+ /// <param name="pool">The service publisher instance</param>
+ void PublishServices(IPluginServicePool pool);
}
} \ No newline at end of file
diff --git a/lib/Plugins/src/IPluginServicePool.cs b/lib/Plugins/src/IPluginServicePool.cs
new file mode 100644
index 0000000..a4c5e9a
--- /dev/null
+++ b/lib/Plugins/src/IPluginServicePool.cs
@@ -0,0 +1,42 @@
+/*
+* Copyright (c) 2024 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Plugins
+* File: IPluginServicePool.cs
+*
+* IPluginServicePool.cs is part of VNLib.Plugins which is part of the larger
+* VNLib collection of libraries and utilities.
+*
+* VNLib.Plugins is free software: you can redistribute it and/or modify
+* it under the terms of the GNU 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 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
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Plugins. If not, see http://www.gnu.org/licenses/.
+*/
+
+using System;
+
+namespace VNLib.Plugins
+{
+ /// <summary>
+ /// Represents a type that exposes services to the loading application
+ /// </summary>
+ public interface IPluginServicePool
+ {
+ /// <summary>
+ /// Publishes a generic service to the service pool
+ /// </summary>
+ /// <param name="serviceType">The <see cref="Type"/> to expose to the pool for searching</param>
+ /// <param name="service">The service instance to publish</param>
+ /// <param name="flags">Optional flags to pass during export</param>
+ void ExportService(Type serviceType, object service, ExportFlags flags = ExportFlags.None);
+ }
+} \ No newline at end of file