using System.Reflection; using System.Runtime.Loader; using ZymonicGateway.Contracts; using ZymonicGateway.Options; namespace ZymonicGateway.Services; public class ZymonicGatewayPluginCatalog { private readonly List _plugins = new(); public IReadOnlyList Plugins => _plugins; public void Register(IZymonicGatewayPlugin plugin) { _plugins.Add(plugin); } public void LoadFromOptions(ZymonicGatewayOptions options, ILogger logger) { foreach (var assemblyPath in GetAssemblyPaths(options).Distinct(StringComparer.OrdinalIgnoreCase)) { try { var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath); RegisterPluginsFromAssembly(assembly, logger); } catch (Exception ex) { logger.LogError(ex, "Failed to load ZymonicGateway plugin assembly {AssemblyPath}", assemblyPath); } } } private void RegisterPluginsFromAssembly(Assembly assembly, ILogger logger) { foreach (var pluginType in GetLoadableTypes(assembly) .Where(type => !type.IsAbstract && typeof(IZymonicGatewayPlugin).IsAssignableFrom(type))) { if (Activator.CreateInstance(pluginType) is IZymonicGatewayPlugin plugin) { Register(plugin); logger.LogInformation("Loaded ZymonicGateway plugin {PluginName} from {AssemblyName}", plugin.Name, assembly.GetName().Name); } } } private static IEnumerable GetLoadableTypes(Assembly assembly) { try { return assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { return ex.Types.OfType(); } } private static IEnumerable GetAssemblyPaths(ZymonicGatewayOptions options) { foreach (var assemblyPath in options.PluginAssemblies.Where(path => !string.IsNullOrWhiteSpace(path))) { yield return Path.GetFullPath(assemblyPath); } var pluginDirectory = Path.GetFullPath(options.PluginDirectory); if (!Directory.Exists(pluginDirectory)) { yield break; } foreach (var assemblyPath in Directory.EnumerateFiles(pluginDirectory, "*.dll", SearchOption.TopDirectoryOnly)) { yield return assemblyPath; } } }