Server.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.IO.Abstractions;
  5. using System.Linq;
  6. using System.Net;
  7. using System.Net.Sockets;
  8. using BO.ProcessServer.Business.StatisticCounters;
  9. using BO.ProcessServer.Business.Workers;
  10. using BO.ProcessServer.Business.Workers.Contexts;
  11. using BO.ProcessServer.Options;
  12. using Connector.PS;
  13. using Quadarax.Foundation.Core.IO;
  14. using Quadarax.Foundation.Core.Logging;
  15. using Quadarax.Foundation.Core.Object;
  16. namespace BO.ProcessServer.Business
  17. {
  18. internal class Server : DisposableObject
  19. {
  20. #region *** Private fields ***
  21. private Configuration _configuration;
  22. private IFileSystem _fileSystem;
  23. private IList<ServerProcess> _processes;
  24. protected ILog Log { get; }
  25. protected ILog LogLifecycle { get; }
  26. protected Statistics Statistics { get; }
  27. private Pool _pool;
  28. private PullDocumentsWorker _wkPull;
  29. private RetentionWorker _wkRetention;
  30. private ProcessingDocumentsWorker _wkProcessing;
  31. private IConnectionPS _connection;
  32. #endregion
  33. #region *** Public properties ***
  34. public bool IsRunning { get; private set; }
  35. #endregion
  36. #region *** Constructors ***
  37. public Server(Configuration serverConfiguration, IFileSystem fileSystem, ILogger logger)
  38. {
  39. _configuration = serverConfiguration ?? throw new ArgumentNullException(nameof(serverConfiguration));
  40. _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
  41. if (logger == null)
  42. throw new ArgumentNullException(nameof(logger));
  43. Log = logger.GetLogger(typeof(Server));
  44. LogLifecycle = logger.GetLogger("Lifecycle");
  45. Statistics = new Statistics();
  46. ValidateConfiguration();
  47. _processes = new List<ServerProcess>();
  48. _connection = new Connector(_configuration, _fileSystem);
  49. _pool = new Pool(_configuration, _fileSystem, logger);
  50. _wkPull = new PullDocumentsWorker(_connection, _configuration.System.PoolPullInterval, _fileSystem, Statistics, _pool, logger);
  51. _wkRetention = new RetentionWorker(new CfgCtx(_configuration, Statistics, _pool));
  52. _wkProcessing = new ProcessingDocumentsWorker(_configuration, _processes, _connection, _fileSystem, Statistics, _pool, logger);
  53. }
  54. #endregion
  55. #region *** Public operations ***
  56. public void Start()
  57. {
  58. if (IsRunning)
  59. {
  60. LogWarn($"Instance '{_configuration.System.Identification}' already running. Start skipped.");
  61. return;
  62. }
  63. LogDebug($"Starting instance '{_configuration.System.Identification}' ...");
  64. _wkPull.Start();
  65. if (_configuration.System.Retention.Enabled)
  66. _wkRetention.Start();
  67. else
  68. LogWarn("Retention module is disabled by configuration.");
  69. _wkProcessing.Start();
  70. Statistics.GetCounter<TimespanCounter>(Statistics.CNT_PS_ONLINE_DUR).Start();
  71. IsRunning = true;
  72. LogInfo( $"Instance '{_configuration.System.Identification}' STARTED.");
  73. LogLifecycle.Log(LogSeverityEnum.Info, $"Instance '{_configuration.System.Identification}' STARTED.");
  74. }
  75. public void Stop()
  76. {
  77. if (!IsRunning)
  78. {
  79. LogWarn($"Instance '{_configuration.System.Identification}' already stopped. Stop skipped.");
  80. return;
  81. }
  82. LogDebug($"Stopping instance '{_configuration.System.Identification}' ...");
  83. StopAll();
  84. Statistics.GetCounter<TimespanCounter>(Statistics.CNT_PS_ONLINE_DUR).Stop();
  85. IsRunning = false;
  86. LogInfo( $"Instance '{_configuration.System.Identification}' STOPPED.");
  87. LogLifecycle.Log(LogSeverityEnum.Info, $"Instance '{_configuration.System.Identification}' STOPPED.");
  88. LogLifecycle.Log(LogSeverityEnum.Info, Statistics.ToString());
  89. }
  90. #endregion
  91. #region *** Overrides ***
  92. protected override void OnDisposing()
  93. {
  94. if (IsRunning)
  95. StopAll(true);
  96. _wkPull?.Dispose();
  97. _wkRetention?.Dispose();
  98. _wkProcessing?.Dispose();
  99. _connection?.Dispose();
  100. _pool?.Dispose();
  101. LogDebug($"Instance '{_configuration.System.Identification}' disposed.");
  102. _configuration = null;
  103. _fileSystem = null;
  104. _wkPull=null;
  105. _wkRetention=null;
  106. _wkProcessing = null;
  107. _connection=null;
  108. _pool = null;
  109. }
  110. #endregion
  111. #region *** Private Operations ***
  112. public void LogDebug(string message)
  113. {
  114. LogRaw(LogSeverityEnum.Debug, message);
  115. }
  116. public void LogInfo(string message)
  117. {
  118. LogRaw(LogSeverityEnum.Info, message);
  119. }
  120. public void LogWarn(string message)
  121. {
  122. LogRaw(LogSeverityEnum.Warn, message);
  123. }
  124. public void LogRaw(LogSeverityEnum severity, string message)
  125. {
  126. Log.Log(severity, message);
  127. }
  128. private void ValidateConfiguration()
  129. {
  130. if (string.IsNullOrEmpty(_configuration.System.Identification))
  131. throw new ArgumentNullException(nameof(_configuration.System.Identification),
  132. "Mandatory process server identification.");
  133. if (string.IsNullOrEmpty(_configuration.System.PreferredInterface))
  134. _configuration.System.PreferredInterface = "auto";
  135. var host = Dns.GetHostEntry(Dns.GetHostName());
  136. if (_configuration.System.PreferredInterface.Trim().ToLower() == "auto")
  137. {
  138. _configuration.System.PreferredInterface = host.AddressList.Where(x =>
  139. x.AddressFamily == AddressFamily.InterNetwork ||
  140. x.AddressFamily == AddressFamily.InterNetworkV6).OrderBy(x=>x.AddressFamily).FirstOrDefault()
  141. ?.ToString();
  142. }
  143. if (!host.AddressList.Any(x =>
  144. (x.AddressFamily == AddressFamily.InterNetwork ||
  145. x.AddressFamily == AddressFamily.InterNetworkV6) && x.ToString().ToLower() ==
  146. _configuration.System.PreferredInterface.Trim().ToLower()))
  147. throw new ArgumentNullException(nameof(_configuration.System.PreferredInterface),
  148. "Mandatory preferred interface (or auto) for server identification.");
  149. // Pool directories structure
  150. EnsureDirectory(_configuration.System.PoolPathBase);
  151. EnsureDirectory(_fileSystem.Path.Combine(_configuration.System.PoolPathBase, _configuration.System.PoolPathDone));
  152. EnsureDirectory(_fileSystem.Path.Combine(_configuration.System.PoolPathBase, _configuration.System.PoolPathFail));
  153. EnsureDirectory(_fileSystem.Path.Combine(_configuration.System.PoolPathBase, _configuration.System.PoolPathPending));
  154. EnsureDirectory(_fileSystem.Path.Combine(_configuration.System.PoolPathBase, _configuration.System.PoolPathWorking));
  155. // Scenario structure
  156. EnsureDirectory(_configuration.System.ScenarioPathBase);
  157. if (_fileSystem.Directory.GetFiles(TranslatePath(_configuration.System.ScenarioPathBase), "*.*").Length == 0)
  158. LogWarn($"Scenario directory '{TranslatePath(_configuration.System.ScenarioPathBase)}' is empty.");
  159. LogInfo("Configuration validated.");
  160. }
  161. private string EnsureDirectory(string path)
  162. {
  163. var dir = FileUtils.EnsurePathEndsWithDirSeparator(_fileSystem, TranslatePath(path));
  164. if (!_fileSystem.Directory.Exists(dir))
  165. {
  166. _fileSystem.Directory.CreateDirectory(dir);
  167. LogDebug($"Directory '{dir}' created.");
  168. }
  169. if (!_fileSystem.Directory.Exists(dir))
  170. throw new FileNotFoundException($"Directory '{dir}' not found!");
  171. return dir;
  172. }
  173. private string TranslatePath(string path)
  174. {
  175. if (string.IsNullOrEmpty(path))
  176. return path;
  177. return path.Replace("{CD}", _fileSystem.Directory.GetCurrentDirectory());
  178. }
  179. private void StopAll(bool silently = false)
  180. {
  181. _wkPull.Stop(silently);
  182. if (_configuration.System.Retention.Enabled)
  183. _wkRetention.Stop(silently);
  184. _wkProcessing.Stop(silently);
  185. foreach (var process in _processes)
  186. process.Stop(silently);
  187. foreach (var process in _processes)
  188. process.Dispose();
  189. _processes.Clear();
  190. }
  191. #endregion
  192. }
  193. }