refactor: Clean up setup file reader

Previously we already switch to loading the bootstrap file only from the
local file (ec42553910).
This removes the remaining parts of loading the bootstrap file from a
remote location.
This also updates the UI in case of parsing errors of the bootstrap.json.
This commit is contained in:
Lars Toenning
2024-02-04 16:13:30 +01:00
parent 8704d463b8
commit 389431ccef
22 changed files with 106 additions and 1003 deletions

View File

@@ -67,6 +67,5 @@
}, },
"ssrEquipmentHelpUrl": { "ssrEquipmentHelpUrl": {
"url": "https://en.wikipedia.org/wiki/Equipment_codes#Surveillance_equipment_codes" "url": "https://en.wikipedia.org/wiki/Equipment_codes#Surveillance_equipment_codes"
}, }
"wasLoadedFromFile": false
} }

View File

@@ -149,10 +149,6 @@ namespace BlackCore
// global setup // global setup
m_setupReader.reset(new CSetupReader(this)); m_setupReader.reset(new CSetupReader(this));
connect(m_setupReader.data(), &CSetupReader::setupHandlingCompleted, this, &CApplication::onSetupHandlingCompleted, Qt::QueuedConnection);
connect(m_setupReader.data(), &CSetupReader::setupHandlingCompleted, this, &CApplication::setupHandlingCompleted, Qt::QueuedConnection); // hand thru
this->addParserOptions(m_setupReader->getCmdLineOptions()); // add options from reader
// check for updates // check for updates
m_gitHubPackagesReader.reset(new CGitHubPackagesReader(this)); m_gitHubPackagesReader.reset(new CGitHubPackagesReader(this));
@@ -380,13 +376,7 @@ namespace BlackCore
}); });
} }
//! \fixme KB 9/17 waiting for setup reader here is supposed to be replaced by explicitly waiting for reader Q_ASSERT_X(m_setupReader && m_setupReader->isSetupAvailable(), Q_FUNC_INFO, "Setup not available");
if (!m_setupReader->isSetupAvailable())
{
msgs = this->requestReloadOfSetupAndVersion();
if (msgs.isFailure()) { break; }
if (msgs.isSuccess()) { msgs.push_back(this->waitForSetup()); }
}
// start hookin // start hookin
msgs.push_back(this->startHookIn()); msgs.push_back(this->startHookIn());
@@ -413,62 +403,12 @@ namespace BlackCore
return m_started; return m_started;
} }
CStatusMessageList CApplication::waitForSetup(int timeoutMs)
{
if (!m_setupReader) { return CStatusMessage(this).error(u"No setup reader"); }
CEventLoop eventLoop(this);
eventLoop.stopWhen(this, &CApplication::setupHandlingCompleted);
if (!m_setupReader->isSetupAvailable())
{
eventLoop.exec(timeoutMs);
}
// setup handling completed with success or failure, or we run into time out
CStatusMessageList msgs;
if (!eventLoop.isGuardAlive())
{
msgs.push_back(CStatusMessage(this).error(u"Setup not available, already shutting down."));
return msgs;
}
bool forced = false;
if (!m_setupReader->isSetupAvailable())
{
forced = true;
m_setupReader->forceAvailabilityUpdate(); // maybe web reading still hanging
}
if (m_setupReader->isSetupAvailable())
{
msgs.push_back(CStatusMessage(this).info(forced ? QStringLiteral("Setup available after forcing (so likely web read still pending)") : QStringLiteral("Setup available")));
return msgs;
}
// getting here means no "real" read success, and NO available cache
msgs.push_back(CStatusMessage(this).error(u"Setup not available, setup reading failed or timed out."));
if (m_setupReader->getLastSetupReadErrorMessages().hasErrorMessages())
{
msgs.push_back(m_setupReader->getLastSetupReadErrorMessages());
}
if (m_setupReader->hasCmdLineBootstrapUrl())
{
msgs.push_back(CStatusMessage(this).info(u"Bootstrap URL cmd line argument '%1'") << m_setupReader->getCmdLineBootstrapUrl());
}
return msgs;
}
bool CApplication::isSetupAvailable() const bool CApplication::isSetupAvailable() const
{ {
if (m_shutdown || !m_setupReader) { return false; } if (m_shutdown || !m_setupReader) { return false; }
return m_setupReader->isSetupAvailable(); return m_setupReader->isSetupAvailable();
} }
CStatusMessageList CApplication::requestReloadOfSetupAndVersion()
{
if (m_shutdown) { return CStatusMessage(this).warning(u"Shutting down, not reading"); }
if (!m_setupReader) { return CStatusMessage(this).error(u"No reader for setup/version"); }
Q_ASSERT_X(m_parsed, Q_FUNC_INFO, "Not yet parsed");
return m_setupReader->asyncLoad();
}
bool CApplication::hasMinimumMappingVersion() const bool CApplication::hasMinimumMappingVersion() const
{ {
return (this->getGlobalSetup().isSwiftVersionMinimumMappingVersion()); return (this->getGlobalSetup().isSwiftVersionMinimumMappingVersion());
@@ -1103,7 +1043,6 @@ namespace BlackCore
if (m_setupReader) if (m_setupReader)
{ {
m_setupReader->gracefulShutdown();
m_setupReader.reset(); m_setupReader.reset();
} }
@@ -1448,15 +1387,26 @@ namespace BlackCore
if (!this->parsingHookIn()) { return false; } if (!this->parsingHookIn()) { return false; }
// setup reader // setup reader
m_setupReader->parseCmdLineArguments();
m_parsed = true; m_parsed = true;
return true; return true;
} }
bool CApplication::parseAndSynchronizeSetup(int timeoutMs) bool CApplication::parseAndLoadSetup()
{ {
if (!this->parseAndStartupCheck()) return false; if (!this->parseAndStartupCheck()) return false;
return !this->synchronizeSetup(timeoutMs).hasErrorMessages(); const CStatusMessageList msgs = loadSetup();
if (msgs.isFailure())
{
displaySetupLoadFailure(msgs);
}
return msgs.isSuccess();
}
void CApplication::displaySetupLoadFailure(BlackMisc::CStatusMessageList)
{
// Ignore for CLI application
// Already logged to console
} }
bool CApplication::cmdLineWarningMessage(const QString &text, const QString &informativeText) const bool CApplication::cmdLineWarningMessage(const QString &text, const QString &informativeText) const
@@ -1656,17 +1606,14 @@ namespace BlackCore
return m_setupReader.data(); return m_setupReader.data();
} }
QString CApplication::getLastSuccesfulSetupUrl() const CStatusMessageList CApplication::loadSetup()
{ {
if (!this->hasSetupReader()) { return {}; } if (m_shutdown) { return CStatusMessage(this).warning(u"Shutting down, not reading"); }
return m_setupReader->getLastSuccessfulSetupUrl(); if (!m_setupReader) { return CStatusMessage(this).error(u"No reader for setup/version"); }
} Q_ASSERT_X(m_parsed, Q_FUNC_INFO, "Not yet parsed");
const CStatusMessageList requestMsgs = m_setupReader->loadSetup();
CStatusMessageList CApplication::synchronizeSetup(int timeoutMs) onSetupHandlingCompleted(requestMsgs.isSuccess());
{ return requestMsgs;
const CStatusMessageList requestMsgs = this->requestReloadOfSetupAndVersion();
if (requestMsgs.isFailure()) { return requestMsgs; } // request already failed
return this->waitForSetup(timeoutMs);
} }
CUrlList CApplication::getVatsimMetarUrls() const CUrlList CApplication::getVatsimMetarUrls() const

View File

