Lots more work on package support.
Delegate is hooked up in the demo util, and Install forwards control to the delegate too.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
#include <simgear/package/Delegate.hxx>
|
||||
|
||||
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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/package/Delegate.hxx>
|
||||
|
||||
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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -49,7 +49,7 @@ typedef std::map<std::string, Catalog*> 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<Catalog*> m_refreshing;
|
||||
std::deque<Install*> m_updateDeque;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user