|
|
@@ -0,0 +1,221 @@
|
|
|
+using System;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.IO;
|
|
|
+using System.IO.Abstractions;
|
|
|
+using System.Linq;
|
|
|
+using System.Net;
|
|
|
+using System.Net.Sockets;
|
|
|
+using BO.ProcessServer.Business.StatisticCounters;
|
|
|
+using BO.ProcessServer.Business.Workers;
|
|
|
+using BO.ProcessServer.Business.Workers.Contexts;
|
|
|
+using BO.ProcessServer.Options;
|
|
|
+using Connector.PS;
|
|
|
+using Quadarax.Foundation.Core.IO;
|
|
|
+using Quadarax.Foundation.Core.Logging;
|
|
|
+using Quadarax.Foundation.Core.Object;
|
|
|
+
|
|
|
+namespace BO.ProcessServer.Business
|
|
|
+{
|
|
|
+ internal class Server : DisposableObject
|
|
|
+ {
|
|
|
+ #region *** Private fields ***
|
|
|
+ private Configuration _configuration;
|
|
|
+ private IFileSystem _fileSystem;
|
|
|
+ private IList<ServerProcess> _processes;
|
|
|
+ protected ILog Log { get; }
|
|
|
+ protected Statistics Statistics { get; }
|
|
|
+ private Pool _pool;
|
|
|
+ private PullDocumentsWorker _wkPull;
|
|
|
+ private RetentionWorker _wkRetention;
|
|
|
+ private ProcessingDocumentsWorker _wkProcessing;
|
|
|
+ private Connection _connection;
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region *** Public properties ***
|
|
|
+ public bool IsRunning { get; private set; }
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region *** Constructors ***
|
|
|
+ public Server(Configuration serverConfiguration, IFileSystem fileSystem, ILogger logger)
|
|
|
+ {
|
|
|
+ _configuration = serverConfiguration ?? throw new ArgumentNullException(nameof(serverConfiguration));
|
|
|
+ _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
|
|
|
+ if (logger == null)
|
|
|
+ throw new ArgumentNullException(nameof(logger));
|
|
|
+ Log = logger.GetLogger(typeof(Server));
|
|
|
+ Statistics = new Statistics();
|
|
|
+
|
|
|
+ _processes = new List<ServerProcess>();
|
|
|
+
|
|
|
+ _connection = new Connection(_configuration.ApiBaseUrl, _configuration.ApiTimeout,
|
|
|
+ _configuration.System.Identification, IPAddress.Parse(_configuration.System.PreferredInterface),
|
|
|
+ _configuration.System.PoolPullDocuments);
|
|
|
+
|
|
|
+ _pool = new Pool(_configuration, _fileSystem, logger);
|
|
|
+
|
|
|
+ _wkPull = new PullDocumentsWorker(_connection, _configuration.System.PoolPullInterval, fileSystem, Statistics, _pool, logger);
|
|
|
+ _wkRetention = new RetentionWorker(new CfgCtx(_configuration, Statistics, _pool));
|
|
|
+ _wkProcessing = new ProcessingDocumentsWorker(_configuration, _processes, _connection, fileSystem, Statistics, _pool, logger);
|
|
|
+ }
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region *** Public operations ***
|
|
|
+ public void Start()
|
|
|
+ {
|
|
|
+ if (IsRunning)
|
|
|
+ {
|
|
|
+ LogWarn($"Instance '{_configuration.System.Identification}' already running. Start skipped.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ LogDebug($"Starting instance '{_configuration.System.Identification}' ...");
|
|
|
+ ValidateConfiguration();
|
|
|
+
|
|
|
+ _wkPull.Start();
|
|
|
+ _wkRetention.Start();
|
|
|
+ _wkProcessing.Start();
|
|
|
+
|
|
|
+ Statistics.GetCounter<TimespanCounter>(Statistics.CNT_PS_ONLINE_DUR).Start();
|
|
|
+ IsRunning = true;
|
|
|
+ LogInfo( $"Instance '{_configuration.System.Identification}' STARTED.");
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public void Stop()
|
|
|
+ {
|
|
|
+ if (!IsRunning)
|
|
|
+ {
|
|
|
+ LogWarn($"Instance '{_configuration.System.Identification}' already stopped. Stop skipped.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ LogDebug($"Stopping instance '{_configuration.System.Identification}' ...");
|
|
|
+
|
|
|
+ StopAll();
|
|
|
+
|
|
|
+ Statistics.GetCounter<TimespanCounter>(Statistics.CNT_PS_ONLINE_DUR).Stop();
|
|
|
+ IsRunning = false;
|
|
|
+ LogInfo( $"Instance '{_configuration.System.Identification}' STOPPED.");
|
|
|
+ }
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region *** Overrides ***
|
|
|
+ protected override void OnDisposing()
|
|
|
+ {
|
|
|
+ if (IsRunning)
|
|
|
+ StopAll(true);
|
|
|
+ _wkPull?.Dispose();
|
|
|
+ _wkRetention?.Dispose();
|
|
|
+ _wkProcessing?.Dispose();
|
|
|
+ _connection?.Dispose();
|
|
|
+ _pool?.Dispose();
|
|
|
+
|
|
|
+ LogDebug($"Instance '{_configuration.System.Identification}' disposed.");
|
|
|
+ _configuration = null;
|
|
|
+ _fileSystem = null;
|
|
|
+ _wkPull=null;
|
|
|
+ _wkRetention=null;
|
|
|
+ _wkProcessing = null;
|
|
|
+ _connection=null;
|
|
|
+ _pool = null;
|
|
|
+ }
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region *** Private Operations ***
|
|
|
+
|
|
|
+ public void LogDebug(string message)
|
|
|
+ {
|
|
|
+ LogRaw(LogSeverityEnum.Debug, message);
|
|
|
+ }
|
|
|
+ public void LogInfo(string message)
|
|
|
+ {
|
|
|
+ LogRaw(LogSeverityEnum.Info, message);
|
|
|
+ }
|
|
|
+ public void LogWarn(string message)
|
|
|
+ {
|
|
|
+ LogRaw(LogSeverityEnum.Warn, message);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void LogRaw(LogSeverityEnum severity, string message)
|
|
|
+ {
|
|
|
+ Log.Log(severity, message);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void ValidateConfiguration()
|
|
|
+ {
|
|
|
+ if (string.IsNullOrEmpty(_configuration.System.Identification))
|
|
|
+ throw new ArgumentNullException(nameof(_configuration.System.Identification),
|
|
|
+ "Mandatory process server identification.");
|
|
|
+
|
|
|
+ if (string.IsNullOrEmpty(_configuration.System.PreferredInterface))
|
|
|
+ _configuration.System.PreferredInterface = "auto";
|
|
|
+
|
|
|
+
|
|
|
+ var host = Dns.GetHostEntry(Dns.GetHostName());
|
|
|
+ if (_configuration.System.PreferredInterface.Trim().ToLower() == "auto")
|
|
|
+ {
|
|
|
+ _configuration.System.PreferredInterface = host.AddressList.FirstOrDefault(x =>
|
|
|
+ x.AddressFamily == AddressFamily.InterNetwork ||
|
|
|
+ x.AddressFamily == AddressFamily.InterNetworkV6)
|
|
|
+ ?.ToString();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!host.AddressList.Any(x =>
|
|
|
+ (x.AddressFamily == AddressFamily.InterNetwork ||
|
|
|
+ x.AddressFamily == AddressFamily.InterNetworkV6) && x.ToString().ToLower() ==
|
|
|
+ _configuration.System.PreferredInterface.Trim().ToLower()))
|
|
|
+ throw new ArgumentNullException(nameof(_configuration.System.PreferredInterface),
|
|
|
+ "Mandatory preferred interface (or auto) for server identification.");
|
|
|
+
|
|
|
+ // Pool directories structure
|
|
|
+ EnsureDirectory(_configuration.System.PoolPathBase);
|
|
|
+ EnsureDirectory(_fileSystem.Path.Combine(_configuration.System.PoolPathBase, _configuration.System.PoolPathDone));
|
|
|
+ EnsureDirectory(_fileSystem.Path.Combine(_configuration.System.PoolPathBase, _configuration.System.PoolPathFail));
|
|
|
+ EnsureDirectory(_fileSystem.Path.Combine(_configuration.System.PoolPathBase, _configuration.System.PoolPathPending));
|
|
|
+ EnsureDirectory(_fileSystem.Path.Combine(_configuration.System.PoolPathBase, _configuration.System.PoolPathWorking));
|
|
|
+
|
|
|
+ // Scenario structure
|
|
|
+ EnsureDirectory(_configuration.System.ScenarioPathBase);
|
|
|
+ if (_fileSystem.Directory.GetFiles(TranslatePath(_configuration.System.ScenarioPathBase), "*.*").Length == 0)
|
|
|
+ LogWarn($"Scenario directory '{TranslatePath(_configuration.System.ScenarioPathBase)}' is empty.");
|
|
|
+
|
|
|
+ LogInfo("Configuration validated.");
|
|
|
+ }
|
|
|
+
|
|
|
+ private string EnsureDirectory(string path)
|
|
|
+ {
|
|
|
+ var dir = FileUtils.EnsurePathEndsWithDirSeparator(_fileSystem, TranslatePath(path));
|
|
|
+ if (!_fileSystem.Directory.Exists(dir))
|
|
|
+ {
|
|
|
+ _fileSystem.Directory.CreateDirectory(dir);
|
|
|
+ LogDebug($"Directory '{dir}' created.");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!_fileSystem.Directory.Exists(dir))
|
|
|
+ throw new FileNotFoundException($"Directory '{dir}' not found!");
|
|
|
+ return dir;
|
|
|
+ }
|
|
|
+
|
|
|
+ private string TranslatePath(string path)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrEmpty(path))
|
|
|
+ return path;
|
|
|
+ return path.Replace("{CD}", _fileSystem.Directory.GetCurrentDirectory());
|
|
|
+ }
|
|
|
+
|
|
|
+ private void StopAll(bool silently = false)
|
|
|
+ {
|
|
|
+ _wkPull.Stop(silently);
|
|
|
+ _wkRetention.Stop(silently);
|
|
|
+ _wkProcessing.Stop(silently);
|
|
|
+
|
|
|
+ foreach (var process in _processes)
|
|
|
+ process.Stop(silently);
|
|
|
+
|
|
|
+ foreach (var process in _processes)
|
|
|
+ process.Dispose();
|
|
|
+
|
|
|
+ _processes.Clear();
|
|
|
+ }
|
|
|
+ #endregion
|
|
|
+ }
|
|
|
+}
|