@@ -298,8 +298,8 @@ namespace BlackCore
//! Combined function //! Combined function
//! \see parseAndStartupCheck //! \see parseAndStartupCheck
//! \see synchronizeSetup //! \see loadSetup
virtual bool parseAndSynchronizeSetup(int timeoutMs = BlackMisc::Network::CNetworkUtils::getLongTimeoutMs()); bool parseAndLoadSetup();
//! Display warning message //! Display warning message
virtual bool cmdLineWarningMessage(const QString &text, const QString &informativeText = "") const; virtual bool cmdLineWarningMessage(const QString &text, const QString &informativeText = "") const;
@@ -406,20 +406,9 @@ namespace BlackCore
// ----------------------- setup data --------------------------------- // ----------------------- setup data ---------------------------------
//! Last setup URL (successfully read)
//! \threadsafe
QString getLastSuccesfulSetupUrl() const;
//! Reload setup and version
BlackMisc::CStatusMessageList requestReloadOfSetupAndVersion();
//! Minimum mapping version check //! Minimum mapping version check
virtual bool hasMinimumMappingVersion() const; virtual bool hasMinimumMappingVersion() const;
//! Read and wait for setup
//! \sa waitForSetup
BlackMisc::CStatusMessageList synchronizeSetup(int timeoutMs = BlackMisc::Network::CNetworkUtils::getLongTimeoutMs());
//! Setup reader? //! Setup reader?
bool hasSetupReader() const; bool hasSetupReader() const;
@@ -570,9 +559,6 @@ namespace BlackCore
//! @} //! @}
signals: signals:
//! Setup available (cache, web load, ..) or failed to load setup
void setupHandlingCompleted(bool success);
//! Update info available (cache, web load) //! Update info available (cache, web load)
void updateInfoAvailable(bool success); void updateInfoAvailable(bool success);
@@ -597,13 +583,12 @@ namespace BlackCore
void aboutToShutdown(); void aboutToShutdown();
protected: protected:
//! Display the failures caused by loading the setup file
virtual void displaySetupLoadFailure(BlackMisc::CStatusMessageList msgs);
//! Setup read/synchronized //! Setup read/synchronized
void onSetupHandlingCompleted(bool available); void onSetupHandlingCompleted(bool available);
//! Wait for setup data by calling the event loop and waiting until everything is ready
//! \remark requires parsing upfront
BlackMisc::CStatusMessageList waitForSetup(int timeoutMs = BlackMisc::Network::CNetworkUtils::getLongTimeoutMs());
//! Startup completed //! Startup completed
virtual void onStartUpCompleted(); virtual void onStartUpCompleted();
@@ -672,6 +657,9 @@ namespace BlackCore
std::atomic_bool m_shutdownInProgress { false }; //!< shutdown in progress? std::atomic_bool m_shutdownInProgress { false }; //!< shutdown in progress?
private: private:
//! Read the setup
BlackMisc::CStatusMessageList loadSetup();
//! Problem with network access manager //! Problem with network access manager
void onChangedNetworkAccessibility(QNetworkAccessManager::NetworkAccessibility accessible); void onChangedNetworkAccessibility(QNetworkAccessManager::NetworkAccessibility accessible);

View File

