Hermod
A cross-platform, modular and fully GDPR-compliant email archival solution!
Loading...
Searching...
No Matches
Hermod.PluginFramework.PluginRegistry Class Reference

Handles the loading, unloading, and general management of plugins. More...

Package Functions

void LoadPlugin (FileInfo pluginFile)
 Loads one or plugins from an Assembly on disk.
 
bool IsAssembly (FileInfo pluginFile)
 Gets a value indicating whether or not a give file is a valid assembly or not.
 
bool ContainsPlugins (Assembly assembly, out List< Type > pluginTypes)
 Gets a value indicating whether or not a given assembly contains members inheriting from IPlugin.
 
void RegisterPlugin (ref Assembly assembly, Type type)
 Internally registers an IPlugin class and calls the IPlugin.OnLoad(Serilog.ILogger) method once loaded.
 
void DeregisterPlugin (Assembly assembly, Type type)
 
List< ICommandGetAllCommands ()
 Gets a list containing all ICommand instances known to the application at the current time.
 
void AddSubscription (IPlugin plugin, string topic)
 Adds a plugin to a topic subscription list.
 
void RemoveSubscription (IPlugin plugin, string topic)
 Removes a plugin from a topic subscription list.
 

Properties

ILogger? AppLogger = null [get, set]
 
static PluginRegistry Instance [get]
 Gets the current instance of this object.
 
List< IPluginPlugins [get]
 Gets a list of all loaded IPlugin instances.
 
Dictionary< Assembly, Dictionary< Type, IPlugin > > LoadedAssemblies = new Dictionary<Assembly, Dictionary<Type, IPlugin>>() [get]
 A Dictionary<Assembly, List<IPlugin>> containing all loaded assemblies and plugins contained within.
 
List< PluginDelegatorPluginDelegators = new List<PluginDelegator>() [get]
 
List< ICommand >? BuiltInCommands = null [get, set]
 
IPluginLastRegisteredPlugin = null [get, set]
 Gets or sets the last IPlugin to be registered.
 
Dictionary< string, List< IPlugin > > TopicSubscriptions = new Dictionary<string, List<IPlugin>>() [get]
 The topic subscription list.
 

Private Member Functions

 PluginRegistry ()
 
void ConfigManager_ConfigLoaded (object? sender, ConfigLoadedEventArgs e)
 
void ConfigManager_ConfigChanged (object? sender, ConfigChangedEventArgs e)
 
Assembly? CurrentDomain_AssemblyResolve (object? sender, ResolveEventArgs args)
 

Static Private Attributes

static ? PluginRegistry _instance
 

Detailed Description

Handles the loading, unloading, and general management of plugins.

This class knows which plugins are loaded at any given time, can all any commands provided by the plugin and also fire events.

Definition at line 21 of file PluginRegistry.cs.

Constructor & Destructor Documentation

◆ PluginRegistry()

Hermod.PluginFramework.PluginRegistry.PluginRegistry ( )
inlineprivate

Definition at line 26 of file PluginRegistry.cs.

26 {
27 ConfigManager.Instance.ConfigChanged += ConfigManager_ConfigChanged;
28 ConfigManager.Instance.ConfigLoaded += ConfigManager_ConfigLoaded;
29 AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
30 }
void ConfigManager_ConfigLoaded(object? sender, ConfigLoadedEventArgs e)
void ConfigManager_ConfigChanged(object? sender, ConfigChangedEventArgs e)
Assembly? CurrentDomain_AssemblyResolve(object? sender, ResolveEventArgs args)

References Hermod.PluginFramework.PluginRegistry.ConfigManager_ConfigChanged(), Hermod.PluginFramework.PluginRegistry.ConfigManager_ConfigLoaded(), and Hermod.PluginFramework.PluginRegistry.CurrentDomain_AssemblyResolve().

Member Function Documentation

◆ AddSubscription()

void Hermod.PluginFramework.PluginRegistry.AddSubscription ( IPlugin  plugin,
string  topic 
)
inlinepackage

Adds a plugin to a topic subscription list.

Parameters
pluginThe plugin to add.
topicThe topic to subscribe to.
Exceptions
MalformedTopicException

Definition at line 23 of file PluginRegistry.Delegation.cs.

