diff --git a/src/blackcore/db/networkwatchdog.cpp b/src/blackcore/db/networkwatchdog.cpp new file mode 100644 index 000000000..2f7814ad2 --- /dev/null +++ b/src/blackcore/db/networkwatchdog.cpp @@ -0,0 +1,212 @@ +/* Copyright (C) 2017 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "networkwatchdog.h" +#include "application.h" +#include "blackmisc/network/networkutils.h" + +using namespace BlackMisc; +using namespace BlackMisc::Network; + +namespace BlackCore +{ + namespace Db + { + CNetworkWatchdog::CNetworkWatchdog(QObject *parent) : CContinuousWorker(parent, "swift DB watchdog") + { + Q_ASSERT_X(sApp, Q_FUNC_INFO, "Need sApp"); + const bool network = sApp->isNetworkAccessible(); // default + m_networkAccessible = network; + m_dbAccessible = network && m_checkDbAccessibility; + m_internetAccessible = network; + + if (network) + { + this->initWorkingSharedUrlFromSetup(); + } + + m_updateTimer.setInterval(10 * 1000); + connect(&m_updateTimer, &QTimer::timeout, this, &CNetworkWatchdog::doWork); + } + + void CNetworkWatchdog::setDbAccessibility(bool available) + { + m_dbAccessible = available; + m_internetAccessible = m_internetAccessible && m_networkAccessible; + QTimer::singleShot(0, &m_updateTimer, [this] { this->m_updateTimer.start(); }); // restart + } + + CUrl CNetworkWatchdog::getWorkingSharedUrl() const + { + if (!m_networkAccessible) return CUrl(); + QReadLocker l(&m_lockSharedUrl); + return m_workingSharedUrl; + } + + int CNetworkWatchdog::triggerCheck() + { + if (!this->doWorkCheck()) return false; // senseless + if (m_checkInProgress) return -1; + + const int n = this->getCheckCount(); + QTimer::singleShot(0, this, &CNetworkWatchdog::doWork); + return n; // triggered + } + + void CNetworkWatchdog::setWorkingSharedUrl(const CUrl &workingUrl) + { + QWriteLocker l(&m_lockSharedUrl); + m_workingSharedUrl = workingUrl; + } + + bool CNetworkWatchdog::isDbUrl(const CUrl &url) + { + const QString host(url.getHost()); + return host == dbHost(); + } + + void CNetworkWatchdog::doWork() + { + if (!this->doWorkCheck()) { return; } + if (m_checkInProgress) { return; } + m_checkInProgress = true; + + do + { + const bool wasDbAvailable = m_dbAccessible; + const bool wasInternetAvailable = m_internetAccessible; + const bool networkAccess = m_networkAccessible; + const bool canConnectDb = m_checkDbAccessibility && networkAccess && + CNetworkUtils::canConnect(dbTestUrl()); // running in background worker + bool canConnectInternet = canConnectDb; + bool checkInternetAccess = !canConnectDb; + + m_dbAccessible = canConnectDb; + if (canConnectDb) + { + // DB available means internet available + m_internetAccessible = canConnectDb; + } + + // check shared URL + if (!this->doWorkCheck()) { break; } + if (m_checkSharedUrl && networkAccess) + { + if (CNetworkUtils::canConnect(this->getWorkingSharedUrl())) + { + canConnectInternet = true; + checkInternetAccess = false; + } + else + { + const CUrl sharedUrl = this->getWorkingSharedUrl(); + if (!sharedUrl.isEmpty()) + { + canConnectInternet = true; + checkInternetAccess = false; + this->setWorkingSharedUrl(sharedUrl); + } + } + } + + // check internet access + if (!this->doWorkCheck()) { break; } + if (checkInternetAccess) + { + QString message; + static const QString testHost1("www.google.com"); // what else? + canConnectInternet = CNetworkUtils::canConnect(testHost1, 443, message); // running in background worker + if (!canConnectInternet) + { + static const QString testHost2("www.microsoft.com"); // secondary test + canConnectInternet = CNetworkUtils::canConnect(testHost2, 80, message); // running in background worker + } + } + m_internetAccessible = networkAccess && canConnectInternet; + + // signals + m_checkCount++; + this->triggerChangedSignals(wasDbAvailable, wasInternetAvailable); + } + while (false); + + m_updateTimer.start(); // restart + m_checkInProgress = false; + } + + bool CNetworkWatchdog::doWorkCheck() const + { + if (!sApp) { return false; } + if (sApp->isShuttingDown()) { return false; } + if (!this->isEnabled()) { return false; } + return true; + } + + void CNetworkWatchdog::onChangedNetworkAccessibility(QNetworkAccessManager::NetworkAccessibility accessible) + { + const bool db = m_dbAccessible; + const bool internet = m_internetAccessible; + + // Intentionally rating unknown as "accessible" + if (accessible == QNetworkAccessManager::NotAccessible) + { + m_networkAccessible = false; + m_dbAccessible = false; + m_internetAccessible = false; + this->triggerChangedSignals(db, internet); + } + else + { + m_networkAccessible = true; + QTimer::singleShot(0, this, &CNetworkWatchdog::doWork); + } + } + + void CNetworkWatchdog::triggerChangedSignals(bool oldDbAccessible, bool oldInternetAccessible) + { + if (!this->doWorkCheck()) { return; } + + // trigger really queued + if (oldDbAccessible != m_dbAccessible) + { + QTimer::singleShot(0, this, [this] { emit this->changedSwiftDbAccessibility(m_dbAccessible);}); + } + if (oldInternetAccessible != m_internetAccessible) + { + QTimer::singleShot(0, this, [this] { emit this->changedInternetAccessibility(m_internetAccessible);}); + } + } + + void CNetworkWatchdog::initWorkingSharedUrlFromSetup() + { + const CUrl workingUrl(CNetworkWatchdog::workingSharedUrlFromSetup()); // takes long + this->setWorkingSharedUrl(workingUrl); + } + + BlackMisc::Network::CUrl CNetworkWatchdog::dbTestUrl() + { + // requires global setup to be read + const CUrl testUrl(sApp->getGlobalSetup().getDbHomePageUrl()); + return testUrl; + } + + QString CNetworkWatchdog::dbHost() + { + const QString host = dbTestUrl().getHost(); + return host; + } + + CUrl CNetworkWatchdog::workingSharedUrlFromSetup() + { + const CUrlList urls(sApp->getGlobalSetup().getSwiftSharedUrls()); + CFailoverUrlList failoverUrls(urls); + return failoverUrls.getRandomWorkingUrl(); // use CNetworkUtils::canConnect + } + } // ns +} // ns diff --git a/src/blackcore/db/networkwatchdog.h b/src/blackcore/db/networkwatchdog.h new file mode 100644 index 000000000..8f358627e --- /dev/null +++ b/src/blackcore/db/networkwatchdog.h @@ -0,0 +1,130 @@ +/* Copyright (C) 2017 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKCORE_DB_NETWORKWATCHDOG_H +#define BLACKCORE_DB_NETWORKWATCHDOG_H + +#include "blackmisc/worker.h" +#include "blackmisc/network/url.h" +#include +#include +#include + +namespace BlackCore +{ + namespace Db + { + /** + * Monitoring the swift DB, internet access, shared URL + */ + class CNetworkWatchdog : public BlackMisc::CContinuousWorker + { + Q_OBJECT + + public: + //! Ctor + explicit CNetworkWatchdog(QObject *parent = nullptr); + + //! DB available? + //! \threadsafe + bool isSwiftDbAccessible() const { return m_dbAccessible; } + + //! Set DB as avialable (from external) + //! \remark if data was read from DB, this can save another check + //! \threadsafe + void setDbAccessibility(bool available); + + //! DB is accessible + //! \threadsafe + void setDbIsAccessible() { this->setDbAccessibility(true); } + + //! DB is NOT accessible + //! \threadsafe + void setDbIsNotAccessible() { this->setDbAccessibility(false); } + + //! Check the DB availability, can disable the check + //! \threadsafe + void setCheckDbAccessibility(bool check) { m_checkDbAccessibility = check; } + + //! Check the shared URL, can disable the check + //! \threadsafe + void setCheckSharedUrl(bool check) { m_checkSharedUrl = check; } + + //! Internet available? + //! \threadsafe + bool isInternetAccessible() const { return m_internetAccessible; } + + //! A working shared URL + //! \threadsafe + BlackMisc::Network::CUrl getWorkingSharedUrl() const; + + //! Run a check + int triggerCheck(); + + //! Number of completed checks + int getCheckCount() const { return m_checkCount; } + + //! Set working URL from external + //! \threadsafe + void setWorkingSharedUrl(const BlackMisc::Network::CUrl &workingUrl); + + //! Network status changed, use this function to inform the watchdog + void onChangedNetworkAccessibility(QNetworkAccessManager::NetworkAccessibility accessible); + + //! URL referring to the DB + //! \remark depends on BlackCore::Application::getGlobalSetup() + static bool isDbUrl(const BlackMisc::Network::CUrl &url); + + signals: + //! DB was available, but not longer is and vice versa + void changedSwiftDbAccessibility(bool available); + + //! Internet was available, but not longer is and vice versa + void changedInternetAccessibility(bool available); + + private: + //! Do work, i.e. check connectivity + void doWork(); + + //! Do check + bool doWorkCheck() const; + + //! Trigger the changed signals + void triggerChangedSignals(bool oldDbAccessible, bool oldInternetAccessible); + + //! Init a working shared URL + void initWorkingSharedUrlFromSetup(); + + //! The URL being tested + //! \remark depends on BlackCore::Application::getGlobalSetup() + static BlackMisc::Network::CUrl dbTestUrl(); + + //! The DB server + //! \remark depends on BlackCore::Application::getGlobalSetup() + static QString dbHost(); + + //! Obtain working DB data file location URL + //! \remark depends on BlackCore::Application::getGlobalSetup() + static BlackMisc::Network::CUrl workingSharedUrlFromSetup(); + + std::atomic_bool m_networkAccessible { true }; + std::atomic_bool m_internetAccessible { true }; + std::atomic_bool m_dbAccessible { true }; + std::atomic_bool m_checkDbAccessibility { true }; + std::atomic_bool m_checkSharedUrl { true }; + std::atomic_bool m_checkInProgress { false }; //!< a check is currently in progress + std::atomic_int m_checkCount { 0 }; //!< counting number of checks + BlackMisc::Network::CUrl m_workingSharedUrl; + mutable QReadWriteLock m_lockSharedUrl; + }; + } // ns +} // ns +#endif // guard