@@ -33,11 +33,6 @@ namespace BlackCore::Data
this->initDefaultValues(); this->initDefaultValues();
} }
bool CGlobalSetup::wasLoaded() const
{
return this->wasLoadedFromFile();
}
void CGlobalSetup::initDefaultValues() void CGlobalSetup::initDefaultValues()
{ {
m_mappingMinimumVersion = CBuildConfig::getVersionString(); m_mappingMinimumVersion = CBuildConfig::getVersionString();
@@ -87,17 +82,6 @@ namespace BlackCore::Data
return m_sharedUrls; return m_sharedUrls;
} }
CUrl CGlobalSetup::getCorrespondingSharedUrl(const CUrl &candidate) const
{
CUrlList sameHosts = this->getSwiftSharedUrls().findByHost(candidate.getHost());
return sameHosts.frontOrDefault();
}
CUrlList CGlobalSetup::getSwiftBootstrapFileUrls() const
{
return getSwiftSharedUrls().withAppendedPath(CGlobalSetup::schemaVersionString() + "/bootstrap/" + CSwiftDirectories::bootstrapFileName());
}
CUrlList CGlobalSetup::getSwiftUpdateInfoFileUrls() const CUrlList CGlobalSetup::getSwiftUpdateInfoFileUrls() const
{ {
return getSwiftSharedUrls().withAppendedPath(CGlobalSetup::schemaVersionString() + "/updateinfo/updateinfo.json"); return getSwiftSharedUrls().withAppendedPath(CGlobalSetup::schemaVersionString() + "/updateinfo/updateinfo.json");
@@ -179,31 +163,6 @@ namespace BlackCore::Data
m_dbDebugFlag = debug; m_dbDebugFlag = debug;
} }
QString CGlobalSetup::buildBootstrapFileUrl(const QString &candidate)
{
if (candidate.isEmpty()) return {}; // not possible
static const QString version(QString(CGlobalSetup::schemaVersionString()).append("/"));
if (candidate.endsWith(CSwiftDirectories::bootstrapFileName())) { return candidate; }
CUrl url(candidate);
if (candidate.contains("/bootstrap"))
{
url.appendPath(CSwiftDirectories::bootstrapFileName());
}
else if (candidate.endsWith(CGlobalSetup::schemaVersionString()) || candidate.endsWith(version))
{
url.appendPath("/bootstrap/" + CSwiftDirectories::bootstrapFileName());
}
else if (candidate.endsWith("shared") || candidate.endsWith("shared/"))
{
url.appendPath(CGlobalSetup::schemaVersionString() + "/bootstrap/" + CSwiftDirectories::bootstrapFileName());
}
else
{
url.appendPath("shared/" + CGlobalSetup::schemaVersionString() + "/bootstrap/" + CSwiftDirectories::bootstrapFileName());
}
return url.getFullUrl();
}
CUrl CGlobalSetup::buildDbDataDirectoryUrl(const CUrl &candidate) CUrl CGlobalSetup::buildDbDataDirectoryUrl(const CUrl &candidate)
{ {
if (candidate.isEmpty()) return CUrl(); // not possible if (candidate.isEmpty()) return CUrl(); // not possible
@@ -257,11 +216,11 @@ namespace BlackCore::Data
QString CGlobalSetup::convertToQString(const QString &separator, bool i18n) const QString CGlobalSetup::convertToQString(const QString &separator, bool i18n) const
{ {
QString s = QString s =
u"timestamp: " % this->getFormattedUtcTimestampYmdhms() % separator % u"Global setup loaded: " % boolToYesNo(this->wasLoadedFromFile()) % separator u"timestamp: " % this->getFormattedUtcTimestampYmdhms() % separator % u"Global setup loaded: "
% u"Mapping min.version: " % this->getMappingMinimumVersionString() % separator % u"Mapping min.version: " % this->getMappingMinimumVersionString() % separator
% u"Distribution URLs: " % getSwiftUpdateInfoFileUrls().toQString(i18n) % separator % u"Bootstrap URLs: " % getSwiftBootstrapFileUrls().toQString(i18n) % separator % u"Help URLs: " % m_onlineHelpUrls.toQString(i18n) % separator; % u"Distribution URLs: " % getSwiftUpdateInfoFileUrls().toQString(i18n) % separator % u"Help URLs: " % m_onlineHelpUrls.toQString(i18n) % separator;
s += s +=
u"DB root directory: " % getDbRootDirectoryUrl().toQString(i18n) % separator % u"ICAO DB reader: " % getDbIcaoReaderUrl().toQString(i18n) % separator % u"Model DB reader: " % getDbModelReaderUrl().toQString(i18n) % separator % u"Airport DB reader: " % getDbAirportReaderUrl().toQString(i18n) % separator % u"DB home page: " % getDbHomePageUrl().toQString(i18n) % separator % u"DB login service: " % getDbLoginServiceUrl().toQString(i18n) % separator % u"DB client ping service: " % getDbClientPingServiceUrl().toQString(i18n); u"DB root directory: " % getDbRootDirectoryUrl().toQString(i18n) % separator % u"ICAO DB reader: " % getDbIcaoReaderUrl().toQString(i18n) % separator % u"Model DB reader: " % getDbModelReaderUrl().toQString(i18n) % separator % u"Airport DB reader: " % getDbAirportReaderUrl().toQString(i18n) % separator % u"DB home page: " % getDbHomePageUrl().toQString(i18n) % separator % u"DB login service: " % getDbLoginServiceUrl().toQString(i18n) % separator % u"DB client ping service: " % getDbClientPingServiceUrl().toQString(i18n);
s += s +=
@@ -292,12 +251,10 @@ namespace BlackCore::Data
case IndexVatsimServer: return QVariant::fromValue(m_vatsimServerFileUrl); case IndexVatsimServer: return QVariant::fromValue(m_vatsimServerFileUrl);
case IndexVatsimHttpFsd: return QVariant::fromValue(m_vatsimFsdHttpUrl); case IndexVatsimHttpFsd: return QVariant::fromValue(m_vatsimFsdHttpUrl);
case IndexVatsimMetars: return QVariant::fromValue(m_vatsimMetarsUrls); case IndexVatsimMetars: return QVariant::fromValue(m_vatsimMetarsUrls);
case IndexBootstrapFileUrls: return QVariant::fromValue(this->getSwiftBootstrapFileUrls());
case IndexUpdateInfoFileUrls: return QVariant::fromValue(this->getSwiftUpdateInfoFileUrls()); case IndexUpdateInfoFileUrls: return QVariant::fromValue(this->getSwiftUpdateInfoFileUrls());
case IndexSharedUrls: return QVariant::fromValue(m_sharedUrls); case IndexSharedUrls: return QVariant::fromValue(m_sharedUrls);
case IndexOnlineHelpUrls: return QVariant::fromValue(m_onlineHelpUrls); case IndexOnlineHelpUrls: return QVariant::fromValue(m_onlineHelpUrls);
case IndexCrashReportServerUrl: return QVariant::fromValue(m_crashReportServerUrl); case IndexCrashReportServerUrl: return QVariant::fromValue(m_crashReportServerUrl);
case IndexWasLoadedFromFile: return QVariant::fromValue(m_wasLoadedFromFile);
case IndexMappingMinimumVersion: return QVariant::fromValue(m_mappingMinimumVersion); case IndexMappingMinimumVersion: return QVariant::fromValue(m_mappingMinimumVersion);
case IndexPredefinedServers: return QVariant::fromValue(m_predefinedServers); case IndexPredefinedServers: return QVariant::fromValue(m_predefinedServers);
default: return CValueObject::propertyByIndex(index); default: return CValueObject::propertyByIndex(index);
@@ -332,7 +289,6 @@ namespace BlackCore::Data
case IndexSharedUrls: m_sharedUrls = variant.value<CUrlList>(); break; case IndexSharedUrls: m_sharedUrls = variant.value<CUrlList>(); break;
case IndexOnlineHelpUrls: m_onlineHelpUrls = variant.value<CUrlList>(); break; case IndexOnlineHelpUrls: m_onlineHelpUrls = variant.value<CUrlList>(); break;
case IndexCrashReportServerUrl: m_crashReportServerUrl = variant.value<CUrl>(); break; case IndexCrashReportServerUrl: m_crashReportServerUrl = variant.value<CUrl>(); break;
case IndexWasLoadedFromFile: m_wasLoadedFromFile = variant.toBool(); break;
case IndexMappingMinimumVersion: m_mappingMinimumVersion = variant.toString(); break; case IndexMappingMinimumVersion: m_mappingMinimumVersion = variant.toString(); break;
case IndexPredefinedServers: m_predefinedServers = variant.value<CServerList>(); break; case IndexPredefinedServers: m_predefinedServers = variant.value<CServerList>(); break;
default: CValueObject::setPropertyByIndex(index, variant); break; default: CValueObject::setPropertyByIndex(index, variant); break;

View File

@@ -47,12 +47,9 @@ namespace BlackCore::Data
IndexVatsimServer, IndexVatsimServer,
IndexVatsimHttpFsd, IndexVatsimHttpFsd,
IndexSwiftDbFiles, IndexSwiftDbFiles,
IndexBootstrapFileUrls,
IndexUpdateInfoFileUrls, IndexUpdateInfoFileUrls,
IndexOnlineHelpUrls, IndexOnlineHelpUrls,
IndexCrashReportServerUrl, IndexCrashReportServerUrl,
IndexWasLoadedFromWeb,
IndexWasLoadedFromFile,
IndexSharedUrls, IndexSharedUrls,
IndexMappingMinimumVersion, IndexMappingMinimumVersion,
IndexPredefinedServers IndexPredefinedServers
@@ -72,15 +69,6 @@ namespace BlackCore::Data
//! Default constructor //! Default constructor
CGlobalSetup(); CGlobalSetup();
//! Has data loaded from file
bool wasLoadedFromFile() const { return m_wasLoadedFromFile; }
//! Loaded (web/file)
bool wasLoaded() const;
//! Mark as loaded from file
void markAsLoadedFromFile(bool loaded) { m_wasLoadedFromFile = loaded; }
//! Http port //! Http port
int getDbHttpPort() const { return m_dbHttpPort; } int getDbHttpPort() const { return m_dbHttpPort; }
@@ -144,14 +132,6 @@ namespace BlackCore::Data
//! Shared URLs //! Shared URLs
const BlackMisc::Network::CUrlList &getSwiftSharedUrls() const; const BlackMisc::Network::CUrlList &getSwiftSharedUrls() const;
//! Get pure shared URL as in getSwiftSharedUrls from bootstrap, distribution or other shared URL
//! \remark normally based on one of the getSwiftSharedUrls
BlackMisc::Network::CUrl getCorrespondingSharedUrl(const BlackMisc::Network::CUrl &candidate) const;
//! Bootstrap URLs
//! \remark based on getSwiftSharedUrls
BlackMisc::Network::CUrlList getSwiftBootstrapFileUrls() const;
//! Distribution URLs //! Distribution URLs
//! \remark based on getSwiftSharedUrls //! \remark based on getSwiftSharedUrls
BlackMisc::Network::CUrlList getSwiftUpdateInfoFileUrls() const; BlackMisc::Network::CUrlList getSwiftUpdateInfoFileUrls() const;
@@ -216,9 +196,6 @@ namespace BlackCore::Data
//! Schema version (shared files, bootstrap file) //! Schema version (shared files, bootstrap file)
static const QString &schemaVersionString(); static const QString &schemaVersionString();
//! Build bootstrap file URL from shared URL
static QString buildBootstrapFileUrl(const QString &candidate);
//! Build the full dbdata directory URL from shared URL //! Build the full dbdata directory URL from shared URL
static BlackMisc::Network::CUrl buildDbDataDirectoryUrl(const BlackMisc::Network::CUrl &candidate); static BlackMisc::Network::CUrl buildDbDataDirectoryUrl(const BlackMisc::Network::CUrl &candidate);
@@ -226,7 +203,6 @@ namespace BlackCore::Data
static CGlobalSetup fromJsonFile(const QString &fileNameAndPath, bool acceptCacheFormat); static CGlobalSetup fromJsonFile(const QString &fileNameAndPath, bool acceptCacheFormat);
private: private:
bool m_wasLoadedFromFile = false; //!< Loaded from local file
int m_dbHttpPort = 80; //!< port int m_dbHttpPort = 80; //!< port
int m_dbHttpsPort = 443; //!< SSL port int m_dbHttpsPort = 443; //!< SSL port
qint64 m_pingIntervalSecs = 180; //!< seconds between datastore pings qint64 m_pingIntervalSecs = 180; //!< seconds between datastore pings
@@ -252,7 +228,6 @@ namespace BlackCore::Data
BLACK_METACLASS( BLACK_METACLASS(
CGlobalSetup, CGlobalSetup,
BLACK_METAMEMBER(wasLoadedFromFile),
BLACK_METAMEMBER(timestampMSecsSinceEpoch), BLACK_METAMEMBER(timestampMSecsSinceEpoch),
BLACK_METAMEMBER(crashReportServerUrl), BLACK_METAMEMBER(crashReportServerUrl),
BLACK_METAMEMBER(dbRootDirectoryUrl), BLACK_METAMEMBER(dbRootDirectoryUrl),
@@ -275,16 +250,6 @@ namespace BlackCore::Data
BLACK_METAMEMBER(dbDebugFlag) BLACK_METAMEMBER(dbDebugFlag)
); );
}; };
//! Trait for global setup data
struct TGlobalSetup : public BlackMisc::TDataTrait<CGlobalSetup>
{
//! Key in data cache
static const char *key() { return "bootstrap"; }
//! First load is synchronous
static constexpr bool isPinned() { return true; }
};
} // ns } // ns
Q_DECLARE_METATYPE(BlackCore::Data::CGlobalSetup) Q_DECLARE_METATYPE(BlackCore::Data::CGlobalSetup)

View File

@@ -34,162 +34,41 @@ using namespace BlackCore::Data;
namespace BlackCore namespace BlackCore
{ {
CSetupReader::CSetupReader(QObject *parent) : QObject(parent), CSetupReader::CSetupReader(QObject *parent) : QObject(parent)
m_cmdBootstrapUrl {
{ "url", "bootstrapurl" },
QCoreApplication::translate("application", "Bootstrap URL, e.g. https://datastore.swift-project.org/shared"),
"bootstrapurl",
(sApp->getApplicationInfo().isUnitTest()) ? unitTestBootstrapUrl() : ""
},
m_cmdBootstrapMode {
{ "bmode", "bootstrapmode" },
QCoreApplication::translate("application", "Bootstrap mode: explicit, implicit, cache(-only)"),
"bootstrapmode",
"explicit"
}
{} {}
QList<QCommandLineOption> CSetupReader::getCmdLineOptions() const CStatusMessageList CSetupReader::loadSetup()
{
return QList<QCommandLineOption> { { m_cmdBootstrapUrl, m_cmdBootstrapMode } };
}
CStatusMessageList CSetupReader::asyncLoad()
{ {
CStatusMessageList msgs; CStatusMessageList msgs;
if (m_localSetupFileValue.isEmpty()) msgs = this->readLocalBootstrapFile();
{ m_setupAvailable = msgs.isSuccess();
// TODO Check if file exists or is broken
msgs = this->readLocalBootstrapFile(CSwiftDirectories::shareDirectory() + "/shared/bootstrap/bootstrap.json");
}
else
{
msgs = this->readLocalBootstrapFile(m_localSetupFileValue);
}
msgs.push_back(this->manageSetupAvailability(false, msgs.isSuccess()));
return msgs; return msgs;
} }
bool CSetupReader::parseCmdLineArguments() CStatusMessageList CSetupReader::readLocalBootstrapFile()
{ {
// copy vars at beginning to simplify a threadsafe version in the future const QString fileName = CSwiftDirectories::bootstrapResourceFilePath();
const QString cmdLineBootstrapUrl = this->getCmdLineBootstrapUrl(); // TODO Check if file exists or is broken
BootstrapMode bootstrapMode = stringToEnum(sApp->getParserValue(m_cmdBootstrapMode));
const bool ignoreCmdBootstrapUrl = m_ignoreCmdBootstrapUrl;
const bool checkCmdBootstrapUrl = m_checkCmdBootstrapUrl;
const QString bootstrapUrlFileValue = CGlobalSetup::buildBootstrapFileUrl(cmdLineBootstrapUrl);
QString localSetupFileValue;
const QUrl url(bootstrapUrlFileValue);
const QString urlString(url.toString());
bool ok = false;
if (urlString.isEmpty() && bootstrapMode == Explicit)
{
bootstrapMode = Implicit; // no URL, we use implicit mode
}
do
{
// check on local file
if (url.isLocalFile())
{
localSetupFileValue = url.toLocalFile();
const QFile f(localSetupFileValue);
if (!f.exists())
{
sApp->cmdLineErrorMessage(QStringLiteral("File '%1' does not exist)").arg(localSetupFileValue));
break;
}
}
// check on explicit URL
if (bootstrapMode == Explicit)
{
if (!url.isLocalFile())
{
bool retry = false;
// "retry" possible in some cases
do
{
if (ignoreCmdBootstrapUrl || !checkCmdBootstrapUrl || CNetworkUtils::canConnect(url, CNetworkUtils::getLongTimeoutMs())) // cppcheck-suppress knownConditionTrueFalse
{
ok = true;
break;
}
retry = sApp->cmdLineErrorMessage(QStringLiteral("URL '%1' not reachable").arg(urlString), "", true);
}
while (retry);
}
}
}
while (false);
m_localSetupFileValue = localSetupFileValue;
m_bootstrapUrlFileValue = bootstrapUrlFileValue;
m_bootstrapMode = bootstrapMode;
return ok;
}
void CSetupReader::gracefulShutdown()
{
m_shutdown = true;
}
void CSetupReader::forceAvailabilityUpdate()
{
this->manageSetupAvailability(false, false); // fake a failed web read
}
CSetupReader::BootstrapMode CSetupReader::stringToEnum(const QString &s)
{
const QString bsm(s.toLower().trimmed());
if (bsm.startsWith("expl")) { return Explicit; }
if (bsm.startsWith("cache")) { return CacheOnly; }
return Implicit;
}
const QString &CSetupReader::unitTestBootstrapUrl()
{
static const QString url("https://datastore.swift-project.org/shared");
return url;
}
CStatusMessageList CSetupReader::readLocalBootstrapFile(const QString &fileName)
{
if (fileName.isEmpty()) { return CStatusMessage(this).error(u"No file name for local bootstrap file"); } if (fileName.isEmpty()) { return CStatusMessage(this).error(u"No file name for local bootstrap file"); }
if (!sApp || sApp->isShuttingDown()) { return CStatusMessage(this).error(u"No sApp, shutting down?"); } if (!sApp || sApp->isShuttingDown()) { return CStatusMessage(this).error(u"No sApp, shutting down?"); }
QString fn;
const QFile file(fileName); const QFile file(fileName);
if (!file.exists()) if (!file.exists())
{ {
// relative name? return CStatusMessage(this).error(u"File '%1' not existing") << fileName;
const QString dir(sApp->getCmdSwiftPrivateSharedDir());
if (dir.isEmpty()) { return CStatusMessage(this).error(u"Empty shared directory '%1' for bootstrap file") << dir; }
// no version for local files, as those come with the current code
fn = CFileUtils::appendFilePaths(dir, "bootstrap/" + CSwiftDirectories::bootstrapFileName());
}
else
{
fn = fileName;
} }
const QString content(CFileUtils::readFileToString(fn)); const QString content(CFileUtils::readFileToString(fileName));
if (content.isEmpty()) { return CStatusMessage(this).error(u"File '%1' not existing or empty") << fn; } if (content.isEmpty()) { return CStatusMessage(this).error(u"File '%1' empty") << fileName; }
try try
{ {
CGlobalSetup s; m_setup.convertFromJson(content);
s.convertFromJson(content); return CStatusMessage(this).info(u"Setup loaded from local file '%1'") << fileName;
s.markAsLoadedFromFile(true);
const CStatusMessage setMsg = m_setup.set(s);
const CStatusMessage setInfo = CStatusMessage(this).info(u"Setup cache updated from local file '%1'") << fn;
return setMsg.isSuccess() ? setInfo : setMsg;
} }
catch (const CJsonException &ex) catch (const CJsonException &ex)
{ {
return CStatusMessage::fromJsonException(ex, this, QStringLiteral("Parsing local setup file '%1'").arg(fn)); return CStatusMessage::fromJsonException(ex, this, QStringLiteral("Parsing local setup file '%1'").arg(fileName));
} }
} }
@@ -199,136 +78,8 @@ namespace BlackCore
return cats; return cats;
} }
bool CSetupReader::hasCmdLineBootstrapUrl() const
{
return !this->getCmdLineBootstrapUrl().isEmpty();
}
QString CSetupReader::getCmdLineBootstrapUrl() const
{
if (m_ignoreCmdBootstrapUrl) return {};
return sApp->getParserValue(m_cmdBootstrapUrl);
}
void CSetupReader::setIgnoreCmdLineBootstrapUrl(bool ignore)
{
m_ignoreCmdBootstrapUrl = ignore;
this->parseCmdLineArguments(); // T156 this part not threadsafe, currently not a real problem as setup reader runs in main thread
}
CGlobalSetup CSetupReader::getSetup() const CGlobalSetup CSetupReader::getSetup() const
{ {
return m_setup.get(); return m_setup;
}
bool CSetupReader::hasCachedSetup() const
{
const CGlobalSetup cachedSetup = m_setup.get();
const bool cacheAvailable = cachedSetup.wasLoaded();
return cacheAvailable;
}
QDateTime CSetupReader::getSetupCacheTimestamp() const
{
return m_setup.getTimestamp();
}
bool CSetupReader::prefillCacheWithLocalResourceBootstrapFile()
{
if (m_shutdown) { return false; }
m_setup.synchronize(); // make sure it is loaded
const CGlobalSetup cachedSetup = m_setup.get();
const bool cacheAvailable = cachedSetup.wasLoaded();
if (cacheAvailable)
{
CLogMessage(this).info(u"Setup cache prefill (bootstrap already cached, no prefill needed");
return false;
}
const QString fn = CSwiftDirectories::bootstrapResourceFilePath();
const CStatusMessageList msgs = this->readLocalBootstrapFile(fn);
CLogMessage::preformatted(msgs);
return true;
}
QString CSetupReader::getLastSuccessfulSetupUrl() const
{
QReadLocker l(&m_lockSetup);
return m_lastSuccessfulSetupUrl;
}
void CSetupReader::synchronize()
{
m_setup.synchronize();
}
CStatusMessageList CSetupReader::getLastSetupReadErrorMessages() const
{
QReadLocker l(&m_lockSetup);
return m_setupReadErrorMsgs;
}
const QString &CSetupReader::getBootstrapUrlFile() const
{
if (!m_localSetupFileValue.isEmpty()) { return m_localSetupFileValue; }
return m_bootstrapUrlFileValue;
}
QString CSetupReader::getBootstrapModeAsString() const
{
switch (m_bootstrapMode)
{
case CacheOnly: return QStringLiteral("cache only");
case Explicit: return QStringLiteral("explicit");
case Implicit: return QStringLiteral("implicit");
default: break;
}
return {};
}
void CSetupReader::setLastSetupReadErrorMessages(const CStatusMessageList &messages)
{
QWriteLocker l(&m_lockSetup);
m_setupReadErrorMsgs = messages.getErrorMessages();
}
void CSetupReader::networkReplyProgress(int logId, qint64 current, qint64 max, const QUrl &url)
{
Q_UNUSED(url)
Q_UNUSED(logId)
Q_UNUSED(current)
Q_UNUSED(max)
}
CStatusMessageList CSetupReader::manageSetupAvailability(bool webRead, bool localRead)
{
Q_ASSERT_X(!(webRead && localRead), Q_FUNC_INFO, "Local and web read together seems to be wrong");
CStatusMessageList msgs;
bool available = false;
if (webRead || localRead)
{
available = true;
}
else
{
const bool cacheAvailable = m_setup.get().wasLoaded(); // loaded from web or file
available = cacheAvailable && m_bootstrapMode != Explicit;
}
if (available && !webRead && !localRead)
{
msgs.push_back(CStatusMessage(this, CStatusMessage::SeverityInfo, u"Setup available, but not updated this time"));
}
else if (!available)
{
msgs.push_back(CStatusMessage(this, CStatusMessage::SeverityError, u"Setup not available"));
if (m_bootstrapMode == Explicit)
{
msgs.push_back(CStatusMessage(this, CStatusMessage::SeverityError, u"Mode is 'explicit', likely URL '" % m_bootstrapUrlFileValue % u"' is not reachable"));
}
}
m_setupAvailable = available;
emit this->setupHandlingCompleted(available);
return msgs;
} }
} // namespace } // namespace