23 {
24 if (!TopicIsValid(ref topic)) {
25 throw new MalformedTopicException(topic, "The topic is invalid!");
26 }
27
28 if (!TopicSubscriptions.ContainsKey(topic)) {
29 TopicSubscriptions.Add(topic, new List<IPlugin>());
30 }
31
32 var subList = TopicSubscriptions[topic];
33 if (subList.Contains(plugin)) { return; }
34
35 subList.Add(plugin);
36 }
Dictionary< string, List< IPlugin > > TopicSubscriptions
The topic subscription list.

References Hermod.PluginFramework.PluginRegistry.TopicSubscriptions.

Referenced by Hermod.PluginFramework.PluginDelegator.SubscribeTopics().

◆ ConfigManager_ConfigChanged()

void Hermod.PluginFramework.PluginRegistry.ConfigManager_ConfigChanged ( object?  sender,
ConfigChangedEventArgs  e 
)
inlineprivate

Definition at line 193 of file PluginRegistry.cs.

193 {
194 foreach (var plugin in Plugins) {
195 plugin.OnConfigChanged(e);
196 }
197 }
List< IPlugin > Plugins
Gets a list of all loaded IPlugin instances.

References Hermod.PluginFramework.PluginRegistry.Plugins.

Referenced by Hermod.PluginFramework.PluginRegistry.PluginRegistry().

◆ ConfigManager_ConfigLoaded()

void Hermod.PluginFramework.PluginRegistry.ConfigManager_ConfigLoaded ( object?  sender,
ConfigLoadedEventArgs  e 
)
inlineprivate

Definition at line 187 of file PluginRegistry.cs.

187 {
188 foreach (var plugin in Plugins) {
189 plugin.OnConfigLoaded();
190 }
191 }

References Hermod.PluginFramework.PluginRegistry.Plugins.

Referenced by Hermod.PluginFramework.PluginRegistry.PluginRegistry().

◆ ContainsPlugins()

bool Hermod.PluginFramework.PluginRegistry.ContainsPlugins ( Assembly  assembly,
out List< Type >  pluginTypes 
)
inlinepackage

Gets a value indicating whether or not a given assembly contains members inheriting from IPlugin.

Parameters
assemblyThe Assembly to check in.
pluginTypesOut var; the list of Types inheriting from IPlugin contained within the assembly.
Returns

Definition at line 116 of file PluginRegistry.cs.

116 {
117 pluginTypes = new List<Type>();
118
119 foreach (var type in assembly.GetTypes()) {
120 var isSubclass = type.IsSubclassOf(typeof(IPlugin)) || type.IsSubclassOf(typeof(Plugin));
121 var attribute = type.GetCustomAttribute<PluginAttribute>();
122
123 if (isSubclass && attribute is not null) {
124 pluginTypes.Add(type);
125 }
126 }
127
128 return pluginTypes.Count > 0;
129 }

Referenced by Hermod.PluginFramework.PluginRegistry.LoadPlugin().

◆ CurrentDomain_AssemblyResolve()

Assembly? Hermod.PluginFramework.PluginRegistry.CurrentDomain_AssemblyResolve ( object?  sender,
ResolveEventArgs  args 
)
inlineprivate

Definition at line 199 of file PluginRegistry.cs.

199 {
200 AppLogger?.Warning("Failed to resolve assembly {argname}", args.Name);
201 var loadedAssembly = AppDomain.CurrentDomain.GetAssemblies()?.Where(x => x.FullName == args.RequestingAssembly?.FullName)?.FirstOrDefault();
202 if (loadedAssembly is not null) {
203 return loadedAssembly;
204 }
205
206 var folderPath = Path.GetDirectoryName(args.RequestingAssembly?.Location);
207 var asmName = new AssemblyName(args.Name);
208 if (folderPath is null || asmName.Name is null) {
209 AppLogger?.Error("Could not find assembly in {location}", folderPath);
210 return default;
211 }
212
213 var rawPath = Path.Combine(folderPath, asmName.Name);
214 var asmPath = rawPath + ".dll";
215
216 if (!File.Exists(asmPath)) {
217 AppLogger?.Warning("Could not find {asmPath}! Is it an executable?", asmPath);
218 asmPath = rawPath + ".exe";
219 if (!File.Exists(asmPath)) {
220 AppLogger?.Warning("Could not find {asmPath}!", asmPath);
221 return default;
222 }
223 }
224
225 return Assembly.LoadFrom(asmPath);
226 }

