]> git.mxchange.org Git - simgear.git/commitdiff
Lots more work on package support.
authorJames Turner <zakalawe@mac.com>
Sat, 2 Mar 2013 16:30:39 +0000 (16:30 +0000)
committerJames Turner <zakalawe@mac.com>
Sat, 2 Mar 2013 16:30:39 +0000 (16:30 +0000)
Delegate is hooked up in the demo util, and Install forwards control to the delegate too.

simgear/package/Catalog.cxx
simgear/package/Catalog.hxx
simgear/package/Delegate.hxx
simgear/package/Install.cxx
simgear/package/Install.hxx
simgear/package/Package.cxx
simgear/package/Package.hxx
simgear/package/Root.cxx
simgear/package/Root.hxx
simgear/package/pkgutil.cxx

index da37fb964f1e95858c9a9115bf615d1942fc4175..9a7a71f1909c3fdb434f79b41692addf0717b8df 100644 (file)
@@ -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);
 }
 
 
index a367c9c3107d7afbec8372a2d3ad0b7e2982befe..bd561a44dc3af2641dede96faf20c84135fe64d8 100644 (file)
@@ -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();
index 2925d24885a13be5296df1cd5a9cf9031fa3e5c5..a1b0f147741a59e97049df22c75c7dd3547ef6a9 100644 (file)
@@ -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;
     
 };  
index 36c47150f52d960855a5ad77319e69a40c35e8c3..a702b0efae597e704fd896cde28beaf8ebfb7b50 100644 (file)
@@ -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
index 5fafb540e0d5d18c89298a2161ca9463b6b72a9b..3ad0ecd1655348f4e57cb7c8e0fdeb7bc2b322c3 100644 (file)
@@ -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
index cf156195e295f3d1e43e9311f22dc56c112e3cfc..cc118e70fe8adca336a2ae4f5711e414c7bcfb9f 100644 (file)
@@ -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();
index 0bbe22073e19ddcad11b9e6dbd4b85110a658636..fe0894e13732c2af2270fbd75d8a6468c1e34ed4 100644 (file)
@@ -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
      */
index 197f96c9b42f09af8422bb7015da70098f655b44..c91719138735562f4b06d9b1ead393fd064786a5 100644 (file)
@@ -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) {
index ae22b9a2004cba5e7a81100e96b0150d895e6836..ef232187d0d7c548e3c56964c8a69b9a6a631bea 100644 (file)
@@ -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;
index 33f15706b3e7b23cb9b7982c468a7e530fd6110a..f0f19d76851f512d0fe770f28ba772cd25f63cdf 100644 (file)
@@ -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;