Browse Source

Plugin interface simplified.

pull/443/head
Sebastian Stehle 6 years ago
parent
commit
e6cda35a46
  1. 16
      backend/extensions/Squidex.SamplePlugin/SamplePlugin.cs
  2. 20
      backend/extensions/Squidex.SamplePlugin/Squidex.SamplePlugin.csproj
  3. 24
      backend/src/Squidex.Infrastructure/Plugins/PluginLoaders.cs
  4. 78
      backend/src/Squidex.Infrastructure/Plugins/PluginManager.cs
  5. 4
      backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj
  6. 43
      backend/src/Squidex/Pipeline/Plugins/PluginExtensions.cs
  7. 1
      backend/src/Squidex/Squidex.csproj
  8. 3
      backend/src/Squidex/Startup.cs

16
backend/src/Squidex.Infrastructure/Plugins/IWebPlugin.cs → backend/extensions/Squidex.SamplePlugin/SamplePlugin.cs

@ -5,14 +5,18 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Microsoft.AspNetCore.Builder;
using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Squidex.Infrastructure.Plugins;
namespace Squidex.Infrastructure.Plugins
namespace Squidex.SamplePlugin
{
public interface IWebPlugin : IPlugin
public sealed class SamplePlugin : IPlugin
{
void ConfigureBefore(IApplicationBuilder app);
void ConfigureAfter(IApplicationBuilder app);
public void ConfigureServices(IServiceCollection services, IConfiguration config)
{
throw new NotImplementedException();
}
}
}

20
backend/extensions/Squidex.SamplePlugin/Squidex.SamplePlugin.csproj

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Squidex.Domain.Apps.Entities\Squidex.Domain.Apps.Entities.csproj" />
<ProjectReference Include="..\..\src\Squidex.Web\Squidex.Web.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
</ItemGroup>
<PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project>

24
backend/src/Squidex/Pipeline/Plugins/PluginLoaders.cs → backend/src/Squidex.Infrastructure/Plugins/PluginLoaders.cs

