diff --git a/src/blackcore/application.cpp b/src/blackcore/application.cpp index 0120fec3b..9b242825a 100644 --- a/src/blackcore/application.cpp +++ b/src/blackcore/application.cpp @@ -41,10 +41,9 @@ BlackCore::CApplication *sApp = nullptr; // set by constructor namespace BlackCore { - CApplication::CApplication(const QString &applicationName, bool init) : - m_cookieManager( {}, this), - m_applicationName(applicationName), - m_coreFacadeConfig(CCoreFacadeConfig::allEmpty()) + CApplication::CApplication( + const QString &applicationName, bool init) : + m_cookieManager( {}, this), m_applicationName(applicationName), m_coreFacadeConfig(CCoreFacadeConfig::allEmpty()) { Q_ASSERT_X(!sApp, Q_FUNC_INFO, "already initialized"); Q_ASSERT_X(QCoreApplication::instance(), Q_FUNC_INFO, "no application object"); @@ -67,25 +66,26 @@ namespace BlackCore this->initParser(); this->initLogging(); + // + // cmd line arguments not yet parsed here + // + // Translations QFile file(":blackmisc/translations/blackmisc_i18n_de.qm"); CLogMessage(this).debug() << (file.exists() ? "Found translations in resources" : "No translations in resources"); QTranslator translator; - if (translator.load("blackmisc_i18n_de", ":blackmisc/translations/")) - { - CLogMessage(this).debug() << "Translator loaded"; - } + if (translator.load("blackmisc_i18n_de", ":blackmisc/translations/")) { CLogMessage(this).debug() << "Translator loaded"; } QCoreApplication::instance()->installTranslator(&translator); - // Global setup / bootstraping + // Init network this->m_cookieManager.setParent(&this->m_accessManager); this->m_accessManager.setCookieJar(&this->m_cookieManager); // global setup sApp = this; this->m_setupReader.reset(new CSetupReader(this)); - connect(this->m_setupReader.data(), &CSetupReader::setupSynchronized, this, &CApplication::ps_setupSyncronized); - connect(this->m_setupReader.data(), &CSetupReader::updateInfoSynchronized, this, &CApplication::updateInfoSynchronized); + connect(this->m_setupReader.data(), &CSetupReader::setupAvailable, this, &CApplication::ps_setupAvailable); + connect(this->m_setupReader.data(), &CSetupReader::updateInfoAvailable, this, &CApplication::updateInfoAvailable); this->m_parser.addOptions(this->m_setupReader->getCmdLineOptions()); @@ -97,7 +97,6 @@ namespace BlackCore } } - CApplication::~CApplication() { this->gracefulShutdown(); @@ -137,13 +136,21 @@ namespace BlackCore if (!s) { return false; } } - // parsing itself is done - if (this->m_startSetupReader && !this->m_setupReader->isSetupSyncronized()) + // clear cache? + if (this->isSetOrTrue(this->m_cmdClearCache)) { - CStatusMessage m(this->requestReloadOfSetupAndVersion()); - if (m.isWarningOrAbove()) + QStringList files(CApplication::clearCaches()); + CLogMessage(this).debug() << "Cleared cache, " << files.size() << " files"; + } + + // parsing itself is done + if (this->m_startSetupReader && !this->m_setupReader->isSetupAvailable()) + { + const CStatusMessageList msgs(this->requestReloadOfSetupAndVersion()); + CLogMessage::preformatted(msgs); + if (msgs.isFailure()) { - this->cmdLineErrorMessage(m.getMessage()); + this->cmdLineErrorMessage(msgs.getWarningAndErrorMessages().toSingleMessage().getMessage()); return false; } } @@ -188,13 +195,13 @@ namespace BlackCore return this->m_started; } - bool CApplication::isSetupSyncronized() const + bool CApplication::isSetupAvailable() const { if (this->m_shutdown || !this->m_setupReader) { return false; } - return this->m_setupReader->isSetupSyncronized(); + return this->m_setupReader->isSetupAvailable(); } - CStatusMessage CApplication::requestReloadOfSetupAndVersion() + CStatusMessageList CApplication::requestReloadOfSetupAndVersion() { if (!this->m_shutdown) { @@ -256,16 +263,14 @@ namespace BlackCore return a.constData(); } - bool CApplication::isRunningInDeveloperEnvironment() const + bool CApplication::initIsRunningInDeveloperEnvironment() const { if (!CBuildConfig::canRunInDeveloperEnvironment()) { return false; } - if (!this->m_parser.value(this->m_cmdDevelopment).isEmpty()) + if (this->m_parser.isSet(this->m_cmdDevelopment)) { - // explicit value - const QString v(this->m_parser.value(this->m_cmdDevelopment)); - return stringToBool(v); + return this->isSetOrTrue(this->m_cmdDevelopment); } - else if (this->isSetupSyncronized()) + else if (this->isSetupAvailable()) { // assume value from setup return this->getGlobalSetup().isDevelopment(); @@ -442,7 +447,7 @@ namespace BlackCore { if (!this->m_useContexts) { return true; } // we do not use context, so no need to startup if (!this->m_parsed) { return false; } - if (!this->m_setupReader || !this->m_setupReader->isSetupSyncronized()) { return false; } + if (!this->m_setupReader || !this->m_setupReader->isSetupAvailable()) { return false; } Q_ASSERT_X(this->m_coreFacade.isNull(), Q_FUNC_INFO, "Cannot alter facade"); Q_ASSERT_X(this->m_setupReader, Q_FUNC_INFO, "No facade without setup possible"); @@ -460,7 +465,7 @@ namespace BlackCore { if (!this->m_useWebData) { return true; } if (!this->m_parsed) { return false; } - if (!this->m_setupReader || !this->m_setupReader->isSetupSyncronized()) { return false; } + if (!this->m_setupReader || !this->m_setupReader->isSetupAvailable()) { return false; } Q_ASSERT_X(this->m_setupReader, Q_FUNC_INFO, "No web data services without setup possible"); if (!this->m_webDataServices) @@ -490,7 +495,7 @@ namespace BlackCore this->m_cmdHelp = this->m_parser.addHelpOption(); this->m_cmdVersion = this->m_parser.addVersionOption(); - this->m_cmdDevelopment = QCommandLineOption({ "dev", "developemnt" }, + this->m_cmdDevelopment = QCommandLineOption({ "dev", "development" }, QCoreApplication::translate("application", "Dev.system feature?"), "development"); this->addParserOption(this->m_cmdDevelopment); @@ -499,6 +504,22 @@ namespace BlackCore QCoreApplication::translate("application", "Local shared directory."), "shared"); this->addParserOption(this->m_cmdSharedDir); + + this->m_cmdClearCache = QCommandLineOption({ "ccache", "clearcache" }, + QCoreApplication::translate("application", "Clear (reset) the caches."), + "clearcache"); + this->addParserOption(this->m_cmdClearCache); + } + + bool CApplication::isSetOrTrue(const QCommandLineOption &option) const + { + if (!this->m_parser.isSet(option)) { return false; } + + // explicit value + const QString v(this->m_parser.value(option).trimmed()); + if (v.isEmpty()) { return true; } // just flag + if (v.startsWith("-")) { return true; } // just flag, because value is already next parameter + return stringToBool(v); } void CApplication::registerMetadata() @@ -507,6 +528,13 @@ namespace BlackCore BlackCore::registerMetadata(); } + QStringList CApplication::clearCaches() + { + const QStringList files(CDataCache::instance()->enumerateStore()); + CDataCache::instance()->clearAllValues(); + return files; + } + void CApplication::gracefulShutdown() { if (this->m_shutdown) { return; } @@ -553,14 +581,15 @@ namespace BlackCore this->m_fileLogger->close(); } - void CApplication::ps_setupSyncronized(bool success) + void CApplication::ps_setupAvailable(bool available) { - if (success) + if (available) { - emit setupSyncronized(success); this->m_started = this->asyncWebAndContextStart(); } this->m_startUpCompleted = true; + emit setupAvailable(available); + if (this->m_signalStartup) { emit this->startUpCompleted(this->m_started); @@ -624,7 +653,7 @@ namespace BlackCore void CApplication::addDBusAddressOption() { - this->m_cmdDBusAddress = QCommandLineOption({ "dbus", "dbus-address", "dbusaddress" }, + this->m_cmdDBusAddress = QCommandLineOption({ "dbus", "dbusaddress" }, QCoreApplication::translate("application", "DBus address."), "dbusaddress"); this->addParserOption(this->m_cmdDBusAddress); @@ -704,6 +733,9 @@ namespace BlackCore return true; } + // dev. + this->m_devEnv = this->initIsRunningInDeveloperEnvironment(); + // Hookin, other parsing if (!this->parsingHookIn()) { return false; } diff --git a/src/blackcore/application.h b/src/blackcore/application.h index 3af25f024..6b9269e86 100644 --- a/src/blackcore/application.h +++ b/src/blackcore/application.h @@ -90,10 +90,10 @@ namespace BlackCore void deleteAllCookies(); //! Setup already syncronized - bool isSetupSyncronized() const; + bool isSetupAvailable() const; //! Reload setup and version - BlackMisc::CStatusMessage requestReloadOfSetupAndVersion(); + BlackMisc::CStatusMessageList requestReloadOfSetupAndVersion(); //! Web data services available? bool hasWebDataServices() const; @@ -114,7 +114,7 @@ namespace BlackCore const char *swiftVersionChar(); //! Running in dev.environment? - bool isRunningInDeveloperEnvironment() const; + bool isRunningInDeveloperEnvironment() const { return this->m_devEnv; } //! Signal startup automatically or individually void signalStartupAutomatically(bool signal = false); @@ -137,6 +137,9 @@ namespace BlackCore //! Process all events for some time static void processEventsFor(int milliseconds); + //! Clear the caches + static QStringList clearCaches(); + // ----------------------- parsing ---------------------------------------- //! \name parsing of command line options @@ -259,11 +262,11 @@ namespace BlackCore const BlackMisc::CSlot &callback); signals: - //! Setup syncronized - void setupSyncronized(bool success); + //! Setup available (cache, web load, ..) + void setupAvailable(bool success); - //! Update info syncronized - void updateInfoSynchronized(bool success); + //! Update info available (cache, web load) + void updateInfoAvailable(bool success); //! Startup has been completed void startUpCompleted(bool success); @@ -276,7 +279,7 @@ namespace BlackCore protected slots: //! Setup read/syncronized - void ps_setupSyncronized(bool success); + void ps_setupAvailable(bool available); //! Startup completed virtual void ps_startupCompleted(); @@ -297,6 +300,9 @@ namespace BlackCore //! Can be used to start special services virtual bool startHookIn() { return true; } + //! Flag set or explicitly set to true + bool isSetOrTrue(const QCommandLineOption &option) const; + //! Severe issue during startup, most likely it does not make sense to continue //! \note call this here if the parsing stage is over and reaction to a runtime issue //! is needed @@ -317,17 +323,17 @@ namespace BlackCore static void registerMetadata(); // cmd parsing - QCommandLineParser m_parser; //!< cmd parser - QCommandLineOption m_cmdHelp {"help"}; //!< help option - QCommandLineOption m_cmdVersion {"version"}; //!< version option - QCommandLineOption m_cmdDBusAddress {"empty"}; //!< DBus address - QCommandLineOption m_cmdDevelopment {"dev"}; //!< Dev flag - QCommandLineOption m_cmdSharedDir {"shared"}; //!< Dev flag - - bool m_parsed = false; //!< Parsing accomplished? - bool m_started = false; //!< started with success? - bool m_startUpCompleted = false; //!< startup phase completed? Can mean startup failed - bool m_startSetupReader = false; //!< start the setup reader + QCommandLineParser m_parser; //!< cmd parser + QCommandLineOption m_cmdHelp {"help"}; //!< help option + QCommandLineOption m_cmdVersion {"version"}; //!< version option + QCommandLineOption m_cmdDBusAddress {"empty"}; //!< DBus address + QCommandLineOption m_cmdDevelopment {"dev"}; //!< Dev. flag + QCommandLineOption m_cmdSharedDir {"shared"}; //!< Shared directory + QCommandLineOption m_cmdClearCache {"clearcache"}; //!< Clear cache + bool m_parsed = false; //!< Parsing accomplished? + bool m_started = false; //!< started with success? + bool m_startUpCompleted = false; //!< startup phase completed? Can mean startup failed + bool m_startSetupReader = false; //!< start the setup reader private: //! init logging system @@ -336,6 +342,9 @@ namespace BlackCore //! Init parser void initParser(); + //! Dev.environment + bool initIsRunningInDeveloperEnvironment() const; + //! Async. start when setup is loaded bool asyncWebAndContextStart(); @@ -354,6 +363,7 @@ namespace BlackCore bool m_useContexts = false; //!< use contexts bool m_useWebData = false; //!< use web data bool m_signalStartup = true; //!< signal startup automatically + bool m_devEnv = false; //!< dev. environment }; } // namespace diff --git a/src/blackcore/data/globalsetup.cpp b/src/blackcore/data/globalsetup.cpp index 2a7047e4e..bdf7f9bd5 100644 --- a/src/blackcore/data/globalsetup.cpp +++ b/src/blackcore/data/globalsetup.cpp @@ -82,14 +82,14 @@ namespace BlackCore QString CGlobalSetup::buildBootstrapFileUrl(const QString &candidate) { - static const QString vS(QString(versionString()).append("/")); + static const QString version(QString(versionString()).append("/")); if (candidate.endsWith("bootstrap.json")) { return candidate; } CUrl url(candidate); if (candidate.contains("/bootstrap")) { url.appendPath("bootstrap.json"); } - else if (candidate.endsWith(versionString()) || candidate.endsWith(vS)) + else if (candidate.endsWith(versionString()) || candidate.endsWith(version)) { url.appendPath("/bootstrap/bootstrap.json"); } diff --git a/src/blackcore/setupreader.cpp b/src/blackcore/setupreader.cpp index 6960e7f0f..ff98bd01a 100644 --- a/src/blackcore/setupreader.cpp +++ b/src/blackcore/setupreader.cpp @@ -30,10 +30,7 @@ namespace BlackCore { CSetupReader::CSetupReader(QObject *parent) : QObject(parent) - { - connect(this, &CSetupReader::setupSynchronized, this, &CSetupReader::ps_setupSyncronized); - connect(this, &CSetupReader::updateInfoSynchronized, this, &CSetupReader::ps_versionInfoSyncronized); - } + { } QList CSetupReader::getCmdLineOptions() const { @@ -46,24 +43,33 @@ namespace BlackCore }; } - CStatusMessage CSetupReader::asyncLoad() + CStatusMessageList CSetupReader::asyncLoad() { + CStatusMessageList msgs; if (this->readLocalBootstrapFile(this->m_localSetupFileValue)) { // initialized by local file for testing - emit this->setupSynchronized(true); - return CStatusMessage(this, CStatusMessage::SeverityInfo, "Using local bootstrap file: " + this->m_localSetupFileValue); + msgs.push_back(CStatusMessage(this, CStatusMessage::SeverityInfo, "Using local bootstrap file: " + this->m_localSetupFileValue)); + msgs.push_back(this->manageSetupAvailability(false, true)); + return msgs; } m_setup.synchronize(); // make sure it is loaded + CGlobalSetup cachedSetup = m_setup.getCopy(); + const bool cacheAvailable = cachedSetup.wasLoaded(); + msgs.push_back(cacheAvailable ? + CStatusMessage(this, CStatusMessage::SeverityInfo , "Cached setup syncronized and contains data") : + CStatusMessage(this, CStatusMessage::SeverityInfo , "Cached setup syncronized, but no data") + ); if (this->m_bootstrapMode == CacheOnly) { - - CGlobalSetup currentSetup = m_setup.getCopy(); - this->m_updateInfoUrls = currentSetup.getUpdateInfoFileUrls(); - emit this->setupSynchronized(true); - emit this->updateInfoSynchronized(true); - return CStatusMessage(this, CStatusMessage::SeverityInfo, "Cache only setup, using it as it is"); + this->m_updateInfoUrls = cachedSetup.getUpdateInfoFileUrls(); + msgs.push_back(cacheAvailable ? + CStatusMessage(this, CStatusMessage::SeverityInfo, "Cache only setup, using it as it is") : + CStatusMessage(this, CStatusMessage::SeverityError, "Cache only setup, but cache is empty") + ); + msgs.push_back(this->manageSetupAvailability(false, false)); + return msgs; } this->m_bootstrapUrls.clear(); // clean up previous values @@ -76,37 +82,45 @@ namespace BlackCore } // if ever loaded add those URLs - const CGlobalSetup currentSetup = m_setup.getCopy(); - if (this->m_bootstrapMode != Explicit) + if (cacheAvailable) { - // also use previously cached URLs - this->m_bootstrapUrls.push_back(currentSetup.getBootstrapFileUrls()); - - // fail over if still empty - //! \todo do we want to keep this or use a cmd line flag to enable the behaviour. Risk here to use an undesired setup - if (this->m_bootstrapUrls.isEmpty()) + if (this->m_bootstrapMode != Explicit) { - // use file from disk delivered with swift - // there is a potential risk here, if the URL passed via cmd args is actually adressing an entirely diffent scenario, - // this would load a file for something else - CGlobalSetup resourceSetup(CGlobalSetup::fromJsonFile( - CBuildConfig::getBootstrapResourceFile() - )); - this->m_bootstrapUrls.push_back(resourceSetup.getBootstrapFileUrls()); + // also use previously cached URLs + const CUrlList bootstrapCacheUrls(cachedSetup.getBootstrapFileUrls()); + this->m_bootstrapUrls.push_back(bootstrapCacheUrls); + msgs.push_back(bootstrapCacheUrls.isEmpty() ? + CStatusMessage(this, CStatusMessage::SeverityWarning, "No bootsrap URLs in cache") : + CStatusMessage(this, CStatusMessage::SeverityInfo, "Adding " + QString::number(bootstrapCacheUrls.size()) + " bootstrap URLs from cache")); } } + else + { + msgs.push_back(CStatusMessage(this, CStatusMessage::SeverityInfo, "Empty cache, will not add URLs")); + } this->m_bootstrapUrls.removeDuplicates(); // clean up if (this->m_bootstrapUrls.isEmpty()) { // after all still empty - return CStatusMessage(this, CStatusMessage::SeverityError, "No bootstrap URLs, cannot load setup"); + msgs.push_back(CStatusMessage(this, CStatusMessage::SeverityError, "No bootstrap URLs, cannot load setup")); } else { - this->ps_readSetup(); // start reading - return CStatusMessage(this, CStatusMessage::SeverityInfo, "Will start loading setup"); + CStatusMessageList readMsgs = triggerReadSetup(); + if (cacheAvailable && readMsgs.isFailure()) + { + // error but cache is available, we can continue + readMsgs.clipSeverity(CStatusMessage::SeverityWarning); + msgs.push_back(CStatusMessage(this, CStatusMessage::SeverityWarning, "Loading setup failed, but cache is available, will continue")); + msgs.push_back(readMsgs); + } + else + { + msgs.push_back(readMsgs); + } } + return msgs; } bool CSetupReader::parseCmdLineArguments() @@ -114,8 +128,13 @@ namespace BlackCore this->m_bootstrapUrlFileValue = CGlobalSetup::buildBootstrapFileUrl( sApp->getParserValue(this->m_cmdBootstrapUrl) ); - this->m_bootstrapMode = stringToEnum(sApp->getParserValue(this->m_cmdBootstrapMode)); QUrl url(this->m_bootstrapUrlFileValue); + const QString urlString(url.toString()); + this->m_bootstrapMode = stringToEnum(sApp->getParserValue(this->m_cmdBootstrapMode)); + if (urlString.isEmpty() && this->m_bootstrapMode == Explicit) + { + this->m_bootstrapMode = Implicit; // no URL, we use implicit mode + } // check on local file if (url.isLocalFile()) @@ -136,7 +155,7 @@ namespace BlackCore { if (!CNetworkUtils::canConnect(url)) { - sApp->cmdLineErrorMessage("URL " + url.toString() + " not reachable"); + sApp->cmdLineErrorMessage("URL " + urlString + " not reachable"); return false; } } @@ -151,17 +170,29 @@ namespace BlackCore void CSetupReader::ps_readSetup() { + const CStatusMessageList msgs(this->triggerReadSetup()); + if (!msgs.isSuccess()) + { + CLogMessage::preformatted(msgs); + } + } + + CStatusMessageList CSetupReader::triggerReadSetup() + { + if (m_shutdown) { return CStatusMessage(this, CStatusMessage::SeverityError, "shutdown"); } const CUrl url(this->m_bootstrapUrls.obtainNextWorkingUrl()); if (url.isEmpty()) { - CLogMessage(this).warning("Cannot read setup, URLs: %1, failed URLs: %2") - << this->m_bootstrapUrls - << this->m_bootstrapUrls.getFailedUrls(); - emit setupSynchronized(false); - return; + const CStatusMessage m(this, CStatusMessage::SeverityError, + "Cannot read setup, URLs: " + this->m_bootstrapUrls.toQString() + + " failed URLs: " + this->m_bootstrapUrls.getFailedUrls().toQString()); + CStatusMessageList msgs(m); + msgs.push_back(this->manageSetupAvailability(false, false)); + return msgs; } - if (m_shutdown) { return; } + const CStatusMessage m(this, CStatusMessage::SeverityInfo, "Start reading URL: " + url.toQString()); sApp->getFromNetwork(url.toNetworkRequest(), { this, &CSetupReader::ps_parseSetupFile }); + return m; } void CSetupReader::ps_readUpdateInfo() @@ -172,33 +203,13 @@ namespace BlackCore CLogMessage(this).warning("Cannot read update info, URLs: %1, failed URLs: %2") << this->m_updateInfoUrls << this->m_updateInfoUrls.getFailedUrls(); - emit updateInfoSynchronized(false); + this->manageUpdateAvailability(false); return; } if (m_shutdown) { return; } sApp->getFromNetwork(url.toNetworkRequest(), { this, &CSetupReader::ps_parseUpdateInfoFile}); } - void CSetupReader::ps_setupSyncronized(bool success) - { - // trigger consecutive read - this->m_setupSyncronized = success; - if (success) - { - CLogMessage(this).info("Setup synchronized, will trigger read of update information"); - QTimer::singleShot(500, this, &CSetupReader::ps_readUpdateInfo); - } - else - { - CLogMessage(this).error("Setup reading failed, hence version info will not be loaded"); - } - } - - void CSetupReader::ps_versionInfoSyncronized(bool success) - { - this->m_updateInfoSyncronized = success; - } - void CSetupReader::ps_setupChanged() { // settings have changed on disk @@ -209,7 +220,7 @@ namespace BlackCore const QString bsm(s.toLower().trimmed()); if (bsm.startsWith("expl")) return Explicit; if (bsm.startsWith("cache")) return CacheOnly; - return Default; + return Implicit; } bool CSetupReader::readLocalBootstrapFile(QString &fileName) @@ -273,7 +284,7 @@ namespace BlackCore { this->m_updateInfoUrls = currentSetup.getUpdateInfoFileUrls(); // defaults CLogMessage(this).info("Same setup version loaded from %1 as already in data cache %2") << urlString << m_setup.getFilename(); - emit setupSynchronized(true); + CLogMessage::preformatted(this->manageSetupAvailability(true)); return; // success } @@ -284,14 +295,14 @@ namespace BlackCore { m.setCategories(getLogCategories()); CLogMessage::preformatted(m); - emit setupSynchronized(false); + CLogMessage::preformatted(this->manageSetupAvailability(false)); return; // issue with cache } else { this->m_updateInfoUrls = loadedSetup.getUpdateInfoFileUrls(); CLogMessage(this).info("Setup: Updated data cache in %1") << this->m_setup.getFilename(); - emit setupSynchronized(true); + CLogMessage::preformatted(this->manageSetupAvailability(true)); return; // success } // cache @@ -311,7 +322,7 @@ namespace BlackCore } else { - emit setupSynchronized(false); + CLogMessage::preformatted(manageSetupAvailability(false)); } } @@ -346,7 +357,7 @@ namespace BlackCore if (sameVersionLoaded) { CLogMessage(this).info("Same update info version loaded from %1 as already in data cache %2") << urlString << m_setup.getFilename(); - emit updateInfoSynchronized(true); + this->manageUpdateAvailability(true); return; // success } @@ -355,13 +366,13 @@ namespace BlackCore { m.setCategories(getLogCategories()); CLogMessage::preformatted(m); - emit updateInfoSynchronized(false); + this->manageUpdateAvailability(false); return; // issue with cache } else { CLogMessage(this).info("Update info: Updated data cache in %1") << m_updateInfo.getFilename(); - emit updateInfoSynchronized(true); + this->manageUpdateAvailability(true); return; // success } // cache } // json empty @@ -380,13 +391,13 @@ namespace BlackCore } else { - emit updateInfoSynchronized(false); + this->manageUpdateAvailability(false); } } // function const CLogCategoryList &CSetupReader::getLogCategories() { - static const CLogCategoryList cats({ CLogCategory("swift.setupreader"), CLogCategory::webservice()}); + static const CLogCategoryList cats({ CLogCategory("swift.setupreader"), CLogCategory::webservice(), CLogCategory::startup()}); return cats; } @@ -399,4 +410,65 @@ namespace BlackCore { return m_updateInfo.getCopy(); } + + 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; + if (webRead) + { + msgs.push_back(CLogMessage(this).info("Setup loaded from web, will trigger read of update information")); + QTimer::singleShot(500, this, &CSetupReader::ps_readUpdateInfo); + } + if (localRead) + { + msgs.push_back(CLogMessage(this).info("Setup loaded locally, will trigger read of update information")); + QTimer::singleShot(500, this, &CSetupReader::ps_readUpdateInfo); + } + + bool available = false; + if (webRead || localRead) + { + available = true; + } + else + { + bool cacheAvailable = this->m_setup.get().wasLoaded(); + available = cacheAvailable && this->m_bootstrapMode != Explicit; + } + + if (available && !webRead && !localRead) + { + msgs.push_back(CStatusMessage(this, CStatusMessage::SeverityInfo, "Setup available, but not updated this time")); + } + else if (!available) + { + msgs.push_back(CStatusMessage(this, CStatusMessage::SeverityError, "Setup not available")); + } + + this->m_setupAvailable = available; + emit setupAvailable(available); + + if (!webRead && !localRead) + { + msgs.push_back(CStatusMessage(this).warning("Since setup was not updated this time, will not start loading of update information")); + this->manageUpdateAvailability(false); + } + return msgs; + } + + void CSetupReader::manageUpdateAvailability(bool webRead) + { + if (webRead) + { + this->m_updateInfoAvailable = true; + emit updateInfoAvailable(true); + } + else + { + bool cached = this->m_updateInfo.isSaved(); + this->m_updateInfoAvailable = cached; + emit updateInfoAvailable(cached); + } + } } // namespace diff --git a/src/blackcore/setupreader.h b/src/blackcore/setupreader.h index 5d53c430b..0c31ef097 100644 --- a/src/blackcore/setupreader.h +++ b/src/blackcore/setupreader.h @@ -52,18 +52,18 @@ namespace BlackCore BlackCore::Data::CUpdateInfo getUpdateInfo() const; signals: - //! Setup has been read - void setupSynchronized(bool success); + //! Setup avialable (from web, cache, or local file) + void setupAvailable(bool available); - //! Version bas been read - void updateInfoSynchronized(bool success); + //! Setup avialable (from web, cache + void updateInfoAvailable(bool available); protected: //! Constructor explicit CSetupReader(QObject *parent); //! Load the data - BlackMisc::CStatusMessage asyncLoad(); + BlackMisc::CStatusMessageList asyncLoad(); //! Parse cmd line arguments bool parseCmdLineArguments(); @@ -74,13 +74,13 @@ namespace BlackCore //! Terminate void gracefulShutdown(); - //! Setup loaded? + //! Setup available? //! \threadsafe - bool isSetupSyncronized() const { return m_setupSyncronized; } + bool isSetupAvailable() const { return m_setupAvailable; } - //! Version info loaded? + //! Version info available? //! \threadsafe - bool isUpdateSyncronized() const { return m_updateInfoSyncronized; } + bool isUpdateInfoAvailable() const { return m_updateInfoAvailable; } private slots: //! Setup has been read @@ -95,12 +95,6 @@ namespace BlackCore //! Do reading void ps_readUpdateInfo(); - //! Setup has beem syncronized - void ps_setupSyncronized(bool success); - - //! Version info has beem syncronized - void ps_versionInfoSyncronized(bool success); - //! Setup has been changed void ps_setupChanged(); @@ -108,17 +102,17 @@ namespace BlackCore //! Bootstrap mode enum BootstrapMode { - Default, + Implicit, Explicit, CacheOnly }; bool m_shutdown = false; - std::atomic m_setupSyncronized { false }; - std::atomic m_updateInfoSyncronized { false }; + std::atomic m_setupAvailable { false }; + std::atomic m_updateInfoAvailable { false }; QString m_localSetupFileValue; //! Local file for setup, passed by cmd line arguments QString m_bootstrapUrlFileValue; //! Bootstrap URL if not local - BootstrapMode m_bootstrapMode; //! How to bootstrap + BootstrapMode m_bootstrapMode = Explicit; //! How to bootstrap BlackMisc::Network::CFailoverUrlList m_bootstrapUrls; //!< location of setup files BlackMisc::Network::CFailoverUrlList m_updateInfoUrls; //!< location of info files BlackMisc::CData m_setup {this, &CSetupReader::ps_setupChanged}; //!< data cache setup @@ -126,20 +120,31 @@ namespace BlackCore QCommandLineOption m_cmdBootstrapUrl { - { "url", "bootstrap-url", "bootstrapurl" }, + { "url", "bootstrapurl" }, QCoreApplication::translate("application", "bootstrap URL, e.g. datastore.swift-project.org"), "bootstrapurl" - }; //!< bootstrap URL + }; //!< 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 + { "bmode", "bootstrapmode" }, + QCoreApplication::translate("application", "bootstrap mode: explicit, implicit, cache(-only)"), + "bootstrapmode", "explicit" + }; //!< bootstrap mode - //! Read by local individual file + //! Read by local individual file and update cache from that bool readLocalBootstrapFile(QString &fileName); + //! Trigger reading + BlackMisc::CStatusMessageList triggerReadSetup(); + + //! Emit the availability signal and state + //! \threadsafe + BlackMisc::CStatusMessageList manageSetupAvailability(bool webRead, bool localRead = false); + + //! Emit the available signal + //! \threadsafe + void manageUpdateAvailability(bool webRead); + //! Convert string to mode static BootstrapMode stringToEnum(const QString &s); }; diff --git a/src/blackcore/webdataservices.cpp b/src/blackcore/webdataservices.cpp index ef8ae83a0..ef2a9dc58 100644 --- a/src/blackcore/webdataservices.cpp +++ b/src/blackcore/webdataservices.cpp @@ -43,7 +43,7 @@ namespace BlackCore if (!sApp) { return; } // shutting down Q_ASSERT_X(QSslSocket::supportsSsl(), Q_FUNC_INFO, "missing SSL support"); - Q_ASSERT_X(sApp->isSetupSyncronized(), Q_FUNC_INFO, "Setup not syncronized"); + Q_ASSERT_X(sApp->isSetupAvailable(), Q_FUNC_INFO, "Setup not syncronized"); this->setObjectName("CWebDataReader"); this->initReaders(readerFlags); this->initWriters(); diff --git a/src/blackgui/components/logcomponent.cpp b/src/blackgui/components/logcomponent.cpp index e3ffd4e86..51b25c0c2 100644 --- a/src/blackgui/components/logcomponent.cpp +++ b/src/blackgui/components/logcomponent.cpp @@ -72,10 +72,16 @@ namespace BlackGui void CLogComponent::appendStatusMessageToList(const CStatusMessage &statusMessage) { - if (statusMessage.isEmpty()) return; + if (statusMessage.isEmpty()) { return; } this->ui->tvp_StatusMessages->insert(statusMessage); } + void CLogComponent::appendStatusMessagesToList(const CStatusMessageList &statusMessages) + { + if (statusMessages.isEmpty()) { return; } + this->ui->tvp_StatusMessages->insert(statusMessages); + } + void CLogComponent::CLogMenu::customMenu(QMenu &menu) const { CLogComponent *logComp = qobject_cast(this->parent()); diff --git a/src/blackgui/components/logcomponent.h b/src/blackgui/components/logcomponent.h index 865bba482..62a98b837 100644 --- a/src/blackgui/components/logcomponent.h +++ b/src/blackgui/components/logcomponent.h @@ -72,6 +72,9 @@ namespace BlackGui //! Append status message to list void appendStatusMessageToList(const BlackMisc::CStatusMessage &statusMessage); + //! Append status messages to list + void appendStatusMessagesToList(const BlackMisc::CStatusMessageList &statusMessages); + private: QScopedPointer ui; diff --git a/src/blackgui/guiapplication.cpp b/src/blackgui/guiapplication.cpp index 8837e3bd6..b04d14598 100644 --- a/src/blackgui/guiapplication.cpp +++ b/src/blackgui/guiapplication.cpp @@ -307,8 +307,8 @@ namespace BlackGui a = sm->addAction("Reset cache"); c = connect(a, &QAction::triggered, this, [this]() { - CDataCache::instance()->clearAllValues(); - this->displayTextInConsole("Cleared cache!"); + const QStringList files = CApplication::clearCaches(); + this->displayTextInConsole("Cleared caches! " + QString::number(files.size()) + " files"); }); Q_ASSERT_X(c, Q_FUNC_INFO, "Connect failed"); diff --git a/src/blackmisc/logcategory.h b/src/blackmisc/logcategory.h index 6bf06b1ea..33ca5a77b 100644 --- a/src/blackmisc/logcategory.h +++ b/src/blackmisc/logcategory.h @@ -106,6 +106,13 @@ namespace BlackMisc return cat; } + //! Startup of application + static const CLogCategory &startup() + { + static const CLogCategory cat { "swift.startup" }; + return cat; + } + //! Webservice with swift DB static const CLogCategory &swiftDbWebservice() { @@ -135,6 +142,7 @@ namespace BlackMisc matching(), swiftDbWebservice(), services(), + startup(), validation(), vatsimSpecific(), verification(), diff --git a/src/blackmisc/logpattern.cpp b/src/blackmisc/logpattern.cpp index 8d2ef019b..d9436b5b2 100644 --- a/src/blackmisc/logpattern.cpp +++ b/src/blackmisc/logpattern.cpp @@ -28,6 +28,7 @@ namespace BlackMisc { "downloading data", exactMatch(CLogCategory::download()) }, { "VASTIM specific", exactMatch(CLogCategory::vatsimSpecific()) }, { "webservice related", exactMatch(CLogCategory::webservice()) }, + { "startup phase", exactMatch(CLogCategory::startup()) }, { "swift DB webservice related", exactMatch(CLogCategory::swiftDbWebservice()) }, { "Qt library", startsWith("qt.") }, { "uncategorized (other)", empty() } diff --git a/src/blackmisc/statusmessage.cpp b/src/blackmisc/statusmessage.cpp index 83fd9e5e5..d502da5b8 100644 --- a/src/blackmisc/statusmessage.cpp +++ b/src/blackmisc/statusmessage.cpp @@ -197,6 +197,13 @@ namespace BlackMisc return c.isEmpty() ? this->getCategoriesAsString() : c; } + bool CStatusMessage::clipSeverity(CStatusMessage::StatusSeverity severity) + { + if (this->getSeverity() <= severity) { return false; } + this->setSeverity(severity); + return true; + } + bool CStatusMessage::isSuccess() const { return !isFailure(); diff --git a/src/blackmisc/statusmessage.h b/src/blackmisc/statusmessage.h index 689a00e82..08ca5b04f 100644 --- a/src/blackmisc/statusmessage.h +++ b/src/blackmisc/statusmessage.h @@ -205,6 +205,9 @@ namespace BlackMisc //! Message severity StatusSeverity getSeverity() const { return this->m_severity; } + //! Clip/reduce severity if higher (more critical) + bool clipSeverity(StatusSeverity severity); + //! Info or debug, no warning or error bool isSeverityInfoOrLess() const { return this->m_severity == SeverityInfo || this->m_severity == SeverityDebug; } diff --git a/src/blackmisc/statusmessagelist.cpp b/src/blackmisc/statusmessagelist.cpp index 4229dd61f..539e86db7 100644 --- a/src/blackmisc/statusmessagelist.cpp +++ b/src/blackmisc/statusmessagelist.cpp @@ -17,6 +17,11 @@ namespace BlackMisc CSequence(other) { } + CStatusMessageList::CStatusMessageList(const CStatusMessage &statusMessage) + { + this->push_back(statusMessage); + } + CStatusMessageList CStatusMessageList::findByCategory(const CLogCategory &category) const { return this->findBy([ & ](const CStatusMessage & msg) { return msg.getCategories().contains(category); }); @@ -43,6 +48,29 @@ namespace BlackMisc ([ = ](const CStatusMessage & m) { return m.getSeverity() == CStatusMessage::SeverityWarning || m.getSeverity() == CStatusMessage::SeverityError; }); } + bool CStatusMessageList::isSuccess() const + { + return !this->isFailure(); + } + + bool CStatusMessageList::isFailure() const + { + return this->contains(&CStatusMessage::isFailure, true); + } + + CStatusMessageList CStatusMessageList::getErrorMessages() const + { + return findBySeverity(SeverityError); + } + + CStatusMessageList CStatusMessageList::getWarningAndErrorMessages() const + { + return this->findBy([ & ](const CStatusMessage & msg) + { + return msg.getSeverity() >= CStatusMessage::SeverityWarning; + }); + } + void CStatusMessageList::addCategory(const CLogCategory &category) { for (auto &msg : *this) @@ -73,8 +101,16 @@ namespace BlackMisc { msg.setCategories(categories); } - } - + } + + void CStatusMessageList::clipSeverity(CStatusMessage::StatusSeverity severity) + { + for (auto &msg : *this) + { + msg.clipSeverity(severity); + } + } + void CStatusMessageList::removeWarningsAndBelow() { if (this->isEmpty()) { return; } diff --git a/src/blackmisc/statusmessagelist.h b/src/blackmisc/statusmessagelist.h index 7f4376408..2b09abb89 100644 --- a/src/blackmisc/statusmessagelist.h +++ b/src/blackmisc/statusmessagelist.h @@ -37,6 +37,9 @@ namespace BlackMisc //! Construct from a base class object. CStatusMessageList(const CSequence &other); + //! Construct from single message + CStatusMessageList(const CStatusMessage &statusMessage); + //! Find by type CStatusMessageList findByCategory(const CLogCategory &category) const; @@ -52,6 +55,18 @@ namespace BlackMisc //! Warning or error messages bool hasWarningOrErrorMessages() const; + //! All messages are marked as success + bool isSuccess() const; + + //! Any message is marked as failure + bool isFailure() const; + + //! Get all error messages + CStatusMessageList getErrorMessages() const; + + //! Get all warning and error messages + CStatusMessageList getWarningAndErrorMessages() const; + //! Add a category to all messages in the list void addCategory(const CLogCategory &category); @@ -64,6 +79,9 @@ namespace BlackMisc //! Reset the categories of all messages in the list void setCategories(const CLogCategoryList &categories); + //! And higher (more critical) severity will be clipped to given severity + void clipSeverity(CStatusMessage::StatusSeverity severity); + //! Remove warnings and below void removeWarningsAndBelow(); diff --git a/src/swiftlauncher/swiftlauncher.cpp b/src/swiftlauncher/swiftlauncher.cpp index 020706de7..5071a6091 100644 --- a/src/swiftlauncher/swiftlauncher.cpp +++ b/src/swiftlauncher/swiftlauncher.cpp @@ -47,7 +47,7 @@ CSwiftLauncher::CSwiftLauncher(QWidget *parent) : connect(ui->tb_BackToMain, &QToolButton::pressed, this, &CSwiftLauncher::ps_showMainPage); // use version signal as trigger for completion - connect(sGui, &CApplication::updateInfoSynchronized, this, &CSwiftLauncher::ps_loadedUpdateInfo); + connect(sGui, &CApplication::updateInfoAvailable, this, &CSwiftLauncher::ps_loadedUpdateInfo); new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_L), this, SLOT(ps_showLogPage())); this->ui->le_DBusServerPort->setValidator(new QIntValidator(0, 65535, this)); @@ -287,8 +287,8 @@ void CSwiftLauncher::ps_loadSetup() if (!this->ui->le_LatestVersion->text().isEmpty()) { this->ui->le_LatestVersion->setText(""); - const CStatusMessage m(sApp->requestReloadOfSetupAndVersion()); - this->ps_appendLogMessage(m); + const CStatusMessageList msgs(sApp->requestReloadOfSetupAndVersion()); + this->ps_appendLogMessages(msgs); } } @@ -382,6 +382,15 @@ void CSwiftLauncher::ps_appendLogMessage(const CStatusMessage &message) } } +void CSwiftLauncher::ps_appendLogMessages(const CStatusMessageList &messages) +{ + ui->fr_SwiftLauncherLog->appendStatusMessagesToList(messages); + if (messages.hasErrorMessages()) + { + this->ps_showStatusMessage(messages.getErrorMessages().toSingleMessage()); + } +} + void CSwiftLauncher::ps_showMainPage() { this->ui->sw_SwiftLauncher->setCurrentWidget(this->ui->pg_SwiftLauncherMain); diff --git a/src/swiftlauncher/swiftlauncher.h b/src/swiftlauncher/swiftlauncher.h index f52ea351b..de7ea4dff 100644 --- a/src/swiftlauncher/swiftlauncher.h +++ b/src/swiftlauncher/swiftlauncher.h @@ -145,6 +145,9 @@ private slots: //! Append status message void ps_appendLogMessage(const BlackMisc::CStatusMessage &message); + //! Append status messages + void ps_appendLogMessages(const BlackMisc::CStatusMessageList &messages); + //! Show set main page void ps_showMainPage();