References Hermod.PluginFramework.PluginRegistry.AppLogger.

Referenced by Hermod.PluginFramework.PluginRegistry.PluginRegistry().

◆ DeregisterPlugin()

void Hermod.PluginFramework.PluginRegistry.DeregisterPlugin ( Assembly  assembly,
Type  type 
)
inlinepackage

Definition at line 171 of file PluginRegistry.cs.

171 {
172 if (LoadedAssemblies is null || LoadedAssemblies?.Count == 0) { return; }
173
174 var pluginAssembly = LoadedAssemblies?.FirstOrDefault(a => a.Key == assembly);
175 if (pluginAssembly is null) { return; }
176
177 pluginAssembly?.Value.Remove(type);
178 try {
179 PluginDelegators?.Remove(PluginDelegators.First(pd => pd.Plugin?.GetType() == type));
180 } catch { /* if an exception occurs, no PluginDelegator instance was loaded. */ }
181
182 if (pluginAssembly?.Value.Count == 0) {
183 LoadedAssemblies?.Remove(assembly);
184 }
185 }
Dictionary< Assembly, Dictionary< Type, IPlugin > > LoadedAssemblies
A Dictionary<Assembly, List<IPlugin>> containing all loaded assemblies and plugins contained within.
List< PluginDelegator > PluginDelegators

References Hermod.PluginFramework.PluginRegistry.LoadedAssemblies, and Hermod.PluginFramework.PluginRegistry.PluginDelegators.

Referenced by Hermod.PluginFramework.PluginRegistry.RegisterPlugin().

◆ GetAllCommands()

List< ICommand > Hermod.PluginFramework.PluginRegistry.GetAllCommands ( )
inlinepackage

Gets a list containing all ICommand instances known to the application at the current time.

Returns
A list of all known commands.

Definition at line 232 of file PluginRegistry.cs.

232 {
233 var list = BuiltInCommands ?? new List<ICommand>();
234
235 Plugins.ForEach(p => list.AddRange(p.PluginCommands));
236
237 return list;
238 }

References Hermod.PluginFramework.PluginRegistry.BuiltInCommands, and Hermod.PluginFramework.PluginRegistry.Plugins.

Referenced by Hermod.Hermod.GetAutocompletion(), and Hermod.Hermod.HandleDisplayCommandHelp().

◆ IsAssembly()

bool Hermod.PluginFramework.PluginRegistry.IsAssembly ( FileInfo  pluginFile)
inlinepackage

Gets a value indicating whether or not a give file is a valid assembly or not.

Parameters
pluginFileThe file to check for validity.
Returns
true
if the file contains a valid assembly.

Definition at line 98 of file PluginRegistry.cs.

98 {
99 try {
100 using (var fStream = pluginFile.OpenRead())
101 using (var peReader = new PEReader(fStream)) {
102 if (!peReader.HasMetadata) { return false; }
103
104 var metaDataReader = peReader.GetMetadataReader();
105 return metaDataReader.IsAssembly;
106 }
107 } catch (Exception) { return false; }
108 }

Referenced by Hermod.PluginFramework.PluginRegistry.LoadPlugin().

◆ LoadPlugin()

void Hermod.PluginFramework.PluginRegistry.LoadPlugin ( FileInfo  pluginFile)
inlinepackage

Loads one or plugins from an Assembly on disk.

Parameters
pluginFileThe file from which to load plugins.
Exceptions
FileNotFoundExceptionIf the file does not exist.
NotAPluginExceptionIf the given file is not a valid Assembly or does not contain instances off IPlugin or Plugin

Definition at line 74 of file PluginRegistry.cs.