View File

@@ -22,10 +22,6 @@
#include <QString> #include <QString>
#include <atomic> #include <atomic>
namespace BlackMisc
{
class CLogCategoryList;
}
namespace BlackCoreTest namespace BlackCoreTest
{ {
class CTestConnectivity; class CTestConnectivity;
@@ -39,144 +35,38 @@ namespace BlackCore
//! \note This class is no(!) BlackCore::CThreadedReader as it will be loaded once during startup //! \note This class is no(!) BlackCore::CThreadedReader as it will be loaded once during startup
//! and reading setup data is fast. The read file is also called "bootstrap" file as it tells //! and reading setup data is fast. The read file is also called "bootstrap" file as it tells
//! swift which data and versions are located where. Without that file we cannot start. //! swift which data and versions are located where. Without that file we cannot start.
//! Once the file is in place (i.e. in the cache) it can be automatically updated.
//! //!
//! \sa BlackCore::Data::TGlobalSetup
//! \sa BlackMisc::Db::TUpdateInfo //! \sa BlackMisc::Db::TUpdateInfo
class BLACKCORE_EXPORT CSetupReader : public QObject class BLACKCORE_EXPORT CSetupReader : public QObject
{ {
Q_OBJECT Q_OBJECT
friend class CApplication; //!< only using class
friend class BlackCoreTest::CTestConnectivity; friend class BlackCoreTest::CTestConnectivity;
public: public:
//! Categories //! Categories
static const QStringList &getLogCategories(); static const QStringList &getLogCategories();
//! Has a given cmd line argument for bootstrap URL? //! Constructor
bool hasCmdLineBootstrapUrl() const; explicit CSetupReader(QObject *parent);
//! CMD line argument for bootstrap URL //! Load the setup. If the setup is already loaded, the setup is reloaded
QString getCmdLineBootstrapUrl() const; BlackMisc::CStatusMessageList loadSetup();
//! Ignore the bootstrap URL //! Setup available?
//! \threadsafe //! \threadsafe
void setIgnoreCmdLineBootstrapUrl(bool ignore); bool isSetupAvailable() const { return m_setupAvailable; }
//! Check connection of the bootstrap URL
//! \threadsafe
void setCheckCmdLineBootstrapUrl(bool check) { m_checkCmdBootstrapUrl = check; }
//! Current setup (reader URLs, DB location, crash server) //! Current setup (reader URLs, DB location, crash server)
//! \remarks aka "bootstrap file" //! \remarks aka "bootstrap file"
//! \threadsafe //! \threadsafe
Data::CGlobalSetup getSetup() const; Data::CGlobalSetup getSetup() const;
//! Has cached setup ("bootstrap") data?
//! \threadsafe
bool hasCachedSetup() const;
//! Get setup cache timestamp
//! \threadsafe
QDateTime getSetupCacheTimestamp() const;
//! Load the cache file local bootstrap file
//! \remark can be used during installation as failover
//! \threadsafe
bool prefillCacheWithLocalResourceBootstrapFile();
//! Last distribution URL successfully read
//! \threadsafe
QString getLastSuccessfulSetupUrl() const;
//! Synchronize the caches
void synchronize();
//! Last setup parsing error messages (if any)
BlackMisc::CStatusMessageList getLastSetupReadErrorMessages() const;
//! Get bootstrap URL, either m_bootstrapUrlFileValue or m_localSetupFileValue
const QString &getBootstrapUrlFile() const;
//! Mode as string
QString getBootstrapModeAsString() const;
signals:
//! Setup fetched or failed (from web, cache, or local file)?
void setupHandlingCompleted(bool available);
//! Message about the loading status
void setupLoadingMessages(const BlackMisc::CStatusMessageList &messages);
protected:
//! Constructor
explicit CSetupReader(QObject *parent);
//! Load the data
BlackMisc::CStatusMessageList asyncLoad();
//! Parse cmd line arguments
bool parseCmdLineArguments();
//! Add cmd line arguments to BlackCore::CApplication
QList<QCommandLineOption> getCmdLineOptions() const;
//! Terminate
void gracefulShutdown();
//! Setup available?
//! \threadsafe
bool isSetupAvailable() const { return m_setupAvailable; }
//! Force an availability update
//! \remark check for cached setup if the read check never got triggered
void forceAvailabilityUpdate();
private: private:
//! Bootstrap mode
enum BootstrapMode
{
Implicit,
Explicit,
CacheOnly
};
std::atomic<bool> m_shutdown { false }; //!< shutdown in progress
std::atomic<bool> m_setupAvailable { false }; //!< setup available? std::atomic<bool> m_setupAvailable { false }; //!< setup available?
std::atomic<bool> m_ignoreCmdBootstrapUrl { false }; //!< ignore the explicitly set bootstrap URL Data::CGlobalSetup m_setup {}; //!< data setup
std::atomic<bool> m_checkCmdBootstrapUrl { true }; //!< check connection on CMD bootstrap URL
std::atomic_int m_bootstrapReadErrors { 0 }; //!< failed bootstrap reads
std::atomic_int m_updateInfoReadErrors { 0 }; //!< failed version info reads
QString m_localSetupFileValue; //!< Local file for setup, passed by cmd line arguments
QString m_bootstrapUrlFileValue; //!< Bootstrap URL if not local
BootstrapMode m_bootstrapMode = Explicit; //!< How to bootstrap
BlackMisc::Network::CUrlList m_bootstrapUrls; //!< location of setup files
QCommandLineOption m_cmdBootstrapUrl; //!< bootstrap URL
QCommandLineOption m_cmdBootstrapMode; //!< bootstrap mode
mutable QReadWriteLock m_lockSetup; //!< lock for setup
mutable QReadWriteLock m_lockUpdateInfo; //!< lock for update info
QString m_lastSuccessfulSetupUrl; //!< last successful read setup URL
BlackMisc::CStatusMessageList m_setupReadErrorMsgs; //!< last parsing error messages
BlackMisc::CData<Data::TGlobalSetup> m_setup { this }; //!< data cache setup
//! Read by local individual file and update cache from that //! Read by local file
BlackMisc::CStatusMessageList readLocalBootstrapFile(const QString &fileName); BlackMisc::CStatusMessageList readLocalBootstrapFile();
//! Emit the availability signal and state and trigger follow up actions
//! \threadsafe
BlackMisc::CStatusMessageList manageSetupAvailability(bool webRead, bool localRead = false);
//! Set last setup parsing messages
void setLastSetupReadErrorMessages(const BlackMisc::CStatusMessageList &messages);
//! Progress
void networkReplyProgress(int logId, qint64 current, qint64 max, const QUrl &url);
//! Convert string to bootstrap mode
static BootstrapMode stringToEnum(const QString &s);
//! Bootsrap URL used for unit tests
static const QString &unitTestBootstrapUrl();
}; };
} // ns } // ns

View File

@@ -377,12 +377,6 @@ namespace BlackGui::Components
{ {
this->initMultiSimulatorCache(&m_modelCaches, file); this->initMultiSimulatorCache(&m_modelCaches, file);
} }
else if (file.contains(CSwiftDirectories::bootstrapFileName()))
{
CData<TGlobalSetup> setup { this }; //!< data cache setup
const CGlobalSetup s = CGlobalSetup::fromJsonFile(file, true);
setup.set(s);
}
} }
// allow the cache files to be generated before we will override them // allow the cache files to be generated before we will override them

