init
This commit is contained in:
27
Common/DTS.Common.Core/PluginLib/PluginConfig.cs
Normal file
27
Common/DTS.Common.Core/PluginLib/PluginConfig.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using DTS.Common.Core.Config;
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace DTS.Common.Core.PluginLib
|
||||
{
|
||||
/// <summary>
|
||||
/// Static class used to store plugin configuration info
|
||||
/// </summary>
|
||||
public static class PluginConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// name of setting to look in config for equipment database
|
||||
/// </summary>
|
||||
public const string DTSPlugins = "DTSPlugins";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// concatenate plugin name from app setting with class exporter name for DTS plugins
|
||||
/// </summary>
|
||||
/// <param name="setting"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetDTSPluginsSetting(string setting)
|
||||
{
|
||||
return DTSConfig.GetAppSetting(DTSPlugins) + "." + setting;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
Common/DTS.Common.Core/PluginLib/PluginConfigData.cs
Normal file
14
Common/DTS.Common.Core/PluginLib/PluginConfigData.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace DTS.Common.Core.PluginLib
|
||||
{
|
||||
/// <summary>
|
||||
/// Support class that stores data de-serialized from DatPro.Core.PluginLib.Config in App.Config configuration section
|
||||
/// </summary>
|
||||
[XmlRoot(ElementName = "DatPro.Core.PluginLib.Config")]
|
||||
public class PluginConfigData
|
||||
{
|
||||
[XmlArrayItem("Folder")]
|
||||
public string[] PluginFolders;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using System.Configuration;
|
||||
|
||||
namespace DTS.Common.Core.PluginLib
|
||||
{
|
||||
/// <summary>
|
||||
/// Support class that parses data from DTS.Common.Core.PluginLib.Config configuration section
|
||||
/// </summary>
|
||||
public class PluginConfigSectionHandler : ConfigurationSection
|
||||
{
|
||||
[ConfigurationProperty("PluginFolders")]
|
||||
public FilterHashKeyCollection HashKeys => (FilterHashKeyCollection)base["PluginFolders"];
|
||||
}
|
||||
|
||||
[ConfigurationCollection(typeof(FilterHashElement))]
|
||||
public class FilterHashKeyCollection : ConfigurationElementCollection
|
||||
{
|
||||
protected override ConfigurationElement CreateNewElement()
|
||||
{
|
||||
return new FilterHashElement();
|
||||
}
|
||||
|
||||
protected override object GetElementKey(ConfigurationElement element)
|
||||
{
|
||||
return ((FilterHashElement)element).Key;
|
||||
}
|
||||
|
||||
public FilterHashElement this[int idx] => (FilterHashElement)BaseGet(idx);
|
||||
}
|
||||
|
||||
public class FilterHashElement : ConfigurationElement
|
||||
{
|
||||
[ConfigurationProperty("key", DefaultValue = "", IsKey = true, IsRequired = true)]
|
||||
public string Key
|
||||
{
|
||||
get => (string)base["key"];
|
||||
set => base["key"] = value;
|
||||
}
|
||||
|
||||
[ConfigurationProperty("value", DefaultValue = "", IsKey = false, IsRequired = false)]
|
||||
public string Value
|
||||
{
|
||||
get => (string)base["value"];
|
||||
set => base["value"] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
272
Common/DTS.Common.Core/PluginLib/PluginManager.cs
Normal file
272
Common/DTS.Common.Core/PluginLib/PluginManager.cs
Normal file
@@ -0,0 +1,272 @@
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// PluginManager class to manage MEF plugins
|
||||
/// NOTE: This class has to be thread safe
|
||||
/// </summary>
|
||||
public class PluginManager
|
||||
{
|
||||
// reference to singleton plugin manager
|
||||
private static PluginManager _pluginManager;
|
||||
|
||||
/// <summary>
|
||||
/// MEF catalog
|
||||
/// </summary>
|
||||
public AggregateCatalog PluginCatalog { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// MEF container
|
||||
/// </summary>
|
||||
private CompositionContainer PluginContainer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Thread lock
|
||||
/// </summary>
|
||||
private static readonly object THREAD_LOCK = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that intializes catalog and container
|
||||
/// </summary>
|
||||
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<Assembly> GetPluginList<T>() where T : class
|
||||
{
|
||||
var assemblyList = new List<Assembly>();
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns MEF plugin of type t
|
||||
/// Throws error if no type exported by plugin or more than one plugin has exported type
|
||||
/// </summary>
|
||||
/// <typeparam name="T">type to get</typeparam>
|
||||
/// <returns>reference to type t from MEF plugin</returns>
|
||||
public static T GetPlugin<T>() where T : class
|
||||
{
|
||||
var export = GetPluginManager(string.Empty).PluginContainer.GetExport<T>();
|
||||
return export?.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns MEF plugin of type T from a collection of plugins that export the same type
|
||||
/// </summary>
|
||||
/// <typeparam name="T">exported type implemented by potentially multiple plugins</typeparam>
|
||||
/// <param name="configPluginSetting">string that tells us which specific plugin we want</param>
|
||||
/// <returns>type T</returns>
|
||||
public static T GetPlugin<T>(string configPluginSetting) where T : class
|
||||
{
|
||||
var result = new Lazy<T>();
|
||||
var manager = GetPluginManager(configPluginSetting);
|
||||
var plugins = manager.PluginContainer.GetExports<T>();
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns list of MEF plugins that export type t
|
||||
/// </summary>
|
||||
/// <typeparam name="T">type to get</typeparam>
|
||||
/// <returns>list of references to type t from MEF plugins</returns>
|
||||
public static IEnumerable<Lazy<T>> GetPlugins<T>() where T : class
|
||||
{
|
||||
var manager = GetPluginManager(string.Empty);
|
||||
return manager.PluginContainer.GetExports<T>();
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns static singleton reference to plugin manager class
|
||||
/// </summary>
|
||||
/// <returns>plugin class</returns>
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This event triggered if system can't find plugin dependancies
|
||||
/// CurrentDomain_AssemblyResolve can search through list of directories instead of just one
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user