//****************************************************************************************************** // AdapterLoader.cs - Gbtc // // Copyright © 2012, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may // not use this file except in compliance with the License. You may obtain a copy of the License at: // // http://www.opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 07/20/2009 - Pinal C. Patel // Generated original version of source code. // 08/06/2009 - Pinal C. Patel // Modified Dispose(boolean) to iterate through the adapter collection correctly. // 09/14/2009 - Stephen C. Wills // Added new header and license agreement. // 09/17/2009 - Pinal C. Patel // Modified ProcessAdapter() to instantiate types with a default public constructor only. // 12/10/2009 - Pinal C. Patel // Added new AdapterCreated event. // Implemented IProvideStatus interface. // Enhanced the implementation of Enabled property. // 09/23/2010 - Pinal C. Patel // Added adapter isolation capability to allow for loading qualified adapters in separate // application domain for isolated execution. // 10/01/2010 - Pinal C. Patel // Added adapter monitoring capability to monitor the resource utilization of adapters. // 03/08/2011 - Pinal C. Patel // Made IAdapter a requirement for using AdapterLoader for effectively managing serialized // adapter instances. // Added AdapterFileExtension and AdapterFileFormat properties to allow loading of binary and XML // serialized adapter instances. // Updated AdapterLoadException event to not provide the adapter's type since it might not be // available when processing serialized adapter instances. // 04/05/2011 - Pinal C. Patel // Modified deserializer class to use TypeName property instead of Type property to get the type // of the object being deserialized when deserializing from an XML file. // 04/14/2011 - Pinal C. Patel // Updated to use new serialization methods in GSF.Serialization class. // 05/11/2011 - Pinal C. Patel // Implemented IPersistSettings interface. // Changed the unit for AllowableProcessMemoryUsage and AllowableAdapterMemoryUsage properties // from bytes to megabytes. // 09/21/2011 - J. Ritchie Carroll // Added Mono implementation exception regions. // 11/23/2011 - J. Ritchie Carroll // Modified to support buffer optimized ISupportBinaryImage. // 12/06/2011 - Pinal C. Patel // Updated to instantiate a FileSystemWatcher object that watches for adapters only if needed // to avoid a issue introduced in .NET 4.0 that causes memory leak. // 12/13/2012 - Starlynn Danyelle Gilliam // Modified Header. // 01/29/2016 - Pinal C. Patel // Fixed a bug in ProcessAdapter(string) method that was always replacing the first adapter // in the adapter list instead of the adapter that was modified. // Updated Deserializer.Deserialize() method to get an exclusive lock on the adapter file prior to // processing it. // //****************************************************************************************************** using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Configuration; using System.Diagnostics; using System.IO; using System.Text; using System.Xml.Linq; using System.Xml.Serialization; using GSF.Collections; using GSF.Configuration; using GSF.IO; #if !MONO using GSF.Units; using System.Threading; #endif namespace GSF.Adapters { #region [ Enumerations ] /// /// Specifies the file format of the adapters to be loaded by the . /// public enum AdapterFileFormat { /// /// Adapters are s inside files (DLLs). /// Assembly, /// /// Adapters are binary serialized instances persisted to files. /// SerializedBin, /// /// Adapters are XML serialized instances persisted to files. /// SerializedXml } #endregion /// /// Represents a generic loader of adapters. /// /// of adapters to be loaded. /// /// This example show how to use the to isolate adapters in separate s and monitor their resource usage: /// /// using System; /// using System.Collections.Generic; /// using System.Text; /// using System.Threading; /// using GSF; /// using GSF.Adapters; /// using GSF.Security.Cryptography; /// /// class Program /// { /// static AdapterLoader<PublishAdapterBase> s_adapterLoader; /// /// static void Main(string[] args) /// { /// // Enable app domain resource monitoring. /// AppDomain.MonitoringIsEnabled = true; /// /// // Load adapters that mimic data publishing. /// s_adapterLoader = new AdapterLoader<PublishAdapterBase>(); /// s_adapterLoader.IsolateAdapters = true; /// s_adapterLoader.MonitorAdapters = true; /// s_adapterLoader.AdapterFileExtension = "*.exe"; /// s_adapterLoader.AllowableProcessMemoryUsage = 200; /// s_adapterLoader.AllowableProcessProcessorUsage = 50; /// s_adapterLoader.AllowableAdapterMemoryUsage = 100; /// s_adapterLoader.AllowableAdapterProcessorUsage = 25; /// s_adapterLoader.AdapterLoaded += OnAdapterLoaded; /// s_adapterLoader.AdapterUnloaded += OnAdapterUnloaded; /// s_adapterLoader.AdapterResourceUsageExceeded += OnAdapterResourceUsageExceeded; /// s_adapterLoader.Initialize(); /// /// // Shutdown. /// Console.ReadLine(); /// s_adapterLoader.Dispose(); /// } /// /// static void OnAdapterLoaded(object sender, EventArgs<PublishAdapterBase> e) /// { /// Console.WriteLine("{0} has been loaded\r\n", e.Argument.GetType().Name); /// } /// /// static void OnAdapterUnloaded(object sender, EventArgs<PublishAdapterBase> e) /// { /// Console.WriteLine("{0} has been unloaded\r\n", e.Argument.GetType().Name); /// } /// /// static void OnAdapterResourceUsageExceeded(object sender, GSF.EventArgs<PublishAdapterBase> e) /// { /// Console.WriteLine("{0} status:", e.Argument.Name); /// Console.WriteLine(e.Argument.Status); /// /// // Remove the adapter in order to reclaim the resources used by it. /// lock (s_adapterLoader.Adapters) /// { /// s_adapterLoader.Adapters.Remove(e.Argument); /// } /// } /// } /// /// /// <summary> /// /// Base adapter class. /// /// </summary> /// public abstract class PublishAdapterBase : Adapter /// { /// public PublishAdapterBase() /// { /// Data = new List<byte[]>(); /// } /// /// public List<byte[]> Data { get; set; } /// /// public override void Initialize() /// { /// base.Initialize(); /// new Thread(Publish).Start(); /// } /// /// protected abstract void Publish(); /// } /// /// /// <summary> /// /// Adapter that does not manage memory well. /// /// </summary> /// public class PublishAdapterA : PublishAdapterBase /// { /// protected override void Publish() /// { /// while (true) /// { /// for (int i = 0; i < 10000; i++) /// { /// Data.Add(new byte[10]); /// } /// Thread.Sleep(100); /// } /// } /// } /// /// /// <summary> /// /// Adapter that uses the processor in excess. /// /// </summary> /// public class PublishAdapterB : PublishAdapterBase /// { /// protected override void Publish() /// { /// string text = string.Empty; /// System.Random random = new System.Random(); /// while (true) /// { /// for (int i = 0; i < 10; i++) /// { /// for (int j = 0; j < 4; j++) /// { /// text += (char)random.Next(256); /// } /// Data.Add(Encoding.ASCII.GetBytes(text.Encrypt("C1pH3r", CipherStrength.Aes256)).BlockCopy(0, 1)); /// } /// Thread.Sleep(10); /// } /// } /// } /// /// /// /// public class AdapterLoader : ISupportLifecycle, IProvideStatus, IPersistSettings where T : class, IAdapter { #region [ Members ] // Nested Types private class Deserializer : MarshalByRefObject { public T Deserialize(string adapterFile, AdapterFileFormat adapterFormat) { // Attempt binary deserialization. if (adapterFormat == AdapterFileFormat.SerializedBin) { FilePath.WaitForReadLockExclusive(adapterFile); return Serialization.Deserialize(File.ReadAllBytes(adapterFile), SerializationFormat.Binary); } if (adapterFormat != AdapterFileFormat.SerializedXml) throw new NotSupportedException(); // Attempt XML deserialization. FilePath.WaitForReadLockExclusive(adapterFile); XDocument xml = XDocument.Parse(File.ReadAllText(adapterFile)); if ((object)xml.Root != null) { XElement type = xml.Root.Element("TypeName"); if ((object)type != null) { // Type element required for looking up the adapter's type. XmlSerializer serializer = new XmlSerializer(Type.GetType(type.Value)); using (StringReader reader = new StringReader(xml.ToString())) { return (T)serializer.Deserialize(reader); } } } throw new InvalidOperationException("TypeName element is missing in the XML"); } } // Constants /// /// Specifies the default value for the property. /// public const string DefaultAdapterDirectory = ""; /// /// Specifies the default value for the property. /// public const string DefaultAdapterFileExtension = "*.dll"; /// /// Specifies the default value for the property. /// public const AdapterFileFormat DefaultAdapterFileFormat = AdapterFileFormat.Assembly; /// /// Specifies the default value for the property. /// public const bool DefaultWatchForAdapters = true; /// /// Specifies the default value for the property. /// public const bool DefaultIsolateAdapters = false; /// /// Specifies the default value for the property. /// public const bool DefaultMonitorAdapters = false; /// /// Specifies the default value for the property /// public const double DefaultAllowableProcessMemoryUsage = 500; /// /// Specifies the default value for the property. /// public const double DefaultAllowableProcessProcessorUsage = 75; /// /// Specifies the default value for the property. /// public const double DefaultAllowableAdapterMemoryUsage = 100; /// /// Specifies the default value for the property. /// public const double DefaultAllowableAdapterProcessorUsage = 50; // Events /// /// Occurs when a new adapter is found and instantiated. /// /// /// is the adapter that was created. /// public event EventHandler> AdapterCreated; /// /// Occurs when a new adapter is loaded to the list. /// /// /// is the adapter that was loaded. /// public event EventHandler> AdapterLoaded; /// /// Occurs when an existing adapter is unloaded from the list. /// /// /// is the adapter that was unloaded. /// public event EventHandler> AdapterUnloaded; /// /// Occurs when an adapter has exceeded either the or . /// /// is the adapter that exceeded the allowable system resource utilization. public event EventHandler> AdapterResourceUsageExceeded; /// /// Occurs when an is encountered when loading an adapter. /// /// /// is the encountered when loading the adapter. /// public event EventHandler> AdapterLoadException; /// /// Occurs when an is encountered while executing a queued operation on one the . /// /// /// is the adapter on which the operation was being executed.
/// is the encountered when executing an operation on the adapter. ///
public event EventHandler> OperationExecutionException; /// /// Occurs when the class has been disposed. /// public event EventHandler Disposed; // Fields private string m_adapterDirectory; private string m_adapterFileExtension; private AdapterFileFormat m_adapterFileFormat; private bool m_watchForAdapters; private bool m_isolateAdapters; private bool m_monitorAdapters; private double m_allowableProcessMemoryUsage; private double m_allowableProcessProcessorUsage; private double m_allowableAdapterMemoryUsage; private double m_allowableAdapterProcessorUsage; private bool m_persistSettings; private string m_settingsCategory; private readonly ObservableCollection m_adapters; private SafeFileWatcher m_adapterWatcher; private readonly AsyncQueue m_operationQueue; private readonly Dictionary m_enabledStates; private bool m_enabled; private bool m_disposed; private bool m_initialized; #if !MONO private Thread m_adapterMonitoringThread; #endif #endregion #region [ Constructors ] /// /// Initializes a new instance of the class. /// public AdapterLoader() { m_adapterDirectory = DefaultAdapterDirectory; m_adapterFileExtension = DefaultAdapterFileExtension; m_adapterFileFormat = DefaultAdapterFileFormat; m_watchForAdapters = DefaultWatchForAdapters; m_isolateAdapters = DefaultIsolateAdapters; m_monitorAdapters = DefaultMonitorAdapters; m_allowableProcessMemoryUsage = DefaultAllowableProcessMemoryUsage; m_allowableProcessProcessorUsage = DefaultAllowableProcessProcessorUsage; m_allowableAdapterMemoryUsage = DefaultAllowableAdapterMemoryUsage; m_allowableAdapterProcessorUsage = DefaultAllowableAdapterProcessorUsage; m_settingsCategory = GetType().Name; m_adapters = new ObservableCollection(); m_adapters.CollectionChanged += Adapters_CollectionChanged; m_operationQueue = new AsyncQueue(); m_enabledStates = new Dictionary(); m_operationQueue.ProcessItemFunction = ExecuteOperation; } /// /// Releases the unmanaged resources before the is reclaimed by . /// ~AdapterLoader() { Dispose(false); } #endregion #region [ Properties ] /// /// Gets or sets the directory where are located. /// /// /// When an empty string is assigned to , are loaded from the directory where application is executing. /// /// The value being assigned is a null string. public string AdapterDirectory { get { return m_adapterDirectory; } set { if ((object)value == null) throw new ArgumentNullException(nameof(value)); m_adapterDirectory = value; } } /// /// Gets or sets the file extension of the . /// /// The value being set is a null or empty string. public string AdapterFileExtension { get { return m_adapterFileExtension; } set { if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(value)); m_adapterFileExtension = value; } } /// /// Gets or sets the file format of the . /// public AdapterFileFormat AdapterFileFormat { get { return m_adapterFileFormat; } set { m_adapterFileFormat = value; } } /// /// Gets or sets a boolean value that indicates whether the is to be monitored for new . /// public bool WatchForAdapters { get { return m_watchForAdapters; } set { m_watchForAdapters = value; } } /// /// Gets or sets a boolean value that indicates whether are loaded in separate for isolated execution. /// public bool IsolateAdapters { get { return m_isolateAdapters; } set { m_isolateAdapters = value; } } /// /// Gets or sets a boolean value that indicates whether resource utilization of executing in isolation is to be monitored. /// /// /// /// Use , , and /// properties to configure how adapter resource utilization is to be monitored. /// /// /// This option is ignored under Mono deployments. /// /// public bool MonitorAdapters { get { return m_monitorAdapters; } set { m_monitorAdapters = value; } } /// /// Gets or sets the memory in megabytes the current process is allowed to use before the internal monitoring process starts looking for offending . /// /// The value being assigned is zero or negative. public double AllowableProcessMemoryUsage { get { return m_allowableProcessMemoryUsage; } set { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value)); m_allowableProcessMemoryUsage = value; } } /// /// Gets or sets the processor time in % the current process is allowed to use before the internal monitoring process starts looking for offending . /// /// The value being assigned is zero or negative. public double AllowableProcessProcessorUsage { get { return m_allowableProcessProcessorUsage; } set { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value)); m_allowableProcessProcessorUsage = value; } } /// /// Gets or sets the memory in megabytes the are allowed to use before being flagged as offending by the internal monitoring process. /// /// The value being assigned is zero or negative. public double AllowableAdapterMemoryUsage { get { return m_allowableAdapterMemoryUsage; } set { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value)); m_allowableAdapterMemoryUsage = value; } } /// /// Gets or sets the processor time in % the are allowed to use before being flagged as offending by the internal monitoring process. /// /// The value being assigned is zero or negative. public double AllowableAdapterProcessorUsage { get { return m_allowableAdapterProcessorUsage; } set { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value)); m_allowableAdapterProcessorUsage = value; } } /// /// Gets or sets a boolean value that indicates whether settings are to be saved to the config file. /// public bool PersistSettings { get { return m_persistSettings; } set { m_persistSettings = value; } } /// /// Gets or sets the category under which settings are to be saved to the config file if the property is set to true. /// /// The value being assigned is a null or empty string. public string SettingsCategory { get { return m_settingsCategory; } set { if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(value)); m_settingsCategory = value; } } /// /// Gets or sets a boolean value that indicates whether the is currently enabled. /// public bool Enabled { get { return m_enabled; } set { if (!m_initialized && value) { // Initialize if uninitialized when enabled. Initialize(); } else { // Change current state of various components. if (value && !m_enabled) { // Enable - restore previously saved state. if ((object)m_adapterWatcher != null) m_adapterWatcher.EnableRaisingEvents = m_enabledStates[m_adapterWatcher.GetType()]; if (m_operationQueue != null) m_operationQueue.Enabled = m_enabledStates[m_operationQueue.GetType()]; bool savedState; lock (m_adapters) { foreach (T adapter in m_adapters) { if (m_enabledStates.TryGetValue(adapter.GetType(), out savedState)) adapter.Enabled = savedState; } } } else if (!value && m_enabled) { // Disable - save current state and disable. SaveCurrentState(); if ((object)m_adapterWatcher != null) m_adapterWatcher.EnableRaisingEvents = false; if (m_operationQueue != null) m_operationQueue.Enabled = false; lock (m_adapters) { foreach (T adapter in m_adapters) { adapter.Enabled = false; } } } } m_enabled = value; } } /// /// Gets a flag that indicates whether the object has been disposed. /// public bool IsDisposed { get { return m_disposed; } } /// /// Gets the unique identifier of the . /// public string Name { get { return GetType().Name; } } /// /// Gets the descriptive status of the . /// public string Status { get { StringBuilder status = new StringBuilder(); status.Append(" Adapters type: "); status.Append(typeof(T).Name); status.AppendLine(); status.Append(" Adapters directory: "); status.Append(FilePath.TrimFileName(m_adapterDirectory, 30)); status.AppendLine(); status.Append(" Adapter isolation: "); status.Append(m_isolateAdapters ? "Enabled" : "Disabled"); status.AppendLine(); status.Append(" Adapter monitoring: "); status.Append(m_monitorAdapters ? "Enabled" : "Disabled"); status.AppendLine(); status.Append(" Dynamic loading: "); status.Append((object)m_adapterWatcher != null && m_adapterWatcher.EnableRaisingEvents ? "Enabled" : "Disabled"); status.AppendLine(); status.Append(" Operations queue: "); status.Append(m_operationQueue.Enabled ? "Enabled" : "Disabled"); status.AppendLine(); lock (m_adapters) { status.Append(" Total loaded adapters: "); status.Append(m_adapters.Count); status.AppendLine(); foreach (T adapter in m_adapters) { status.AppendLine(); status.Append(" Adapter name: "); status.Append(adapter.Name); status.AppendLine(); status.Append(adapter.Status); } } return status.ToString(); } } /// /// Gets a list of adapters loaded from the . /// public IList Adapters { get { return m_adapters; } } /// /// Gets the object watching for new adapter assemblies added at runtime. /// protected SafeFileWatcher AdapterWatcher { get { return m_adapterWatcher; } } /// /// Gets the object to be used for queuing operations to be executed on . /// protected AsyncQueue OperationQueue { get { return m_operationQueue; } } #endregion #region [ Methods ] /// /// Releases all the resources used by the . /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Initializes the . /// public virtual void Initialize() { Initialize(null); } /// /// Initializes the . /// /// Collection of adapter s from which are to be created. public virtual void Initialize(IEnumerable adapterTypes) { if (!m_initialized) { // Load settings from the config file. LoadSettings(); // Process adapters. m_adapterDirectory = FilePath.GetAbsolutePath(m_adapterDirectory); if (m_adapterFileFormat == AdapterFileFormat.Assembly) { if ((object)adapterTypes == null) adapterTypes = typeof(T).LoadImplementations(Path.Combine(m_adapterDirectory, m_adapterFileExtension)); foreach (Type type in adapterTypes) { ProcessAdapter(type); } } else { foreach (string adapterFile in Directory.GetFiles(m_adapterDirectory, m_adapterFileExtension)) { ProcessAdapter(adapterFile); } } // Watch for adapters. if (m_watchForAdapters) { m_adapterWatcher = new SafeFileWatcher(); m_adapterWatcher.Path = m_adapterDirectory; m_adapterWatcher.EnableRaisingEvents = true; m_adapterWatcher.Created += AdapterWatcher_Events; m_adapterWatcher.Changed += AdapterWatcher_Events; m_adapterWatcher.Deleted += AdapterWatcher_Events; } // Save current state. SaveCurrentState(); // Start adapter monitoring. #if !MONO if (m_monitorAdapters && m_isolateAdapters) { // Following must be true in order for us to monitor adapter resources: // - Adapter monitoring is enabled. // - Adapter isolation is enabled. m_adapterMonitoringThread = new Thread(MonitorAdapterResources); m_adapterMonitoringThread.Start(); } #endif m_enabled = true; // Mark as enabled. m_initialized = true; // Initialize only once. } } /// /// Saves settings to the config file if the property is set to true. /// /// has a value of null or empty string. public void SaveSettings() { if (m_persistSettings) { // Ensure that settings category is specified. if (string.IsNullOrEmpty(m_settingsCategory)) throw new ConfigurationErrorsException("SettingsCategory property has not been set."); // Save settings under the specified category. ConfigurationFile config = ConfigurationFile.Current; CategorizedSettingsElementCollection settings = config.Settings[m_settingsCategory]; settings["AdapterDirectory", true].Update(m_adapterDirectory); settings["AdapterFileExtension", true].Update(m_adapterFileExtension); settings["AdapterFileFormat", true].Update(m_adapterFileFormat); settings["WatchForAdapters", true].Update(m_watchForAdapters); settings["IsolateAdapters", true].Update(m_isolateAdapters); settings["MonitorAdapters", true].Update(m_monitorAdapters); settings["AllowableProcessMemoryUsage", true].Update(m_allowableProcessMemoryUsage); settings["AllowableProcessProcessorUsage", true].Update(m_allowableProcessProcessorUsage); settings["AllowableAdapterMemoryUsage", true].Update(m_allowableAdapterMemoryUsage); settings["AllowableAdapterProcessorUsage", true].Update(m_allowableAdapterProcessorUsage); config.Save(); } } /// /// Loads saved settings from the config file if the property is set to true. /// /// has a value of null or empty string. public void LoadSettings() { if (m_persistSettings) { // Ensure that settings category is specified. if (string.IsNullOrEmpty(m_settingsCategory)) throw new ConfigurationErrorsException("SettingsCategory property has not been set."); // Load settings from the specified category. ConfigurationFile config = ConfigurationFile.Current; CategorizedSettingsElementCollection settings = config.Settings[m_settingsCategory]; settings.Add("AdapterDirectory", m_adapterDirectory, "Directory where adapters are located."); settings.Add("AdapterFileExtension", m_adapterFileExtension, "Extension of the adapter files."); settings.Add("AdapterFileFormat", m_adapterFileFormat, "Format (Assembly; SerializedBin; SerializedXml) of the adapter files."); settings.Add("WatchForAdapters", m_watchForAdapters, "True to monitor adapter directory for new adapters, otherwise False."); settings.Add("IsolateAdapters", m_isolateAdapters, "True to isolate adapters in separate application domains, otherwise False."); settings.Add("MonitorAdapters", m_monitorAdapters, "True to monitor adapter resource utilization when isolated in separate application domains, otherwise False."); settings.Add("AllowableProcessMemoryUsage", m_allowableProcessMemoryUsage, "Memory in megabytes the current process is allowed to use before the internal monitoring process starts looking for offending adapters."); settings.Add("AllowableProcessProcessorUsage", m_allowableProcessProcessorUsage, "Processor time in % the current process is allowed to use before the internal monitoring process starts looking for offending adapters."); settings.Add("AllowableAdapterMemoryUsage", m_allowableAdapterMemoryUsage, "Memory in megabytes the adapters are allowed to use before being flagged as offending by the internal monitoring process."); settings.Add("AllowableAdapterProcessorUsage", m_allowableAdapterProcessorUsage, "Processor time in % the adapters are allowed to use before being flagged as offending by the internal monitoring process."); AdapterDirectory = settings["AdapterDirectory"].ValueAs(m_adapterDirectory); AdapterFileExtension = settings["AdapterFileExtension"].ValueAs(m_adapterFileExtension); AdapterFileFormat = settings["AdapterFileFormat"].ValueAs(m_adapterFileFormat); WatchForAdapters = settings["WatchForAdapters"].ValueAs(m_watchForAdapters); IsolateAdapters = settings["IsolateAdapters"].ValueAs(m_isolateAdapters); MonitorAdapters = settings["MonitorAdapters"].ValueAs(m_monitorAdapters); AllowableProcessMemoryUsage = settings["AllowableProcessMemoryUsage"].ValueAs(m_allowableProcessMemoryUsage); AllowableProcessProcessorUsage = settings["AllowableProcessProcessorUsage"].ValueAs(m_allowableProcessProcessorUsage); AllowableAdapterMemoryUsage = settings["AllowableAdapterMemoryUsage"].ValueAs(m_allowableAdapterMemoryUsage); AllowableAdapterProcessorUsage = settings["AllowableAdapterProcessorUsage"].ValueAs(m_allowableAdapterProcessorUsage); } } /// /// Processes the by deserializing it. /// /// Path to the adapter file to be deserialized. protected virtual void ProcessAdapter(string adapterFile) { T adapter = default(T); try { Deserializer deserializer; if (m_isolateAdapters) { // Adapter isolation is enabled. AppDomain domain = AppDomain.CreateDomain(Guid.NewGuid().ToString()); deserializer = (Deserializer)domain.CreateInstanceAndUnwrap(typeof(Deserializer).Assembly.FullName, typeof(Deserializer).FullName); } else { // Adapter isolation is not enabled. deserializer = new Deserializer(); } // Deserialize adapter instance. adapter = deserializer.Deserialize(adapterFile, m_adapterFileFormat); SetAdapterFilePath(adapter, adapterFile); OnAdapterCreated(adapter); // Add adapter and notify via event. lock (m_adapters) { int adapterIndex = m_adapters.IndexOf(currentAdapter => currentAdapter.HostFile == adapterFile); if (adapterIndex < 0) m_adapters.Add(adapter); // Add adapter. else m_adapters[adapterIndex] = adapter; // Update adapter. } } catch (Exception ex) { OnAdapterLoadException(adapter, ex); } } /// /// Processes the by instantiating it. /// /// of the adapter to be instantiated. protected virtual void ProcessAdapter(Type adapterType) { T adapter = default(T); try { if ((object)adapterType.GetConstructor(Type.EmptyTypes) != null) { // Instantiate adapter instance. if (m_isolateAdapters && (adapterType.IsMarshalByRef || adapterType.IsSerializable)) { // Adapter isolation is enabled and possible. AppDomain domain = AppDomain.CreateDomain(Guid.NewGuid().ToString()); adapter = (T)domain.CreateInstanceAndUnwrap(adapterType.Assembly.FullName, adapterType.FullName); } else { // Load adapter in the current executing domain. adapter = (T)(Activator.CreateInstance(adapterType)); } OnAdapterCreated(adapter); // Add adapter and notify via event. lock (m_adapters) { m_adapters.Add(adapter); } } } catch (Exception ex) { OnAdapterLoadException(adapter, ex); } } /// /// Monitors the resource utilization of . /// /// /// monitoring is not enabled under Mono deployments. /// protected virtual void MonitorAdapterResources() { #if !MONO // Enable individual application domain resource tracking if it is enabled. if (!AppDomain.MonitoringIsEnabled) AppDomain.MonitoringIsEnabled = true; Process currentProcess; List offendingAdapters = new List(); while (!m_disposed) { Thread.Sleep(5000); // Don't interfere if process memory and processor utilization is in check. currentProcess = Process.GetCurrentProcess(); if (GetMemoryUsage(currentProcess) / SI2.Mega <= m_allowableProcessMemoryUsage && GetProcessorUsage(currentProcess) <= m_allowableProcessProcessorUsage) continue; if (Monitor.TryEnter(m_adapters)) { try { // JRC - This is not a great idea... //// Force a full blocking GC so the memory usage of adapters is updated. //GC.Collect(); foreach (T adapter in m_adapters) { if (adapter.MemoryUsage / SI2.Mega > m_allowableAdapterMemoryUsage || adapter.ProcessorUsage > m_allowableAdapterProcessorUsage) offendingAdapters.Add(adapter); } } finally { Monitor.Exit(m_adapters); } // Notify about the offending adapters. foreach (T adapter in offendingAdapters) { OnAdapterResourceUsageExceeded(adapter); } offendingAdapters.Clear(); } } #endif } /// /// Gets the memory usage in bytes of the specified . /// /// The whose memory usage is to be determined. /// Memory usage in bytes of the specified . protected virtual double GetMemoryUsage(Process process) { return process.WorkingSet64; } /// /// Gets the % processor usage of the specified . /// /// The whose processor usage is to be determined. /// Processor usage in % of the specified . protected virtual double GetProcessorUsage(Process process) { return process.TotalProcessorTime.TotalSeconds / Ticks.ToSeconds(DateTime.Now - process.StartTime) / Environment.ProcessorCount * 100; } /// /// Executes an operation on the with the given . /// /// Adapter on which an operation is to be executed. /// Data to be used when executing an operation. /// Always protected virtual void ExecuteAdapterOperation(T adapter, object data) { throw new NotSupportedException(); } /// /// Releases the unmanaged resources used by the and optionally releases the managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (!m_disposed) { try { // This will be done regardless of whether the object is finalized or disposed. if (disposing) { // This will be done only when the object is disposed by calling Dispose(). SaveSettings(); if ((object)m_enabledStates != null) m_enabledStates.Clear(); if ((object)m_adapterWatcher != null) { m_adapterWatcher.EnableRaisingEvents = false; m_adapterWatcher.Created -= AdapterWatcher_Events; m_adapterWatcher.Changed -= AdapterWatcher_Events; m_adapterWatcher.Deleted -= AdapterWatcher_Events; m_adapterWatcher.Dispose(); } if ((object)m_adapters != null) { lock (m_adapters) { for (int i = 0; i < m_adapters.Count; i += 0) { m_adapters.RemoveAt(0); } } m_adapters.CollectionChanged -= Adapters_CollectionChanged; } } } finally { m_enabled = false; // Mark as disabled. m_disposed = true; // Prevent duplicate dispose. if (Disposed != null) Disposed(this, EventArgs.Empty); } } } /// /// Raises the event. /// /// Adapter instance to send to event. protected virtual void OnAdapterCreated(T adapter) { // Raise the event. if ((object)AdapterCreated != null) AdapterCreated(this, new EventArgs(adapter)); } /// /// Raises the event. /// /// Adapter instance to send to event. protected virtual void OnAdapterLoaded(T adapter) { // Initialize the adapter. if ((object)adapter != null) adapter.Initialize(); // Raise the event. if ((object)AdapterLoaded != null) AdapterLoaded(this, new EventArgs(adapter)); } /// /// Raises the event. /// /// Adapter instance to send to event. protected virtual void OnAdapterUnloaded(T adapter) { // Dispose the adapter. try { if ((object)adapter != null) adapter.Dispose(); } catch { } // Unload the adapter domain. try { if ((object)adapter != null && !adapter.Domain.IsDefaultAppDomain()) AppDomain.Unload(adapter.Domain); } catch { } // Raise the event. if ((object)AdapterUnloaded != null) AdapterUnloaded(this, new EventArgs(adapter)); } /// /// Raises the event. /// /// Adapter instance to send to event. protected virtual void OnAdapterResourceUsageExceeded(T adapter) { // Raise the event. if ((object)AdapterResourceUsageExceeded != null) AdapterResourceUsageExceeded(this, new EventArgs(adapter)); } /// /// Raises the event. /// /// Adapter instance that caused the . /// to send to event. protected virtual void OnAdapterLoadException(T adapter, Exception exception) { // Remove the adapter if it exists. if ((object)adapter != null) { lock (m_adapters) { if (m_adapters.Contains(adapter)) m_adapters.Remove(adapter); } } // Raise the event. if ((object)AdapterLoadException != null) AdapterLoadException(this, new EventArgs(exception)); } /// /// Raises the event. /// /// Adapter instance to send to event. /// to send to event. protected virtual void OnOperationExecutionException(T adapter, Exception exception) { // Raise the event. if ((object)OperationExecutionException != null) OperationExecutionException(this, new EventArgs(adapter, exception)); } private static string GetAdapterFilePath(T adapter) { if ((object)adapter != null) return adapter.HostFile; return null; } private static bool SetAdapterFilePath(T adapter, string adapterFile) { if ((object)adapter != null) { adapter.HostFile = adapterFile; return true; } return false; } private void SaveCurrentState() { if ((object)m_adapterWatcher != null) m_enabledStates[m_adapterWatcher.GetType()] = m_adapterWatcher.EnableRaisingEvents; if (m_operationQueue != null) m_enabledStates[m_operationQueue.GetType()] = m_operationQueue.Enabled; lock (m_adapters) { foreach (T adapter in m_adapters) { m_enabledStates[adapter.GetType()] = adapter.Enabled; } } } private void ExecuteOperation(object data) { lock (m_adapters) { foreach (T adapter in m_adapters) { try { ExecuteAdapterOperation(adapter, data); } catch (Exception ex) { OnOperationExecutionException(adapter, ex); } } } } private void AdapterWatcher_Events(object sender, FileSystemEventArgs e) { if (FilePath.IsFilePatternMatch(m_adapterFileExtension, FilePath.GetFileName(e.FullPath), true)) { // Updated file has a matching extension. if (e.ChangeType == WatcherChangeTypes.Deleted) { // Remove loaded adapter. lock (m_adapters) { int adapterIndex = m_adapters.IndexOf(currentAdapter => currentAdapter.HostFile == e.FullPath); if (adapterIndex >= 0) m_adapters.RemoveAt(adapterIndex); } } else { // Process new adapter. if (m_adapterFileFormat == AdapterFileFormat.Assembly) { foreach (Type type in typeof(T).LoadImplementations(e.FullPath)) { ProcessAdapter(type); } } else { ProcessAdapter(e.FullPath); } } } } private void Adapters_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { switch (e.Action) { case NotifyCollectionChangedAction.Add: // Notify additions. foreach (T adapter in e.NewItems) { OnAdapterLoaded(adapter); } break; case NotifyCollectionChangedAction.Remove: // Notify deletions. foreach (T adapter in e.OldItems) { OnAdapterUnloaded(adapter); } break; case NotifyCollectionChangedAction.Replace: // Notify deletions. foreach (T adapter in e.OldItems) { OnAdapterUnloaded(adapter); } // Notify additions. foreach (T adapter in e.NewItems) { OnAdapterLoaded(adapter); } break; } } #endregion } }