//******************************************************************************************************
// 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