@ -8,32 +8,14 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using McMaster.NETCore.Plugins;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Events;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Plugins;
using Squidex.Web;
namespace Squidex.Pipeline.Plugins
namespace Squidex.Infrastructure.Plugins
{
public static class PluginLoaders
{
private static readonly Type[] SharedTypes =
{
typeof(IPlugin),
typeof(SquidexCoreModel),
typeof(SquidexCoreOperations),
typeof(SquidexEntities),
typeof(SquidexEvents),
typeof(SquidexInfrastructure),
typeof(SquidexWeb)
};
public static PluginLoader? LoadPlugin(string pluginPath)
public static PluginLoader? LoadPlugin(string pluginPath, AssemblyName[] sharedAssemblies)
{
foreach (var candidate in GetPaths(pluginPath))
{
@ -43,7 +25,7 @@ namespace Squidex.Pipeline.Plugins
{
config.PreferSharedTypes = true;
config.SharedAssemblies.AddRange(SharedTypes.Select(x => x.Assembly.GetName()));
config.SharedAssemblies.AddRange(sharedAssemblies);
});
}
}

78
backend/src/Squidex.Infrastructure/Plugins/PluginManager.cs

@ -7,29 +7,69 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using McMaster.NETCore.Plugins;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Squidex.Infrastructure.Log;
namespace Squidex.Infrastructure.Plugins
{
public sealed class PluginManager
public sealed class PluginManager : DisposableObjectBase
{
private readonly HashSet<PluginLoader> pluginLoaders = new HashSet<PluginLoader>();
private readonly HashSet<IPlugin> loadedPlugins = new HashSet<IPlugin>();
private readonly List<(string Plugin, string Action, Exception Exception)> exceptions = new List<(string, string, Exception)>();
public IReadOnlyCollection<IPlugin> Plugins
protected override void DisposeObject(bool disposing)
{
get { return loadedPlugins; }
if (disposing)
{
foreach (var loader in pluginLoaders)
{
loader.Dispose();
}
}
}
public void Add(string name, Assembly assembly)
public Assembly? Load(string path, AssemblyName[] sharedAssemblies)
{
Guard.NotNull(assembly);
Guard.NotNullOrEmpty(path);
Guard.NotNull(sharedAssemblies);
Assembly? assembly = null;
var loader = PluginLoaders.LoadPlugin(path, sharedAssemblies);
if (loader != null)
{
try
{
assembly = loader.LoadDefaultAssembly();
Add(path, assembly);
pluginLoaders.Add(loader);
}
catch (Exception ex)
{
LogException(path, "LoadingAssembly", ex);
loader.Dispose();
}
}
else
{
LogException(path, "LoadingPlugin", new FileNotFoundException($"Cannot find plugin at {path}"));
}
return assembly;
}
private void Add(string name, Assembly assembly)
{
var pluginTypes =
assembly.GetTypes()
.Where(t => typeof(IPlugin).IsAssignableFrom(t))
@ -50,12 +90,8 @@ namespace Squidex.Infrastructure.Plugins
}
}
public void LogException(string plugin, string action, Exception exception)
private void LogException(string plugin, string action, Exception exception)
{
Guard.NotNull(plugin);
Guard.NotNull(action);
Guard.NotNull(exception);
exceptions.Add((plugin, action, exception));
}
@ -70,26 +106,6 @@ namespace Squidex.Infrastructure.Plugins
}
}
public void ConfigureBefore(IApplicationBuilder app)
{
Guard.NotNull(app);
foreach (var plugin in loadedPlugins.OfType<IWebPlugin>())
{
plugin.ConfigureBefore(app);
}
}
public void ConfigureAfter(IApplicationBuilder app)
{
Guard.NotNull(app);
foreach (var plugin in loadedPlugins.OfType<IWebPlugin>())
{
plugin.ConfigureAfter(app);
}
}
public void Log(ISemanticLog log)
{
Guard.NotNull(log);

4
backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj

@ -8,11 +8,9 @@
<DebugType>full</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FluentFTP" Version="28.0.0" />
<PackageReference Include="McMaster.NETCore.Plugins" Version="0.3.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.0.0" />

43
backend/src/Squidex/Pipeline/Plugins/PluginExtensions.cs

@ -6,7 +6,8 @@
// ==========================================================================
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@ -17,6 +18,12 @@ namespace Squidex.Pipeline.Plugins
{
public static class PluginExtensions
{
private static readonly AssemblyName[] SharedAssemblies =
Assembly.GetEntryAssembly()!
.GetReferencedAssemblies()
.Where(x => x.Name?.StartsWith("Squidex.", StringComparison.OrdinalIgnoreCase) == true)
.ToArray();
public static IMvcBuilder AddSquidexPlugins(this IMvcBuilder mvcBuilder, IConfiguration config)
{
var pluginManager = new PluginManager();
@ -27,25 +34,11 @@ namespace Squidex.Pipeline.Plugins
{
foreach (var path in options.Plugins)
{
var plugin = PluginLoaders.LoadPlugin(path);
if (plugin != null)
{
try
{
var pluginAssembly = plugin.LoadDefaultAssembly();
var pluginAssembly = pluginManager.Load(path, SharedAssemblies);
pluginAssembly.AddParts(mvcBuilder);
pluginManager.Add(path, pluginAssembly);
}
catch (Exception ex)
{
pluginManager.LogException(path, "LoadingAssembly", ex);
}
}
else
if (pluginAssembly != null)
{
pluginManager.LogException(path, "LoadingPlugin", new FileNotFoundException($"Cannot find plugin at {path}"));
pluginAssembly.AddParts(mvcBuilder);
}
}
}
@ -57,20 +50,6 @@ namespace Squidex.Pipeline.Plugins
return mvcBuilder;
}
public static void UsePluginsBefore(this IApplicationBuilder app)
{
var pluginManager = app.ApplicationServices.GetRequiredService<PluginManager>();
pluginManager.ConfigureBefore(app);
}
public static void UsePluginsAfter(this IApplicationBuilder app)
{
var pluginManager = app.ApplicationServices.GetRequiredService<PluginManager>();
pluginManager.ConfigureAfter(app);
}
public static void UsePlugins(this IApplicationBuilder app)
{
var pluginManager = app.ApplicationServices.GetRequiredService<PluginManager>();

1
backend/src/Squidex/Squidex.csproj

@ -38,7 +38,6 @@
<PackageReference Include="IdentityServer4" Version="3.0.2" />
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="3.0.2" />
<PackageReference Include="McMaster.NETCore.Plugins" Version="0.3.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="3.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="3.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.0.0" />

3
backend/src/Squidex/Startup.cs

@ -71,8 +71,6 @@ namespace Squidex
public void Configure(IApplicationBuilder app)
{
app.UsePluginsBefore();
app.UseSquidexHealthCheck();
app.UseSquidexRobotsTxt();
app.UseSquidexTracking();
@ -86,7 +84,6 @@ namespace Squidex
app.ConfigureIdentityServer();
app.ConfigureFrontend();
app.UsePluginsAfter();
app.UsePlugins();
}
}

Loading…
Cancel
Save