View File

@@ -37,11 +37,6 @@ namespace BlackGui::Components
ui->comp_CopyConfiguration->setNameFilterDisables(disable); ui->comp_CopyConfiguration->setNameFilterDisables(disable);
} }
void CCopyConfigurationDialog::setWithBootstrapFile(bool withBootstrapFile)
{
ui->comp_CopyConfiguration->setWithBootstrapFile(withBootstrapFile);
}
bool CCopyConfigurationDialog::event(QEvent *event) bool CCopyConfigurationDialog::event(QEvent *event)
{ {
if (CGuiApplication::triggerShowHelp(this, event)) { return true; } if (CGuiApplication::triggerShowHelp(this, event)) { return true; }

View File

@@ -41,9 +41,6 @@ namespace BlackGui::Components
//! \copydoc QFileSystemModel::setNameFilterDisables //! \copydoc QFileSystemModel::setNameFilterDisables
void setNameFilterDisables(bool disable); void setNameFilterDisables(bool disable);
//! \copydoc CCopyConfigurationComponent::setWithBootstrapFile
void setWithBootstrapFile(bool withBootstrapFile);
protected: protected:
//! \copydoc QObject::event //! \copydoc QObject::event
virtual bool event(QEvent *event) override; virtual bool event(QEvent *event) override;

View File

@@ -5,16 +5,11 @@
#include "blackgui/components/copymodelsfromotherswiftversionsdialog.h" #include "blackgui/components/copymodelsfromotherswiftversionsdialog.h"
#include "ui_setuploadingdialog.h" #include "ui_setuploadingdialog.h"
#include "blackgui/guiapplication.h" #include "blackgui/guiapplication.h"
#include "blackcore/data/globalsetup.h"
#include "blackcore/setupreader.h" #include "blackcore/setupreader.h"
#include "blackmisc/swiftdirectories.h" #include "blackmisc/swiftdirectories.h"
#include "blackmisc/directoryutils.h"
#include "blackmisc/network/urllist.h"
#include <QPushButton> #include <QPushButton>
#include <QDesktopServices> #include <QDesktopServices>
#include <QTimer>
#include <QPointer>
using namespace BlackMisc; using namespace BlackMisc;
using namespace BlackMisc::Network; using namespace BlackMisc::Network;
@@ -27,30 +22,15 @@ namespace BlackGui::Components
ui(new Ui::CSetupLoadingDialog) ui(new Ui::CSetupLoadingDialog)
{ {
Q_ASSERT_X(sApp, Q_FUNC_INFO, "Need sApp"); Q_ASSERT_X(sApp, Q_FUNC_INFO, "Need sApp");
if (this->hasSetupReader())
{
// reset if it was temporarily ignored
sApp->getSetupReader()->setIgnoreCmdLineBootstrapUrl(false);
connect(sApp, &CGuiApplication::setupHandlingCompleted, this, &CSetupLoadingDialog::onSetupHandlingCompleted);
}
ui->setupUi(this); ui->setupUi(this);
connect(ui->pb_IgnoreExplicitBootstrapUrl, &QPushButton::clicked, this, &CSetupLoadingDialog::tryAgainWithoutBootstrapUrl);
connect(ui->pb_LoadFromDisk, &QPushButton::clicked, this, &CSetupLoadingDialog::prefillSetupCache);
connect(ui->pb_Help, &QPushButton::clicked, this, &CSetupLoadingDialog::openHelpPage);
connect(ui->pb_CopyFromSwift, &QPushButton::clicked, this, &CSetupLoadingDialog::copyFromOtherSwiftVersions);
connect(ui->pb_OpemDirectory, &QPushButton::clicked, this, &CSetupLoadingDialog::openDirectory);
connect(ui->pb_TryToFix, &QPushButton::clicked, this, &CSetupLoadingDialog::tryToFix);
QPushButton *retry = ui->bb_Dialog->button(QDialogButtonBox::Retry); const QString bootstrapPath = CSwiftDirectories::bootstrapResourceFilePath();
retry->setDefault(true); ui->lbl_ownBootstrap->setText("Your boostrap.json file is available at <a href=" + bootstrapPath + ">" + bootstrapPath + "</>");
this->displaySetupCacheInfo(); // hide unnecessary details
this->displayCmdBoostrapUrl(); ui->comp_Messages->hideFilterBar();
this->displayGlobalSetup(); ui->comp_Messages->showDetails(false);
this->displayOtherVersionsInfo();
ui->comp_Messages->hideFilterBar(); // saves space, we only expect aview messages
} }
CSetupLoadingDialog::CSetupLoadingDialog(const CStatusMessageList &msgs, QWidget *parent) : CSetupLoadingDialog(parent) CSetupLoadingDialog::CSetupLoadingDialog(const CStatusMessageList &msgs, QWidget *parent) : CSetupLoadingDialog(parent)
{ {
@@ -59,124 +39,4 @@ namespace BlackGui::Components
CSetupLoadingDialog::~CSetupLoadingDialog() CSetupLoadingDialog::~CSetupLoadingDialog()
{} {}
bool CSetupLoadingDialog::hasCachedSetup() const
{
return this->hasSetupReader() && sApp->getSetupReader()->hasCachedSetup();
}
bool CSetupLoadingDialog::hasSetupReader() const
{
return sApp && sApp->hasSetupReader();
}
void CSetupLoadingDialog::displayCmdBoostrapUrl()
{
if (!sApp->hasSetupReader()) { return; }
ui->le_CmdLine->setText(sApp->cmdLineArgumentsAsString());
ui->le_BootstrapMode->setText(sApp->getSetupReader()->getBootstrapModeAsString());
const QString bsUrl = sApp->getSetupReader()->getCmdLineBootstrapUrl();
ui->pb_IgnoreExplicitBootstrapUrl->setVisible(!bsUrl.isEmpty());
ui->le_BootstrapUrl->setText(bsUrl);
}
void CSetupLoadingDialog::displayGlobalSetup()
{
const QString gs = sApp->getGlobalSetup().convertToQString("\n", true);
Q_UNUSED(gs)
// ui->comp_Messages->appendPlainTextToConsole(gs);
//! \fixme create plain text console for this (used to be part of the log component, changed by issue T36)
}
void CSetupLoadingDialog::openHelpPage()
{
const CUrl url = sApp->getGlobalSetup().getHelpPageUrl("bootstrap");
if (url.isEmpty()) { return; }
QDesktopServices::openUrl(url);
}
void CSetupLoadingDialog::tryAgainWithoutBootstrapUrl()
{
if (!sApp->hasSetupReader()) { return; }
sApp->getSetupReader()->setIgnoreCmdLineBootstrapUrl(true);
this->accept();
}
void CSetupLoadingDialog::tryToFix()
{
this->prefillSetupCache();
QPushButton *retry = ui->bb_Dialog->button(QDialogButtonBox::Retry);
if (!retry) { return; }
QPointer<CSetupLoadingDialog> myself(this);
QTimer::singleShot(2000, this, [=] {
if (!sApp || !myself) { return; }
retry->click();
});
}
void CSetupLoadingDialog::prefillSetupCache()
{
if (!sApp || sApp->isShuttingDown()) { return; }
if (!this->hasSetupReader()) { return; }
sApp->getSetupReader()->prefillCacheWithLocalResourceBootstrapFile();
this->displaySetupCacheInfo();
}
void CSetupLoadingDialog::displaySetupCacheInfo()
{
if (this->hasSetupReader())
{
// reset if it was temporarily ignored
const CSetupReader *sr = sApp->getSetupReader();
const QDateTime setupTs = sr->getSetupCacheTimestamp();
static const QDateTime zeroTime = QDateTime::fromMSecsSinceEpoch(0);
ui->le_SetupCache->setText(setupTs.isValid() && setupTs > zeroTime ?
setupTs.toString(Qt::ISODateWithMs) :
"No cache timestamp");
}
else
{
ui->le_SetupCache->setText("No setup reader");
}
const bool hasCachedSetup = this->hasCachedSetup();
ui->pb_LoadFromDisk->setEnabled(!hasCachedSetup);
ui->pb_LoadFromDisk->setToolTip(hasCachedSetup ? "Cached setup already available" : "No cached setup");
ui->pb_TryToFix->setEnabled(!hasCachedSetup);
}
void CSetupLoadingDialog::displayOtherVersionsInfo()
{
const int other = CSwiftDirectories::applicationDataDirectoriesCount() - 1;
ui->le_OtherSwiftVersions->setText(QStringLiteral("There is/are %1 other swift version(s) installed").arg(other));
ui->pb_CopyFromSwift->setEnabled(other > 0);
}
void CSetupLoadingDialog::openDirectory()
{
const QUrl url = QUrl::fromLocalFile(CSwiftDirectories::normalizedApplicationDataDirectory());
QDesktopServices::openUrl(url);
}
void CSetupLoadingDialog::copyFromOtherSwiftVersions()
{
if (!m_copyFromOtherSwiftVersion)
{
CCopyModelsFromOtherSwiftVersionsDialog *d = new CCopyModelsFromOtherSwiftVersionsDialog(this);
d->setModal(true);
m_copyFromOtherSwiftVersion.reset(d);
}
const int r = m_copyFromOtherSwiftVersion->exec();
Q_UNUSED(r);
this->displaySetupCacheInfo();
}
void CSetupLoadingDialog::onSetupHandlingCompleted(bool success)
{
Q_UNUSED(success);
this->displaySetupCacheInfo();
}
} // ns } // ns

View File

@@ -15,19 +15,14 @@ namespace Ui
} }
namespace BlackGui::Components namespace BlackGui::Components
{ {
class CCopyModelsFromOtherSwiftVersionsDialog;
/*! /*!
* Setup dialog, if something goes wrong allows to copy bootstrap file * Setup dialog, if loading the boostrap file fails
*/ */
class CSetupLoadingDialog : public QDialog class CSetupLoadingDialog : public QDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
//! Ctor
explicit CSetupLoadingDialog(QWidget *parent = nullptr);
//! Ctor with messages //! Ctor with messages
CSetupLoadingDialog(const BlackMisc::CStatusMessageList &msgs, QWidget *parent = nullptr); CSetupLoadingDialog(const BlackMisc::CStatusMessageList &msgs, QWidget *parent = nullptr);
@@ -36,46 +31,9 @@ namespace BlackGui::Components
private: private:
QScopedPointer<Ui::CSetupLoadingDialog> ui; QScopedPointer<Ui::CSetupLoadingDialog> ui;
QScopedPointer<CCopyModelsFromOtherSwiftVersionsDialog> m_copyFromOtherSwiftVersion;
//! Cached setup available? //! Ctor
bool hasCachedSetup() const; explicit CSetupLoadingDialog(QWidget *parent = nullptr);
//! Setup reader?
bool hasSetupReader() const;
//! Display bootstrap URL
void displayCmdBoostrapUrl();
//! Display global setup
void displayGlobalSetup();
//! Open the help page
void openHelpPage();
//! Try again without explicit bootstrap URL
void tryAgainWithoutBootstrapUrl();
//! Try to fix
void tryToFix();
//! Prefill setup cache
void prefillSetupCache();
//! Display the setup cache info
void displaySetupCacheInfo();
//! Display other versions info
void displayOtherVersionsInfo();
//! Open directory
void openDirectory();
//! Copy from other swift versions
void copyFromOtherSwiftVersions();
//! Setup loading has been completed
void onSetupHandlingCompleted(bool success);
}; };
} // ns } // ns

