- fadd-15373.zip
- trunk
- dlls
- xunit.dll
- Examples
- Plugins
- ExampleApplication.Shared
- ExampleApplication
- ExamplePlugin.Shared
- ExamplePlugin
- Fadd.Globalization.Yaml
- fadd.sln
- fadd
|
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Security;
using System.Security.Policy;
namespace Fadd.Plugins
{
/// <summary>
/// This class is responsible of loading all modules in a system.
/// </summary>
public class PluginManager<T> : IApplication where T : class, IPlugin
{
private const string DefaultPath = "*.dll";
private readonly IApplication _pluginManager;
private readonly List<T> _plugins = new List<T>();
private string _pluginPath = DefaultPath;
private static readonly Evidence DefaultEvidence = new Evidence(
new object[] {new Zone(SecurityZone.Internet)},
new object[] {});
/// <summary>
/// Initializes a new instance of the <see cref="PluginManager<T>"/> class.
/// </summary>
/// <param name="application">Interface exposed to plugins.</param>
public PluginManager(IApplication application)
{
_pluginManager = application;
}
/// <summary>
/// Initializes a new instance of the <see cref="PluginManager<T>"/> class.
/// </summary>
public PluginManager() : this(null)
{
}
/// <summary>
/// Access a loaded plugin.
/// </summary>
/// <value>plugin if found; otherwise null.</value>
public T this[string pluginName]
{
get
{
foreach (T plugin in _plugins)
{
if (string.Compare(plugin.PluginName, pluginName, true) == 0)
return plugin;
}
return null;
}
}
/// <summary>
/// Folder in which plugins resides.
/// </summary>
/// <remarks>Can also contain wildcards</remarks>
/// <example>
/// <code>
/// pluginMgr.Path = "plugins\\plugin*.dll";
/// </code>
/// </example>
public string PluginPath
{
get { return _pluginPath; }
set { _pluginPath = value; }
}
/// <summary>
/// Called when a plugin assembly is about to be loaded.
/// </summary>
/// <param name="typeInfo">Information about the assembly.</param>
/// <returns>Evidence that the <see cref="Assembly"/> should be loaded with.</returns>
/// <remarks>
/// The assembly is used to determine which security settings a plugin <see cref="Assembly"/> should be loaded with.
/// </remarks>
protected virtual Evidence GetEvidence(PluginTypeInfo typeInfo)
{
EvidenceRequestedEventArgs eventArgs = new EvidenceRequestedEventArgs(typeInfo);
EvidenceRequested(this, eventArgs);
return eventArgs.Evidence ?? DefaultEvidence;
}
/// <summary>
/// Loads modules with the correct <see cref="Evidence"/> and correct protection level.
/// </summary>
protected void LoadModules()
{
PluginFinder finder = new PluginFinder();
try
{
IList<Type> type = new List<Type>();
type.Add(typeof (T));
finder.Find(_pluginPath, type);
}
catch (ReflectionTypeLoadException err)
{
Console.WriteLine(err);
foreach (Exception e in err.LoaderExceptions)
Console.WriteLine(e);
}
catch (TypeLoadException err)
{
Console.WriteLine(err);
}
foreach (PluginTypeInfo typeInfo in finder.Plugins)
{
Evidence evidence = GetEvidence(typeInfo);
Assembly assembly = Assembly.Load(typeInfo.Location, evidence);
foreach (Type type in typeInfo.Types)
{
if (!typeof (T).IsAssignableFrom(type))
continue;
T loadedPlugin = (T) Activator.CreateInstance(type);
OnPluginLoaded(loadedPlugin, assembly, typeInfo);
_plugins.Add(loadedPlugin);
}
}
}
/// <summary>
/// Loads all modules that can be loaded (their dependencies have been loaded.)
/// </summary>
/// <param name="loaded"></param>
/// <param name="dependent"></param>
/// <returns></returns>
/// <remarks>Call this method as long as it returns true.</remarks>
private bool LoadModules(IDictionary<string, T> loaded, IDictionary<string, T> dependent)
{
foreach (KeyValuePair<string, T> pair in dependent)
{
bool dependenciesMissing = false;
foreach (string dependency in pair.Value.Dependencies)
{
if (loaded.ContainsKey(dependency))
continue;
dependenciesMissing = true;
break;
}
if (dependenciesMissing)
continue;
dependent.Remove(pair.Value.PluginName);
loaded.Add(pair.Value.PluginName, pair.Value);
StartPlugin(pair.Value);
return true;
}
return false;
}
/// <summary>
/// Called when a plugin have been loaded.
/// </summary>
/// <param name="plugin">Module being loaded.</param>
/// <param name="assembly">Assembly that the plugin exist in.</param>
/// <param name="typeInfo">information about the plugin</param>
/// <remarks>Plugin have not been started yet.</remarks>
protected virtual void OnPluginLoaded(T plugin, Assembly assembly, PluginTypeInfo typeInfo)
{
PluginLoaded(this, new PluginLoadedHandlerEventArgs<T>(plugin, assembly, typeInfo));
}
/// <summary>
/// Loads all modules
/// </summary>
public void Start()
{
LoadModules();
Dictionary<string, T> loadedModules = new Dictionary<string, T>(),
dependencyModules = new Dictionary<string, T>();
// Put all modules in the dependent side
foreach (T plugin in _plugins)
dependencyModules.Add(plugin.PluginName, plugin);
// Load a plugin at a time (since foreach breaks when something is removed)
#pragma warning disable 642
while (LoadModules(loadedModules, dependencyModules)) ;
#pragma warning restore 642
foreach (KeyValuePair<string, T> pair in dependencyModules)
throw new InvalidOperationException("Module '" + pair.Key + "' is missing dependency.");
}
/// <summary>
/// Calls start in a <see cref="IPlugin"/>.
/// </summary>
/// <param name="plugin">plugin to start</param>
/// <exception cref="ArgumentNullException"></exception>
protected virtual void StartPlugin(T plugin)
{
Check.Require(plugin, "plugin");
ApplicationRequestedEventArgs<T> args = new ApplicationRequestedEventArgs<T>(plugin);
ApplicationRequested(this, args);
plugin.Start(args.Application ?? _pluginManager ?? this);
}
/// <summary>
/// A plugin have been loaded into the system
/// </summary>
public event PluginLoadedHandler<T> PluginLoaded = delegate { };
/// <summary>
/// A plugin have been started.
/// </summary>
public event PluginStartedHandler<T> PluginStarted = delegate { };
/// <summary>
/// An plugin assembly is about to be loaded and the system needs to know which <see cref="Evidence"/> the plugin assembly should be loaded with.
/// </summary>
/// <remarks>
/// Plugins are loaded into the Internet zone per default.
/// </remarks>
public event EvidenceRequestedHandler EvidenceRequested = delegate { };
/// <summary>
/// Called when a plugin is about to be started and we need to get the application interface
/// that should be used when starting the plugin.
/// </summary>
public event ApplicationRequestedHandler<T> ApplicationRequested = delegate{};
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.