Delegate is hooked up in the demo util, and Install forwards control to the delegate too.
{
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;
}
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;
}
time(&m_owner->m_retrievedTime);
m_owner->writeTimestamp();
- m_owner->refreshComplete(true);
+ m_owner->refreshComplete(Delegate::FAIL_SUCCESS);
}
private:
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);
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);
}
#include <simgear/misc/sg_path.hxx>
#include <simgear/props/props.hxx>
+#include <simgear/package/Delegate.hxx>
+
namespace simgear
{
void parseProps(const SGPropertyNode* aProps);
- void refreshComplete(bool aSuccess);
+ void refreshComplete(Delegate::FailureCode aReason);
void parseTimestamp();
void writeTimestamp();
-
+// 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
{
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;
};
virtual void responseHeadersComplete()
{
- std::cout << "starting download of " << m_owner->package()->id() << " from "
- << url() << std::endl;
Dir d(m_extractPath);
d.create(0755);
{
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;
}
<< "\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;
}
}
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:
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
}
m_download = new PackageArchiveDownloader(this);
m_package->catalog()->root()->getHTTPClient()->makeRequest(m_download);
+ m_package->catalog()->root()->startInstall(this);
}
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
#include <vector>
#include <simgear/misc/sg_path.hxx>
+#include <simgear/package/Delegate.hxx>
namespace simgear
{
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
return m_props->getIntValue("revision");
}
+std::string Package::description() const
+{
+ return getLocalisedProp("decription");
+}
+
SGPropertyNode* Package::properties() const
{
return m_props.ptr();
std::string id() const;
+ /**
+ * syntactic sugar to get the localised description
+ */
+ std::string description() const;
+
/**
* access the raw property data in the package
*/
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");
{
}
-
+
+std::string Root::catalogVersion() const
+{
+ return m_version;
+}
+
Catalog* Root::getCatalogById(const std::string& aId) const
{
CatalogDict::const_iterator it = m_catalogs.find(aId);
}
}
+void Root::setDelegate(simgear::pkg::Delegate *aDelegate)
+{
+ m_delegate = aDelegate;
+}
+
void Root::setLocale(const std::string& aLocale)
{
m_locale = aLocale;
bool wasEmpty = m_updateDeque.empty();
m_updateDeque.push_back(aInstall);
+
if (wasEmpty) {
aInstall->startUpdate();
}
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) {
class Root
{
public:
- Root(const SGPath& aPath);
+ Root(const SGPath& aPath, const std::string& aVersion);
virtual ~Root();
SGPath path() const
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.
void catalogRefreshBegin(Catalog* aCat);
- void catalogRefreshComplete(Catalog* aCat, bool aSuccess);
+ void catalogRefreshComplete(Catalog* aCat, Delegate::FailureCode aReason);
void startNext(Install* aCurrent);
CatalogDict m_catalogs;
unsigned int m_maxAgeSeconds;
Delegate* m_delegate;
+ std::string m_version;
std::set<Catalog*> m_refreshing;
std::deque<Install*> m_updateDeque;
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;