View File

@@ -17,7 +17,7 @@
</size> </size>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Loading the setup (&quot;bootstrap file&quot;)</string> <string>Loading the setup file failed</string>
</property> </property>
<property name="modal"> <property name="modal">
<bool>true</bool> <bool>true</bool>
@@ -36,173 +36,29 @@
<number>6</number> <number>6</number>
</property> </property>
<item> <item>
<widget class="QGroupBox" name="gb_Details"> <widget class="QLabel" name="lbl_Info">
<property name="title"> <property name="text">
<string>Setup and caches</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Loading the setup file (bootstrap.json) has failed!&lt;/span&gt; This file is required for &lt;span style=&quot; font-style:italic;&quot;&gt;swift&lt;/span&gt; to work properly. Fix the errors mentioned below and restart swift. You can also replace your bootstrap.json with the latest version available &lt;a href=&quot;https://raw.githubusercontent.com/swift-project/pilotclient/main/resources/share/shared/bootstrap/bootstrap.json&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;from GitHub.&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbl_ownBootstrap">
<property name="text">
<string>TextLabel</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property> </property>
<layout class="QGridLayout" name="gl_Details" columnstretch="0,0,0,0">
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item row="2" column="1">
<widget class="QLineEdit" name="le_BootstrapMode">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The mode: 'implicit' means no URL is provided and the value is obtained from an existing setup. 'explicit' means an URL is provided via command line arguments.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lbl_BootstrapMode">
<property name="text">
<string>Mode:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="le_SetupCache">
<property name="whatsThis">
<string>Timestamp of the setup cache.</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lbl_CmdLine">
<property name="text">
<string>Command:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="lbl_BootstrapUrl">
<property name="toolTip">
<string>Where the bootstrap file is located</string>
</property>
<property name="text">
<string>Bootstrap URL:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="le_BootstrapUrl">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The bootstrap URL provided by command line options. This is where the setup data are loaded from.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1" colspan="3">
<widget class="QLineEdit" name="le_CmdLine">
<property name="whatsThis">
<string>The command line.</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="lbl_SetupCache">
<property name="text">
<string>Setup cache:</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="lbl_CopyOver">
<property name="text">
<string>Copy over:</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QPushButton" name="pb_LoadFromDisk">
<property name="whatsThis">
<string>load the cache data from disk, i.e. from the file which came with the installer</string>
</property>
<property name="text">
<string>from disk</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="lbl_Info">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:9pt; font-weight:600;&quot;&gt;Loading the setup (aka &amp;quot;bootstrap file&amp;quot;) has failed!&lt;/span&gt;&lt;span style=&quot; font-size:9pt;&quot;&gt; This file is required for &lt;/span&gt;&lt;span style=&quot; font-size:9pt; font-style:italic;&quot;&gt;swift&lt;/span&gt;&lt;span style=&quot; font-size:9pt;&quot;&gt; to work properly. You can try to load the file again (&amp;quot;Retry&amp;quot;) or give up (&amp;quot;Cancel&amp;quot;). If you have set an explicit bootstrap URL, you can also ignore this URL and use cached setup data (if there are any). If all goes wrong, you can try to load the setup cache from disk.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QPushButton" name="pb_IgnoreExplicitBootstrapUrl">
<property name="whatsThis">
<string>ignore the bootstrap URL and try to read from cache</string>
</property>
<property name="text">
<string>ignore</string>
</property>
</widget>
</item>
<item row="5" column="3">
<widget class="QPushButton" name="pb_OpemDirectory">
<property name="text">
<string>open dir.</string>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QPushButton" name="pb_Help">
<property name="text">
<string>help page</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLineEdit" name="le_OtherSwiftVersions">
<property name="whatsThis">
<string>Info about other swift versions installed</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QPushButton" name="pb_CopyFromSwift">
<property name="whatsThis">
<string>copy cache data from another swift version</string>
</property>
<property name="text">
<string>copy over</string>
</property>
</widget>
</item>
<item row="2" column="2" colspan="2">
<widget class="QPushButton" name="pb_TryToFix">
<property name="text">
<string>try to fix</string>
</property>
</widget>
</item>
</layout>
</widget> </widget>
</item> </item>
<item> <item>
@@ -236,7 +92,7 @@
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="standardButtons"> <property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Retry</set> <set>QDialogButtonBox::Close</set>
</property> </property>
</widget> </widget>
</item> </item>
@@ -250,18 +106,6 @@
<container>1</container> <container>1</container>
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<tabstops>
<tabstop>le_CmdLine</tabstop>
<tabstop>le_BootstrapMode</tabstop>
<tabstop>le_BootstrapUrl</tabstop>
<tabstop>pb_IgnoreExplicitBootstrapUrl</tabstop>
<tabstop>le_SetupCache</tabstop>
<tabstop>pb_LoadFromDisk</tabstop>
<tabstop>pb_Help</tabstop>
<tabstop>le_OtherSwiftVersions</tabstop>
<tabstop>pb_CopyFromSwift</tabstop>
<tabstop>pb_OpemDirectory</tabstop>
</tabstops>
<resources/> <resources/>
<connections> <connections>
<connection> <connection>