74 {
75 AppLogger?.Information($"Attempting to load { pluginFile.FullName }...");
76 if (!pluginFile.Exists) { throw new FileNotFoundException("The file does not exist.", pluginFile.FullName); }
77 if (!IsAssembly(pluginFile)) { throw new NotAPluginException(pluginFile); }
78
79 AppLogger?.Information("Loading assembly...");
80 var assembly = Assembly.LoadFile(pluginFile.FullName);
81
82 List<Type> pluginTypes;
83 if (!ContainsPlugins(assembly, out pluginTypes)) {
84 throw new NotAPluginException(pluginFile);
85 }
86
87 AppLogger?.Information($"Found { pluginTypes.Count } plugins in assembly...");
88 foreach (var pluginType in pluginTypes) {
89 RegisterPlugin(ref assembly, pluginType);
90 }
91 }
bool ContainsPlugins(Assembly assembly, out List< Type > pluginTypes)
Gets a value indicating whether or not a given assembly contains members inheriting from IPlugin.
void RegisterPlugin(ref Assembly assembly, Type type)
Internally registers an IPlugin class and calls the IPlugin.OnLoad(Serilog.ILogger) method once loade...
bool IsAssembly(FileInfo pluginFile)
Gets a value indicating whether or not a give file is a valid assembly or not.

References Hermod.PluginFramework.PluginRegistry.AppLogger, Hermod.PluginFramework.PluginRegistry.ContainsPlugins(), Hermod.PluginFramework.PluginRegistry.IsAssembly(), and Hermod.PluginFramework.PluginRegistry.RegisterPlugin().

Referenced by Hermod.Hermod.HandleLoadPlugin(), and Hermod.Hermod.StartUp().

◆ RegisterPlugin()

void Hermod.PluginFramework.PluginRegistry.RegisterPlugin ( ref Assembly  assembly,
Type  type 
)
inlinepackage

Internally registers an IPlugin class and calls the IPlugin.OnLoad(Serilog.ILogger) method once loaded.

Parameters
assemblyThe Assembly in which the plugin resides.
typeThe Type of the plugin.

Definition at line 136 of file PluginRegistry.cs.

136 {
137 if (!LoadedAssemblies.ContainsKey(assembly)) {
138 AppLogger?.Debug($"Plugin assembly seems to be new; registering { assembly.GetName().FullName } for the first time!");
139 LoadedAssemblies.Add(assembly, new Dictionary<Type, IPlugin>());
140 } else if (LoadedAssemblies[assembly].ContainsKey(type)) {
141 AppLogger?.Error($"The plugin { type.Name } has already been loaded as a plugin! Silently ignoring...");
142 return;
143 }
144
145 try {
146 var plugin = Activator.CreateInstance(type) as IPlugin;
147 if (plugin is null) {
148 throw new PluginLoadException(type.Name);
149 }
150
151 LoadedAssemblies[assembly].Add(type, plugin);
152 var pluginDelegator = new PluginDelegator(plugin);
153 PluginDelegators.Add(pluginDelegator);
154
155 plugin = LoadedAssemblies[assembly][type];
156 plugin.OnLoad(pluginDelegator);
157
158 LastRegisteredPlugin = plugin;
159 AppLogger?.Information($"Loaded plugin { plugin.PluginName } { plugin.PluginVersion.ToString() }");
160 } catch (Exception ex) {
161 AppLogger?.Error("Failed to load plugin from assembly!");
162 AppLogger?.Error($"Error: { ex.Message }");
163
164 AppLogger?.Warning("Deregisterung plugin...");
165 DeregisterPlugin(assembly, type);
166
167 throw;
168 }
169 }
void DeregisterPlugin(Assembly assembly, Type type)
IPlugin? LastRegisteredPlugin
Gets or sets the last IPlugin to be registered.

References Hermod.PluginFramework.PluginRegistry.AppLogger, Hermod.PluginFramework.PluginRegistry.DeregisterPlugin(), Hermod.PluginFramework.PluginRegistry.LastRegisteredPlugin, Hermod.PluginFramework.PluginRegistry.LoadedAssemblies, and Hermod.PluginFramework.PluginRegistry.PluginDelegators.

Referenced by Hermod.PluginFramework.PluginRegistry.LoadPlugin().

◆ RemoveSubscription()

void Hermod.PluginFramework.PluginRegistry.RemoveSubscription ( IPlugin  plugin,
string  topic 
)
inlinepackage

Removes a plugin from a topic subscription list.

Parameters
pluginThe plugin to remove from the list.
topicThe topic to unsubscribe the plugin from.
Exceptions
MalformedTopicExceptionIf the passed topic is invalid or malformed.

