internal SessionManager(ISessionFactory sessionFactory, ITypeLoader typeLoader,
                                IParameterStore parameterStore, INetworkStatusProvider defaultNetworkProvider, ILoggerFactory loggerFactory)
        {
            StackTraceMinimizerExceptionExtensions.DoCompressStackTrace = true;

            _typeLoader             = typeLoader;
            _parameterStore         = parameterStore;
            _sessionFactory         = sessionFactory;
            _loggerFactory          = loggerFactory;
            _defaultNetworkProvider = defaultNetworkProvider;
            _logger      = _loggerFactory.CreateLogger <SessionManager>();
            _configIdMap = new PersistentConfigFileIdMap(_parameterStore);

            Directory.CreateDirectory(ExtraConfigDirPath);

            _logger.LogInformation($"Default configuration file is '{DefaultConfigPath}'");
            _logger.LogInformation($"Extra configuration directory is '{ExtraConfigDirPath}'");

            _defaultConfigWatcher = new FileSystemWatcher(Path.GetDirectoryName(DefaultConfigPath))
            {
                Filter = Path.GetFileName(DefaultConfigPath)
            };
            _extraconfigWatcher = new FileSystemWatcher(ExtraConfigDirPath)
            {
                Filter = "*.json"
            };
            HookFileWatcherEvents(_defaultConfigWatcher);
            HookFileWatcherEvents(_extraconfigWatcher);
            instance = this;
        }
        /// <summary>
        /// Determine the ID of a Session based on the config file path.
        /// </summary>
        /// <param name="configPath">Path of the config file</param>
        /// <returns>ID of the <see cref="ISession"/></returns>
        private int GetIdOfConfigFile(string configPath, PersistentConfigFileIdMap configFileIdMap)
        {
            if (configFileIdMap.TryGetValue(configPath, out int id))
            {
                return(id);
            }

            return(configFileIdMap.MaxUsedId + 1);
        }
        /// <summary>
        /// Check the config file, if it has changed, start a new session and stop the old one.
        /// </summary>
        /// <returns>True iff a new session has been successfully started.</returns>
        private void PollConfig(string configPath, bool isDefault, PersistentConfigFileIdMap configIdMap)
        {
            // Don't process any files outside of the expected directories.
            // This allows us to support validated sessions by placing them in different directories.
            if (!configPath.StartsWith(DefaultConfigPath) && !configPath.StartsWith(ExtraConfigDirPath))
            {
                return;
            }

            try
            {
                var lastWriteTime = File.GetLastWriteTime(configPath);
                if (ConfigPathToSessionMap.TryGetValue(configPath, out var existingSession))
                {
                    // the session is running
                    if (lastWriteTime <= existingSession.StartTime)
                    {
                        // the config file hasn't changed, nothing to do here
                        return;
                    }

                    _logger.LogInformation($"Config file '{configPath}' has changed.");

                    // stop the existing session
                    TerminateSession(existingSession, false);
                    ConfigPathToSessionMap.TryRemove(configPath, out existingSession);
                }
                else
                {
                    _logger.LogInformation($"Config file '{configPath}' is added.");
                }

                // start new session
                var newSession = LaunchSession(configPath, isDefault, configIdMap);
                ConfigPathToSessionMap[configPath] = newSession;
                PublishCounters(string.Empty, MetricsConstants.CATEGORY_PROGRAM, CounterTypeEnum.CurrentValue, new Dictionary <string, MetricValue>()
                {
                    { MetricsConstants.CONFIGS_LOADED, new MetricValue(1) },
                    { MetricsConstants.CONFIGS_FAILED_TO_LOAD, new MetricValue(0) },
                });
            }
            catch (SessionLaunchedException sessionLaunchEx)
            {
                _logger.LogError(0, sessionLaunchEx.InnerException, $"Error lauching session '{sessionLaunchEx.ConfigPath}'");
                PublishCounters(string.Empty, MetricsConstants.CATEGORY_PROGRAM, CounterTypeEnum.CurrentValue, new Dictionary <string, MetricValue>()
                {
                    { MetricsConstants.CONFIGS_LOADED, new MetricValue(0) },
                    { MetricsConstants.CONFIGS_FAILED_TO_LOAD, new MetricValue(1) },
                });
                return;
            }
            catch (Exception ex)
            {
                _logger.LogError(0, ex, $"Error while monitoring config file '{configPath}'");
                return;
            }
        }
        private ISession LaunchSession(string configPath, bool isDefault, PersistentConfigFileIdMap configFileIdMap)
        {
            bool isValidated = !configPath.StartsWith(DefaultConfigPath) && !configPath.StartsWith(ExtraConfigDirPath);

            try
            {
                Guard.ArgumentNotNullOrEmpty(configPath, nameof(configPath));

                var startTime = File.GetLastWriteTime(configPath);

                if (isDefault)
                {
                    Debug.Assert(configPath == DefaultConfigPath,
                                 $"Default config path should be {DefaultConfigPath}, but {configPath} is initialized");
                }

                var id = isDefault
                    ? DefaultSessionId
                    : GetIdOfConfigFile(configPath, configFileIdMap);

                var config = new ConfigurationBuilder()
                             .AddJsonFile(configPath, optional: false, reloadOnChange: false)
                             .Build();

                if (string.IsNullOrWhiteSpace(config[ConfigConstants.CONFIG_DESCRIPTIVE_NAME]))
                {
                    config[ConfigConstants.CONFIG_DESCRIPTIVE_NAME] = isDefault
                        ? "default"
                        : Path.GetFileNameWithoutExtension(configPath);
                }

                // If the configuration has it's own credentials, use them.
                // Otherwise, use the creds from the default config file.
                // If this is the default config, set the private field so subsequent configs can use it.
                var credSection          = config.GetSection("Credentials");
                var configHasCredentials = credSection.GetChildren().Any();
                if (configHasCredentials)
                {
                    _logger.LogDebug("Configuration {0} has credentials, these will be used for sources.", id);
                    if (isDefault)
                    {
                        _defaultCredentialConfig = credSection;
                    }
                }
                else
                {
                    _logger.LogDebug("Configuration {0} has no credentials, using default credential section for sources.", id);
                    credSection = _defaultCredentialConfig;
                }

                var session = _sessionFactory.Create(id, config, startTime,
                                                     _typeLoader, _parameterStore,
                                                     _loggerFactory, _defaultNetworkProvider, credSection, isValidated);

                // start the session
                _logger.LogDebug("Starting session {0}", id);
                session.Start();

                if (!isDefault)
                {
                    configFileIdMap[configPath] = id;
                }

                return(session);
            }
            catch (Exception ex)
            {
                throw new SessionLaunchedException(configPath, ex);
            }
        }