//******************************************************************************************************// ServiceHostBase.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:// ----------------------------------------------------------------------------------------------------// 08/20/2010 - J. Ritchie Carroll// Generated original version of source code.// 01/30/2011 - Pinal C. Patel// Updated to subscribe to new ProcessException event of MultiDestinationExporter class.// 04/21/2011 - Timothy M. Yardley / Erich A. Heine (UIUC)// Implemented an optional, but enabled by default, non-broadcast method of directly routed// measurement distribution as a system performance optimization.// 12/20/2012 - Starlynn Danyelle Gilliam// Modified Header.////****************************************************************************************************** using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Diagnostics;using System.Globalization;using System.IO;using System.Linq;using System.Reflection;using System.Runtime;using System.Security;using System.Security.Cryptography.X509Certificates;using System.Security.Principal;using System.ServiceProcess;using System.Text;using System.Threading;using System.Threading.Tasks;using System.Xml;using GSF.Collections;using GSF.Communication;using GSF.Configuration;using GSF.Console;using GSF.Data;using GSF.Diagnostics;using GSF.IO;using GSF.Net.Security;using GSF.Reflection;using GSF.Security;using GSF.ServiceProcess;using GSF.Threading;using GSF.TimeSeries.Adapters;using GSF.TimeSeries.Configuration;using GSF.TimeSeries.Reports;using GSF.TimeSeries.Statistics;using GSF.Units; namespace GSF.TimeSeries{ #region [ Enumerations ] /// <summary> /// Configuration data source type enumeration. /// </summary> public enum ConfigurationType { /// <summary> /// Configuration source is a database. /// </summary> Database, /// <summary> /// Configuration source is a web-service. /// </summary> WebService, /// <summary> /// Configuration source is a binary file. /// </summary> BinaryFile, /// <summary> /// Configuration source is a XML file. /// </summary> XmlFile } #endregion /// <summary> /// Represents the time-series framework service host. /// </summary> public partial class ServiceHostBase : ServiceBase { #region [ Members ] // Constants private const int DefaultMinThreadPoolSize = 25; private const int DefaultMaxThreadPoolSize = 100; private const int DefaultConfigurationBackups = 5; private const int DefaultMaxLogFiles = 300; internal event EventHandler<EventArgs<Guid, string, UpdateType>> UpdatedStatus; internal event EventHandler<EventArgs<Exception>> LoggedException; // Fields private IaonSession m_iaonSession; private string m_nodeIDQueryString; private ConfigurationType m_configurationType; private IConfigurationLoader m_configurationLoader; private BinaryFileConfigurationLoader m_binaryCacheConfigurationLoader; private XMLConfigurationLoader m_xmlCacheConfigurationLoader; private string m_cachedXmlConfigurationFile; private string m_cachedBinaryConfigurationFile; private int m_configurationBackups; private bool m_uniqueAdapterIDs; private bool m_allowRemoteRestart; private bool m_preferCachedConfiguration; private MultipleDestinationExporter m_healthExporter; private MultipleDestinationExporter m_statusExporter; private ReportingProcessCollection m_reportingProcesses; private ProcessQueue<Tuple<string, Action<bool>>> m_reloadConfigQueue; private LongSynchronizedOperation m_configurationCacheOperation; private volatile DataSet m_latestConfiguration; private RunTimeLog m_runTimeLog; private ServiceHelper m_serviceHelper; private ServerBase m_remotingServer; private bool m_disposed; #endregion #region [ Constructors ] /// <summary> /// Creates a new <see cref="ServiceHostBase"/>. /// </summary> public ServiceHostBase() { InitializeComponent(); ServiceName = "IaonHost"; } /// <summary> /// Creates a new <see cref="ServiceHostBase"/> from specified parameters. /// </summary> /// <param name="container">Service host <see cref="IContainer"/>.</param> public ServiceHostBase(IContainer container) : this() { container?.Add(this); } #endregion #region [ Properties ] /// <summary> /// Gets the related remote console application name. /// </summary> protected virtual string ConsoleApplicationName => ServiceName + "Console.exe"; /// <summary> /// Gets access to the <see cref="GSF.ServiceProcess.ServiceHelper"/>. /// </summary> protected ServiceHelper ServiceHelper => m_serviceHelper; /// <summary> /// Gets reference to the <see cref="TcpServer"/> based remoting server. /// </summary> protected ServerBase RemotingServer => m_remotingServer; /// <summary> /// Gets reference to the <see cref="AllAdaptersCollection"/>. /// </summary> protected AllAdaptersCollection AllAdapters => m_iaonSession.AllAdapters; /// <summary> /// Gets reference to the <see cref="InputAdapterCollection"/>. /// </summary> protected InputAdapterCollection InputAdapters => m_iaonSession.InputAdapters; /// <summary> /// Gets reference to the <see cref="ActionAdapterCollection"/>. /// </summary> protected ActionAdapterCollection ActionAdapters => m_iaonSession.ActionAdapters; /// <summary> /// Gets reference to the <see cref="OutputAdapterCollection"/>. /// </summary> protected OutputAdapterCollection OutputAdapters => m_iaonSession.OutputAdapters; /// <summary> /// Gets the current node ID. /// </summary> protected Guid NodeID => m_iaonSession.NodeID; /// <summary> /// Gets the current node ID formatted for use in a SQL query string based on <see cref="ServiceHostBase.ConfigurationType"/>. /// </summary> protected string NodeIDQueryString => m_nodeIDQueryString; /// <summary> /// Gets the currently loaded system configuration <see cref="DataSet"/>. /// </summary> protected DataSet DataSource => m_iaonSession.DataSource; /// <summary> /// Gets the defined system <see cref="GSF.TimeSeries.ConfigurationType"/>. /// </summary> protected ConfigurationType ConfigurationType => m_configurationType; #endregion #region [ Methods ] #region [ Service Event Handlers ] /// <summary> /// Event handler for service starting operations. /// </summary> /// <param name="sender">Event source.</param> /// <param name="e">Event arguments containing command line arguments passed into service at startup.</param> /// <remarks> /// Time-series framework uses this handler to load settings from configuration file as service is starting. /// </remarks> protected virtual void ServiceStartingHandler(object sender, EventArgs<string[]> e) { ShutdownHandler.Initialize(); // Define a run-time log m_runTimeLog = new RunTimeLog(); m_runTimeLog.FileName = "RunTimeLog.txt"; m_runTimeLog.ProcessException += ProcessExceptionHandler; m_runTimeLog.Initialize(); // Use this run-time log for system up-time calculations PerformanceStatistics.SystemRunTimeLog = m_runTimeLog; // Initialize Iaon session m_iaonSession = new IaonSession(); m_iaonSession.StatusMessage += StatusMessageHandler; m_iaonSession.ProcessException += ProcessExceptionHandler; m_iaonSession.ConfigurationChanged += ConfigurationChangedHandler; // Create a handler for unobserved task exceptions TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; // Make sure default service settings exist ConfigurationFile configFile = ConfigurationFile.Current; string servicePath = FilePath.GetAbsolutePath(""); string cachePath = string.Format("{0}{1}ConfigurationCache{1}", servicePath, Path.DirectorySeparatorChar); string defaultLogPath = string.Format("{0}{1}Logs{1}", servicePath, Path.DirectorySeparatorChar); // System settings CategorizedSettingsElementCollection systemSettings = configFile.Settings["systemSettings"]; systemSettings.Add("ConfigurationType", "Database", "Specifies type of configuration: Database, WebService, BinaryFile or XmlFile"); systemSettings.Add("ConnectionString", "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=IaonHost.mdb", "Configuration database connection string"); systemSettings.Add("DataProviderString", "AssemblyName={System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089};ConnectionType=System.Data.OleDb.OleDbConnection;AdapterType=System.Data.OleDb.OleDbDataAdapter", "Configuration database ADO.NET data provider assembly type creation string"); systemSettings.Add("ConfigurationCachePath", cachePath, "Defines the path used to cache serialized configurations"); systemSettings.Add("LogPath", defaultLogPath, "Defines the path used to archive log files"); systemSettings.Add("MaxLogFiles", DefaultMaxLogFiles, "Defines the maximum number of log files to keep"); systemSettings.Add("CachedConfigurationFile", "SystemConfiguration.xml", "File name for last known good system configuration (only cached for a Database or WebService connection)"); systemSettings.Add("UniqueAdaptersIDs", "True", "Set to true if all runtime adapter ID's will be unique to allow for easier adapter specification"); systemSettings.Add("ProcessPriority", "High", "Sets desired process priority: Normal, AboveNormal, High, RealTime"); systemSettings.Add("AllowRemoteRestart", "True", "Controls ability to remotely restart the host service."); systemSettings.Add("MinThreadPoolWorkerThreads", DefaultMinThreadPoolSize, "Defines the minimum number of allowed thread pool worker threads."); systemSettings.Add("MaxThreadPoolWorkerThreads", DefaultMaxThreadPoolSize, "Defines the maximum number of allowed thread pool worker threads."); systemSettings.Add("MinThreadPoolIOPortThreads", DefaultMinThreadPoolSize, "Defines the minimum number of allowed thread pool I/O completion port threads (used by socket layer)."); systemSettings.Add("MaxThreadPoolIOPortThreads", DefaultMaxThreadPoolSize, "Defines the maximum number of allowed thread pool I/O completion port threads (used by socket layer)."); systemSettings.Add("ConfigurationBackups", DefaultConfigurationBackups, "Defines the total number of older backup configurations to maintain."); systemSettings.Add("PreferCachedConfiguration", "False", "Set to true to try the cached configuration first, before loading database configuration - typically used when cache is updated by external process."); systemSettings.Add("LocalCertificate", $"{ServiceName}.cer", "Path to the local certificate used by this server for authentication."); systemSettings.Add("RemoteCertificatesPath", @"Certs\Remotes", "Path to the directory where remote certificates are stored."); systemSettings.Add("DefaultCulture", "en-US", "Default culture to use for language, country/region and calendar formats."); // Example connection settings CategorizedSettingsElementCollection exampleSettings = configFile.Settings["exampleConnectionSettings"]; exampleSettings.Add("SqlServer.ConnectionString", "Data Source=serverName; Initial Catalog=databaseName; User ID=userName; Password=password", "Example SQL Server database connection string"); exampleSettings.Add("SqlServer.DataProviderString", "AssemblyName={System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}; ConnectionType=System.Data.SqlClient.SqlConnection; AdapterType=System.Data.SqlClient.SqlDataAdapter", "Example SQL Server database .NET provider string"); exampleSettings.Add("MySQL.ConnectionString", "Server=serverName;Database=databaseName; Uid=root; Pwd=password; allow user variables = true;", "Example MySQL database connection string"); exampleSettings.Add("MySQL.DataProviderString", "AssemblyName={MySql.Data, Version=6.3.6.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d}; ConnectionType=MySql.Data.MySqlClient.MySqlConnection; AdapterType=MySql.Data.MySqlClient.MySqlDataAdapter", "Example MySQL database .NET provider string"); exampleSettings.Add("Oracle.ConnectionString", "Data Source=tnsName; User ID=schemaUserName; Password=schemaPassword", "Example Oracle database connection string"); exampleSettings.Add("Oracle.DataProviderString", "AssemblyName={Oracle.DataAccess, Version=2.112.2.0, Culture=neutral, PublicKeyToken=89b483f429c47342}; ConnectionType=Oracle.DataAccess.Client.OracleConnection; AdapterType=Oracle.DataAccess.Client.OracleDataAdapter", "Example Oracle database .NET provider string"); exampleSettings.Add("SQLite.ConnectionString", "Data Source=databaseName.db; Version=3; Foreign Keys=True; FailIfMissing=True", "Example SQLite database connection string"); exampleSettings.Add("SQLite.DataProviderString", "AssemblyName={System.Data.SQLite, Version=1.0.109.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139}; ConnectionType=System.Data.SQLite.SQLiteConnection; AdapterType=System.Data.SQLite.SQLiteDataAdapter", "Example SQLite database .NET provider string"); exampleSettings.Add("OleDB.ConnectionString", "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=databaseName.mdb", "Example Microsoft Access (via OleDb) database connection string"); exampleSettings.Add("OleDB.DataProviderString", "AssemblyName={System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}; ConnectionType=System.Data.OleDb.OleDbConnection; AdapterType=System.Data.OleDb.OleDbDataAdapter", "Example OleDb database .NET provider string"); exampleSettings.Add("Odbc.ConnectionString", "Driver={SQL Server Native Client 10.0}; Server=serverName; Database=databaseName; Uid=userName; Pwd=password;", "Example ODBC database connection string"); exampleSettings.Add("Odbc.DataProviderString", "AssemblyName={System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}; ConnectionType=System.Data.Odbc.OdbcConnection; AdapterType=System.Data.Odbc.OdbcDataAdapter", "Example ODBC database .NET provider string"); exampleSettings.Add("WebService.ConnectionString", "http://localhost/ConfigSource/SystemConfiguration.xml", "Example web service connection string"); exampleSettings.Add("XmlFile.ConnectionString", "SystemConfiguration.xml", "Example XML configuration file connection string"); // Attempt to set default culture try { string defaultCulture = systemSettings["DefaultCulture"].ValueAs("en-US"); CultureInfo.DefaultThreadCurrentCulture = CultureInfo.CreateSpecificCulture(defaultCulture); // Defaults for date formatting, etc. CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.CreateSpecificCulture(defaultCulture); // Culture for resource strings, etc. } catch (Exception ex) { DisplayStatusMessage("Failed to set default culture due to exception, defaulting to \"{1}\": {0}", UpdateType.Alarm, ex.Message, CultureInfo.CurrentCulture.Name.ToNonNullNorEmptyString("Undetermined")); LogException(ex); } // Retrieve application log path as defined in the config file string logPath = FilePath.GetAbsolutePath(systemSettings["LogPath"].Value); // Make sure log directory exists try { if (!Directory.Exists(logPath)) Directory.CreateDirectory(logPath); } catch (Exception ex) { // Attempt to default back to common log file path if (!Directory.Exists(defaultLogPath)) { try { Directory.CreateDirectory(defaultLogPath); } catch { defaultLogPath = servicePath; } } DisplayStatusMessage("Failed to create logging directory \"{0}\" due to exception, defaulting to \"{1}\": {2}", UpdateType.Alarm, logPath, defaultLogPath, ex.Message); LogException(ex); logPath = defaultLogPath; } int maxLogFiles = systemSettings["MaxLogFiles"].ValueAs(DefaultMaxLogFiles); try { Logger.FileWriter.SetPath(logPath); Logger.FileWriter.SetLoggingFileCount(maxLogFiles); } catch (Exception ex) { DisplayStatusMessage("Failed to set logging path \"{0}\" or max file count \"{1}\" due to exception: {2}", UpdateType.Alarm, logPath, maxLogFiles, ex.Message); LogException(ex); } // Retrieve configuration cache directory as defined in the config file cachePath = FilePath.GetAbsolutePath(systemSettings["ConfigurationCachePath"].Value); // Make sure configuration cache directory exists try { if (!Directory.Exists(cachePath)) Directory.CreateDirectory(cachePath); } catch (Exception ex) { DisplayStatusMessage("Failed to create configuration cache directory \"{0}\" due to exception: {1}", UpdateType.Alarm, cachePath, ex.Message); LogException(ex); } try { Directory.SetCurrentDirectory(servicePath); } catch (Exception ex) { DisplayStatusMessage("Failed to set current directory to execution path \"{0}\" due to exception: {1}", UpdateType.Alarm, servicePath, ex.Message); LogException(ex); } // Initialize system settings m_configurationType = systemSettings["ConfigurationType"].ValueAs<ConfigurationType>(); m_cachedXmlConfigurationFile = FilePath.AddPathSuffix(cachePath) + systemSettings["CachedConfigurationFile"].Value; m_cachedBinaryConfigurationFile = FilePath.AddPathSuffix(cachePath) + FilePath.GetFileNameWithoutExtension(m_cachedXmlConfigurationFile) + ".bin"; m_configurationBackups = systemSettings["ConfigurationBackups"].ValueAs(DefaultConfigurationBackups); m_uniqueAdapterIDs = systemSettings["UniqueAdaptersIDs"].ValueAsBoolean(true); m_allowRemoteRestart = systemSettings["AllowRemoteRestart"].ValueAsBoolean(true); m_preferCachedConfiguration = systemSettings["PreferCachedConfiguration"].ValueAsBoolean(false); m_reloadConfigQueue = ProcessQueue<Tuple<string, Action<bool>>>.CreateSynchronousQueue(ExecuteReloadConfig, 500.0D, Timeout.Infinite, false, false); m_reloadConfigQueue.ProcessException += m_iaonSession.ProcessExceptionHandler; m_configurationCacheOperation = new LongSynchronizedOperation(ExecuteConfigurationCache) { IsBackground = true }; // Setup default thread pool size try { ThreadPool.SetMinThreads(systemSettings["MinThreadPoolWorkerThreads"].ValueAs(DefaultMinThreadPoolSize), systemSettings["MinThreadPoolIOPortThreads"].ValueAs(DefaultMinThreadPoolSize)); ThreadPool.SetMaxThreads(systemSettings["MaxThreadPoolWorkerThreads"].ValueAs(DefaultMaxThreadPoolSize), systemSettings["MaxThreadPoolIOPortThreads"].ValueAs(DefaultMaxThreadPoolSize)); } catch (Exception ex) { DisplayStatusMessage("Failed to set desired thread pool size due to exception: {0}", UpdateType.Alarm, ex.Message); LogException(ex); } // Define guid with query string delimiters according to database needs if (string.IsNullOrWhiteSpace(m_nodeIDQueryString)) m_nodeIDQueryString = "'" + m_iaonSession.NodeID + "'"; // Set up the configuration loader switch (m_configurationType) { case ConfigurationType.Database: m_configurationLoader = new DatabaseConfigurationLoader { ConnectionString = systemSettings["ConnectionString"].Value, DataProviderString = systemSettings["DataProviderString"].Value, NodeIDQueryString = m_nodeIDQueryString }; break; case ConfigurationType.WebService: m_configurationLoader = new WebServiceConfigurationLoader { URI = systemSettings["ConnectionString"].Value }; break; case ConfigurationType.BinaryFile: m_configurationLoader = new BinaryFileConfigurationLoader { FilePath = systemSettings["ConnectionString"].Value }; break; case ConfigurationType.XmlFile: m_configurationLoader = new XMLConfigurationLoader { FilePath = systemSettings["ConnectionString"].Value }; break; } m_binaryCacheConfigurationLoader = new BinaryFileConfigurationLoader { FilePath = m_cachedBinaryConfigurationFile }; m_xmlCacheConfigurationLoader = new XMLConfigurationLoader { FilePath = m_cachedXmlConfigurationFile }; m_configurationLoader.StatusMessage += (o, args) => DisplayStatusMessage(args.Argument, UpdateType.Information); m_binaryCacheConfigurationLoader.StatusMessage += (o, args) => DisplayStatusMessage(args.Argument, UpdateType.Information); m_xmlCacheConfigurationLoader.StatusMessage += (o, args) => DisplayStatusMessage(args.Argument, UpdateType.Information); m_configurationLoader.ProcessException += ConfigurationLoader_ProcessException; m_binaryCacheConfigurationLoader.ProcessException += ConfigurationLoader_ProcessException; m_xmlCacheConfigurationLoader.ProcessException += ConfigurationLoader_ProcessException; m_reloadConfigQueue.Start(); #if !MONO try { // Attempt to assign desired process priority. Note that process will require SeIncreaseBasePriorityPrivilege or // Administrative privileges to make this change Process.GetCurrentProcess().PriorityClass = systemSettings["ProcessPriority"].ValueAs<ProcessPriorityClass>(); } catch (Exception ex) { LogException(ex); } #endif } /// <summary> /// Event handler for service started operation. /// </summary> /// <param name="sender">Event source.</param> /// <param name="e">Event arguments.</param> /// <remarks> /// Time-series framework uses this handler to handle initialization of system objects. /// </remarks> protected virtual void ServiceStartedHandler(object sender, EventArgs e) { // Define a line of asterisks for emphasis string stars = new string('*', 79); // Get current process memory usage long processMemory = GSF.Common.GetProcessMemory();Lines 1–500 of 3745. Load more
//****************************************************************************************************** // ServiceHostBase.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: // ---------------------------------------------------------------------------------------------------- // 08/20/2010 - J. Ritchie Carroll // Generated original version of source code. // 01/30/2011 - Pinal C. Patel // Updated to subscribe to new ProcessException event of MultiDestinationExporter class. // 04/21/2011 - Timothy M. Yardley / Erich A. Heine (UIUC) // Implemented an optional, but enabled by default, non-broadcast method of directly routed // measurement distribution as a system performance optimization. // 12/20/2012 - Starlynn Danyelle Gilliam // Modified Header. // //****************************************************************************************************** using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime; using System.Security; using System.Security.Cryptography.X509Certificates; using System.Security.Principal; using System.ServiceProcess; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; using GSF.Collections; using GSF.Communication; using GSF.Configuration; using GSF.Console; using GSF.Data; using GSF.Diagnostics; using GSF.IO; using GSF.Net.Security; using GSF.Reflection; using GSF.Security; using GSF.ServiceProcess; using GSF.Threading; using GSF.TimeSeries.Adapters; using GSF.TimeSeries.Configuration; using GSF.TimeSeries.Reports; using GSF.TimeSeries.Statistics; using GSF.Units; namespace GSF.TimeSeries { #region [ Enumerations ] /// <summary> /// Configuration data source type enumeration. /// </summary> public enum ConfigurationType { /// <summary> /// Configuration source is a database. /// </summary> Database, /// <summary> /// Configuration source is a web-service. /// </summary> WebService, /// <summary> /// Configuration source is a binary file. /// </summary> BinaryFile, /// <summary> /// Configuration source is a XML file. /// </summary> XmlFile } #endregion /// <summary> /// Represents the time-series framework service host. /// </summary> public partial class ServiceHostBase : ServiceBase { #region [ Members ] // Constants private const int DefaultMinThreadPoolSize = 25; private const int DefaultMaxThreadPoolSize = 100; private const int DefaultConfigurationBackups = 5; private const int DefaultMaxLogFiles = 300; internal event EventHandler<EventArgs<Guid, string, UpdateType>> UpdatedStatus; internal event EventHandler<EventArgs<Exception>> LoggedException; // Fields private IaonSession m_iaonSession; private string m_nodeIDQueryString; private ConfigurationType m_configurationType; private IConfigurationLoader m_configurationLoader; private BinaryFileConfigurationLoader m_binaryCacheConfigurationLoader; private XMLConfigurationLoader m_xmlCacheConfigurationLoader; private string m_cachedXmlConfigurationFile; private string m_cachedBinaryConfigurationFile; private int m_configurationBackups; private bool m_uniqueAdapterIDs; private bool m_allowRemoteRestart; private bool m_preferCachedConfiguration; private MultipleDestinationExporter m_healthExporter; private MultipleDestinationExporter m_statusExporter; private ReportingProcessCollection m_reportingProcesses; private ProcessQueue<Tuple<string, Action<bool>>> m_reloadConfigQueue; private LongSynchronizedOperation m_configurationCacheOperation; private volatile DataSet m_latestConfiguration; private RunTimeLog m_runTimeLog; private ServiceHelper m_serviceHelper; private ServerBase m_remotingServer; private bool m_disposed; #endregion #region [ Constructors ] /// <summary> /// Creates a new <see cref="ServiceHostBase"/>. /// </summary> public ServiceHostBase() { InitializeComponent(); ServiceName = "IaonHost"; } /// <summary> /// Creates a new <see cref="ServiceHostBase"/> from specified parameters. /// </summary> /// <param name="container">Service host <see cref="IContainer"/>.</param> public ServiceHostBase(IContainer container) : this() { container?.Add(this); } #endregion #region [ Properties ] /// <summary> /// Gets the related remote console application name. /// </summary> protected virtual string ConsoleApplicationName => ServiceName + "Console.exe"; /// <summary> /// Gets access to the <see cref="GSF.ServiceProcess.ServiceHelper"/>. /// </summary> protected ServiceHelper ServiceHelper => m_serviceHelper; /// <summary> /// Gets reference to the <see cref="TcpServer"/> based remoting server. /// </summary> protected ServerBase RemotingServer => m_remotingServer; /// <summary> /// Gets reference to the <see cref="AllAdaptersCollection"/>. /// </summary> protected AllAdaptersCollection AllAdapters => m_iaonSession.AllAdapters; /// <summary> /// Gets reference to the <see cref="InputAdapterCollection"/>. /// </summary> protected InputAdapterCollection InputAdapters => m_iaonSession.InputAdapters; /// <summary> /// Gets reference to the <see cref="ActionAdapterCollection"/>. /// </summary> protected ActionAdapterCollection ActionAdapters => m_iaonSession.ActionAdapters; /// <summary> /// Gets reference to the <see cref="OutputAdapterCollection"/>. /// </summary> protected OutputAdapterCollection OutputAdapters => m_iaonSession.OutputAdapters; /// <summary> /// Gets the current node ID. /// </summary> protected Guid NodeID => m_iaonSession.NodeID; /// <summary> /// Gets the current node ID formatted for use in a SQL query string based on <see cref="ServiceHostBase.ConfigurationType"/>. /// </summary> protected string NodeIDQueryString => m_nodeIDQueryString; /// <summary> /// Gets the currently loaded system configuration <see cref="DataSet"/>. /// </summary> protected DataSet DataSource => m_iaonSession.DataSource; /// <summary> /// Gets the defined system <see cref="GSF.TimeSeries.ConfigurationType"/>. /// </summary> protected ConfigurationType ConfigurationType => m_configurationType; #endregion #region [ Methods ] #region [ Service Event Handlers ] /// <summary> /// Event handler for service starting operations. /// </summary> /// <param name="sender">Event source.</param> /// <param name="e">Event arguments containing command line arguments passed into service at startup.</param> /// <remarks> /// Time-series framework uses this handler to load settings from configuration file as service is starting. /// </remarks> protected virtual void ServiceStartingHandler(object sender, EventArgs<string[]> e) { ShutdownHandler.Initialize(); // Define a run-time log m_runTimeLog = new RunTimeLog(); m_runTimeLog.FileName = "RunTimeLog.txt"; m_runTimeLog.ProcessException += ProcessExceptionHandler; m_runTimeLog.Initialize(); // Use this run-time log for system up-time calculations PerformanceStatistics.SystemRunTimeLog = m_runTimeLog; // Initialize Iaon session m_iaonSession = new IaonSession(); m_iaonSession.StatusMessage += StatusMessageHandler; m_iaonSession.ProcessException += ProcessExceptionHandler; m_iaonSession.ConfigurationChanged += ConfigurationChangedHandler; // Create a handler for unobserved task exceptions TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; // Make sure default service settings exist ConfigurationFile configFile = ConfigurationFile.Current; string servicePath = FilePath.GetAbsolutePath(""); string cachePath = string.Format("{0}{1}ConfigurationCache{1}", servicePath, Path.DirectorySeparatorChar); string defaultLogPath = string.Format("{0}{1}Logs{1}", servicePath, Path.DirectorySeparatorChar); // System settings CategorizedSettingsElementCollection systemSettings = configFile.Settings["systemSettings"]; systemSettings.Add("ConfigurationType", "Database", "Specifies type of configuration: Database, WebService, BinaryFile or XmlFile"); systemSettings.Add("ConnectionString", "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=IaonHost.mdb", "Configuration database connection string"); systemSettings.Add("DataProviderString", "AssemblyName={System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089};ConnectionType=System.Data.OleDb.OleDbConnection;AdapterType=System.Data.OleDb.OleDbDataAdapter", "Configuration database ADO.NET data provider assembly type creation string"); systemSettings.Add("ConfigurationCachePath", cachePath, "Defines the path used to cache serialized configurations"); systemSettings.Add("LogPath", defaultLogPath, "Defines the path used to archive log files"); systemSettings.Add("MaxLogFiles", DefaultMaxLogFiles, "Defines the maximum number of log files to keep"); systemSettings.Add("CachedConfigurationFile", "SystemConfiguration.xml", "File name for last known good system configuration (only cached for a Database or WebService connection)"); systemSettings.Add("UniqueAdaptersIDs", "True", "Set to true if all runtime adapter ID's will be unique to allow for easier adapter specification"); systemSettings.Add("ProcessPriority", "High", "Sets desired process priority: Normal, AboveNormal, High, RealTime"); systemSettings.Add("AllowRemoteRestart", "True", "Controls ability to remotely restart the host service."); systemSettings.Add("MinThreadPoolWorkerThreads", DefaultMinThreadPoolSize, "Defines the minimum number of allowed thread pool worker threads."); systemSettings.Add("MaxThreadPoolWorkerThreads", DefaultMaxThreadPoolSize, "Defines the maximum number of allowed thread pool worker threads."); systemSettings.Add("MinThreadPoolIOPortThreads", DefaultMinThreadPoolSize, "Defines the minimum number of allowed thread pool I/O completion port threads (used by socket layer)."); systemSettings.Add("MaxThreadPoolIOPortThreads", DefaultMaxThreadPoolSize, "Defines the maximum number of allowed thread pool I/O completion port threads (used by socket layer)."); systemSettings.Add("ConfigurationBackups", DefaultConfigurationBackups, "Defines the total number of older backup configurations to maintain."); systemSettings.Add("PreferCachedConfiguration", "False", "Set to true to try the cached configuration first, before loading database configuration - typically used when cache is updated by external process."); systemSettings.Add("LocalCertificate", $"{ServiceName}.cer", "Path to the local certificate used by this server for authentication."); systemSettings.Add("RemoteCertificatesPath", @"Certs\Remotes", "Path to the directory where remote certificates are stored."); systemSettings.Add("DefaultCulture", "en-US", "Default culture to use for language, country/region and calendar formats."); // Example connection settings CategorizedSettingsElementCollection exampleSettings = configFile.Settings["exampleConnectionSettings"]; exampleSettings.Add("SqlServer.ConnectionString", "Data Source=serverName; Initial Catalog=databaseName; User ID=userName; Password=password", "Example SQL Server database connection string"); exampleSettings.Add("SqlServer.DataProviderString", "AssemblyName={System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}; ConnectionType=System.Data.SqlClient.SqlConnection; AdapterType=System.Data.SqlClient.SqlDataAdapter", "Example SQL Server database .NET provider string"); exampleSettings.Add("MySQL.ConnectionString", "Server=serverName;Database=databaseName; Uid=root; Pwd=password; allow user variables = true;", "Example MySQL database connection string"); exampleSettings.Add("MySQL.DataProviderString", "AssemblyName={MySql.Data, Version=6.3.6.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d}; ConnectionType=MySql.Data.MySqlClient.MySqlConnection; AdapterType=MySql.Data.MySqlClient.MySqlDataAdapter", "Example MySQL database .NET provider string"); exampleSettings.Add("Oracle.ConnectionString", "Data Source=tnsName; User ID=schemaUserName; Password=schemaPassword", "Example Oracle database connection string"); exampleSettings.Add("Oracle.DataProviderString", "AssemblyName={Oracle.DataAccess, Version=2.112.2.0, Culture=neutral, PublicKeyToken=89b483f429c47342}; ConnectionType=Oracle.DataAccess.Client.OracleConnection; AdapterType=Oracle.DataAccess.Client.OracleDataAdapter", "Example Oracle database .NET provider string"); exampleSettings.Add("SQLite.ConnectionString", "Data Source=databaseName.db; Version=3; Foreign Keys=True; FailIfMissing=True", "Example SQLite database connection string"); exampleSettings.Add("SQLite.DataProviderString", "AssemblyName={System.Data.SQLite, Version=1.0.109.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139}; ConnectionType=System.Data.SQLite.SQLiteConnection; AdapterType=System.Data.SQLite.SQLiteDataAdapter", "Example SQLite database .NET provider string"); exampleSettings.Add("OleDB.ConnectionString", "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=databaseName.mdb", "Example Microsoft Access (via OleDb) database connection string"); exampleSettings.Add("OleDB.DataProviderString", "AssemblyName={System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}; ConnectionType=System.Data.OleDb.OleDbConnection; AdapterType=System.Data.OleDb.OleDbDataAdapter", "Example OleDb database .NET provider string"); exampleSettings.Add("Odbc.ConnectionString", "Driver={SQL Server Native Client 10.0}; Server=serverName; Database=databaseName; Uid=userName; Pwd=password;", "Example ODBC database connection string"); exampleSettings.Add("Odbc.DataProviderString", "AssemblyName={System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}; ConnectionType=System.Data.Odbc.OdbcConnection; AdapterType=System.Data.Odbc.OdbcDataAdapter", "Example ODBC database .NET provider string"); exampleSettings.Add("WebService.ConnectionString", "http://localhost/ConfigSource/SystemConfiguration.xml", "Example web service connection string"); exampleSettings.Add("XmlFile.ConnectionString", "SystemConfiguration.xml", "Example XML configuration file connection string"); // Attempt to set default culture try { string defaultCulture = systemSettings["DefaultCulture"].ValueAs("en-US"); CultureInfo.DefaultThreadCurrentCulture = CultureInfo.CreateSpecificCulture(defaultCulture); // Defaults for date formatting, etc. CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.CreateSpecificCulture(defaultCulture); // Culture for resource strings, etc. } catch (Exception ex) { DisplayStatusMessage("Failed to set default culture due to exception, defaulting to \"{1}\": {0}", UpdateType.Alarm, ex.Message, CultureInfo.CurrentCulture.Name.ToNonNullNorEmptyString("Undetermined")); LogException(ex); } // Retrieve application log path as defined in the config file string logPath = FilePath.GetAbsolutePath(systemSettings["LogPath"].Value); // Make sure log directory exists try { if (!Directory.Exists(logPath)) Directory.CreateDirectory(logPath); } catch (Exception ex) { // Attempt to default back to common log file path if (!Directory.Exists(defaultLogPath)) { try { Directory.CreateDirectory(defaultLogPath); } catch { defaultLogPath = servicePath; } } DisplayStatusMessage("Failed to create logging directory \"{0}\" due to exception, defaulting to \"{1}\": {2}", UpdateType.Alarm, logPath, defaultLogPath, ex.Message); LogException(ex); logPath = defaultLogPath; } int maxLogFiles = systemSettings["MaxLogFiles"].ValueAs(DefaultMaxLogFiles); try { Logger.FileWriter.SetPath(logPath); Logger.FileWriter.SetLoggingFileCount(maxLogFiles); } catch (Exception ex) { DisplayStatusMessage("Failed to set logging path \"{0}\" or max file count \"{1}\" due to exception: {2}", UpdateType.Alarm, logPath, maxLogFiles, ex.Message); LogException(ex); } // Retrieve configuration cache directory as defined in the config file cachePath = FilePath.GetAbsolutePath(systemSettings["ConfigurationCachePath"].Value); // Make sure configuration cache directory exists try { if (!Directory.Exists(cachePath)) Directory.CreateDirectory(cachePath); } catch (Exception ex) { DisplayStatusMessage("Failed to create configuration cache directory \"{0}\" due to exception: {1}", UpdateType.Alarm, cachePath, ex.Message); LogException(ex); } try { Directory.SetCurrentDirectory(servicePath); } catch (Exception ex) { DisplayStatusMessage("Failed to set current directory to execution path \"{0}\" due to exception: {1}", UpdateType.Alarm, servicePath, ex.Message); LogException(ex); } // Initialize system settings m_configurationType = systemSettings["ConfigurationType"].ValueAs<ConfigurationType>(); m_cachedXmlConfigurationFile = FilePath.AddPathSuffix(cachePath) + systemSettings["CachedConfigurationFile"].Value; m_cachedBinaryConfigurationFile = FilePath.AddPathSuffix(cachePath) + FilePath.GetFileNameWithoutExtension(m_cachedXmlConfigurationFile) + ".bin"; m_configurationBackups = systemSettings["ConfigurationBackups"].ValueAs(DefaultConfigurationBackups); m_uniqueAdapterIDs = systemSettings["UniqueAdaptersIDs"].ValueAsBoolean(true); m_allowRemoteRestart = systemSettings["AllowRemoteRestart"].ValueAsBoolean(true); m_preferCachedConfiguration = systemSettings["PreferCachedConfiguration"].ValueAsBoolean(false); m_reloadConfigQueue = ProcessQueue<Tuple<string, Action<bool>>>.CreateSynchronousQueue(ExecuteReloadConfig, 500.0D, Timeout.Infinite, false, false); m_reloadConfigQueue.ProcessException += m_iaonSession.ProcessExceptionHandler; m_configurationCacheOperation = new LongSynchronizedOperation(ExecuteConfigurationCache) { IsBackground = true }; // Setup default thread pool size try { ThreadPool.SetMinThreads(systemSettings["MinThreadPoolWorkerThreads"].ValueAs(DefaultMinThreadPoolSize), systemSettings["MinThreadPoolIOPortThreads"].ValueAs(DefaultMinThreadPoolSize)); ThreadPool.SetMaxThreads(systemSettings["MaxThreadPoolWorkerThreads"].ValueAs(DefaultMaxThreadPoolSize), systemSettings["MaxThreadPoolIOPortThreads"].ValueAs(DefaultMaxThreadPoolSize)); } catch (Exception ex) { DisplayStatusMessage("Failed to set desired thread pool size due to exception: {0}", UpdateType.Alarm, ex.Message); LogException(ex); } // Define guid with query string delimiters according to database needs if (string.IsNullOrWhiteSpace(m_nodeIDQueryString)) m_nodeIDQueryString = "'" + m_iaonSession.NodeID + "'"; // Set up the configuration loader switch (m_configurationType) { case ConfigurationType.Database: m_configurationLoader = new DatabaseConfigurationLoader { ConnectionString = systemSettings["ConnectionString"].Value, DataProviderString = systemSettings["DataProviderString"].Value, NodeIDQueryString = m_nodeIDQueryString }; break; case ConfigurationType.WebService: m_configurationLoader = new WebServiceConfigurationLoader { URI = systemSettings["ConnectionString"].Value }; break; case ConfigurationType.BinaryFile: m_configurationLoader = new BinaryFileConfigurationLoader { FilePath = systemSettings["ConnectionString"].Value }; break; case ConfigurationType.XmlFile: m_configurationLoader = new XMLConfigurationLoader { FilePath = systemSettings["ConnectionString"].Value }; break; } m_binaryCacheConfigurationLoader = new BinaryFileConfigurationLoader { FilePath = m_cachedBinaryConfigurationFile }; m_xmlCacheConfigurationLoader = new XMLConfigurationLoader { FilePath = m_cachedXmlConfigurationFile }; m_configurationLoader.StatusMessage += (o, args) => DisplayStatusMessage(args.Argument, UpdateType.Information); m_binaryCacheConfigurationLoader.StatusMessage += (o, args) => DisplayStatusMessage(args.Argument, UpdateType.Information); m_xmlCacheConfigurationLoader.StatusMessage += (o, args) => DisplayStatusMessage(args.Argument, UpdateType.Information); m_configurationLoader.ProcessException += ConfigurationLoader_ProcessException; m_binaryCacheConfigurationLoader.ProcessException += ConfigurationLoader_ProcessException; m_xmlCacheConfigurationLoader.ProcessException += ConfigurationLoader_ProcessException; m_reloadConfigQueue.Start(); #if !MONO try { // Attempt to assign desired process priority. Note that process will require SeIncreaseBasePriorityPrivilege or // Administrative privileges to make this change Process.GetCurrentProcess().PriorityClass = systemSettings["ProcessPriority"].ValueAs<ProcessPriorityClass>(); } catch (Exception ex) { LogException(ex); } #endif } /// <summary> /// Event handler for service started operation. /// </summary> /// <param name="sender">Event source.</param> /// <param name="e">Event arguments.</param> /// <remarks> /// Time-series framework uses this handler to handle initialization of system objects. /// </remarks> protected virtual void ServiceStartedHandler(object sender, EventArgs e) { // Define a line of asterisks for emphasis string stars = new string('*', 79); // Get current process memory usage long processMemory = GSF.Common.GetProcessMemory();