From f624e1b464bd17755058f516e4ae114cc759041f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 15 Feb 2018 15:10:51 -0800 Subject: [PATCH 1/4] add a content settings backup handler --- .../src/ContentSettingsBackupHandler.cpp | 66 +++++++++++++++++++ .../src/ContentSettingsBackupHandler.h | 35 ++++++++++ domain-server/src/DomainServer.cpp | 2 + .../src/DomainServerSettingsManager.h | 10 +-- domain-server/src/EntitiesBackupHandler.cpp | 14 ++-- 5 files changed, 116 insertions(+), 11 deletions(-) create mode 100644 domain-server/src/ContentSettingsBackupHandler.cpp create mode 100644 domain-server/src/ContentSettingsBackupHandler.h diff --git a/domain-server/src/ContentSettingsBackupHandler.cpp b/domain-server/src/ContentSettingsBackupHandler.cpp new file mode 100644 index 0000000000..be470bdd9f --- /dev/null +++ b/domain-server/src/ContentSettingsBackupHandler.cpp @@ -0,0 +1,66 @@ +// +// ContentSettingsBackupHandler.cpp +// domain-server/src +// +// Created by Stephen Birarda on 2/15/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ContentSettingsBackupHandler.h" + +#include +#include + +ContentSettingsBackupHandler::ContentSettingsBackupHandler(DomainServerSettingsManager& domainServerSettingsManager) : + _settingsManager(domainServerSettingsManager) +{ + +} + +static const QString CONTENT_SETTINGS_BACKUP_FILENAME = "content-settings.json"; + +void ContentSettingsBackupHandler::createBackup(QuaZip& zip) { + + // grab the content settings as JSON,excluding default values and values hidden from backup + QJsonObject contentSettingsJSON = _settingsManager.settingsResponseObjectForType("", true, false, true, false, true); + + // make a QJSonDocument using the object + QJsonDocument contentSettingsDocument { contentSettingsJSON }; + + QuaZipFile zipFile { &zip }; + + zipFile.open(QIODevice::WriteOnly, QuaZipNewInfo(CONTENT_SETTINGS_BACKUP_FILENAME)); + zipFile.write(contentSettingsDocument.toJson()); + zipFile.close(); + + if (zipFile.getZipError() != UNZ_OK) { + qCritical().nospace() << "Failed to zip " << CONTENT_SETTINGS_BACKUP_FILENAME << ": " << zipFile.getZipError(); + } +} + +void ContentSettingsBackupHandler::recoverBackup(QuaZip& zip) { + if (!zip.setCurrentFile(CONTENT_SETTINGS_BACKUP_FILENAME)) { + qWarning() << "Failed to find" << CONTENT_SETTINGS_BACKUP_FILENAME << "while recovering backup"; + return; + } + + QuaZipFile zipFile { &zip }; + if (!zipFile.open(QIODevice::ReadOnly)) { + qCritical() << "Failed to open" << CONTENT_SETTINGS_BACKUP_FILENAME << "in backup"; + return; + } + + auto rawData = zipFile.readAll(); + zipFile.close(); + + QJsonDocument jsonDocument = QJsonDocument::fromJson(rawData); + + if (!_settingsManager.restoreSettingsFromObject(jsonDocument.object(), ContentSettings)) { + qCritical() << "Failed to restore settings from" << CONTENT_SETTINGS_BACKUP_FILENAME << "in content archive"; + return; + } + +} diff --git a/domain-server/src/ContentSettingsBackupHandler.h b/domain-server/src/ContentSettingsBackupHandler.h new file mode 100644 index 0000000000..932b7c0c3f --- /dev/null +++ b/domain-server/src/ContentSettingsBackupHandler.h @@ -0,0 +1,35 @@ +// +// ContentSettingsBackupHandler.h +// domain-server/src +// +// Created by Stephen Birarda on 2/15/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ContentSettingsBackupHandler_h +#define hifi_ContentSettingsBackupHandler_h + +#include "BackupHandler.h" +#include "DomainServerSettingsManager.h" + +class ContentSettingsBackupHandler : public BackupHandlerInterface { +public: + ContentSettingsBackupHandler(DomainServerSettingsManager& domainServerSettingsManager); + + void loadBackup(QuaZip& zip) {}; + + void createBackup(QuaZip& zip); + + void recoverBackup(QuaZip& zip); + + void deleteBackup(QuaZip& zip) {}; + + void consolidateBackup(QuaZip& zip) {}; +private: + DomainServerSettingsManager& _settingsManager; +}; + +#endif // hifi_ContentSettingsBackupHandler_h diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 4c72423f74..d3bc5fdff1 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -46,6 +46,7 @@ #include #include "AssetsBackupHandler.h" +#include "ContentSettingsBackupHandler.h" #include "DomainServerNodeData.h" #include "EntitiesBackupHandler.h" #include "NodeConnectionData.h" @@ -299,6 +300,7 @@ DomainServer::DomainServer(int argc, char* argv[]) : _contentManager.reset(new DomainContentBackupManager(getContentBackupDir(), _settingsManager.settingsResponseObjectForType("6")["entity_server_settings"].toObject())); _contentManager->addBackupHandler(BackupHandlerPointer(new EntitiesBackupHandler(getEntitiesFilePath(), getEntitiesReplacementFilePath()))); _contentManager->addBackupHandler(BackupHandlerPointer(new AssetsBackupHandler(getContentBackupDir()))); + _contentManager->addBackupHandler(BackupHandlerPointer(new ContentSettingsBackupHandler(_settingsManager))); _contentManager->initialize(true); qDebug() << "Existing backups:"; diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index 9b2427b344..4316534685 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -111,6 +111,11 @@ public: void debugDumpGroupsState(); + QJsonObject settingsResponseObjectForType(const QString& typeValue, bool isAuthenticated = false, + bool includeDomainSettings = true, bool includeContentSettings = true, + bool includeDefaults = true, bool isForBackup = false); + bool restoreSettingsFromObject(QJsonObject settingsToRestore, SettingsType settingsType); + signals: void updateNodePermissions(); void settingsUpdated(); @@ -130,9 +135,6 @@ private: QStringList _argumentList; QJsonArray filteredDescriptionArray(bool isContentSettings); - QJsonObject settingsResponseObjectForType(const QString& typeValue, bool isAuthenticated = false, - bool includeDomainSettings = true, bool includeContentSettings = true, - bool includeDefaults = true, bool isForBackup = false); bool recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject, SettingsType settingsType); void updateSetting(const QString& key, const QJsonValue& newValue, QVariantMap& settingMap, @@ -143,8 +145,6 @@ private: void splitSettingsDescription(); - bool restoreSettingsFromObject(QJsonObject settingsToRestore, SettingsType settingsType); - double _descriptionVersion; QJsonArray _descriptionArray; diff --git a/domain-server/src/EntitiesBackupHandler.cpp b/domain-server/src/EntitiesBackupHandler.cpp index a95d68b007..6ad00d01c8 100644 --- a/domain-server/src/EntitiesBackupHandler.cpp +++ b/domain-server/src/EntitiesBackupHandler.cpp @@ -24,28 +24,30 @@ EntitiesBackupHandler::EntitiesBackupHandler(QString entitiesFilePath, QString e { } +static const QString ENTITIES_BACKUP_FILENAME = "models.json.gz"; + void EntitiesBackupHandler::createBackup(QuaZip& zip) { QFile entitiesFile { _entitiesFilePath }; if (entitiesFile.open(QIODevice::ReadOnly)) { QuaZipFile zipFile { &zip }; - zipFile.open(QIODevice::WriteOnly, QuaZipNewInfo("models.json.gz", _entitiesFilePath)); + zipFile.open(QIODevice::WriteOnly, QuaZipNewInfo(ENTITIES_BACKUP_FILENAME, _entitiesFilePath)); zipFile.write(entitiesFile.readAll()); zipFile.close(); if (zipFile.getZipError() != UNZ_OK) { - qCritical() << "Failed to zip models.json.gz: " << zipFile.getZipError(); + qCritical().nospace() << "Failed to zip " << ENTITIES_BACKUP_FILENAME << ": " << zipFile.getZipError(); } } } void EntitiesBackupHandler::recoverBackup(QuaZip& zip) { - if (!zip.setCurrentFile("models.json.gz")) { - qWarning() << "Failed to find models.json.gz while recovering backup"; + if (!zip.setCurrentFile(ENTITIES_BACKUP_FILENAME)) { + qWarning() << "Failed to find" << ENTITIES_BACKUP_FILENAME << "while recovering backup"; return; } QuaZipFile zipFile { &zip }; if (!zipFile.open(QIODevice::ReadOnly)) { - qCritical() << "Failed to open models.json.gz in backup"; + qCritical() << "Failed to open" << ENTITIES_BACKUP_FILENAME << "in backup"; return; } auto rawData = zipFile.readAll(); @@ -61,7 +63,7 @@ void EntitiesBackupHandler::recoverBackup(QuaZip& zip) { data.resetIdAndVersion(); if (zipFile.getZipError() != UNZ_OK) { - qCritical() << "Failed to unzip models.json.gz: " << zipFile.getZipError(); + qCritical().nospace() << "Failed to unzip " << ENTITIES_BACKUP_FILENAME << ": " << zipFile.getZipError(); return; } From f5cad5683dbb4028c76beafbdafddb249655d0b7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 15 Feb 2018 15:39:25 -0800 Subject: [PATCH 2/4] make sure backup handlers end up on the correct thread --- domain-server/src/DomainContentBackupManager.cpp | 3 --- domain-server/src/DomainServer.cpp | 12 ++++++++---- libraries/shared/src/GenericThread.cpp | 2 ++ libraries/shared/src/GenericThread.h | 1 + 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/domain-server/src/DomainContentBackupManager.cpp b/domain-server/src/DomainContentBackupManager.cpp index f39737c92e..c68ff0c6ea 100644 --- a/domain-server/src/DomainContentBackupManager.cpp +++ b/domain-server/src/DomainContentBackupManager.cpp @@ -155,9 +155,6 @@ bool DomainContentBackupManager::process() { } void DomainContentBackupManager::aboutToFinish() { - qCDebug(domain_server) << "Persist thread about to finish..."; - backup(); - qCDebug(domain_server) << "Persist thread done with about to finish..."; _stopThread = true; } diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index d3bc5fdff1..599f09ae94 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -298,9 +298,13 @@ DomainServer::DomainServer(int argc, char* argv[]) : maybeHandleReplacementEntityFile(); _contentManager.reset(new DomainContentBackupManager(getContentBackupDir(), _settingsManager.settingsResponseObjectForType("6")["entity_server_settings"].toObject())); - _contentManager->addBackupHandler(BackupHandlerPointer(new EntitiesBackupHandler(getEntitiesFilePath(), getEntitiesReplacementFilePath()))); - _contentManager->addBackupHandler(BackupHandlerPointer(new AssetsBackupHandler(getContentBackupDir()))); - _contentManager->addBackupHandler(BackupHandlerPointer(new ContentSettingsBackupHandler(_settingsManager))); + + connect(_contentManager.get(), &DomainContentBackupManager::started, _contentManager.get(), [this](){ + _contentManager->addBackupHandler(BackupHandlerPointer(new EntitiesBackupHandler(getEntitiesFilePath(), getEntitiesReplacementFilePath()))); + _contentManager->addBackupHandler(BackupHandlerPointer(new AssetsBackupHandler(getContentBackupDir()))); + _contentManager->addBackupHandler(BackupHandlerPointer(new ContentSettingsBackupHandler(_settingsManager))); + }); + _contentManager->initialize(true); qDebug() << "Existing backups:"; @@ -382,7 +386,7 @@ DomainServer::~DomainServer() { if (_contentManager) { _contentManager->aboutToFinish(); - _contentManager->terminating(); + _contentManager->terminate(); } } diff --git a/libraries/shared/src/GenericThread.cpp b/libraries/shared/src/GenericThread.cpp index 2e126f12c9..50655820af 100644 --- a/libraries/shared/src/GenericThread.cpp +++ b/libraries/shared/src/GenericThread.cpp @@ -38,6 +38,8 @@ void GenericThread::initialize(bool isThreaded, QThread::Priority priority) { _thread->setObjectName(objectName()); // when the worker thread is started, call our engine's run.. + + connect(_thread, &QThread::started, this, &GenericThread::started); connect(_thread, &QThread::started, this, &GenericThread::threadRoutine); connect(_thread, &QThread::finished, this, &GenericThread::finished); diff --git a/libraries/shared/src/GenericThread.h b/libraries/shared/src/GenericThread.h index 09872b32cd..c1f946d6aa 100644 --- a/libraries/shared/src/GenericThread.h +++ b/libraries/shared/src/GenericThread.h @@ -47,6 +47,7 @@ public slots: void threadRoutine(); signals: + void started(); void finished(); protected: From e06c95f5863d3aeb67f1c18d624acc25743e605f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 15 Feb 2018 15:51:49 -0800 Subject: [PATCH 3/4] make settings manager methods used for backup/restore thread safe --- .../src/DomainServerSettingsManager.cpp | 24 +++++++++++++++++++ .../src/DomainServerSettingsManager.h | 6 +++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 8febbd5769..5e3c8e9cee 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -1196,6 +1197,16 @@ bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection } bool DomainServerSettingsManager::restoreSettingsFromObject(QJsonObject settingsToRestore, SettingsType settingsType) { + + if (thread() != QThread::currentThread()) { + bool success; + QMetaObject::invokeMethod(this, "restoreSettingsFromObject", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, success), + Q_ARG(QJsonObject, settingsToRestore), + Q_ARG(SettingsType, settingsType)); + return success; + } + QJsonArray& filteredDescriptionArray = settingsType == DomainSettings ? _domainSettingsDescription : _contentSettingsDescription; @@ -1321,6 +1332,19 @@ QJsonObject DomainServerSettingsManager::settingsResponseObjectForType(const QSt bool includeDefaults, bool isForBackup) { QJsonObject responseObject; + if (thread() != QThread::currentThread()) { + QMetaObject::invokeMethod(this, "settingsResponseObjectForType", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QJsonObject, responseObject), + Q_ARG(const QString&, typeValue), + Q_ARG(bool, isAuthenticated), + Q_ARG(bool, includeDomainSettings), + Q_ARG(bool, includeContentSettings), + Q_ARG(bool, includeDefaults), + Q_ARG(bool, isForBackup)); + + return responseObject; + } + if (!typeValue.isEmpty() || isAuthenticated) { // convert the string type value to a QJsonValue QJsonValue queryType = typeValue.isEmpty() ? QJsonValue() : QJsonValue(typeValue.toInt()); diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index 4316534685..abc70751a8 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -111,10 +111,12 @@ public: void debugDumpGroupsState(); - QJsonObject settingsResponseObjectForType(const QString& typeValue, bool isAuthenticated = false, + /// thread safe method to retrieve a JSON representation of settings + Q_INVOKABLE QJsonObject settingsResponseObjectForType(const QString& typeValue, bool isAuthenticated = false, bool includeDomainSettings = true, bool includeContentSettings = true, bool includeDefaults = true, bool isForBackup = false); - bool restoreSettingsFromObject(QJsonObject settingsToRestore, SettingsType settingsType); + /// thread safe method to restore settings from a JSON object + Q_INVOKABLE bool restoreSettingsFromObject(QJsonObject settingsToRestore, SettingsType settingsType); signals: void updateNodePermissions(); From e71f2fa38788c8f74f5ede92749eab7f296a5743 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 15 Feb 2018 16:03:03 -0800 Subject: [PATCH 4/4] use QtHelpers macro for blocking invoke --- .../src/DomainServerSettingsManager.cpp | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 5e3c8e9cee..5f71890898 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -33,6 +33,8 @@ #include #include //for KillAvatarReason #include +#include + #include "DomainServerNodeData.h" const QString SETTINGS_DESCRIPTION_RELATIVE_PATH = "/resources/describe-settings.json"; @@ -1200,10 +1202,11 @@ bool DomainServerSettingsManager::restoreSettingsFromObject(QJsonObject settings if (thread() != QThread::currentThread()) { bool success; - QMetaObject::invokeMethod(this, "restoreSettingsFromObject", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(bool, success), - Q_ARG(QJsonObject, settingsToRestore), - Q_ARG(SettingsType, settingsType)); + + BLOCKING_INVOKE_METHOD(this, "restoreSettingsFromObject", + Q_RETURN_ARG(bool, success), + Q_ARG(QJsonObject, settingsToRestore), + Q_ARG(SettingsType, settingsType)); return success; } @@ -1333,14 +1336,15 @@ QJsonObject DomainServerSettingsManager::settingsResponseObjectForType(const QSt QJsonObject responseObject; if (thread() != QThread::currentThread()) { - QMetaObject::invokeMethod(this, "settingsResponseObjectForType", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QJsonObject, responseObject), - Q_ARG(const QString&, typeValue), - Q_ARG(bool, isAuthenticated), - Q_ARG(bool, includeDomainSettings), - Q_ARG(bool, includeContentSettings), - Q_ARG(bool, includeDefaults), - Q_ARG(bool, isForBackup)); + + BLOCKING_INVOKE_METHOD(this, "settingsResponseObjectForType", + Q_RETURN_ARG(QJsonObject, responseObject), + Q_ARG(const QString&, typeValue), + Q_ARG(bool, isAuthenticated), + Q_ARG(bool, includeDomainSettings), + Q_ARG(bool, includeContentSettings), + Q_ARG(bool, includeDefaults), + Q_ARG(bool, isForBackup)); return responseObject; }