mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-04-16 18:35:35 +08:00
refs #597, use CMD arguments in setup reader.
* Setup reader will now be initialized and handles by CApplication * Setup reader no longer threaded reader as it will be initialized and loade upfront
This commit is contained in:
committed by
Mathew Sutcliffe
parent
6a06aa0460
commit
9ff322ae25
@@ -122,6 +122,11 @@ namespace BlackCore
|
|||||||
QString s("timestamp: ");
|
QString s("timestamp: ");
|
||||||
s.append(this->getFormattedUtcTimestampYmdhms());
|
s.append(this->getFormattedUtcTimestampYmdhms());
|
||||||
s.append(separator);
|
s.append(separator);
|
||||||
|
|
||||||
|
s.append("Loaded: ");
|
||||||
|
s.append(boolToYesNo(this->wasLoaded()));
|
||||||
|
s.append(separator);
|
||||||
|
|
||||||
s.append("For development: ");
|
s.append("For development: ");
|
||||||
s.append(boolToYesNo(isDevelopment()));
|
s.append(boolToYesNo(isDevelopment()));
|
||||||
s.append(separator);
|
s.append(separator);
|
||||||
@@ -200,6 +205,8 @@ namespace BlackCore
|
|||||||
return CVariant::fromValue(this->swiftDbDataFileLocationUrls());
|
return CVariant::fromValue(this->swiftDbDataFileLocationUrls());
|
||||||
case IndexShared:
|
case IndexShared:
|
||||||
return CVariant::fromValue(this->m_sharedUrls);
|
return CVariant::fromValue(this->m_sharedUrls);
|
||||||
|
case IndexWasLoaded:
|
||||||
|
return CVariant::fromValue(this->m_wasLoaded);
|
||||||
default:
|
default:
|
||||||
return CValueObject::propertyByIndex(index);
|
return CValueObject::propertyByIndex(index);
|
||||||
}
|
}
|
||||||
@@ -240,6 +247,9 @@ namespace BlackCore
|
|||||||
case IndexShared:
|
case IndexShared:
|
||||||
this->m_sharedUrls = variant.value<CUrlList>();
|
this->m_sharedUrls = variant.value<CUrlList>();
|
||||||
break;
|
break;
|
||||||
|
case IndexWasLoaded:
|
||||||
|
this->m_wasLoaded = variant.toBool();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
CValueObject::setPropertyByIndex(variant, index);
|
CValueObject::setPropertyByIndex(variant, index);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ namespace BlackCore
|
|||||||
IndexSwiftDbFiles,
|
IndexSwiftDbFiles,
|
||||||
IndexBootstrap,
|
IndexBootstrap,
|
||||||
IndexUpdateInfo,
|
IndexUpdateInfo,
|
||||||
|
IndexWasLoaded,
|
||||||
IndexShared
|
IndexShared
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -52,6 +53,12 @@ namespace BlackCore
|
|||||||
//! Destructor.
|
//! Destructor.
|
||||||
~CGlobalSetup() {}
|
~CGlobalSetup() {}
|
||||||
|
|
||||||
|
//! Has data loaded from web
|
||||||
|
bool wasLoaded() const { return m_wasLoaded; }
|
||||||
|
|
||||||
|
//! Mark as loaded
|
||||||
|
void markAsLoaded(bool loaded) { this->m_wasLoaded = loaded; }
|
||||||
|
|
||||||
//! Http port
|
//! Http port
|
||||||
int dbHttpPort() const { return m_dbHttpPort; }
|
int dbHttpPort() const { return m_dbHttpPort; }
|
||||||
|
|
||||||
@@ -133,6 +140,7 @@ namespace BlackCore
|
|||||||
private:
|
private:
|
||||||
BLACK_ENABLE_TUPLE_CONVERSION(BlackCore::Data::CGlobalSetup)
|
BLACK_ENABLE_TUPLE_CONVERSION(BlackCore::Data::CGlobalSetup)
|
||||||
|
|
||||||
|
bool m_wasLoaded = false; //!< Loaded from web
|
||||||
int m_dbHttpPort = 80; //!< port
|
int m_dbHttpPort = 80; //!< port
|
||||||
int m_dbHttpsPort = 443; //!< SSL port
|
int m_dbHttpsPort = 443; //!< SSL port
|
||||||
bool m_development = false; //!< dev. version?
|
bool m_development = false; //!< dev. version?
|
||||||
@@ -167,6 +175,7 @@ namespace BlackCore
|
|||||||
|
|
||||||
Q_DECLARE_METATYPE(BlackCore::Data::CGlobalSetup)
|
Q_DECLARE_METATYPE(BlackCore::Data::CGlobalSetup)
|
||||||
BLACK_DECLARE_TUPLE_CONVERSION(BlackCore::Data::CGlobalSetup, (
|
BLACK_DECLARE_TUPLE_CONVERSION(BlackCore::Data::CGlobalSetup, (
|
||||||
|
attr(o.m_wasLoaded),
|
||||||
attr(o.m_timestampMSecsSinceEpoch),
|
attr(o.m_timestampMSecsSinceEpoch),
|
||||||
attr(o.m_dbRootDirectoryUrl),
|
attr(o.m_dbRootDirectoryUrl),
|
||||||
attr(o.m_dbHttpPort),
|
attr(o.m_dbHttpPort),
|
||||||
|
|||||||
@@ -30,6 +30,5 @@ namespace BlackCore
|
|||||||
|
|
||||||
BlackCore::Data::CGlobalSetup::registerMetadata();
|
BlackCore::Data::CGlobalSetup::registerMetadata();
|
||||||
BlackCore::Data::CUpdateInfo::registerMetadata();
|
BlackCore::Data::CUpdateInfo::registerMetadata();
|
||||||
BlackCore::CSetupReader::instance(); //! \todo will go into new runtime
|
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -7,9 +7,11 @@
|
|||||||
* contained in the LICENSE file.
|
* contained in the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "blackcore/application.h"
|
||||||
#include "blackmisc/network/networkutils.h"
|
#include "blackmisc/network/networkutils.h"
|
||||||
#include "blackmisc/sequence.h"
|
#include "blackmisc/sequence.h"
|
||||||
#include "blackmisc/logmessage.h"
|
#include "blackmisc/logmessage.h"
|
||||||
|
#include "blackmisc/logcategory.h"
|
||||||
#include "blackmisc/json.h"
|
#include "blackmisc/json.h"
|
||||||
#include "blackmisc/project.h"
|
#include "blackmisc/project.h"
|
||||||
#include "blackmisc/fileutils.h"
|
#include "blackmisc/fileutils.h"
|
||||||
@@ -26,93 +28,145 @@ using namespace BlackCore::Data;
|
|||||||
|
|
||||||
namespace BlackCore
|
namespace BlackCore
|
||||||
{
|
{
|
||||||
CSetupReader::CSetupReader(QObject *owner) :
|
CSetupReader::CSetupReader(QObject *parent) :
|
||||||
CThreadedReader(owner, "CSetupReader")
|
QObject(parent)
|
||||||
{
|
{
|
||||||
connect(this, &CSetupReader::setupSynchronized, this, &CSetupReader::ps_setupSyncronized);
|
connect(this, &CSetupReader::setupSynchronized, this, &CSetupReader::ps_setupSyncronized);
|
||||||
QString localFileName;
|
connect(this, &CSetupReader::updateInfoSynchronized, this, &CSetupReader::ps_versionInfoSyncronized);
|
||||||
if (this->localBootstrapFile(localFileName))
|
}
|
||||||
|
|
||||||
|
QList<QCommandLineOption> CSetupReader::getCmdLineOptions() const
|
||||||
|
{
|
||||||
|
return QList<QCommandLineOption>
|
||||||
|
{
|
||||||
|
{
|
||||||
|
this->m_cmdBootstrapUrl,
|
||||||
|
this->m_cmdBootstrapMode
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
CStatusMessage CSetupReader::asyncLoad()
|
||||||
|
{
|
||||||
|
if (this->readLocalBootstrapFile(this->m_localSetupFileValue))
|
||||||
{
|
{
|
||||||
// initialized by local file for testing
|
// initialized by local file for testing
|
||||||
// I do not even need to start in background here
|
emit this->setupSynchronized(true);
|
||||||
CLogMessage(this).info("Using local bootstrap file: %1") << localFileName;
|
return CStatusMessage(cats(), CStatusMessage::SeverityInfo, "Using local bootstrap file: " + this->m_localSetupFileValue);
|
||||||
BlackMisc::singleShot(1000, QThread::currentThread(), [ = ]()
|
}
|
||||||
{
|
else if (this->m_bootstrapMode == CacheOnly)
|
||||||
emit this->setupSynchronized(true);
|
{
|
||||||
});
|
m_setup.synchronize();
|
||||||
|
CGlobalSetup currentSetup = m_setup.get();
|
||||||
|
this->m_updateInfoUrls = currentSetup.updateInfoFileUrls();
|
||||||
|
emit this->setupSynchronized(true);
|
||||||
|
emit this->updateInfoSynchronized(true);
|
||||||
|
return CStatusMessage(cats(), CStatusMessage::SeverityInfo, "Cache only setup, using it as it is");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this->m_bootstrapUrls.uniqueWrite()->push_back(m_setup.get().bootstrapUrls());
|
// web URL
|
||||||
this->m_updateInfoUrls.uniqueWrite()->push_back(m_setup.get().updateInfoUrls());
|
if (!this->m_bootsrapUrlFileValue.isEmpty())
|
||||||
|
{
|
||||||
|
// start with the one from cmd args
|
||||||
|
this->m_bootstrapUrls.push_front(CUrl(this->m_bootsrapUrlFileValue));
|
||||||
|
}
|
||||||
|
|
||||||
this->m_networkManagerBootstrap = new QNetworkAccessManager(this);
|
// if ever loaded add those URLs
|
||||||
this->connect(this->m_networkManagerBootstrap, &QNetworkAccessManager::finished, this, &CSetupReader::ps_parseSetupFile);
|
m_setup.synchronize();
|
||||||
|
CGlobalSetup currentSetup = m_setup.get();
|
||||||
|
if (currentSetup.wasLoaded())
|
||||||
|
{
|
||||||
|
if (this->m_bootstrapMode != Explicit || this->m_bootstrapUrls.isEmpty())
|
||||||
|
{
|
||||||
|
// also use previously cached URLS
|
||||||
|
this->m_bootstrapUrls.push_back(currentSetup.bootstrapFileUrls());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this->m_networkManagerUpdateInfo = new QNetworkAccessManager(this);
|
if (this->m_bootstrapUrls.isEmpty())
|
||||||
this->connect(this->m_networkManagerUpdateInfo, &QNetworkAccessManager::finished, this, &CSetupReader::ps_parseUpdateInfoFile);
|
{
|
||||||
|
return CStatusMessage(cats(), CStatusMessage::SeverityError, "No bootstrap URLs, cannot load setup");
|
||||||
this->start(QThread::LowPriority);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->m_bootstrapUrls.removeDuplicates();
|
||||||
|
this->ps_readSetup(); // start reading
|
||||||
|
return CStatusMessage(cats(), CStatusMessage::SeverityInfo, "Will start loading setup");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CSetupReader &CSetupReader::instance()
|
bool CSetupReader::parseCmdLineArguments()
|
||||||
{
|
{
|
||||||
static CSetupReader reader(QCoreApplication::instance());
|
this->m_bootsrapUrlFileValue = CGlobalSetup::buildBootstrapFileUrl(
|
||||||
return reader;
|
sApp->getParserOptionValue(this->m_cmdBootstrapUrl)
|
||||||
|
);
|
||||||
|
this->m_bootstrapMode = stringToEnum(sApp->getParserOptionValue(this->m_cmdBootstrapMode));
|
||||||
|
QUrl url(this->m_bootsrapUrlFileValue);
|
||||||
|
|
||||||
|
// check on local file
|
||||||
|
if (url.isLocalFile())
|
||||||
|
{
|
||||||
|
this->m_localSetupFileValue = url.toLocalFile();
|
||||||
|
QFile f(this->m_localSetupFileValue);
|
||||||
|
if (!f.exists())
|
||||||
|
{
|
||||||
|
sApp->errorMessage("File " + this->m_localSetupFileValue + " does not exist");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check on explicit URL
|
||||||
|
if (this->m_bootstrapMode == Explicit)
|
||||||
|
{
|
||||||
|
if (!url.isLocalFile())
|
||||||
|
{
|
||||||
|
if (!CNetworkUtils::canConnect(url))
|
||||||
|
{
|
||||||
|
sApp->errorMessage("URL " + url.toString() + " not reachable");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSetupReader::initialize()
|
void CSetupReader::gracefulShutdown()
|
||||||
{
|
{
|
||||||
// start to read by myself
|
this->m_shutdown = true;
|
||||||
CThreadedReader::initialize();
|
|
||||||
QTimer::singleShot(500, this, &CSetupReader::ps_readSetup);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSetupReader::cleanup()
|
|
||||||
{
|
|
||||||
delete this->m_networkManagerBootstrap;
|
|
||||||
this->m_networkManagerBootstrap = nullptr;
|
|
||||||
|
|
||||||
delete this->m_networkManagerUpdateInfo;
|
|
||||||
this->m_networkManagerUpdateInfo = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSetupReader::ps_readSetup()
|
void CSetupReader::ps_readSetup()
|
||||||
{
|
{
|
||||||
this->threadAssertCheck();
|
CUrl url(this->m_bootstrapUrls.getNextWorkingUrl());
|
||||||
Q_ASSERT_X(this->m_networkManagerBootstrap, Q_FUNC_INFO, "Missing network manager");
|
|
||||||
CUrl url(this->m_bootstrapUrls.uniqueWrite()->getNextWorkingUrl());
|
|
||||||
if (url.isEmpty())
|
if (url.isEmpty())
|
||||||
{
|
{
|
||||||
CLogMessage(this).warning("Cannot read setup, failed URLs: %1") << this->m_bootstrapUrls.read()->getFailedUrls();
|
CLogMessage(this).warning("Cannot read setup, failed URLs: %1") << this->m_bootstrapUrls.getFailedUrls();
|
||||||
emit setupSynchronized(false);
|
emit setupSynchronized(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QNetworkRequest request(url);
|
if (m_shutdown) { return; }
|
||||||
CNetworkUtils::ignoreSslVerification(request);
|
sApp->requestNetworkResource(url.toNetworkRequest(), { this, &CSetupReader::ps_parseSetupFile });
|
||||||
this->m_networkManagerBootstrap->get(request);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSetupReader::ps_readUpdateInfo()
|
void CSetupReader::ps_readUpdateInfo()
|
||||||
{
|
{
|
||||||
this->threadAssertCheck();
|
CUrl url(this->m_updateInfoUrls.getNextWorkingUrl());
|
||||||
Q_ASSERT_X(this->m_networkManagerUpdateInfo, Q_FUNC_INFO, "Missing network manager");
|
|
||||||
CUrl url(this->m_updateInfoUrls.uniqueWrite()->getNextWorkingUrl());
|
|
||||||
if (url.isEmpty())
|
if (url.isEmpty())
|
||||||
{
|
{
|
||||||
CLogMessage(this).warning("Cannot read update info, failed URLs: %1") << this->m_updateInfoUrls.read()->getFailedUrls();
|
CLogMessage(this).warning("Cannot read update info, failed URLs: %1") << this->m_updateInfoUrls.getFailedUrls();
|
||||||
emit versionSynchronized(false);
|
emit updateInfoSynchronized(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QNetworkRequest request(url);
|
if (m_shutdown) { return; }
|
||||||
CNetworkUtils::ignoreSslVerification(request);
|
sApp->requestNetworkResource(url.toNetworkRequest(), { this, &CSetupReader::ps_parseUpdateInfoFile});
|
||||||
this->m_networkManagerUpdateInfo->get(request);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSetupReader::ps_setupSyncronized(bool success)
|
void CSetupReader::ps_setupSyncronized(bool success)
|
||||||
{
|
{
|
||||||
// trigger
|
// trigger consecutive read
|
||||||
|
this->m_setupSyncronized = success;
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
CLogMessage(this).info("Setup synchronized, will trigger read of update information");
|
CLogMessage(this).info("Setup synchronized, will trigger read of update information");
|
||||||
@@ -124,19 +178,44 @@ namespace BlackCore
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CSetupReader::ps_versionInfoSyncronized(bool success)
|
||||||
|
{
|
||||||
|
this->m_updateInfoSyncronized = success;
|
||||||
|
}
|
||||||
|
|
||||||
void CSetupReader::ps_setupChanged()
|
void CSetupReader::ps_setupChanged()
|
||||||
{
|
{
|
||||||
// settings have changed on disk
|
// settings have changed on disk
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSetupReader::localBootstrapFile(QString &fileName)
|
CSetupReader::BootsrapMode CSetupReader::stringToEnum(const QString &s)
|
||||||
{
|
{
|
||||||
QString dir(CProject::getSwiftPrivateResourceDir());
|
const QString bsm(s.toLower().trimmed());
|
||||||
if (dir.isEmpty()) { return false; }
|
if (bsm.startsWith("expl")) return Explicit;
|
||||||
|
if (bsm.startsWith("cache")) return CacheOnly;
|
||||||
|
return Default;
|
||||||
|
}
|
||||||
|
|
||||||
// no version for local files, as those come withe the current code
|
bool CSetupReader::readLocalBootstrapFile(QString &fileName)
|
||||||
fileName = CFileUtils::appendFilePaths(dir, "bootstrap/bootstrap.json");
|
{
|
||||||
QString content(CFileUtils::readFileToString(fileName));
|
if (fileName.isEmpty()) { return false; }
|
||||||
|
QString fn;
|
||||||
|
QFile file(fileName);
|
||||||
|
if (!file.exists())
|
||||||
|
{
|
||||||
|
// relative name?
|
||||||
|
QString dir(CProject::getSwiftPrivateResourceDir());
|
||||||
|
if (dir.isEmpty()) { return false; }
|
||||||
|
|
||||||
|
// no version for local files, as those come withe the current code
|
||||||
|
fn = CFileUtils::appendFilePaths(dir, "bootstrap/bootstrap.json");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fn = fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString content(CFileUtils::readFileToString(fn));
|
||||||
if (content.isEmpty()) { return false; }
|
if (content.isEmpty()) { return false; }
|
||||||
CGlobalSetup s;
|
CGlobalSetup s;
|
||||||
s.convertFromJson(content);
|
s.convertFromJson(content);
|
||||||
@@ -155,22 +234,15 @@ namespace BlackCore
|
|||||||
// wrap pointer, make sure any exit cleans up reply
|
// wrap pointer, make sure any exit cleans up reply
|
||||||
// required to use delete later as object is created in a different thread
|
// required to use delete later as object is created in a different thread
|
||||||
QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nwReply(nwReplyPtr);
|
QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nwReply(nwReplyPtr);
|
||||||
this->threadAssertCheck();
|
if (m_shutdown) { return; }
|
||||||
|
|
||||||
QUrl url(nwReply->url());
|
QUrl url(nwReply->url());
|
||||||
QString urlString(url.toString());
|
QString urlString(url.toString());
|
||||||
QString replyMessage(nwReply->errorString());
|
QString replyMessage(nwReply->errorString());
|
||||||
|
|
||||||
if (this->isAbandoned())
|
|
||||||
{
|
|
||||||
CLogMessage(this).info("Terminated loading bootstrap files");
|
|
||||||
nwReply->abort();
|
|
||||||
emit setupSynchronized(false);
|
|
||||||
return; // stop, terminate straight away, ending thread
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nwReply->error() == QNetworkReply::NoError)
|
if (nwReply->error() == QNetworkReply::NoError)
|
||||||
{
|
{
|
||||||
qint64 lastModified = this->lastModifiedMsSinceEpoch(nwReply.data());
|
qint64 lastModified = CNetworkUtils::lastModifiedMsSinceEpoch(nwReply.data());
|
||||||
QString setupJson(nwReplyPtr->readAll());
|
QString setupJson(nwReplyPtr->readAll());
|
||||||
nwReplyPtr->close();
|
nwReplyPtr->close();
|
||||||
if (setupJson.isEmpty())
|
if (setupJson.isEmpty())
|
||||||
@@ -180,13 +252,11 @@ namespace BlackCore
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CGlobalSetup currentSetup(m_setup.get()); // from cache
|
CGlobalSetup currentSetup = m_setup.get();
|
||||||
CGlobalSetup loadedSetup;
|
CGlobalSetup loadedSetup;
|
||||||
loadedSetup.convertFromJson(Json::jsonObjectFromString(setupJson));
|
loadedSetup.convertFromJson(Json::jsonObjectFromString(setupJson));
|
||||||
loadedSetup.setDevelopment(isForDevelopment()); // always update, regardless what persistent setting says
|
loadedSetup.markAsLoaded(true);
|
||||||
if (loadedSetup.getMSecsSinceEpoch() == 0 && lastModified > 0) { loadedSetup.setMSecsSinceEpoch(lastModified); }
|
if (lastModified > 0 && lastModified > loadedSetup.getMSecsSinceEpoch()) { loadedSetup.setMSecsSinceEpoch(lastModified); }
|
||||||
qint64 currentVersionTimestamp = currentSetup.getMSecsSinceEpoch();
|
|
||||||
qint64 newVersionTimestamp = loadedSetup.getMSecsSinceEpoch();
|
|
||||||
bool sameVersionLoaded = (loadedSetup == currentSetup);
|
bool sameVersionLoaded = (loadedSetup == currentSetup);
|
||||||
if (sameVersionLoaded)
|
if (sameVersionLoaded)
|
||||||
{
|
{
|
||||||
@@ -195,24 +265,27 @@ namespace BlackCore
|
|||||||
return; // success
|
return; // success
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sameType = loadedSetup.hasSameType(currentSetup);
|
qint64 currentVersionTimestamp = currentSetup.getMSecsSinceEpoch();
|
||||||
bool outdatedVersionLoaded = sameType && (newVersionTimestamp < currentVersionTimestamp);
|
qint64 newVersionTimestamp = loadedSetup.getMSecsSinceEpoch();
|
||||||
if (outdatedVersionLoaded)
|
bool outdatedVersionLoaded = (newVersionTimestamp < currentVersionTimestamp);
|
||||||
|
if (this->m_bootstrapMode != Explicit && outdatedVersionLoaded)
|
||||||
{
|
{
|
||||||
CLogMessage(this).info("Setup loaded from %1 outdated, older than version in data cache %2") << urlString << m_setup.getFilename();
|
CLogMessage(this).info("Setup loaded from %1 outdated, older than version in data cache %2") << urlString << m_setup.getFilename();
|
||||||
// try next URL
|
// try next URL
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CStatusMessage m = m_setup.set(loadedSetup);
|
CStatusMessage m = m_setup.set(loadedSetup, loadedSetup.getMSecsSinceEpoch());
|
||||||
if (!m.isEmpty())
|
if (m.isWarningOrAbove())
|
||||||
{
|
{
|
||||||
|
m.setCategories(cats());
|
||||||
CLogMessage(this).preformatted(m);
|
CLogMessage(this).preformatted(m);
|
||||||
emit setupSynchronized(false);
|
emit setupSynchronized(false);
|
||||||
return; // issue with cache
|
return; // issue with cache
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
this->m_updateInfoUrls = loadedSetup.updateInfoFileUrls();
|
||||||
CLogMessage(this).info("Setup: Updated data cache in %1") << this->m_setup.getFilename();
|
CLogMessage(this).info("Setup: Updated data cache in %1") << this->m_setup.getFilename();
|
||||||
emit setupSynchronized(true);
|
emit setupSynchronized(true);
|
||||||
return; // success
|
return; // success
|
||||||
@@ -229,7 +302,7 @@ namespace BlackCore
|
|||||||
}
|
}
|
||||||
|
|
||||||
// try next one if any
|
// try next one if any
|
||||||
if (this->m_bootstrapUrls.uniqueWrite()->addFailedUrl(url))
|
if (this->m_bootstrapUrls.addFailedUrl(url))
|
||||||
{
|
{
|
||||||
QTimer::singleShot(500, this, &CSetupReader::ps_readSetup);
|
QTimer::singleShot(500, this, &CSetupReader::ps_readSetup);
|
||||||
}
|
}
|
||||||
@@ -244,22 +317,15 @@ namespace BlackCore
|
|||||||
// wrap pointer, make sure any exit cleans up reply
|
// wrap pointer, make sure any exit cleans up reply
|
||||||
// required to use delete later as object is created in a different thread
|
// required to use delete later as object is created in a different thread
|
||||||
QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nwReply(nwReplyPtr);
|
QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nwReply(nwReplyPtr);
|
||||||
this->threadAssertCheck();
|
if (m_shutdown) { return; }
|
||||||
|
|
||||||
QUrl url(nwReply->url());
|
QUrl url(nwReply->url());
|
||||||
QString urlString(url.toString());
|
QString urlString(url.toString());
|
||||||
QString replyMessage(nwReply->errorString());
|
QString replyMessage(nwReply->errorString());
|
||||||
|
|
||||||
if (this->isAbandoned())
|
|
||||||
{
|
|
||||||
CLogMessage(this).info("Terminated loading of update info");
|
|
||||||
nwReply->abort();
|
|
||||||
emit versionSynchronized(false);
|
|
||||||
return; // stop, terminate straight away, ending thread
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nwReply->error() == QNetworkReply::NoError)
|
if (nwReply->error() == QNetworkReply::NoError)
|
||||||
{
|
{
|
||||||
qint64 lastModified = this->lastModifiedMsSinceEpoch(nwReply.data());
|
qint64 lastModified = CNetworkUtils::lastModifiedMsSinceEpoch(nwReply.data());
|
||||||
QString setupJson(nwReplyPtr->readAll());
|
QString setupJson(nwReplyPtr->readAll());
|
||||||
nwReplyPtr->close();
|
nwReplyPtr->close();
|
||||||
if (setupJson.isEmpty())
|
if (setupJson.isEmpty())
|
||||||
@@ -273,20 +339,18 @@ namespace BlackCore
|
|||||||
CUpdateInfo loadedUpdateInfo;
|
CUpdateInfo loadedUpdateInfo;
|
||||||
loadedUpdateInfo.convertFromJson(Json::jsonObjectFromString(setupJson));
|
loadedUpdateInfo.convertFromJson(Json::jsonObjectFromString(setupJson));
|
||||||
loadedUpdateInfo.setDevelopment(isForDevelopment()); // always update, regardless what persistent setting says
|
loadedUpdateInfo.setDevelopment(isForDevelopment()); // always update, regardless what persistent setting says
|
||||||
if (loadedUpdateInfo.getMSecsSinceEpoch() == 0 && lastModified > 0) { loadedUpdateInfo.setMSecsSinceEpoch(lastModified); }
|
if (lastModified > 0 && lastModified > loadedUpdateInfo.getMSecsSinceEpoch()) { loadedUpdateInfo.setMSecsSinceEpoch(lastModified); }
|
||||||
qint64 currentVersionTimestamp = currentUpdateInfo.getMSecsSinceEpoch();
|
|
||||||
qint64 newVersionTimestamp = loadedUpdateInfo.getMSecsSinceEpoch();
|
|
||||||
bool sameVersionLoaded = (loadedUpdateInfo == currentUpdateInfo);
|
bool sameVersionLoaded = (loadedUpdateInfo == currentUpdateInfo);
|
||||||
if (sameVersionLoaded)
|
if (sameVersionLoaded)
|
||||||
{
|
{
|
||||||
CLogMessage(this).info("Same update info loaded from %1 as already in data cache %2") << urlString << m_updateInfo.getFilename();
|
CLogMessage(this).info("Same update info version loaded from %1 as already in data cache %2") << urlString << m_setup.getFilename();
|
||||||
this->setUpdateTimestamp();
|
emit updateInfoSynchronized(true);
|
||||||
emit versionSynchronized(true);
|
|
||||||
return; // success
|
return; // success
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sameType = loadedUpdateInfo.hasSameType(currentUpdateInfo);
|
qint64 currentVersionTimestamp = currentUpdateInfo.getMSecsSinceEpoch();
|
||||||
bool outdatedVersionLoaded = sameType && (newVersionTimestamp < currentVersionTimestamp);
|
qint64 newVersionTimestamp = loadedUpdateInfo.getMSecsSinceEpoch();
|
||||||
|
bool outdatedVersionLoaded = (newVersionTimestamp < currentVersionTimestamp);
|
||||||
if (outdatedVersionLoaded)
|
if (outdatedVersionLoaded)
|
||||||
{
|
{
|
||||||
CLogMessage(this).info("Update info loaded from %1 outdated, older than version in data cache %2") << urlString << m_updateInfo.getFilename();
|
CLogMessage(this).info("Update info loaded from %1 outdated, older than version in data cache %2") << urlString << m_updateInfo.getFilename();
|
||||||
@@ -294,21 +358,21 @@ namespace BlackCore
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CStatusMessage m = m_updateInfo.set(loadedUpdateInfo);
|
CStatusMessage m = m_updateInfo.set(loadedUpdateInfo, loadedUpdateInfo.getMSecsSinceEpoch());
|
||||||
if (!m.isEmpty())
|
if (!m.isEmpty())
|
||||||
{
|
{
|
||||||
|
m.setCategories(cats());
|
||||||
CLogMessage(this).preformatted(m);
|
CLogMessage(this).preformatted(m);
|
||||||
emit versionSynchronized(false);
|
emit updateInfoSynchronized(false);
|
||||||
return; // issue with cache
|
return; // issue with cache
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CLogMessage(this).info("Update info: Updated data cache in %1") << m_updateInfo.getFilename();
|
CLogMessage(this).info("Update info: Updated data cache in %1") << m_updateInfo.getFilename();
|
||||||
this->setUpdateTimestamp();
|
emit updateInfoSynchronized(true);
|
||||||
emit versionSynchronized(true);
|
|
||||||
return; // success
|
return; // success
|
||||||
} // cache
|
} // cache
|
||||||
} // outdated?
|
} // outdated
|
||||||
|
|
||||||
} // json empty
|
} // json empty
|
||||||
} // no error
|
} // no error
|
||||||
@@ -320,14 +384,20 @@ namespace BlackCore
|
|||||||
}
|
}
|
||||||
|
|
||||||
// try next one if any
|
// try next one if any
|
||||||
if (this->m_updateInfoUrls.uniqueWrite()->addFailedUrl(url))
|
if (this->m_updateInfoUrls.addFailedUrl(url))
|
||||||
{
|
{
|
||||||
QTimer::singleShot(500, this, &CSetupReader::ps_readSetup);
|
QTimer::singleShot(500, this, &CSetupReader::ps_readSetup);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
emit versionSynchronized(false);
|
emit updateInfoSynchronized(false);
|
||||||
}
|
}
|
||||||
} // method
|
} // function
|
||||||
|
|
||||||
|
const CLogCategoryList &CSetupReader::cats()
|
||||||
|
{
|
||||||
|
static const CLogCategoryList cats(CLogCategoryList(this).join({ CLogCategory::webservice()}));
|
||||||
|
return cats;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -21,39 +21,56 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
|
#include <QCommandLineOption>
|
||||||
|
|
||||||
namespace BlackCore
|
namespace BlackCore
|
||||||
{
|
{
|
||||||
//! Read the central URLs / locations of our data / setup
|
//! Read the central URLs / locations of our data / setup.
|
||||||
class BLACKCORE_EXPORT CSetupReader : public BlackMisc::CThreadedReader
|
//! This should be only used in BlackCore::CApplication
|
||||||
|
//! \note This class is no(!) BlackCore::CThreadedReader as it will be loaded once during startup
|
||||||
|
//! and is usually fast.
|
||||||
|
//! \sa BlackCore::Data::GlobalSetup
|
||||||
|
//! \sa BlackCore::Data::UpdateInfo
|
||||||
|
class BLACKCORE_EXPORT CSetupReader : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
friend class CApplication; //!< only using class
|
||||||
//! Single instance
|
|
||||||
static CSetupReader &instance();
|
protected:
|
||||||
|
//! Constructor
|
||||||
|
explicit CSetupReader(QObject *parent);
|
||||||
|
|
||||||
|
//! Load the data
|
||||||
|
BlackMisc::CStatusMessage asyncLoad();
|
||||||
|
|
||||||
|
//! Parse cmd line arguments
|
||||||
|
bool parseCmdLineArguments();
|
||||||
|
|
||||||
|
//! Add cmd line arguments to BlackCore::CApplication
|
||||||
|
QList<QCommandLineOption> getCmdLineOptions() const;
|
||||||
|
|
||||||
|
//! Terminate
|
||||||
|
void gracefulShutdown();
|
||||||
|
|
||||||
|
//! Setup loaded?
|
||||||
|
bool isSetupSyncronized() const { return m_setupSyncronized; }
|
||||||
|
|
||||||
|
//! Version info loaded?
|
||||||
|
bool isUpdateSyncronized() const { return m_updateInfoSyncronized; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
//! Setup has been read
|
//! Setup has been read
|
||||||
void setupSynchronized(bool success);
|
void setupSynchronized(bool success);
|
||||||
|
|
||||||
//! Version bas been read
|
//! Version bas been read
|
||||||
void versionSynchronized(bool success);
|
void updateInfoSynchronized(bool success);
|
||||||
|
|
||||||
protected slots:
|
|
||||||
//! \copydoc BlackMisc::CThreadedReader::initialize
|
|
||||||
virtual void initialize() override;
|
|
||||||
|
|
||||||
//! \copydoc BlackMisc::CThreadedReader::cleanup
|
|
||||||
virtual void cleanup() override;
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
//! Setup has been read
|
//! Setup has been read
|
||||||
//! \threadsafe
|
|
||||||
void ps_parseSetupFile(QNetworkReply *nwReply);
|
void ps_parseSetupFile(QNetworkReply *nwReply);
|
||||||
|
|
||||||
//! Update info has been read
|
//! Update info has been read
|
||||||
//! \threadsafe
|
|
||||||
void ps_parseUpdateInfoFile(QNetworkReply *nwReplyPtr);
|
void ps_parseUpdateInfoFile(QNetworkReply *nwReplyPtr);
|
||||||
|
|
||||||
//! Do reading
|
//! Do reading
|
||||||
@@ -65,25 +82,56 @@ namespace BlackCore
|
|||||||
//! Setup has beem syncronized
|
//! Setup has beem syncronized
|
||||||
void ps_setupSyncronized(bool success);
|
void ps_setupSyncronized(bool success);
|
||||||
|
|
||||||
|
//! Version info has beem syncronized
|
||||||
|
void ps_versionInfoSyncronized(bool success);
|
||||||
|
|
||||||
//! Setup has been changed
|
//! Setup has been changed
|
||||||
void ps_setupChanged();
|
void ps_setupChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QNetworkAccessManager *m_networkManagerBootstrap = nullptr;
|
//! Bootstrap mode
|
||||||
QNetworkAccessManager *m_networkManagerUpdateInfo = nullptr;
|
enum BootsrapMode
|
||||||
BlackMisc::LockFree<BlackMisc::Network::CFailoverUrlList> m_bootstrapUrls;
|
{
|
||||||
BlackMisc::LockFree<BlackMisc::Network::CFailoverUrlList> m_updateInfoUrls;
|
Default,
|
||||||
|
Explicit,
|
||||||
|
CacheOnly
|
||||||
|
};
|
||||||
|
|
||||||
|
bool m_shutdown = false;
|
||||||
|
bool m_setupSyncronized = false;
|
||||||
|
bool m_updateInfoSyncronized = false;
|
||||||
|
QString m_localSetupFileValue;
|
||||||
|
QString m_bootsrapUrlFileValue;
|
||||||
|
BootsrapMode m_bootstrapMode;
|
||||||
|
BlackMisc::Network::CFailoverUrlList m_bootstrapUrls;
|
||||||
|
BlackMisc::Network::CFailoverUrlList m_updateInfoUrls;
|
||||||
BlackMisc::CData<BlackCore::Data::GlobalSetup> m_setup {this, &CSetupReader::ps_setupChanged}; //!< data cache setup
|
BlackMisc::CData<BlackCore::Data::GlobalSetup> m_setup {this, &CSetupReader::ps_setupChanged}; //!< data cache setup
|
||||||
BlackMisc::CData<BlackCore::Data::UpdateInfo> m_updateInfo {this}; //!< data cache update info
|
BlackMisc::CData<BlackCore::Data::UpdateInfo> m_updateInfo {this}; //!< data cache update info
|
||||||
|
|
||||||
//! Constructor
|
QCommandLineOption m_cmdBootstrapUrl
|
||||||
explicit CSetupReader(QObject *owner);
|
{
|
||||||
|
{ "url", "bootstrap-url", "bootstrapurl" },
|
||||||
|
QCoreApplication::translate("application", "bootsrap URL, e.g. datastore.swift-project.org"),
|
||||||
|
"bootstrapurl"
|
||||||
|
}; //!< bootstrap URL
|
||||||
|
QCommandLineOption m_cmdBootstrapMode
|
||||||
|
{
|
||||||
|
{ "bmode", "bootstrap-mode", "bootstrapmode" },
|
||||||
|
QCoreApplication::translate("application", "bootstrap mode: (e)xplicit, d(default), (c)ache-only"),
|
||||||
|
"bootstrapmode", "default"
|
||||||
|
}; //!< bootstrap mode
|
||||||
|
|
||||||
//! Read by local individual file
|
//! Read by local individual file
|
||||||
bool localBootstrapFile(QString &fileName);
|
bool readLocalBootstrapFile(QString &fileName);
|
||||||
|
|
||||||
|
//! Convert string to mode
|
||||||
|
static BootsrapMode stringToEnum(const QString &s);
|
||||||
|
|
||||||
//! Read for development environment?
|
//! Read for development environment?
|
||||||
static bool isForDevelopment();
|
static bool isForDevelopment();
|
||||||
|
|
||||||
|
//! Categories
|
||||||
|
const BlackMisc::CLogCategoryList &cats();
|
||||||
};
|
};
|
||||||
} // ns
|
} // ns
|
||||||
|
|
||||||
|
|||||||
@@ -246,5 +246,18 @@ namespace BlackMisc
|
|||||||
CNetworkUtils::ignoreSslVerification(request);
|
CNetworkUtils::ignoreSslVerification(request);
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qint64 CNetworkUtils::lastModifiedMsSinceEpoch(QNetworkReply *nwReply)
|
||||||
|
{
|
||||||
|
if (nwReply)
|
||||||
|
{
|
||||||
|
QVariant lastModifiedQv = nwReply->header(QNetworkRequest::LastModifiedHeader);
|
||||||
|
if (lastModifiedQv.isValid() && lastModifiedQv.canConvert<QDateTime>())
|
||||||
|
{
|
||||||
|
return lastModifiedQv.value<QDateTime>().toMSecsSinceEpoch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespacee
|
} // namespacee
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QUrlQuery>
|
#include <QUrlQuery>
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
|
#include <QNetworkReply>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
|
||||||
namespace BlackMisc
|
namespace BlackMisc
|
||||||
@@ -110,10 +111,12 @@ namespace BlackMisc
|
|||||||
//! Our tweakes network request
|
//! Our tweakes network request
|
||||||
static QNetworkRequest getNetworkRequest(const CUrl &url, RequestType type = Get);
|
static QNetworkRequest getNetworkRequest(const CUrl &url, RequestType type = Get);
|
||||||
|
|
||||||
|
//! Last modified from reply
|
||||||
|
static qint64 lastModifiedMsSinceEpoch(QNetworkReply *nwReply);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//! Hidden constructor
|
//! Hidden constructor
|
||||||
CNetworkUtils() {}
|
CNetworkUtils() {}
|
||||||
|
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include "threadedreader.h"
|
#include "threadedreader.h"
|
||||||
#include "blackmisc/threadutils.h"
|
#include "blackmisc/threadutils.h"
|
||||||
|
#include "blackmisc/network/networkutils.h"
|
||||||
|
|
||||||
using namespace BlackMisc;
|
using namespace BlackMisc;
|
||||||
using namespace BlackMisc::Network;
|
using namespace BlackMisc::Network;
|
||||||
@@ -26,15 +27,7 @@ namespace BlackMisc
|
|||||||
|
|
||||||
qint64 CThreadedReader::lastModifiedMsSinceEpoch(QNetworkReply *nwReply) const
|
qint64 CThreadedReader::lastModifiedMsSinceEpoch(QNetworkReply *nwReply) const
|
||||||
{
|
{
|
||||||
if (nwReply)
|
return CNetworkUtils::lastModifiedMsSinceEpoch(nwReply);
|
||||||
{
|
|
||||||
QVariant lastModifiedQv = nwReply->header(QNetworkRequest::LastModifiedHeader);
|
|
||||||
if (lastModifiedQv.isValid() && lastModifiedQv.canConvert<QDateTime>())
|
|
||||||
{
|
|
||||||
return lastModifiedQv.value<QDateTime>().toMSecsSinceEpoch();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QDateTime CThreadedReader::getUpdateTimestamp() const
|
QDateTime CThreadedReader::getUpdateTimestamp() const
|
||||||
|
|||||||
Reference in New Issue
Block a user