Definition at line 44 of file PluginRegistry.Delegation.cs.

44 {
45 if (!TopicIsValid(ref topic)) {
46 throw new MalformedTopicException(topic, "The topic is invalid!");
47 }
48
49 if (!TopicSubscriptions.ContainsKey(topic)) { return; }
50
51 var subList = TopicSubscriptions[topic];
52 if (!subList.Contains(plugin)) { return; }
53
54 subList.Remove(plugin);
55 }

References Hermod.PluginFramework.PluginRegistry.TopicSubscriptions.

Member Data Documentation

◆ _instance

? PluginRegistry Hermod.PluginFramework.PluginRegistry._instance
staticprivate

Definition at line 32 of file PluginRegistry.cs.

Property Documentation

◆ AppLogger

ILogger? Hermod.PluginFramework.PluginRegistry.AppLogger = null
getset

◆ BuiltInCommands

List<ICommand>? Hermod.PluginFramework.PluginRegistry.BuiltInCommands = null
getsetpackage

Definition at line 61 of file PluginRegistry.cs.

61{ get; set; } = null;

Referenced by Hermod.PluginFramework.PluginRegistry.GetAllCommands().

◆ Instance

◆ LastRegisteredPlugin

IPlugin? Hermod.PluginFramework.PluginRegistry.LastRegisteredPlugin = null
getsetpackage

Gets or sets the last IPlugin to be registered.

Definition at line 66 of file PluginRegistry.cs.

66{ get; set; } = null;

Referenced by Hermod.Hermod.HandleLoadPlugin(), and Hermod.PluginFramework.PluginRegistry.RegisterPlugin().

◆ LoadedAssemblies

Dictionary<Assembly, Dictionary<Type, IPlugin> > Hermod.PluginFramework.PluginRegistry.LoadedAssemblies = new Dictionary<Assembly, Dictionary<Type, IPlugin>>()
getpackage

A Dictionary<Assembly, List<IPlugin>> containing all loaded assemblies and plugins contained within.

Definition at line 57 of file PluginRegistry.cs.

57{ get; } = new Dictionary<Assembly, Dictionary<Type, IPlugin>>();

Referenced by Hermod.PluginFramework.PluginRegistry.DeregisterPlugin(), and Hermod.PluginFramework.PluginRegistry.RegisterPlugin().

◆ PluginDelegators

List<PluginDelegator> Hermod.PluginFramework.PluginRegistry.PluginDelegators = new List<PluginDelegator>()
getpackage

Definition at line 59 of file PluginRegistry.cs.

59{ get; } = new List<PluginDelegator>();

Referenced by Hermod.PluginFramework.PluginRegistry.DeregisterPlugin(), and Hermod.PluginFramework.PluginRegistry.RegisterPlugin().

◆ Plugins

List<IPlugin> Hermod.PluginFramework.PluginRegistry.Plugins
getpackage

Gets a list of all loaded IPlugin instances.

Definition at line 43 of file PluginRegistry.cs.

43 {
44 get {
45 List<IPlugin> plugins = new List<IPlugin>();
46 foreach (var pluginList in LoadedAssemblies.Select(x => x.Value.Select(y => y.Value))) {
47 plugins.AddRange(pluginList);
48 }
49
50 return plugins;
51 }
52 }

Referenced by Hermod.PluginFramework.PluginRegistry.ConfigManager_ConfigChanged(), Hermod.PluginFramework.PluginRegistry.ConfigManager_ConfigLoaded(), Hermod.PluginFramework.PluginRegistry.GetAllCommands(), Hermod.Hermod.HandleDisplayHelp(), and Hermod.Hermod.TryGetCommand().

◆ TopicSubscriptions

Dictionary<string, List<IPlugin> > Hermod.PluginFramework.PluginRegistry.TopicSubscriptions = new Dictionary<string, List<IPlugin>>()
getpackage

The topic subscription list.

Definition at line 15 of file PluginRegistry.Delegation.cs.

15{ get; } = new Dictionary<string, List<IPlugin>>();

Referenced by Hermod.PluginFramework.PluginRegistry.AddSubscription(), and Hermod.PluginFramework.PluginRegistry.RemoveSubscription().


The documentation for this class was generated from the following files: