using System; using System.Collections.Generic; using System.Reflection; using System.IO; using System.ComponentModel.Composition.Hosting; using System.ComponentModel.Composition.ReflectionModel; using System.Linq; using DataPro.Core.Config; namespace DataPro.Core.PluginLib { /// /// PluginManager class to manage MEF plugins /// NOTE: This class has to be thread safe /// public class PluginManager { // reference to singleton plugin manager private static PluginManager _pluginManager; /// /// MEF catalog /// private AggregateCatalog PluginCatalog { get; set; } /// /// MEF container /// private CompositionContainer PluginContainer { get; set; } /// /// Thread lock /// private static readonly object ThreadLock = new object(); /// /// Constructor that intializes catalog and container /// private PluginManager() { // Create MEF catalog PluginCatalog = new AggregateCatalog(); //PluginCatalog = new AggregateCatalog(new AssemblyCatalog(typeof(MediaTypeNames.Application).Assembly)); // Get plugin folder from App.Config; var pcsh = (PluginConfigSectionHandler)DataProConfig.GetSection("DataPro.Core.PluginLib.Config"); if (pcsh == null) throw new Exception("Unable to retrieve plugin config data from DataPro.config (DataPro.Core.PluginLib.Config)"); foreach (FilterHashElement element in pcsh.HashKeys) { //Why "absolete path"? //if (!Path.IsPathRooted(element.Value)) throw new IOException(string.Format("Error in DataPro.Config. {0} must be absolete path", element.Value)); var info = new DirectoryInfo(element.Value); if (!info.Exists) throw new IOException(string.Format("Plugin directory does not exist: {0}", element.Value)); PluginCatalog.Catalogs.Add(new DirectoryCatalog(element.Value)); } // Create MEF container PluginContainer = new CompositionContainer(PluginCatalog); foreach (var catalog in PluginCatalog.Catalogs) { var directoryCatalog = catalog as DirectoryCatalog; if (directoryCatalog == null) continue; var pluginDir = directoryCatalog.FullPath; var files = (new DirectoryInfo(pluginDir)).GetFiles("*.dll"); foreach (var assembly in files) { var assemblyName = string.Format("{0}{1}", pluginDir, assembly.Name); // verift assembly exists and matches requested paramaters if (!File.Exists(assemblyName)) continue; var info = AssemblyName.GetAssemblyName(assemblyName); // NOTE: sometimes args.Name only has short assembly name, sometimes full name with public key and version //if (info == null || (info.FullName != args.Name && (info.Name != args.Name))) //{ // continue; //} // Load assembly var asm = Assembly.LoadFrom(assemblyName); if (asm == null) continue; } } } public List GetPluginList() where T : class { var manager = GetPluginManager(); foreach (var catalog in manager.PluginCatalog.Catalogs) { var directoryCatalog = catalog as DirectoryCatalog; if (directoryCatalog == null) continue; return directoryCatalog.Parts.Select(part => ReflectionModelServices.GetPartType(part).Value.Assembly).Distinct().ToList(); } //return export != null ? export.Value : null; return null; } /// /// Returns MEF plugin of type t /// Throws error if no type exported by plugin or more than one plugin has exported type /// /// type to get /// reference to type t from MEF plugin public static T GetPlugin() where T : class { var export = GetPluginManager().PluginContainer.GetExport(); return export != null ? export.Value : null; } /// /// Returns MEF plugin of type T from a collection of plugins that export the same type /// /// exported type implemented by potentially multiple plugins /// string that tells us which specific plugin we want /// type T public static T GetPlugin(string configPluginSetting) where T : class { var result = new Lazy(); var manager = GetPluginManager(); var plugins = manager.PluginContainer.GetExports(); //loop through plugins returned and get the specific one we're looking for foreach (var item in plugins) { if (item.Value.ToString() == configPluginSetting) { result = item; break; } } return result.Value; } /// /// Returns list of MEF plugins that export type t /// /// type to get /// list of references to type t from MEF plugins public static IEnumerable> GetPlugins() where T : class { var manager = GetPluginManager(); return manager.PluginContainer.GetExports(); } /// /// Returns static singleton reference to plugin manager class /// /// plugin class public static PluginManager GetPluginManager() { lock (ThreadLock) { return _pluginManager ?? (_pluginManager = new PluginManager()); } } /// /// This event triggered if system can't find plugin dependancies /// CurrentDomain_AssemblyResolve can search through list of directories instead of just one /// /// /// /// private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { foreach (var catalog in PluginCatalog.Catalogs) { var directoryCatalog = catalog as DirectoryCatalog; if (directoryCatalog == null) continue; var pluginDir = directoryCatalog.FullPath; // get name of assembly var baseName = args.Name.Split(new[] { ',' })[0]; var assemblyName = string.Format("{0}\\{1}.dll", pluginDir, baseName); // verift assembly exists and matches requested paramaters if (!File.Exists(assemblyName)) continue; var info = AssemblyName.GetAssemblyName(assemblyName); // NOTE: sometimes args.Name only has short assembly name, sometimes full name with public key and version if (info == null || (info.FullName != args.Name && (info.Name != args.Name))) { continue; } // Load assembly var asm = Assembly.LoadFrom(assemblyName); if (asm == null) continue; return asm; } return null; } } }