From 71e9548c20f8008ba3d1a9b06ad0acf761775922 Mon Sep 17 00:00:00 2001 From: James Turner Date: Sat, 2 Mar 2013 16:30:39 +0000 Subject: [PATCH] Lots more work on package support. Delegate is hooked up in the demo util, and Install forwards control to the delegate too. --- simgear/package/Catalog.cxx | 24 ++++++++++++---- simgear/package/Catalog.hxx | 4 ++- simgear/package/Delegate.hxx | 54 +++++++++++++++++++++++++++++------- simgear/package/Install.cxx | 43 +++++++++++++++++++++------- simgear/package/Install.hxx | 4 +++ simgear/package/Package.cxx | 5 ++++ simgear/package/Package.hxx | 5 ++++ simgear/package/Root.cxx | 38 ++++++++++++++++++++++--- simgear/package/Root.hxx | 11 ++++++-- simgear/package/pkgutil.cxx | 48 +++++++++++++++++++++++++++++++- 10 files changed, 203 insertions(+), 33 deletions(-) diff --git a/simgear/package/Catalog.cxx b/simgear/package/Catalog.cxx index da37fb96..9a7a71f1 100644 --- a/simgear/package/Catalog.cxx +++ b/simgear/package/Catalog.cxx @@ -64,7 +64,7 @@ protected: { if (responseCode() != 200) { SG_LOG(SG_GENERAL, SG_ALERT, "catalog download failure:" << m_owner->url()); - m_owner->refreshComplete(false); + m_owner->refreshComplete(Delegate::FAIL_DOWNLOAD); return; } @@ -75,7 +75,15 @@ protected: m_owner->parseProps(props); } catch (sg_exception& e) { SG_LOG(SG_GENERAL, SG_ALERT, "catalog parse failure:" << m_owner->url()); - m_owner->refreshComplete(false); + m_owner->refreshComplete(Delegate::FAIL_EXTRACT); + return; + } + + std::string ver(m_owner->root()->catalogVersion()); + if (props->getStringValue("version") != ver) { + SG_LOG(SG_GENERAL, SG_WARN, "downloaded catalog " << m_owner->url() << ", version mismatch:\n\t" + << props->getStringValue("version") << " vs required " << ver); + m_owner->refreshComplete(Delegate::FAIL_VERSION); return; } @@ -89,7 +97,7 @@ protected: time(&m_owner->m_retrievedTime); m_owner->writeTimestamp(); - m_owner->refreshComplete(true); + m_owner->refreshComplete(Delegate::FAIL_SUCCESS); } private: @@ -142,6 +150,12 @@ Catalog* Catalog::createFromPath(Root* aRoot, const SGPath& aPath) return NULL; } + if (props->getStringValue("version") != aRoot->catalogVersion()) { + SG_LOG(SG_GENERAL, SG_WARN, "skipping catalog at " << aPath << ", version mismatch:\n\t" + << props->getStringValue("version") << " vs required " << aRoot->catalogVersion()); + return NULL; + } + Catalog* c = new Catalog(aRoot); c->m_installRoot = aPath; c->parseProps(props); @@ -278,9 +292,9 @@ std::string Catalog::getLocalisedString(const SGPropertyNode* aRoot, const char* return aRoot->getStringValue(aName); } -void Catalog::refreshComplete(bool aSuccess) +void Catalog::refreshComplete(Delegate::FailureCode aReason) { - m_root->catalogRefreshComplete(this, aSuccess); + m_root->catalogRefreshComplete(this, aReason); } diff --git a/simgear/package/Catalog.hxx b/simgear/package/Catalog.hxx index a367c9c3..bd561a44 100644 --- a/simgear/package/Catalog.hxx +++ b/simgear/package/Catalog.hxx @@ -24,6 +24,8 @@ #include #include +#include + namespace simgear { @@ -95,7 +97,7 @@ private: void parseProps(const SGPropertyNode* aProps); - void refreshComplete(bool aSuccess); + void refreshComplete(Delegate::FailureCode aReason); void parseTimestamp(); void writeTimestamp(); diff --git a/simgear/package/Delegate.hxx b/simgear/package/Delegate.hxx index 2925d248..a1b0f147 100644 --- a/simgear/package/Delegate.hxx +++ b/simgear/package/Delegate.hxx @@ -1,4 +1,19 @@ - +// Copyright (C) 2013 James Turner - zakalawe@mac.com +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// #ifndef SG_PACKAGE_DELEGATE_HXX #define SG_PACKAGE_DELEGATE_HXX @@ -10,26 +25,45 @@ namespace pkg { class Install; - +class Catalog; + +/** + * package delegate is the mechanism to discover progress / completion / + * errors in packaging steps asynchronously. + */ class Delegate { public: + typedef enum { + FAIL_SUCCESS = 0, ///< not a failure :) + FAIL_UNKNOWN = 1, + FAIL_CHECKSUM, ///< package MD5 verificstion failed + FAIL_DOWNLOAD, ///< network issue + FAIL_EXTRACT, ///< package archive failed to extract cleanly + FAIL_FILESYSTEM, ///< unknown filesystem error occurred + FAIL_VERSION ///< version check mismatch + } FailureCode; + + virtual ~Delegate() { } + /** + * invoked when all catalogs have finished refreshing - either successfully + * or with a failure. + */ virtual void refreshComplete() = 0; + /** + * emitted when a catalog fails to refresh, due to a network issue or + * some other failure. + */ + virtual void failedRefresh(Catalog*, FailureCode aReason) = 0; + virtual void startInstall(Install* aInstall) = 0; virtual void installProgress(Install* aInstall, unsigned int aBytes, unsigned int aTotal) = 0; virtual void finishInstall(Install* aInstall) = 0; - typedef enum { - FAIL_UNKNOWN = 0, - FAIL_CHECKSUM, - FAIL_DOWNLOAD, - FAIL_EXTRACT, - FAIL_FILESYSTEM - } FailureCode; - + virtual void failedInstall(Install* aInstall, FailureCode aReason) = 0; }; diff --git a/simgear/package/Install.cxx b/simgear/package/Install.cxx index 36c47150..a702b0ef 100644 --- a/simgear/package/Install.cxx +++ b/simgear/package/Install.cxx @@ -66,8 +66,6 @@ protected: virtual void responseHeadersComplete() { - std::cout << "starting download of " << m_owner->package()->id() << " from " - << url() << std::endl; Dir d(m_extractPath); d.create(0755); @@ -79,13 +77,15 @@ protected: { m_buffer += std::string(s, n); MD5Update(&m_md5, (unsigned char*) s, n); + + m_owner->installProgress(m_buffer.size(), responseLength()); } virtual void responseComplete() { if (responseCode() != 200) { SG_LOG(SG_GENERAL, SG_ALERT, "download failure"); - doFailure(); + doFailure(Delegate::FAIL_DOWNLOAD); return; } @@ -103,15 +103,13 @@ protected: << "\t" << hexMd5.str() << "\n\t" << m_owner->package()->md5() << "\n\t" << "downloading from:" << url()); - doFailure(); + doFailure(Delegate::FAIL_CHECKSUM); return; - } else { - std::cout << "MD5 checksum is ok" << std::endl; } if (!extractUnzip()) { SG_LOG(SG_GENERAL, SG_WARN, "zip extraction failed"); - doFailure(); + doFailure(Delegate::FAIL_EXTRACT); return; } @@ -122,9 +120,15 @@ protected: } m_extractPath.append(m_owner->package()->id()); - m_extractPath.rename(m_owner->path()); + bool ok = m_extractPath.rename(m_owner->path()); + if (!ok) { + doFailure(Delegate::FAIL_FILESYSTEM); + return; + } + m_owner->m_revision = m_owner->package()->revision(); m_owner->writeRevisionFile(); + m_owner->installResult(Delegate::FAIL_SUCCESS); } private: @@ -227,15 +231,18 @@ private: return result; } - void doFailure() + void doFailure(Delegate::FailureCode aReason) { Dir dir(m_extractPath); dir.remove(true /* recursive */); + if (m_urls.size() == 1) { - + std::cout << "failure:" << aReason << std::endl; + m_owner->installResult(aReason); return; } + std::cout << "retrying download" << std::endl; m_urls.erase(m_urls.begin()); // pop first URL } @@ -300,6 +307,7 @@ void Install::startUpdate() m_download = new PackageArchiveDownloader(this); m_package->catalog()->root()->getHTTPClient()->makeRequest(m_download); + m_package->catalog()->root()->startInstall(this); } void Install::uninstall() @@ -309,6 +317,21 @@ void Install::uninstall() delete this; } +void Install::installResult(Delegate::FailureCode aReason) +{ + if (aReason == Delegate::FAIL_SUCCESS) { + m_package->catalog()->root()->finishInstall(this); + } else { + m_package->catalog()->root()->failedInstall(this, aReason); + } +} + +void Install::installProgress(unsigned int aBytes, unsigned int aTotal) +{ + m_package->catalog()->root()->installProgress(this, aBytes, aTotal); +} + + } // of namespace pkg } // of namespace simgear diff --git a/simgear/package/Install.hxx b/simgear/package/Install.hxx index 5fafb540..3ad0ecd1 100644 --- a/simgear/package/Install.hxx +++ b/simgear/package/Install.hxx @@ -21,6 +21,7 @@ #include #include +#include namespace simgear { @@ -74,6 +75,9 @@ private: void parseRevision(); void writeRevisionFile(); + void installResult(Delegate::FailureCode aReason); + void installProgress(unsigned int aBytes, unsigned int aTotal); + Package* m_package; unsigned int m_revision; ///< revision on disk SGPath m_path; ///< installation point on disk diff --git a/simgear/package/Package.cxx b/simgear/package/Package.cxx index cf156195..cc118e70 100644 --- a/simgear/package/Package.cxx +++ b/simgear/package/Package.cxx @@ -112,6 +112,11 @@ unsigned int Package::revision() const return m_props->getIntValue("revision"); } +std::string Package::description() const +{ + return getLocalisedProp("decription"); +} + SGPropertyNode* Package::properties() const { return m_props.ptr(); diff --git a/simgear/package/Package.hxx b/simgear/package/Package.hxx index 0bbe2207..fe0894e1 100644 --- a/simgear/package/Package.hxx +++ b/simgear/package/Package.hxx @@ -51,6 +51,11 @@ public: std::string id() const; + /** + * syntactic sugar to get the localised description + */ + std::string description() const; + /** * access the raw property data in the package */ diff --git a/simgear/package/Root.cxx b/simgear/package/Root.cxx index 197f96c9..c9171913 100644 --- a/simgear/package/Root.cxx +++ b/simgear/package/Root.cxx @@ -49,11 +49,12 @@ HTTP::Client* Root::getHTTPClient() const return m_http; } -Root::Root(const SGPath& aPath) : +Root::Root(const SGPath& aPath, const std::string& aVersion) : m_path(aPath), m_http(NULL), m_maxAgeSeconds(60 * 60 * 24), - m_delegate(NULL) + m_delegate(NULL), + m_version(aVersion) { if (getenv("LOCALE")) { m_locale = getenv("LOCALE"); @@ -77,7 +78,12 @@ Root::~Root() { } - + +std::string Root::catalogVersion() const +{ + return m_version; +} + Catalog* Root::getCatalogById(const std::string& aId) const { CatalogDict::const_iterator it = m_catalogs.find(aId); @@ -165,6 +171,11 @@ void Root::refresh(bool aForce) } } +void Root::setDelegate(simgear::pkg::Delegate *aDelegate) +{ + m_delegate = aDelegate; +} + void Root::setLocale(const std::string& aLocale) { m_locale = aLocale; @@ -190,6 +201,7 @@ void Root::scheduleToUpdate(Install* aInstall) bool wasEmpty = m_updateDeque.empty(); m_updateDeque.push_back(aInstall); + if (wasEmpty) { aInstall->startUpdate(); } @@ -247,8 +259,26 @@ void Root::catalogRefreshBegin(Catalog* aCat) m_refreshing.insert(aCat); } -void Root::catalogRefreshComplete(Catalog* aCat, bool aSuccess) +void Root::catalogRefreshComplete(Catalog* aCat, Delegate::FailureCode aReason) { + CatalogDict::iterator catIt = m_catalogs.find(aCat->id()); + if (aReason != Delegate::FAIL_SUCCESS) { + if (m_delegate) { + m_delegate->failedRefresh(aCat, aReason); + } + + // if the failure is permanent, delete the catalog from our + // list (don't touch it on disk) + bool isPermanentFailure = (aReason == Delegate::FAIL_VERSION); + if (isPermanentFailure) { + SG_LOG(SG_GENERAL, SG_WARN, "permanent failure for catalog:" << aCat->id()); + m_catalogs.erase(catIt); + } + } else if (catIt == m_catalogs.end()) { + // first fresh, add to our storage now + m_catalogs.insert(catIt, CatalogDict::value_type(aCat->id(), aCat)); + } + m_refreshing.erase(aCat); if (m_refreshing.empty()) { if (m_delegate) { diff --git a/simgear/package/Root.hxx b/simgear/package/Root.hxx index ae22b9a2..ef232187 100644 --- a/simgear/package/Root.hxx +++ b/simgear/package/Root.hxx @@ -49,7 +49,7 @@ typedef std::map CatalogDict; class Root { public: - Root(const SGPath& aPath); + Root(const SGPath& aPath, const std::string& aVersion); virtual ~Root(); SGPath path() const @@ -69,6 +69,12 @@ public: HTTP::Client* getHTTPClient() const; + /** + * the version string of the root. Catalogs must match this version, + * or they will be ignored / rejected. + */ + std::string catalogVersion() const; + /** * refresh catalogs which are more than the maximum age (24 hours by default) * set force to true, to download all catalogs regardless of age. @@ -98,7 +104,7 @@ private: void catalogRefreshBegin(Catalog* aCat); - void catalogRefreshComplete(Catalog* aCat, bool aSuccess); + void catalogRefreshComplete(Catalog* aCat, Delegate::FailureCode aReason); void startNext(Install* aCurrent); @@ -113,6 +119,7 @@ private: CatalogDict m_catalogs; unsigned int m_maxAgeSeconds; Delegate* m_delegate; + std::string m_version; std::set m_refreshing; std::deque m_updateDeque; diff --git a/simgear/package/pkgutil.cxx b/simgear/package/pkgutil.cxx index 33f15706..f0f19d76 100644 --- a/simgear/package/pkgutil.cxx +++ b/simgear/package/pkgutil.cxx @@ -31,11 +31,57 @@ using namespace std; bool keepRunning = true; +class MyDelegate : public pkg::Delegate +{ +public: + virtual void refreshComplete() + { + } + + virtual void failedRefresh(pkg::Catalog* aCatalog, FailureCode aReason) + { + cerr << "failed refresh of " << aCatalog->description() << ":" << aReason << endl; + } + + virtual void startInstall(pkg::Install* aInstall) + { + _lastPercent = 999; + cout << "starting install of " << aInstall->package()->description() << endl; + } + + virtual void installProgress(pkg::Install* aInstall, unsigned int bytes, unsigned int total) + { + unsigned int percent = (bytes * 100) / total; + if (percent == _lastPercent) { + return; + } + + _lastPercent = percent; + cout << percent << "%" << endl; + } + + virtual void finishInstall(pkg::Install* aInstall) + { + cout << "done install of " << aInstall->package()->description() << endl; + } + + virtual void failedInstall(pkg::Install* aInstall, FailureCode aReason) + { + cerr << "failed install of " << aInstall->package()->description() << endl; + } +private: + unsigned int _lastPercent; + +}; + int main(int argc, char** argv) { HTTP::Client* http = new HTTP::Client(); - pkg::Root* root = new pkg::Root(Dir::current().path()); + pkg::Root* root = new pkg::Root(Dir::current().path(), ""); + + MyDelegate dlg; + root->setDelegate(&dlg); cout << "Package root is:" << Dir::current().path() << endl; cout << "have " << pkg::Catalog::allCatalogs().size() << " catalog(s)" << endl; -- 2.39.5