/* Copyright (C) 2015 * swift project Community / Contributors * * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level * directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated, * or distributed except according to the terms contained in the LICENSE file. */ #include "blackcore/data/globalsetup.h" #include "blackcore/db/databasewriter.h" #include "blackcore/db/databaseutils.h" #include "blackcore/application.h" #include "blackmisc/simulation/autopublishdata.h" #include "blackmisc/network/networkutils.h" #include "blackmisc/db/datastoreutility.h" #include "blackmisc/logcategorylist.h" #include "blackmisc/logcategory.h" #include "blackmisc/statusmessage.h" #include #include #include #include #include #include #include #include #include using namespace BlackMisc; using namespace BlackMisc::Db; using namespace BlackMisc::Network; using namespace BlackMisc::Simulation; using namespace BlackCore::Db; namespace BlackCore { namespace Db { CDatabaseWriter::CDatabaseWriter(const Network::CUrl &baseUrl, QObject *parent) : QObject(parent), m_modelPublishUrl(CDatabaseWriter::getModelPublishUrl(baseUrl)), m_autoPublishUrl(CDatabaseWriter::getAutoPublishUrl(baseUrl)) { // void } CStatusMessageList CDatabaseWriter::asyncPublishModel(const CAircraftModel &model) { return this->asyncPublishModels(CAircraftModelList({ model })); } CStatusMessageList CDatabaseWriter::asyncPublishModels(const CAircraftModelList &models) { CStatusMessageList msgs; if (m_shutdown || !sApp) { msgs.push_back(CStatusMessage(CStatusMessage::SeverityWarning, u"Database writer shutting down")); return msgs; } if (this->isModelReplyOverdue()) { const bool killed = this->killPendingModelReply(); if (killed) { const CStatusMessage msg(CStatusMessage::SeverityWarning, u"Aborted outdated pending reply"); msgs.push_back(CStatusMessage(msg)); // need to let a potential receiver know it has failed emit this->publishedModels(CAircraftModelList(), CAircraftModelList(), msg, false, false); } } if (m_pendingModelPublishReply) { msgs.push_back(CStatusMessage(CStatusMessage::SeverityWarning, u"Another write operation in progress")); return msgs; } const bool compress = models.size() > 3; QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType, this); multiPart->append(CDatabaseUtils::getJsonTextMultipart(models.toDatabaseJson(), compress)); if (sApp->getGlobalSetup().dbDebugFlag()) { multiPart->append(CDatabaseUtils::getMultipartWithDebugFlag()); } QUrl url(m_modelPublishUrl.toQUrl()); if (compress) { url.setQuery(CDatabaseUtils::getCompressedQuery()); } QNetworkRequest request(url); CNetworkUtils::ignoreSslVerification(request); const int logId = m_writeLog.addPendingUrl(url); m_pendingModelPublishReply = sApp->postToNetwork(request, logId, multiPart, { this, &CDatabaseWriter::postedModelsResponse}); m_modelReplyPendingSince = QDateTime::currentMSecsSinceEpoch(); return msgs; } CStatusMessageList CDatabaseWriter::asyncAutoPublish(const CAutoPublishData &data) { CStatusMessageList msgs; if (m_shutdown || !sApp) { msgs.push_back(CStatusMessage(CStatusMessage::SeverityWarning, u"Database writer shutting down")); return msgs; } if (data.isEmpty()) { msgs.push_back(CStatusMessage(CStatusMessage::SeverityWarning, u"No auto update data")); return msgs; } const QString json = data.toDatabaseJson(); const bool compress = json.size() > 2048; QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType, this); multiPart->append(CDatabaseUtils::getJsonTextMultipart(json, compress)); if (sApp->getGlobalSetup().dbDebugFlag()) { multiPart->append(CDatabaseUtils::getMultipartWithDebugFlag()); } QUrl url(m_modelPublishUrl.toQUrl()); if (compress) { url.setQuery(CDatabaseUtils::getCompressedQuery()); } QNetworkRequest request(url); CNetworkUtils::ignoreSslVerification(request); const int logId = m_writeLog.addPendingUrl(url); m_pendingAutoPublishReply = sApp->postToNetwork(request, logId, multiPart, { this, &CDatabaseWriter::postedAutoPublishResponse}); m_autoPublishReplyPendingSince = QDateTime::currentMSecsSinceEpoch(); return msgs; } void CDatabaseWriter::gracefulShutdown() { m_shutdown = true; this->killPendingModelReply(); } const QString &CDatabaseWriter::getName() { static const QString n("Database writer"); return n; } void CDatabaseWriter::postedModelsResponse(QNetworkReply *nwReplyPtr) { static const CLogCategoryList cats(CLogCategoryList(this).join({ CLogCategory::swiftDbWebservice()})); QScopedPointer nwReply(nwReplyPtr); if (m_shutdown || !sApp) { nwReply->abort(); return; } m_pendingModelPublishReply = nullptr; const QUrl url(nwReply->url()); const QString urlString(url.toString()); if (nwReply->error() == QNetworkReply::NoError) { const QString responseData(nwReply->readAll().trimmed()); nwReply->close(); // close asap if (responseData.isEmpty()) { const CStatusMessageList msgs({CStatusMessage(cats, CStatusMessage::SeverityError, u"No response data from " % urlString)}); emit this->publishedModels(CAircraftModelList(), CAircraftModelList(), msgs, false, false); return; } CAircraftModelList modelsPublished; CAircraftModelList modelsSkipped; CStatusMessageList msgs; bool directWrite; const bool sendingSuccessful = CDatastoreUtility::parseSwiftPublishResponse(responseData, modelsPublished, modelsSkipped, msgs, directWrite); const int c = CDatabaseUtils::fillInMissingAircraftAndLiveryEntities(modelsPublished); emit this->publishedModels(modelsPublished, modelsSkipped, msgs, sendingSuccessful, directWrite); if (!modelsPublished.isEmpty()) { emit this->publishedModelsSimplified(modelsPublished, directWrite); } Q_UNUSED(c); } else { const QString error = nwReply->errorString(); nwReply->close(); // close asap const CStatusMessageList msgs({CStatusMessage(cats, CStatusMessage::SeverityError, u"HTTP error: " % error)}); emit this->publishedModels(CAircraftModelList(), CAircraftModelList(), msgs, false, false); } } void CDatabaseWriter::postedAutoPublishResponse(QNetworkReply *nwReplyPtr) { static const CLogCategoryList cats(CLogCategoryList(this).join({ CLogCategory::swiftDbWebservice()})); QScopedPointer nwReply(nwReplyPtr); if (m_shutdown || !sApp) { nwReply->abort(); return; } m_pendingAutoPublishReply = nullptr; const QUrl url(nwReply->url()); const QString urlString(url.toString()); if (nwReply->error() == QNetworkReply::NoError) { const QString responseData(nwReply->readAll().trimmed()); nwReply->close(); // close asap if (responseData.isEmpty()) { const CStatusMessageList msgs({CStatusMessage(cats, CStatusMessage::SeverityError, u"No response data from " % urlString)}); return; } } else { const QString error = nwReply->errorString(); nwReply->close(); // close asap const CStatusMessageList msgs({CStatusMessage(cats, CStatusMessage::SeverityError, u"HTTP error: " % error)}); } } bool CDatabaseWriter::killPendingModelReply() { if (!m_pendingModelPublishReply) { return false; } m_pendingModelPublishReply->abort(); m_pendingModelPublishReply = nullptr; m_modelReplyPendingSince = -1; return true; } bool CDatabaseWriter::isModelReplyOverdue() const { if (m_modelReplyPendingSince < 0 || !m_pendingModelPublishReply) { return false; } const qint64 ms = QDateTime::currentMSecsSinceEpoch() - m_modelReplyPendingSince; return ms > 7500; } CUrl CDatabaseWriter::getModelPublishUrl(const Network::CUrl &baseUrl) { return baseUrl.withAppendedPath("service/publishmodels.php"); } CUrl CDatabaseWriter::getAutoPublishUrl(const CUrl &baseUrl) { return baseUrl.withAppendedPath("service/publishauto.php"); } QList CDatabaseWriter::splitData(const QByteArray &data, int size) { if (data.size() <= size) { return QList({data}); } int pos = 0, arrsize = data.size(); QList arrays; while (pos < arrsize) { QByteArray arr = data.mid(pos, size); arrays << arr; pos += arr.size(); } return arrays; } } // ns } // ns