View File

@@ -129,7 +129,6 @@ namespace BlackGui
{ {
CGuiApplication::registerMetadata(); CGuiApplication::registerMetadata();
CApplication::init(false); // base class without metadata CApplication::init(false); // base class without metadata
if (this->hasSetupReader()) { this->getSetupReader()->setCheckCmdLineBootstrapUrl(false); } // no connect checks on setup reader (handled with interactive setup loading)
CGuiApplication::adjustPalette(); CGuiApplication::adjustPalette();
this->setWindowIcon(icon); this->setWindowIcon(icon);
this->settingsChanged(); this->settingsChanged();
@@ -138,9 +137,6 @@ namespace BlackGui
connect(&m_styleSheetUtility, &CStyleSheetUtility::styleSheetsChanged, this, &CGuiApplication::onStyleSheetsChanged, Qt::QueuedConnection); connect(&m_styleSheetUtility, &CStyleSheetUtility::styleSheetsChanged, this, &CGuiApplication::onStyleSheetsChanged, Qt::QueuedConnection);
connect(this, &CGuiApplication::startUpCompleted, this, &CGuiApplication::superviseWindowMinSizes, Qt::QueuedConnection); connect(this, &CGuiApplication::startUpCompleted, this, &CGuiApplication::superviseWindowMinSizes, Qt::QueuedConnection);
// splash screen
connect(this->getSetupReader(), &CSetupReader::setupLoadingMessages, this, &CGuiApplication::displaySplashMessages, Qt::QueuedConnection);
} }
} }
@@ -1094,48 +1090,21 @@ namespace BlackGui
m_minHeightChars = heightChars; m_minHeightChars = heightChars;
} }
bool CGuiApplication::interactivelySynchronizeSetup(int timeoutMs) void CGuiApplication::displaySetupLoadFailure(BlackMisc::CStatusMessageList msgs)
{ {
bool ok = false; if (msgs.hasErrorMessages())
do
{ {
const CStatusMessageList msgs = this->synchronizeSetup(timeoutMs); CSetupLoadingDialog dialog(msgs, BlackGui::CGuiApplication::mainApplicationWidget());
if (msgs.hasErrorMessages()) if (sGui)
{ {
CSetupLoadingDialog dialog(msgs, this->mainApplicationWidget()); static const QString style = sGui->getStyleSheetUtility().styles(
if (sGui) { CStyleSheetUtility::fileNameFonts(),
{ CStyleSheetUtility::fileNameStandardWidget() });
static const QString style = sGui->getStyleSheetUtility().styles( dialog.setStyleSheet(style);
{ CStyleSheetUtility::fileNameFonts(), }
CStyleSheetUtility::fileNameStandardWidget() });
dialog.setStyleSheet(style);
}
const int r = dialog.exec(); dialog.exec();
if (r == QDialog::Rejected)
{
break; // exit with false state, as file was not loaded
}
else
{
// run loop again and sync again
}
}
else
{
// setup loaded
ok = true;
break;
}
} }
while (!ok);
return ok;
}
bool CGuiApplication::parseAndSynchronizeSetup(int timeoutMs)
{
if (!this->parseAndStartupCheck()) { return false; }
return this->interactivelySynchronizeSetup(timeoutMs);
} }
QDialog::DialogCode CGuiApplication::showCloseDialog(QMainWindow *mainWindow, QCloseEvent *closeEvent) QDialog::DialogCode CGuiApplication::showCloseDialog(QMainWindow *mainWindow, QCloseEvent *closeEvent)

