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 DTS.Common.Core.Config;
using DTS.Common.Utilities.Logging;
// ReSharper disable RedundantJumpStatement
// ReSharper disable UnusedTypeParameter
// ReSharper disable UnusedMember.Local
// ReSharper disable UnusedParameter.Local
namespace DTS.Common.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
///
public AggregateCatalog PluginCatalog { get; set; }
///
/// MEF container
///
private CompositionContainer PluginContainer { get; set; }
///
/// Thread lock
///
private static readonly object THREAD_LOCK = new object();
///
/// Constructor that intializes catalog and container
///
private PluginManager(string appPath)
{
// Create MEF catalog
PluginCatalog = new AggregateCatalog();
DTSConfig.DTSConfigInit(appPath);
// Get plugin folder from App.Config;
var pcsh = (PluginConfigSectionHandler)DTSConfig.GetSection("DTS.Common.Core.PluginLib.Config"); //DTS.Common.Core.PluginLib.Config
if (pcsh == null)
{
APILogger.Log("## Unable to retrieve plugin config data from DTS.Config (DTS.Common.Core.PluginLib.Config)");
throw new Exception("Unable to retrieve plugin config data from DTS.config (DTS.Common.Core.PluginLib.Config)");
}
foreach (FilterHashElement element in pcsh.HashKeys)
{
try
{
var info = new DirectoryInfo(element.Value);
if (!info.Exists)
{
APILogger.Log($"## Plugin directory does not exist: {element.Value}");
throw new IOException($"Plugin directory does not exist: {element.Value}");
}
PluginCatalog.Catalogs.Add(new DirectoryCatalog(element.Value));
}
catch (Exception ex)
{
APILogger.Log(ex);
throw;
}
}
// Create MEF container
PluginContainer = new CompositionContainer(PluginCatalog);
foreach (var catalog in PluginCatalog.Catalogs)
{
var directoryCatalog = catalog as DirectoryCatalog;
if (directoryCatalog == null)
{
APILogger.Log("## directory catalog is null");
continue;
}
var pluginDir = directoryCatalog.FullPath;
var files = new DirectoryInfo(pluginDir).GetFiles("*.dll");
foreach (var assembly in files.Where(f => !f.Name.StartsWith("DTS.Common") && !f.Name.StartsWith("C1") && !f.Name.StartsWith("Xceed")))
{
var assemblyName = pluginDir + @"\" + assembly.Name;
//var assemblyName = $"{pluginDir}{assembly.Name}";
// verift assembly exists and matches requested paramaters
if (!File.Exists(assemblyName))
{
APILogger.Log($"## File Not Found!: {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
try
{
var asm = Assembly.LoadFrom(assemblyName);
}
catch (Exception ex)
{
APILogger.Log("## Failed to load assembly:", ex);
throw;
}
}
}
}
public List GetPluginList() where T : class
{
var assemblyList = new List();
var manager = GetPluginManager(string.Empty);
foreach (var catalog in manager.PluginCatalog.Catalogs)
{
var directoryCatalog = catalog as DirectoryCatalog;
if (directoryCatalog == null) continue;
assemblyList.AddRange(directoryCatalog.Parts.Select(part => ReflectionModelServices.GetPartType(part).Value.Assembly).Distinct().ToList());
}
return assemblyList;
}
///
/// 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(string.Empty).PluginContainer.GetExport();
return export?.Value;
}
///
/// 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(configPluginSetting);
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) continue;
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(string.Empty);
return manager.PluginContainer.GetExports();
}
///
/// Returns static singleton reference to plugin manager class
///
/// plugin class
public static PluginManager GetPluginManager(string appPath)
{
if (!string.IsNullOrWhiteSpace(appPath))
{
var directoryInfo = new DirectoryInfo(appPath);
}
lock (THREAD_LOCK)
{
if (_pluginManager != null)
{
foreach (var catalog in _pluginManager.PluginCatalog.Catalogs)
{
var directoryCatalog = catalog as DirectoryCatalog;
if (directoryCatalog == null) continue;
}
}
return _pluginManager ?? (_pluginManager = new PluginManager(appPath));
}
}
///
/// 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)
{
APILogger.Log("## null directoryCatalog");
continue;
}
var pluginDir = directoryCatalog.FullPath;
// get name of assembly
var baseName = args.Name.Split(new[] { ',' })[0];
var assemblyName = $"{pluginDir}\\{baseName}.dll";
// verift assembly exists and matches requested paramaters
if (!File.Exists(assemblyName))
{
APILogger.Log($"## assembly does not exist: {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)
{
APILogger.Log("## assembly info is incomplete");
continue;
}
// Load assembly
try
{
var asm = Assembly.LoadFrom(assemblyName);
return asm;
}
catch (Exception ex)
{
APILogger.Log("## failed to load", ex);
throw;
}
}
return null;
}
}
}