diff options
Diffstat (limited to 'lib')
10 files changed, 95 insertions, 80 deletions
diff --git a/lib/Net.Rest.Client/src/OAuth2/O2ErrorResponseMessage.cs b/lib/Net.Rest.Client/src/OAuth2/O2ErrorResponseMessage.cs index 151c575..f2ffbaa 100644 --- a/lib/Net.Rest.Client/src/OAuth2/O2ErrorResponseMessage.cs +++ b/lib/Net.Rest.Client/src/OAuth2/O2ErrorResponseMessage.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Net.Rest.Client @@ -56,7 +56,7 @@ namespace VNLib.Net.Rest.Client.OAuth2 /// <param name="description">The OAuth2 error description</param> public O2ErrorResponseMessage(string code, string description) { - this.ErrorCode = code; + ErrorCode = code; ErrorDescription = description; } diff --git a/lib/Net.Rest.Client/src/OAuth2/OAuth2AuthenticationException.cs b/lib/Net.Rest.Client/src/OAuth2/OAuth2AuthenticationException.cs index 7176093..96242f2 100644 --- a/lib/Net.Rest.Client/src/OAuth2/OAuth2AuthenticationException.cs +++ b/lib/Net.Rest.Client/src/OAuth2/OAuth2AuthenticationException.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Net.Rest.Client diff --git a/lib/Net.Rest.Client/src/OAuth2/OAuth2Authenticator.cs b/lib/Net.Rest.Client/src/OAuth2/OAuth2Authenticator.cs index b978404..61af299 100644 --- a/lib/Net.Rest.Client/src/OAuth2/OAuth2Authenticator.cs +++ b/lib/Net.Rest.Client/src/OAuth2/OAuth2Authenticator.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Net.Rest.Client @@ -23,7 +23,6 @@ */ using System; -using System.Net; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -34,8 +33,6 @@ using RestSharp.Authenticators; using VNLib.Utils; using VNLib.Utils.Extensions; -#nullable enable - namespace VNLib.Net.Rest.Client.OAuth2 { /// <summary> @@ -72,7 +69,7 @@ namespace VNLib.Net.Rest.Client.OAuth2 _tokenLock = new(1, 1); } - async ValueTask IAuthenticator.Authenticate(RestClient client, RestRequest request) + async ValueTask IAuthenticator.Authenticate(IRestClient client, RestRequest request) { //Wait for access to the token incase another thread is updating it using SemSlimReleaser releaser = await _tokenLock.GetReleaserAsync(CancellationToken.None); diff --git a/lib/Net.Rest.Client/src/RestClientPool.cs b/lib/Net.Rest.Client/src/RestClientPool.cs index 2d61203..5c3d401 100644 --- a/lib/Net.Rest.Client/src/RestClientPool.cs +++ b/lib/Net.Rest.Client/src/RestClientPool.cs @@ -29,8 +29,6 @@ using RestSharp.Authenticators; using VNLib.Utils.Memory.Caching; -#nullable enable - namespace VNLib.Net.Rest.Client { /// <summary> diff --git a/lib/Net.Rest.Client/src/VNLib.Net.Rest.Client.csproj b/lib/Net.Rest.Client/src/VNLib.Net.Rest.Client.csproj index d25a307..b6b2bd5 100644 --- a/lib/Net.Rest.Client/src/VNLib.Net.Rest.Client.csproj +++ b/lib/Net.Rest.Client/src/VNLib.Net.Rest.Client.csproj @@ -2,6 +2,7 @@ <PropertyGroup> <TargetFramework>net6.0</TargetFramework> + <Nullable>enable</Nullable> <AssemblyName>VNLib.Net.Rest.Client</AssemblyName> <RootNamespace>VNLib.Net.Rest.Client</RootNamespace> <GenerateDocumentationFile>True</GenerateDocumentationFile> @@ -41,7 +42,7 @@ <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="RestSharp" Version="109.0.1" /> + <PackageReference Include="RestSharp" Version="110.2.0" /> </ItemGroup> <ItemGroup> diff --git a/lib/Plugins.Essentials.ServiceStack/src/ManagedPlugin.cs b/lib/Plugins.Essentials.ServiceStack/src/ManagedPlugin.cs index 596ea83..3bf99cb 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/ManagedPlugin.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/ManagedPlugin.cs @@ -27,13 +27,17 @@ using System.IO; using System.Linq; using System.Threading; using System.Reflection; +using System.Runtime.Loader; using System.Threading.Tasks; using System.ComponentModel.Design; +using McMaster.NETCore.Plugins; + using VNLib.Utils; using VNLib.Plugins.Runtime; using VNLib.Plugins.Attributes; + namespace VNLib.Plugins.Essentials.ServiceStack { @@ -44,12 +48,15 @@ namespace VNLib.Plugins.Essentials.ServiceStack private UnloadableServiceContainer? _services; - public ManagedPlugin(string pluginPath, PluginLoadConfiguration config, IPluginEventListener listener) + public ManagedPlugin(string pluginPath, in PluginLoadConfiguration config, IPluginEventListener listener) { PluginPath = pluginPath; + //Get the plugin config for the assembly + PluginConfig pConfig = GetConfigForAssemblyPath(pluginPath, in config); + //configure the loader - _plugin = new(pluginPath, config.HostConfig, config.PluginErrorLog, config.HotReload, config.HotReload); + _plugin = new(pConfig, config.HostConfig, config.PluginErrorLog); //Register listener before loading occurs _plugin.Controller.Register(this, this); @@ -58,6 +65,20 @@ namespace VNLib.Plugins.Essentials.ServiceStack _serviceDomainListener = listener; } + private static PluginConfig GetConfigForAssemblyPath(string asmPath, in PluginLoadConfiguration loadConfig) + { + PluginConfig config = new(asmPath) + { + IsUnloadable = loadConfig.HotReload, + EnableHotReload = loadConfig.HotReload, + IsLazyLoaded = false, + PreferSharedTypes = true, + DefaultContext = AssemblyLoadContext.Default, + ReloadDelay = loadConfig.ReloadDelay + }; + return config; + } + ///<inheritdoc/> public string PluginPath { get; } diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginLoadConfiguration.cs b/lib/Plugins.Essentials.ServiceStack/src/PluginLoadConfiguration.cs index 73cb201..894ae55 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/PluginLoadConfiguration.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/PluginLoadConfiguration.cs @@ -22,13 +22,12 @@ * along with this program. If not, see https://www.gnu.org/licenses/. */ - +using System; using System.Text.Json; using VNLib.Utils.Logging; using VNLib.Plugins.Runtime; - namespace VNLib.Plugins.Essentials.ServiceStack { /// <summary> @@ -58,5 +57,11 @@ namespace VNLib.Plugins.Essentials.ServiceStack /// holding plugins /// </summary> public readonly ILogProvider? PluginErrorLog { get; init; } + + /// <summary> + /// If hot-reload is enabled, sets a time delay the file watcher waits when + /// a plugin assembly has changed. + /// </summary> + public readonly TimeSpan ReloadDelay { get; init; } } } diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs b/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs index cdcf7ba..e4e5670 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs @@ -134,12 +134,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack private async Task InitializePlugin(ManagedPlugin plugin, ILogProvider debugLog) { - try - { - //Load wrapper - await plugin.InitializePluginsAsync().ConfigureAwait(true); - } - catch (Exception ex) + void LogAndRemovePlugin(Exception ex) { debugLog.Error(ex, $"Exception raised during initialzation of {plugin.PluginFileName}. It has been removed from the collection\n{ex}"); @@ -152,6 +147,20 @@ namespace VNLib.Plugins.Essentials.ServiceStack //Dispose the plugin plugin.Dispose(); } + + try + { + //Load wrapper + await plugin.InitializePluginsAsync().ConfigureAwait(true); + } + catch(AggregateException ae) when (ae.InnerException != null) + { + LogAndRemovePlugin(ae.InnerException); + } + catch (Exception ex) + { + LogAndRemovePlugin(ex); + } } private static void LoadPlugin(ManagedPlugin plugin, ILogProvider debugLog) @@ -166,7 +175,19 @@ namespace VNLib.Plugins.Essentials.ServiceStack sw.Stop(); - debugLog.Verbose("Loaded {pl} in {tm} ms", plugin.PluginFileName, sw.ElapsedMilliseconds); + /* + * 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 (!plugin.Controller.Plugins.Any()) + { + debugLog.Warn("No plugin instances were exposed via {ams} assembly. This may be due to an assebmly mismatch", plugin.PluginFileName); + } + else + { + debugLog.Verbose("Loaded {pl} in {tm} ms", plugin.PluginFileName, sw.ElapsedMilliseconds); + } + } catch (Exception ex) { diff --git a/lib/Plugins.PluginBase/src/PluginBase.cs b/lib/Plugins.PluginBase/src/PluginBase.cs index a291e77..80860e7 100644 --- a/lib/Plugins.PluginBase/src/PluginBase.cs +++ b/lib/Plugins.PluginBase/src/PluginBase.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.PluginBase @@ -69,22 +69,27 @@ namespace VNLib.Plugins /// by the host app /// </summary> public ICollection<IEndpoint> Endpoints { get; } = new List<IEndpoint>(); + /// <summary> /// The logging instance /// </summary> public ILogProvider Log { get; private set; } + /// <summary> /// If passed by the host application, the configuration file of the host application and plugin /// </summary> protected JsonDocument Configuration { get; private set; } + /// <summary> /// The configuration data from the host application /// </summary> - public JsonElement HostConfig { get; private set; } + public JsonElement HostConfig => Configuration.RootElement.GetProperty(GlobalConfigDomPropertyName); + /// <summary> /// The configuration data from the plugin's config file passed by the host application /// </summary> - public JsonElement PluginConfig { get; private set; } + public JsonElement PluginConfig => Configuration.RootElement.GetProperty(GetType().Name); + /// <summary> /// <inheritdoc/> /// </summary> @@ -105,10 +110,6 @@ namespace VNLib.Plugins _ = config ?? throw new ArgumentNullException(nameof(config)); //Store config ref to dispose properly Configuration = config; - //Load congfiguration elements - HostConfig = config.RootElement.GetProperty(GlobalConfigDomPropertyName); - //Plugin config propery name is the name of the current type - PluginConfig = config.RootElement.GetProperty(this.GetType().Name); } /// <summary> @@ -154,7 +155,7 @@ namespace VNLib.Plugins //If silent arg is not specified, open log to console if (!(args.Contains("--silent") || args.Contains("-s"))) { - logConfig.WriteTo.Console(outputTemplate: LogTemplate); + _ = logConfig.WriteTo.Console(outputTemplate: LogTemplate, formatProvider:null); } } @@ -170,7 +171,7 @@ namespace VNLib.Plugins int fileSizeLimit = 500 * 1000 * 1024; RollingInterval interval = RollingInterval.Infinite; - //try to get the host's app_log config object + //try to get the host's app_log config object, if it does not exist, do not write logs to file if (HostConfig.TryGetProperty("app_log", out JsonElement logEl)) { IReadOnlyDictionary<string, JsonElement> conf = logEl.EnumerateObject().ToDictionary(static s => s.Name, static s => s.Value); @@ -206,20 +207,21 @@ namespace VNLib.Plugins //Replace the file name filePath = filePath.Replace(appLogName, PluginName, StringComparison.Ordinal); } - } - //Default if not set - filePath ??= Path.Combine(Environment.CurrentDirectory, $"{PluginName}.txt"); - template ??= LogTemplate; - - //Configure the log file writer - logConfig.WriteTo.File(filePath, - buffered: true, - retainedFileCountLimit: retainedLogs, - fileSizeLimitBytes: fileSizeLimit, - rollingInterval: interval, - outputTemplate: template, - flushToDiskInterval: flushInterval); + //Default to exe dir if not set + filePath ??= Path.Combine(Environment.CurrentDirectory, $"{PluginName}.txt"); + template ??= LogTemplate; + + //Configure the log file writer + logConfig.WriteTo.File(filePath, + buffered: true, + retainedFileCountLimit: retainedLogs, + formatProvider: null, + fileSizeLimitBytes: fileSizeLimit, + rollingInterval: interval, + outputTemplate: template, + flushToDiskInterval: flushInterval); + } } /// <summary> @@ -356,6 +358,12 @@ 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> @@ -373,10 +381,5 @@ namespace VNLib.Plugins /// for the current plugin /// </summary> protected virtual void OnGetEndpoints() { } - /// <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); } }
\ No newline at end of file diff --git a/lib/Plugins.Runtime/src/RuntimePluginLoader.cs b/lib/Plugins.Runtime/src/RuntimePluginLoader.cs index 2a03a3d..83aad21 100644 --- a/lib/Plugins.Runtime/src/RuntimePluginLoader.cs +++ b/lib/Plugins.Runtime/src/RuntimePluginLoader.cs @@ -26,7 +26,6 @@ using System; using System.IO; using System.Text.Json; using System.Reflection; -using System.Runtime.Loader; using System.Threading.Tasks; using McMaster.NETCore.Plugins; @@ -59,33 +58,6 @@ namespace VNLib.Plugins.Runtime public string PluginConfigPath { get; } /// <summary> - /// Creates a new <see cref="RuntimePluginLoader"/> with the specified - /// assembly location and host config. - /// </summary> - /// <param name="pluginPath"></param> - /// <param name="log">A nullable log provider</param> - /// <param name="hostConfig">The configuration DOM to merge with plugin config DOM and pass to enabled plugins</param> - /// <param name="unloadable">A value that specifies if the assembly can be unloaded</param> - /// <param name="hotReload">A value that spcifies if the loader will listen for changes to the assembly file and reload the plugins</param> - /// <remarks> - /// The <paramref name="log"/> argument may be null if <paramref name="unloadable"/> is false - /// </remarks> - /// <exception cref="ArgumentNullException"></exception> - public RuntimePluginLoader(string pluginPath, JsonElement? hostConfig = null, ILogProvider? log = null, bool unloadable = false, bool hotReload = false) - :this( - new PluginConfig(pluginPath) - { - IsUnloadable = unloadable || hotReload, - EnableHotReload = hotReload, - ReloadDelay = TimeSpan.FromSeconds(1), - PreferSharedTypes = true, - DefaultContext = AssemblyLoadContext.Default - }, - hostConfig, log) - { - } - - /// <summary> /// Creates a new <see cref="RuntimePluginLoader"/> with the specified config and host config dom. /// </summary> /// <param name="config">The plugin's loader configuration </param> @@ -94,9 +66,6 @@ namespace VNLib.Plugins.Runtime /// <exception cref="ArgumentNullException"></exception> public RuntimePluginLoader(PluginConfig config, JsonElement? hostConfig, ILogProvider? log) { - //Shared types is required so the default load context shares types - config.PreferSharedTypes = true; - //Default to empty config if null, otherwise clone a copy of the host config element HostConfig = hostConfig.HasValue ? Clone(hostConfig.Value) : JsonDocument.Parse("{}"); |