View File

@@ -217,13 +217,8 @@ namespace BlackGui
//! \deprecated kept for experimental tests //! \deprecated kept for experimental tests
void setMinimumSizeInCharacters(int widthChars, int heightChars); void setMinimumSizeInCharacters(int widthChars, int heightChars);
//! Wait for setup, in case it fails display a dialog how to continue //! \copydoc BlackCore::CApplication::displaySetupLoadFailure
bool interactivelySynchronizeSetup(int timeoutMs = BlackMisc::Network::CNetworkUtils::getLongTimeoutMs()); void displaySetupLoadFailure(BlackMisc::CStatusMessageList msgs) override;
//! Combined function
//! \see parseAndStartupCheck
//! \see interactivelySynchronizeSetup
virtual bool parseAndSynchronizeSetup(int timeoutMs = BlackMisc::Network::CNetworkUtils::getLongTimeoutMs()) override;
//! Show close dialog //! Show close dialog
//! \remark will modify CApplication::saveSettingsOnShutdown //! \remark will modify CApplication::saveSettingsOnShutdown

View File

@@ -34,7 +34,7 @@ int main(int argc, char *argv[])
a.addDBusAddressOption(); a.addDBusAddressOption();
a.addVatlibOptions(); a.addVatlibOptions();
a.addAudioOptions(); a.addAudioOptions();
if (!a.parseAndSynchronizeSetup()) { return EXIT_FAILURE; } if (!a.parseAndLoadSetup()) { return EXIT_FAILURE; }
const QString dBusAdress(a.getCmdDBusAddressValue()); const QString dBusAdress(a.getCmdDBusAddressValue());
a.useContexts(CCoreFacadeConfig::forCoreAllLocalInDBus(dBusAdress)); a.useContexts(CCoreFacadeConfig::forCoreAllLocalInDBus(dBusAdress));

View File

@@ -27,7 +27,7 @@ int main(int argc, char *argv[])
CGuiApplication a(CApplicationInfo::swiftMappingTool(), CApplicationInfo::MappingTool, CIcons::swiftDatabase48()); CGuiApplication a(CApplicationInfo::swiftMappingTool(), CApplicationInfo::MappingTool, CIcons::swiftDatabase48());
a.setSignalStartupAutomatically(false); // application will signal startup on its own a.setSignalStartupAutomatically(false); // application will signal startup on its own
a.splashScreen(CIcons::swiftDatabase256()); a.splashScreen(CIcons::swiftDatabase256());
if (!a.parseAndSynchronizeSetup()) { return EXIT_FAILURE; } if (!a.parseAndLoadSetup()) { return EXIT_FAILURE; }
a.useWebDataServices(BlackCore::CWebReaderFlags::AllSwiftDbReaders, CDatabaseReaderConfigList::forMappingTool()); a.useWebDataServices(BlackCore::CWebReaderFlags::AllSwiftDbReaders, CDatabaseReaderConfigList::forMappingTool());
a.useFacadeNoContexts(); a.useFacadeNoContexts();
if (!a.start()) if (!a.start())

View File

@@ -40,7 +40,7 @@ int main(int argc, char *argv[])
a.setSignalStartupAutomatically(false); // application will signal startup on its own a.setSignalStartupAutomatically(false); // application will signal startup on its own
a.splashScreen(CIcons::swift256()); a.splashScreen(CIcons::swift256());
a.setMinimumSizeInCharacters(60, 42); // experimental a.setMinimumSizeInCharacters(60, 42); // experimental
if (!a.parseAndSynchronizeSetup()) { return EXIT_FAILURE; } if (!a.parseAndLoadSetup()) { return EXIT_FAILURE; }
if (!a.hasSetupReader() || !a.start()) if (!a.hasSetupReader() || !a.start())
{ {
a.gracefulShutdown(); a.gracefulShutdown();

View File

@@ -30,7 +30,7 @@ int main(int argc, char *argv[])
CGuiApplication a(CApplicationInfo::swiftLauncher(), CApplicationInfo::Laucher, CIcons::swiftLauncher1024()); CGuiApplication a(CApplicationInfo::swiftLauncher(), CApplicationInfo::Laucher, CIcons::swiftLauncher1024());
a.addVatlibOptions(); // so it can be passed (hand over) to started applications a.addVatlibOptions(); // so it can be passed (hand over) to started applications
a.addParserOption({ { "i", "installer" }, QCoreApplication::translate("main", "Installer setup.") }); a.addParserOption({ { "i", "installer" }, QCoreApplication::translate("main", "Installer setup.") });
if (!a.parseAndSynchronizeSetup()) { return EXIT_FAILURE; } if (!a.parseAndLoadSetup()) { return EXIT_FAILURE; }
a.useWebDataServices(BlackCore::CWebReaderFlags::AllSwiftDbReaders, CDatabaseReaderConfigList::forLauncher()); a.useWebDataServices(BlackCore::CWebReaderFlags::AllSwiftDbReaders, CDatabaseReaderConfigList::forLauncher());
a.useFacadeNoContexts(); a.useFacadeNoContexts();
if (!a.start()) if (!a.start())

View File

@@ -129,11 +129,6 @@ void CSwiftLauncher::installerMode()
const QDir dir = CSwiftDirectories::logDirectory(); const QDir dir = CSwiftDirectories::logDirectory();
if (!dir.exists()) { break; } if (!dir.exists()) { break; }
if (sGui && sGui->getSetupReader())
{
sGui->getSetupReader()->prefillCacheWithLocalResourceBootstrapFile();
}
for (const CSimulatorInfo &sim : CSimulatorInfo::allSimulatorsSet()) for (const CSimulatorInfo &sim : CSimulatorInfo::allSimulatorsSet())
{ {
this->synchronizeCache(sim); this->synchronizeCache(sim);

View File

@@ -100,7 +100,7 @@ int main(int argc, char *argv[])
BLACKTEST_INIT(BlackCoreTest::CTestContext) BLACKTEST_INIT(BlackCoreTest::CTestContext)
CApplication a(CApplicationInfo::UnitTest); CApplication a(CApplicationInfo::UnitTest);
a.addVatlibOptions(); a.addVatlibOptions();
const bool setup = a.parseAndSynchronizeSetup(); const bool setup = a.parseAndLoadSetup();
if (!setup) { qWarning() << "No setup loaded"; } if (!setup) { qWarning() << "No setup loaded"; }
int r = EXIT_FAILURE; int r = EXIT_FAILURE;
if (a.start()) if (a.start())

View File

@@ -124,7 +124,7 @@ int main(int argc, char *argv[])
BLACKTEST_INIT(BlackCoreTest::CTestConnectivity) BLACKTEST_INIT(BlackCoreTest::CTestConnectivity)
CApplication a(CApplicationInfo::UnitTest); CApplication a(CApplicationInfo::UnitTest);
a.addVatlibOptions(); a.addVatlibOptions();
const bool setup = a.parseAndSynchronizeSetup(); const bool setup = a.parseAndLoadSetup();
if (!setup) { qWarning() << "No setup loaded"; } if (!setup) { qWarning() << "No setup loaded"; }
int r = EXIT_FAILURE; int r = EXIT_FAILURE;
if (a.start()) if (a.start())