[haiku-commits] haiku: hrev51693 - in src/apps/haikudepot: server model server/dumpexportpkg server/dumpexportrepository .

  • From: apl@xxxxxxxxxxxxxx
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Tue, 19 Dec 2017 21:03:08 +0100 (CET)

hrev51693 adds 1 changeset to branch 'master'
old head: 11b65332b4e0d10f818d608c8f2f28322b398383
new head: 3094fef308a6aaaef9827863a0a99640bdaaa5af
overview: 
http://cgit.haiku-os.org/haiku/log/?qt=range&q=3094fef308a6+%5E11b65332b4e0

----------------------------------------------------------------------------

3094fef308a6: HaikuDepot : More Backend Communications Improvements
  
  * Further improves the logging and provides some
    basic performance numbers.
  * Moves the bulk-load logic out of the data-model
    class.
  * Introduces a state-machine for the bulk-load
    process so that it will be more easily able to be
    shifted to non-blocking IO when the HTTP libraries
    can do that.
  * Implements concurrent loading of the bulk-data to
    hopefully improve lead time for icons and meta-data.
  * Loads data to a temporary file and then moves to the
    final location in order to avoid partially written
    data in the cache.
  * Handles situations where no network is available;
    prevents attempt to access the network.
  * Allows bulk-load processes to be cancelled when the
    application quits.
  * Introduces command-line arguments to help simulate
    scenarios to help with testing performance and
    network absence.
  * Implements ordered insert and binary search in the
    'List' class + basic unit test.

                                    [ Andrew Lindesay <apl@xxxxxxxxxxxxxx> ]

----------------------------------------------------------------------------

Revision:    hrev51693
Commit:      3094fef308a6aaaef9827863a0a99640bdaaa5af
URL:         http://cgit.haiku-os.org/haiku/commit/?id=3094fef308a6
Author:      Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:        Tue Dec 19 19:56:53 2017 UTC

----------------------------------------------------------------------------

47 files changed, 1544 insertions(+), 948 deletions(-)
src/apps/haikudepot/Jamfile                      |   3 +
src/apps/haikudepot/List.h                       |  92 ++++++
src/apps/haikudepot/model/LocalIconStore.cpp     |  77 ++---
src/apps/haikudepot/model/LocalIconStore.h       |   4 +-
src/apps/haikudepot/model/Logger.h               |   2 -
src/apps/haikudepot/model/Model.cpp              | 329 ++++++++-----------
src/apps/haikudepot/model/Model.h                |  72 ++--
src/apps/haikudepot/model/PackageInfo.cpp        |  29 +-
src/apps/haikudepot/model/PackageInfo.h          |   4 +-
.../haikudepot/server/AbstractServerProcess.cpp  | 276 ++++++++++++++--
.../haikudepot/server/AbstractServerProcess.h    |  80 ++++-
.../haikudepot/server/PkgDataUpdateProcess.cpp   | 266 ++++++++-------
.../haikudepot/server/PkgDataUpdateProcess.h     |  26 +-
.../server/RepositoryDataUpdateProcess.cpp       | 198 +++++------
.../server/RepositoryDataUpdateProcess.h         |  19 +-
.../server/ServerIconExportUpdateProcess.cpp     | 144 +++++++-
.../server/ServerIconExportUpdateProcess.h       |  25 +-
src/apps/haikudepot/server/ServerSettings.cpp    |  44 +++
src/apps/haikudepot/server/ServerSettings.h      |  10 +
.../server/dumpexportpkg/DumpExportPkg.cpp       |   2 +-
.../server/dumpexportpkg/DumpExportPkg.h         |   2 +-
.../dumpexportpkg/DumpExportPkgCategory.cpp      |   2 +-
.../server/dumpexportpkg/DumpExportPkgCategory.h |   2 +-
.../dumpexportpkg/DumpExportPkgJsonListener.cpp  | 325 +++++++++---------
.../dumpexportpkg/DumpExportPkgJsonListener.h    |   4 +-
.../dumpexportpkg/DumpExportPkgScreenshot.cpp    |   2 +-
.../dumpexportpkg/DumpExportPkgScreenshot.h      |   2 +-
.../dumpexportpkg/DumpExportPkgVersion.cpp       |   2 +-
.../server/dumpexportpkg/DumpExportPkgVersion.h  |   2 +-
.../DumpExportRepository.cpp                     |   2 +-
.../dumpexportrepository/DumpExportRepository.h  |   2 +-
.../DumpExportRepositoryJsonListener.cpp         | 217 ++++++------
.../DumpExportRepositoryJsonListener.h           |   4 +-
.../DumpExportRepositorySource.cpp               |  38 ++-
.../DumpExportRepositorySource.h                 |   8 +-
src/apps/haikudepot/tar/TarArchiveService.cpp    |   8 +-
src/apps/haikudepot/tar/TarArchiveService.h      |   4 +-
src/apps/haikudepot/ui/App.cpp                   |  46 ++-
src/apps/haikudepot/ui/MainWindow.cpp            |  11 +-
src/apps/haikudepot/ui/MainWindow.h              |   4 +
src/apps/haikudepot/util/StorageUtils.cpp        |  55 +++-
src/apps/haikudepot/util/StorageUtils.h          |   6 +-
.../util/ToFileUrlProtocolListener.cpp           |   9 +
.../haikudepot/util/ToFileUrlProtocolListener.h  |   3 +
.../DumpExportRepositoryJsonListenerTest.cpp     |   6 +-
.../apps/haikudepot/HaikuDepotTestAddon.cpp      |   2 +
src/tests/apps/haikudepot/Jamfile                |  22 +-

----------------------------------------------------------------------------

diff --git a/src/apps/haikudepot/Jamfile b/src/apps/haikudepot/Jamfile
index f6db9f3..02a988d 100644
--- a/src/apps/haikudepot/Jamfile
+++ b/src/apps/haikudepot/Jamfile
@@ -69,6 +69,8 @@ Application HaikuDepot :
        MarkupTextView.cpp
        MessagePackageListener.cpp
        Model.cpp
+       BulkLoadContext.cpp
+       BulkLoadStateMachine.cpp
        PackageAction.cpp
        PackageActionHandler.cpp
        PackageContentsView.cpp
@@ -98,6 +100,7 @@ Application HaikuDepot :
 
        # network + server
        AbstractServerProcess.cpp
+       AbstractSingleFileServerProcess.cpp
        ServerSettings.cpp
        WebAppInterface.cpp
        PkgDataUpdateProcess.cpp
diff --git a/src/apps/haikudepot/List.h b/src/apps/haikudepot/List.h
index 3c2ce08..f6abc1d 100644
--- a/src/apps/haikudepot/List.h
+++ b/src/apps/haikudepot/List.h
@@ -13,6 +13,9 @@
 #include <SupportDefs.h>
 
 
+#define BINARY_SEARCH_LINEAR_THRESHOLD 4
+
+
 template <typename ItemType, bool PlainOldData, uint32 BlockSize = 8>
 class List {
        typedef List<ItemType, PlainOldData, BlockSize> SelfType;
@@ -110,6 +113,30 @@ public:
                return fCount;
        }
 
+/*! Note that the use of this method will depend on the list being ordered.
+*/
+
+       inline int32 BinarySearch(const void* context,
+               int32 (*compareFunc)(const void* context, const ItemType& item))
+       {
+               if (fCount == 0)
+                       return -1;
+
+               return _BinarySearchBounded(context, compareFunc, 0, fCount - 
1);
+       }
+
+       inline bool AddOrdered(const ItemType& copyFrom,
+               int32 (*compareFunc)(const ItemType& one, const ItemType& two))
+       {
+               // special case
+               if (fCount == 0
+                       || compareFunc(copyFrom, ItemAtFast(fCount - 1)) > 0) {
+                       return Add(copyFrom);
+               }
+
+               return _AddOrderedBounded(copyFrom, compareFunc, 0, fCount - 1);
+       }
+
        inline bool Add(const ItemType& copyFrom)
        {
                if (_Resize(fCount + 1)) {
@@ -229,6 +256,71 @@ public:
        }
 
 private:
+       inline int32 _BinarySearchLinearBounded(
+               const void* context,
+               int32 (*compareFunc)(const void* context, const ItemType& item),
+               int32 start, int32 end)
+       {
+               for(int32 i = start; i <= end; i++) {
+                       if (compareFunc(context, ItemAtFast(i)) == 0)
+                               return i;
+               }
+
+               return -1;
+       }
+
+       inline int32 _BinarySearchBounded(
+               const void* context,
+               int32 (*compareFunc)(const void* context, const ItemType& item),
+               int32 start, int32 end)
+       {
+               if (end - start < BINARY_SEARCH_LINEAR_THRESHOLD)
+                       return _BinarySearchLinearBounded(context, compareFunc, 
start, end);
+
+               int32 mid = start + (end - start);
+
+               if (compareFunc(context, ItemAtFast(mid)) >= 0)
+                       return _BinarySearchBounded(context, compareFunc, mid, 
end);
+               return _BinarySearchBounded(context, compareFunc, start, mid - 
1);
+       }
+
+       inline bool _AddOrderedLinearBounded(
+               const ItemType& copyFrom,
+               int32 (*compareFunc)(const ItemType& one, const ItemType& two),
+               int32 start, int32 end)
+       {
+               for(int32 i = start; i <= (end + 1); i++) {
+                       bool greaterBefore = (i == start)
+                               || (compareFunc(copyFrom, ItemAtFast(i - 1)) > 
0);
+
+                       if (greaterBefore) {
+                               bool lessAfter = (i == end + 1)
+                                       || (compareFunc(copyFrom, 
ItemAtFast(i)) <= 0);
+
+                               if (lessAfter)
+                                       return Add(copyFrom, i);
+                       }
+               }
+
+               printf("illegal state; unable to insert item into list\n");
+               exit(EXIT_FAILURE);
+       }
+
+       inline bool _AddOrderedBounded(
+               const ItemType& copyFrom,
+               int32 (*compareFunc)(const ItemType& one, const ItemType& two),
+               int32 start, int32 end)
+       {
+               if(end - start < BINARY_SEARCH_LINEAR_THRESHOLD)
+                       return _AddOrderedLinearBounded(copyFrom, compareFunc, 
start, end);
+
+               int32 mid = start + (end - start);
+
+               if (compareFunc(copyFrom, ItemAtFast(mid)) >= 0)
+                       return _AddOrderedBounded(copyFrom, compareFunc, mid, 
end);
+               return _AddOrderedBounded(copyFrom, compareFunc, start, mid - 
1);
+       }
+
        inline bool _Resize(uint32 count)
        {
                if (count > fAllocatedCount) {
diff --git a/src/apps/haikudepot/model/LocalIconStore.cpp 
b/src/apps/haikudepot/model/LocalIconStore.cpp
index c01bfd1..5cc8823 100644
--- a/src/apps/haikudepot/model/LocalIconStore.cpp
+++ b/src/apps/haikudepot/model/LocalIconStore.cpp
@@ -15,10 +15,9 @@
 #include "StorageUtils.h"
 
 
-LocalIconStore::LocalIconStore()
+LocalIconStore::LocalIconStore(const BPath& path)
 {
-       if (_EnsureIconStoragePath(fIconStoragePath) != B_OK)
-               fprintf(stdout, "unable to setup icon storage\n");
+       fIconStoragePath = path;
 }
 
 
@@ -42,62 +41,26 @@ LocalIconStore::_HasIconStoragePath() const
 status_t
 LocalIconStore::TryFindIconPath(const BString& pkgName, BPath& path) const
 {
-       if (_HasIconStoragePath()) {
-               BPath bestIconPath;
-               BPath iconPkgPath(fIconStoragePath);
-               bool exists;
-               bool isDir;
-
-               if ( (iconPkgPath.Append("hicn") == B_OK)
-                       && (iconPkgPath.Append(pkgName) == B_OK)
-                       && (StorageUtils::ExistsDirectory(iconPkgPath, &exists, 
&isDir)
-                               == B_OK)
-                       && exists
-                       && isDir
-                       && (_IdentifyBestIconFileAtDirectory(iconPkgPath, 
bestIconPath)
-                               == B_OK)
-               ) {
-                       path = bestIconPath;
-                       return B_OK;
-               }
-       }
-
-       path.Unset();
-       return B_FILE_NOT_FOUND;
-}
-
-
-void
-LocalIconStore::UpdateFromServerIfNecessary() const
-{
-       if (_HasIconStoragePath()) {
-               BPath iconStoragePath(fIconStoragePath);
-               ServerIconExportUpdateProcess service(iconStoragePath);
-               service.Run();
-
-               if (Logger::IsDebugEnabled()) {
-                       printf("did update the icons from server\n");
-               }
-       }
-}
-
-
-status_t
-LocalIconStore::_EnsureIconStoragePath(BPath& path) const
-{
-       BPath iconStoragePath;
-
-       if (find_directory(B_USER_CACHE_DIRECTORY, &iconStoragePath) == B_OK
-               && iconStoragePath.Append("HaikuDepot") == B_OK
-               && iconStoragePath.Append("__allicons") == B_OK
-               && create_directory(iconStoragePath.Path(), 0777) == B_OK) {
-               path.SetTo(iconStoragePath.Path());
+       BPath bestIconPath;
+       BPath iconPkgPath(fIconStoragePath);
+       bool exists;
+       bool isDir;
+
+       if ( (iconPkgPath.Append("hicn") == B_OK)
+               && (iconPkgPath.Append(pkgName) == B_OK)
+               && (StorageUtils::ExistsObject(iconPkgPath, &exists, &isDir, 
NULL)
+                       == B_OK)
+               && exists
+               && isDir
+               && (_IdentifyBestIconFileAtDirectory(iconPkgPath, bestIconPath)
+                       == B_OK)
+       ) {
+               path = bestIconPath;
                return B_OK;
        }
 
        path.Unset();
-       fprintf(stdout, "unable to find the user cache directory for icons");
-       return B_ERROR;
+       return B_FILE_NOT_FOUND;
 }
 
 
@@ -121,8 +84,8 @@ LocalIconStore::_IdentifyBestIconFileAtDirectory(const 
BPath& directory,
                bool isDir;
 
                if ( (workingPath.Append(iconLeafname) == B_OK
-                       && StorageUtils::ExistsDirectory(
-                               workingPath, &exists, &isDir) == B_OK)
+                       && StorageUtils::ExistsObject(
+                               workingPath, &exists, &isDir, NULL) == B_OK)
                        && exists
                        && !isDir) {
                        bestIconPath.SetTo(workingPath.Path());
diff --git a/src/apps/haikudepot/model/LocalIconStore.h 
b/src/apps/haikudepot/model/LocalIconStore.h
index 350455a..5745d21 100644
--- a/src/apps/haikudepot/model/LocalIconStore.h
+++ b/src/apps/haikudepot/model/LocalIconStore.h
@@ -16,15 +16,13 @@
 
 class LocalIconStore {
 public:
-                                                               
LocalIconStore();
+                                                               
LocalIconStore(const BPath& path);
        virtual                                         ~LocalIconStore();
                        status_t                        TryFindIconPath(const 
BString& pkgName,
                                                                        BPath& 
path) const;
-                       void                            
UpdateFromServerIfNecessary() const;
 
 private:
                        bool                            _HasIconStoragePath() 
const;
-                       status_t                        
_EnsureIconStoragePath(BPath& path) const;
                        status_t                        
_IdentifyBestIconFileAtDirectory(
                                                                        const 
BPath& directory,
                                                                        BPath& 
bestIconPath) const;
diff --git a/src/apps/haikudepot/model/Logger.h 
b/src/apps/haikudepot/model/Logger.h
index 33cfa13..95237cd 100644
--- a/src/apps/haikudepot/model/Logger.h
+++ b/src/apps/haikudepot/model/Logger.h
@@ -2,8 +2,6 @@
  * Copyright 2017, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
  * All rights reserved. Distributed under the terms of the MIT License.
  */
-
-
 #ifndef LOGGER_H
 #define LOGGER_H
 
diff --git a/src/apps/haikudepot/model/Model.cpp 
b/src/apps/haikudepot/model/Model.cpp
index 2358646..4093cea 100644
--- a/src/apps/haikudepot/model/Model.cpp
+++ b/src/apps/haikudepot/model/Model.cpp
@@ -23,8 +23,6 @@
 #include <Path.h>
 
 #include "Logger.h"
-#include "PkgDataUpdateProcess.h"
-#include "RepositoryDataUpdateProcess.h"
 #include "StorageUtils.h"
 
 
@@ -359,10 +357,7 @@ Model::Model()
        fShowAvailablePackages(true),
        fShowInstalledPackages(true),
        fShowSourcePackages(false),
-       fShowDevelopPackages(false),
-
-       fPopulateAllPackagesThread(-1),
-       fStopPopulatingAllPackages(false)
+       fShowDevelopPackages(false)
 {
        _UpdateIsFeaturedFilter();
 
@@ -417,7 +412,6 @@ Model::Model()
 
 Model::~Model()
 {
-       StopPopulatingAllPackages();
 }
 
 
@@ -701,7 +695,6 @@ Model::PopulatePackage(const PackageInfoRef& package, 
uint32 flags)
                                        BMessage item;
                                        if (items.FindMessage(name, &item) != 
B_OK)
                                                break;
-//                                     item.PrintToStream();
 
                                        BString user;
                                        BMessage userInfo;
@@ -769,32 +762,6 @@ Model::PopulatePackage(const PackageInfoRef& package, 
uint32 flags)
 
 
 void
-Model::PopulateAllPackages()
-{
-       StopPopulatingAllPackages();
-
-       fStopPopulatingAllPackages = false;
-
-       fPopulateAllPackagesThread = spawn_thread(&_PopulateAllPackagesEntry,
-               "Package populator", B_NORMAL_PRIORITY, this);
-       if (fPopulateAllPackagesThread >= 0)
-               resume_thread(fPopulateAllPackagesThread);
-}
-
-
-void
-Model::StopPopulatingAllPackages()
-{
-       if (fPopulateAllPackagesThread < 0)
-               return;
-
-       fStopPopulatingAllPackages = true;
-       wait_for_thread(fPopulateAllPackagesThread, NULL);
-       fPopulateAllPackagesThread = -1;
-}
-
-
-void
 Model::SetUsername(BString username)
 {
        BString password;
@@ -837,8 +804,6 @@ Model::SetAuthorization(const BString& username, const 
BString& password,
 }
 
 
-// #pragma mark - private
-
 /*! When bulk repository data comes down from the server, it will
     arrive as a json.gz payload.  This is stored locally as a cache
     and this method will provide the on-disk storage location for
@@ -846,7 +811,7 @@ Model::SetAuthorization(const BString& username, const 
BString& password,
 */
 
 status_t
-Model::_DumpExportRepositoryDataPath(BPath& path) const
+Model::DumpExportRepositoryDataPath(BPath& path) const
 {
        BPath repoDataPath;
 
@@ -866,7 +831,26 @@ Model::_DumpExportRepositoryDataPath(BPath& path) const
 
 
 status_t
-Model::_DumpExportPkgDataPath(BPath& path,
+Model::IconStoragePath(BPath& path) const
+{
+       BPath iconStoragePath;
+
+       if (find_directory(B_USER_CACHE_DIRECTORY, &iconStoragePath) == B_OK
+               && iconStoragePath.Append("HaikuDepot") == B_OK
+               && iconStoragePath.Append("__allicons") == B_OK
+               && create_directory(iconStoragePath.Path(), 0777) == B_OK) {
+               path.SetTo(iconStoragePath.Path());
+               return B_OK;
+       }
+
+       path.Unset();
+       fprintf(stdout, "unable to find the user cache directory for icons");
+       return B_ERROR;
+}
+
+
+status_t
+Model::DumpExportPkgDataPath(BPath& path,
        const BString& repositorySourceCode) const
 {
        BPath repoDataPath;
@@ -889,24 +873,6 @@ Model::_DumpExportPkgDataPath(BPath& path,
 }
 
 
-status_t
-Model::PopulateWebAppRepositoryCodes()
-{
-       status_t result = B_OK;
-       BPath dataPath;
-
-       result = _DumpExportRepositoryDataPath(dataPath);
-
-       if (result != B_OK)
-               return result;
-
-       RepositoryDataUpdateProcess process(dataPath, &fDepots);
-       result = process.Run();
-
-       return result;
-}
-
-
 void
 Model::_UpdateIsFeaturedFilter()
 {
@@ -917,141 +883,6 @@ Model::_UpdateIsFeaturedFilter()
 }
 
 
-int32
-Model::_PopulateAllPackagesEntry(void* cookie)
-{
-       Model* model = static_cast<Model*>(cookie);
-       model->_PopulateAllPackagesThread();
-       model->_PopulateAllPackagesIcons();
-       return 0;
-}
-
-
-void
-Model::_PopulateAllPackagesIcons()
-{
-       fLocalIconStore.UpdateFromServerIfNecessary();
-
-       int32 depotIndex = 0;
-       int32 packageIndex = 0;
-       int32 countIconsSet = 0;
-
-       fprintf(stdout, "will populate all packages' icons\n");
-
-       while (true) {
-               PackageInfoRef package;
-               BAutolock locker(&fLock);
-
-               if (depotIndex > fDepots.CountItems()) {
-                       fprintf(stdout, "did populate %" B_PRId32 " packages' 
icons\n",
-                               countIconsSet);
-                       return;
-               }
-
-               const DepotInfo& depot = fDepots.ItemAt(depotIndex);
-               const PackageList& packages = depot.Packages();
-
-               if (packageIndex >= packages.CountItems()) {
-                       // Need the next depot
-                       packageIndex = 0;
-                       depotIndex++;
-               } else {
-                       package = packages.ItemAt(packageIndex);
-
-                       if (Logger::IsDebugEnabled()) {
-                               fprintf(stdout, "will populate package icon for 
[%s]\n",
-                                       package->Name().String());
-                       }
-
-                       if (_PopulatePackageIcon(package) == B_OK)
-                               countIconsSet++;
-
-                       packageIndex++;
-               }
-       }
-}
-
-
-void
-Model::_PopulatePackagesForDepot(const DepotInfo& depotInfo)
-{
-       BString repositorySourceCode = depotInfo.WebAppRepositorySourceCode();
-       BPath repositorySourcePkgDataPath;
-
-       if (B_OK != _DumpExportPkgDataPath(repositorySourcePkgDataPath,
-               repositorySourceCode)) {
-               printf("unable to obtain the path for storing data for [%s]\n",
-                       repositorySourceCode.String());
-       } else {
-
-               printf("will fetch and process data for repository source 
[%s]\n",
-                       repositorySourceCode.String());
-
-               PkgDataUpdateProcess process(
-                       repositorySourcePkgDataPath,
-                       fLock, repositorySourceCode, fPreferredLanguage,
-                       depotInfo.Packages(),
-                       fCategories);
-
-               process.Run();
-
-               printf("did fetch and process data for repository source 
[%s]\n",
-                       repositorySourceCode.String());
-       }
-}
-
-
-void
-Model::_PopulateAllPackagesThread()
-{
-       int32 depotIndex = 0;
-       int32 count = 0;
-
-       for (depotIndex = 0;
-               depotIndex < fDepots.CountItems() && 
!fStopPopulatingAllPackages;
-               depotIndex++) {
-               const DepotInfo& depot = fDepots.ItemAt(depotIndex);
-
-               if (depot.WebAppRepositorySourceCode().Length() > 0) {
-                       _PopulatePackagesForDepot(depot);
-                       count++;
-               }
-       }
-
-       printf("did populate package data for %" B_PRIi32 " depots\n", count);
-}
-
-
-status_t
-Model::_PopulatePackageIcon(const PackageInfoRef& package)
-{
-       BPath bestIconPath;
-
-       if ( fLocalIconStore.TryFindIconPath(
-               package->Name(), bestIconPath) == B_OK) {
-
-               BFile bestIconFile(bestIconPath.Path(), O_RDONLY);
-               BitmapRef 
bitmapRef(new(std::nothrow)SharedBitmap(bestIconFile), true);
-               BAutolock locker(&fLock);
-               package->SetIcon(bitmapRef);
-
-               if (Logger::IsDebugEnabled()) {
-                       fprintf(stdout, "have set the package icon for [%s] 
from [%s]\n",
-                               package->Name().String(), bestIconPath.Path());
-               }
-
-               return B_OK;
-       }
-
-       if (Logger::IsDebugEnabled()) {
-               fprintf(stdout, "did not set the package icon for [%s]; no 
data\n",
-                       package->Name().String());
-       }
-
-       return B_FILE_NOT_FOUND;
-}
-
-
 void
 Model::_PopulatePackageScreenshot(const PackageInfoRef& package,
        const ScreenshotInfo& info, int32 scaledWidth, bool fromCacheOnly)
@@ -1128,3 +959,119 @@ Model::_NotifyAuthorizationChanged()
                        listener->AuthorizationChanged();
        }
 }
+
+
+// temporary - should not be required once the repo info url is used.
+static void
+normalize_repository_base_url(BUrl& url)
+{
+       if (url.Protocol() == "https")
+               url.SetProtocol("http");
+
+       BString path(url.Path());
+
+       if (path.EndsWith("/"))
+               url.SetPath(path.Truncate(path.Length() - 1));
+}
+
+
+void
+Model::ForAllDepots(void (*func)(const DepotInfo& depot, void* context),
+       void* context)
+{
+       for (int32 i = 0; i < fDepots.CountItems(); i++) {
+               DepotInfo depotInfo = fDepots.ItemAtFast(i);
+               func(depotInfo, context);
+       }
+}
+
+
+// TODO; should use the repo.info url and not the base url.
+
+void
+Model::ReplaceDepotByUrl(const BString& url,
+       DepotMapper* depotMapper, void* context)
+{
+       for (int32 i = 0; i < fDepots.CountItems(); i++) {
+               DepotInfo depotInfo = fDepots.ItemAtFast(i);
+
+               BUrl url(url);
+               BUrl depotUrlNormalized(depotInfo.BaseURL());
+
+               normalize_repository_base_url(url);
+               normalize_repository_base_url(depotUrlNormalized);
+
+               if (url == depotUrlNormalized) {
+                       BAutolock locker(&fLock);
+                       fDepots.Replace(i, depotMapper->MapDepot(depotInfo, 
context));
+               }
+       }
+}
+
+
+void
+Model::ForAllPackages(PackageConsumer* packageConsumer, void* context)
+{
+       for (int32 i = 0; i < fDepots.CountItems(); i++) {
+               DepotInfo depotInfo = fDepots.ItemAtFast(i);
+               PackageList packages = depotInfo.Packages();
+               for(int32 j = 0; j < packages.CountItems(); j++) {
+                       const PackageInfoRef& packageInfoRef = 
packages.ItemAtFast(j);
+
+                       if (packageInfoRef != NULL) {
+                               BAutolock locker(&fLock);
+                               if 
(!packageConsumer->ConsumePackage(packageInfoRef, context))
+                                       return;
+                       }
+               }
+       }
+}
+
+
+void
+Model::ForPackageByNameInDepot(const BString& depotName,
+       const BString& packageName, PackageConsumer* packageConsumer, void* 
context)
+{
+       int32 depotCount = fDepots.CountItems();
+
+       for (int32 i = 0; i < depotCount; i++) {
+               DepotInfo depotInfo = fDepots.ItemAtFast(i);
+
+               if (depotInfo.Name() == depotName) {
+                       int32 packageIndex = 
depotInfo.PackageIndexByName(packageName);
+
+                       if (-1 != packageIndex) {
+                               PackageList packages = depotInfo.Packages();
+                               const PackageInfoRef& packageInfoRef =
+                                       packages.ItemAtFast(packageIndex);
+
+                               BAutolock locker(&fLock);
+                               packageConsumer->ConsumePackage(packageInfoRef,
+                                       context);
+                       }
+
+                       return;
+               }
+       }
+}
+
+
+void
+Model::LogDepotsWithNoWebAppRepositoryCode() const
+{
+       int32 i;
+
+       for (i = 0; i < fDepots.CountItems(); i++) {
+               const DepotInfo& depot = fDepots.ItemAt(i);
+
+               if (depot.WebAppRepositoryCode().Length() == 0) {
+                       printf("depot [%s]", depot.Name().String());
+
+                       if (depot.BaseURL().Length() > 0)
+                               printf(" (%s)", depot.BaseURL().String());
+
+                       printf(" correlates with no repository in the haiku"
+                               "depot server system\n");
+               }
+       }
+}
\ No newline at end of file
diff --git a/src/apps/haikudepot/model/Model.h 
b/src/apps/haikudepot/model/Model.h
index 0394e83..fa852b1 100644
--- a/src/apps/haikudepot/model/Model.h
+++ b/src/apps/haikudepot/model/Model.h
@@ -9,7 +9,9 @@
 #include <FindDirectory.h>
 #include <Locker.h>
 
+#include "AbstractServerProcess.h"
 #include "LocalIconStore.h"
+#include "BulkLoadContext.h"
 #include "PackageInfo.h"
 #include "WebAppInterface.h"
 
@@ -37,6 +39,22 @@ public:
        virtual void                            AuthorizationChanged() = 0;
 };
 
+
+class DepotMapper {
+public:
+       virtual DepotInfo                       MapDepot(const DepotInfo& depot,
+                                                                       void* 
context) = 0;
+};
+
+
+class PackageConsumer {
+public:
+       virtual bool                            ConsumePackage(
+                                                                       const 
PackageInfoRef& packageInfoRef,
+                                                                       void* 
context) = 0;
+};
+
+
 typedef BReference<ModelListener> ModelListenerRef;
 typedef List<ModelListenerRef, false> ModelListenerList;
 
@@ -118,8 +136,6 @@ public:
                        bool                            ShowDevelopPackages() 
const
                                                                        { 
return fShowDevelopPackages; }
 
-                       status_t                        
PopulateWebAppRepositoryCodes();
-
                        // Retrieve package information
        static  const uint32            POPULATE_CACHED_RATING  = 1 << 0;
        static  const uint32            POPULATE_CACHED_ICON    = 1 << 1;
@@ -131,8 +147,6 @@ public:
 
                        void                            PopulatePackage(const 
PackageInfoRef& package,
                                                                        uint32 
flags);
-                       void                            PopulateAllPackages();
-                       void                            
StopPopulatingAllPackages();
 
                        const StringList&       SupportedLanguages() const
                                                                        { 
return fSupportedLanguages; }
@@ -149,20 +163,41 @@ public:
                        const WebAppInterface& GetWebAppInterface() const
                                                                        { 
return fWebAppInterface; }
 
+                       void                            ReplaceDepotByUrl(
+                                                                       const 
BString& url,
+                                                                       
DepotMapper* depotMapper,
+                                                                       void* 
context);
 
-private:
-                       status_t                        
_DumpExportRepositoryDataPath(
-                                                                       BPath& 
path) const;
-                       status_t                        
_DumpExportPkgDataPath(BPath& path,
+                       void                            ForAllDepots(
+                                                                       void 
(*func)(const DepotInfo& depot,
+                                                                               
void* context),
+                                                                       void* 
context);
+
+                       void                            
ForAllPackages(PackageConsumer* packageConsumer,
+                                                                       void* 
context);
+
+                       void                            ForPackageByNameInDepot(
+                                                                       const 
BString& depotName,
+                                                                       const 
BString& packageName,
+                                                                       
PackageConsumer* packageConsumer,
+                                                                       void* 
context);
+
+                       status_t                        IconStoragePath(BPath& 
path) const;
+                       status_t                        
DumpExportRepositoryDataPath(BPath& path) const;
+                       status_t                        
DumpExportPkgDataPath(BPath& path,
                                                                        const 
BString& repositorySourceCode) const;
+
+                       void                            
LogDepotsWithNoWebAppRepositoryCode() const;
+
+private:
                        void                            
_UpdateIsFeaturedFilter();
 
        static  int32                           _PopulateAllPackagesEntry(void* 
cookie);
-                       void                            
_PopulateAllPackagesThread();
-                       void                            
_PopulatePackagesForDepot(
-                                                                       const 
DepotInfo& depotInfo);
 
-                       void                            
_PopulateAllPackagesIcons();
+                       void                            
_PopulatePackageScreenshot(
+                                                                       const 
PackageInfoRef& package,
+                                                                       const 
ScreenshotInfo& info,
+                                                                       int32 
scaledWidth, bool fromCacheOnly);
 
                        bool                            _GetCacheFile(BPath& 
path, BFile& file,
                                                                        
directory_which directory,
@@ -175,14 +210,6 @@ private:
                                                                        const 
char* fileName,
                                                                        bool 
ignoreAge, time_t maxAge) const;
 
-                       status_t                        _PopulatePackageIcon(
-                                                                       const 
PackageInfoRef& package);
-                       void                            
_PopulatePackageScreenshot(
-                                                                       const 
PackageInfoRef& package,
-                                                                       const 
ScreenshotInfo& info,
-                                                                       int32 
scaledWidth,
-                                                                       bool 
fromCacheOnly);
-
                        void                            
_NotifyAuthorizationChanged();
 
 private:
@@ -190,8 +217,6 @@ private:
 
                        DepotList                       fDepots;
 
-                       LocalIconStore          fLocalIconStore;
-
                        CategoryRef                     fCategoryAudio;
                        CategoryRef                     fCategoryBusiness;
                        CategoryRef                     fCategoryDevelopment;
@@ -225,9 +250,6 @@ private:
                        bool                            fShowSourcePackages;
                        bool                            fShowDevelopPackages;
 
-                       thread_id                       
fPopulateAllPackagesThread;
-       volatile bool                           fStopPopulatingAllPackages;
-
                        StringList                      fSupportedLanguages;
                        BString                         fPreferredLanguage;
 
diff --git a/src/apps/haikudepot/model/PackageInfo.cpp 
b/src/apps/haikudepot/model/PackageInfo.cpp
index 94e67f9..6607a35 100644
--- a/src/apps/haikudepot/model/PackageInfo.cpp
+++ b/src/apps/haikudepot/model/PackageInfo.cpp
@@ -1,7 +1,7 @@
 /*
  * Copyright 2013-2014, Stephan Aßmus <superstippi@xxxxxx>.
  * Copyright 2013, Rene Gollent <rene@xxxxxxxxxxx>.
- * Copyright 2016, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * Copyright 2016-2017, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
  * All rights reserved. Distributed under the terms of the MIT License.
  */
 
@@ -1047,10 +1047,35 @@ DepotInfo::operator!=(const DepotInfo& other) const
 }
 
 
+static int32 PackageCompare(const PackageInfoRef& p1, const PackageInfoRef& p2)
+{
+       return p1->Name().Compare(p2->Name());
+}
+
+
+/*! This method will insert the package into the list of packages
+    in order so that the list of packages remains in order.
+ */
+
 bool
 DepotInfo::AddPackage(const PackageInfoRef& package)
 {
-       return fPackages.Add(package);
+       return fPackages.AddOrdered(package, &PackageCompare);
+}
+
+
+static int32 PackageFixedNameCompare(const void* context,
+       const PackageInfoRef& package)
+{
+       const BString* packageName = static_cast<BString*>(context);
+       return packageName->Compare(package->Name());
+}
+
+
+int32
+DepotInfo::PackageIndexByName(const BString& packageName)
+{
+       return fPackages.BinarySearch(&packageName, &PackageFixedNameCompare);
 }
 
 
diff --git a/src/apps/haikudepot/model/PackageInfo.h 
b/src/apps/haikudepot/model/PackageInfo.h
index 0a754a8..99d508a 100644
--- a/src/apps/haikudepot/model/PackageInfo.h
+++ b/src/apps/haikudepot/model/PackageInfo.h
@@ -1,6 +1,6 @@
 /*
  * Copyright 2013-2014, Stephan Aßmus <superstippi@xxxxxx>.
- * Copyright 2016, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * Copyright 2016-2017, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
  * All rights reserved. Distributed under the terms of the MIT License.
  */
 #ifndef PACKAGE_INFO_H
@@ -411,6 +411,8 @@ public:
 
                        bool                            AddPackage(const 
PackageInfoRef& package);
 
+                       int32                           
PackageIndexByName(const BString& packageName);
+
                        void                            SyncPackages(const 
PackageList& packages);
 
                        void                            SetBaseURL(const 
BString& baseURL);
diff --git a/src/apps/haikudepot/server/AbstractServerProcess.cpp 
b/src/apps/haikudepot/server/AbstractServerProcess.cpp
index ebb20ba..5b8b77f 100644
--- a/src/apps/haikudepot/server/AbstractServerProcess.cpp
+++ b/src/apps/haikudepot/server/AbstractServerProcess.cpp
@@ -4,13 +4,15 @@
  */
 #include "AbstractServerProcess.h"
 
+#include <unistd.h>
 #include <errno.h>
 #include <string.h>
 
 #include <AutoDeleter.h>
+#include <Autolock.h>
 #include <FileIO.h>
-#include <HttpRequest.h>
 #include <HttpTime.h>
+#include <Locker.h>
 #include <UrlProtocolRoster.h>
 
 #include <support/ZlibCompressionAlgorithm.h>
@@ -18,6 +20,7 @@
 #include "Logger.h"
 #include "ServerSettings.h"
 #include "StandardMetaDataJsonEventListener.h"
+#include "StorageUtils.h"
 #include "ToFileUrlProtocolListener.h"
 
 
@@ -31,6 +34,140 @@
 #define TIMEOUT_MICROSECONDS 3e+7
 
 
+AbstractServerProcess::AbstractServerProcess(
+       AbstractServerProcessListener* listener, uint32 options)
+       :
+       fLock(),
+       fListener(listener),
+       fWasStopped(false),
+       fProcessState(SERVER_PROCESS_INITIAL),
+       fErrorStatus(B_OK),
+       fOptions(options),
+       fRequest(NULL)
+{
+}
+
+
+AbstractServerProcess::~AbstractServerProcess()
+{
+}
+
+
+bool
+AbstractServerProcess::HasOption(uint32 flag)
+{
+       return (fOptions & flag) == flag;
+}
+
+
+bool
+AbstractServerProcess::ShouldAttemptNetworkDownload(bool hasDataAlready)
+{
+       return
+               !HasOption(SERVER_PROCESS_NO_NETWORKING)
+               && !(HasOption(SERVER_PROCESS_PREFER_CACHE) && hasDataAlready);
+}
+
+
+status_t
+AbstractServerProcess::Run()
+{
+       {
+               BAutolock locker(&fLock);
+
+               if (ProcessState() != SERVER_PROCESS_INITIAL) {
+                       printf("cannot start server process as it is not idle");
+                       return B_NOT_ALLOWED;
+               }
+
+               fProcessState = SERVER_PROCESS_RUNNING;
+       }
+
+       SetErrorStatus(RunInternal());
+
+       SetProcessState(SERVER_PROCESS_COMPLETE);
+
+               // this process may be part of a larger bulk-load process and
+               // if so, the process orchestration needs to know when this
+               // process has completed.
+
+       if (fListener != NULL)
+               fListener->ServerProcessExited();
+
+       return ErrorStatus();
+}
+
+
+bool
+AbstractServerProcess::WasStopped()
+{
+       BAutolock locker(&fLock);
+       return fWasStopped;
+}
+
+
+status_t
+AbstractServerProcess::ErrorStatus()
+{
+       BAutolock locker(&fLock);
+       return fErrorStatus;
+}
+
+
+status_t
+AbstractServerProcess::Stop()
+{
+       BAutolock locker(&fLock);
+       fWasStopped = true;
+       return StopInternal();
+}
+
+
+status_t
+AbstractServerProcess::StopInternal()
+{
+       if (fRequest != NULL) {
+               return fRequest->Stop();
+       }
+
+       return B_NOT_ALLOWED;
+}
+
+
+bool
+AbstractServerProcess::IsRunning()
+{
+       return ProcessState() == SERVER_PROCESS_RUNNING;
+}
+
+
+void
+AbstractServerProcess::SetErrorStatus(status_t value)
+{
+       BAutolock locker(&fLock);
+
+       if (fErrorStatus == B_OK) {
+               fErrorStatus = value;
+       }
+}
+
+
+void
+AbstractServerProcess::SetProcessState(process_state value)
+{
+       BAutolock locker(&fLock);
+       fProcessState = value;
+}
+
+
+process_state
+AbstractServerProcess::ProcessState()
+{
+       BAutolock locker(&fLock);
+       return fProcessState;
+}
+
+
 status_t
 AbstractServerProcess::IfModifiedSinceHeaderValue(BString& headerValue) const
 {
@@ -74,8 +211,9 @@ AbstractServerProcess::IfModifiedSinceHeaderValue(BString& 
headerValue,
                headerValue.SetTo(modifiedHttpTime
                        .ToString(BPrivate::B_HTTP_TIME_FORMAT_COOKIE));
        } else {
-               fprintf(stderr, "unable to parse the meta-data date and time -"
-                       " cannot set the 'If-Modified-Since' header\n");
+               fprintf(stderr, "unable to parse the meta-data date and time 
from [%s]"
+                       " - cannot set the 'If-Modified-Since' header\n",
+                       metaDataPath.Path());
        }
 
        return result;
@@ -163,10 +301,52 @@ AbstractServerProcess::ParseJsonFromFileWithListener(
 }
 
 
+/*! In order to reduce the chance of failure half way through downloading a
+    large file, this method will download the file to a temporary file and
+    then it can rename the file to the final target file.
+*/
+
+status_t
+AbstractServerProcess::DownloadToLocalFileAtomically(
+       const BPath& targetFilePath,
+       const BUrl& url)
+{
+       BPath temporaryFilePath(tmpnam(NULL), NULL, true);
+       status_t result = DownloadToLocalFile(
+               temporaryFilePath, url, 0, 0);
+
+               // not copying if the data has not changed because the data 
will be
+               // zero length.  This is if the result is APP_ERR_NOT_MODIFIED.
+       if (result == B_OK) {
+
+                       // if the file is zero length then assume that 
something has
+                       // gone wrong.
+               off_t size;
+               bool hasFile;
+
+               result = StorageUtils::ExistsObject(temporaryFilePath, 
&hasFile, NULL,
+                       &size);
+
+               if (result == B_OK && hasFile && size > 0) {
+                       if (rename(temporaryFilePath.Path(), 
targetFilePath.Path()) != 0) {
+                               printf("[%s] did rename [%s] --> [%s]\n",
+                                       Name(), temporaryFilePath.Path(), 
targetFilePath.Path());
+                               result = B_IO_ERROR;
+                       }
+               }
+       }
+
+       return result;
+}
+
+
 status_t
 AbstractServerProcess::DownloadToLocalFile(const BPath& targetFilePath,
        const BUrl& url, uint32 redirects, uint32 failures)
 {
+       if (WasStopped())
+               return B_CANCELED;
+
        if (redirects > MAX_REDIRECTS) {
                fprintf(stdout, "exceeded %d redirects --> failure\n", 
MAX_REDIRECTS);
                return B_IO_ERROR;
@@ -177,10 +357,10 @@ AbstractServerProcess::DownloadToLocalFile(const BPath& 
targetFilePath,
                return B_IO_ERROR;
        }
 
-       fprintf(stdout, "will stream '%s' to [%s]\n", url.UrlString().String(),
-               targetFilePath.Path());
+       fprintf(stdout, "[%s] will stream '%s' to [%s]\n",
+               Name(), url.UrlString().String(), targetFilePath.Path());
 
-       ToFileUrlProtocolListener listener(targetFilePath, LoggingName(),
+       ToFileUrlProtocolListener listener(targetFilePath, Name(),
                Logger::IsTraceEnabled());
 
        BHttpHeaders headers;
@@ -195,57 +375,77 @@ AbstractServerProcess::DownloadToLocalFile(const BPath& 
targetFilePath,
                headers.AddHeader("If-Modified-Since", ifModifiedSinceHeader);
        }
 
-       BHttpRequest *request = dynamic_cast<BHttpRequest *>(
-               BUrlProtocolRoster::MakeRequest(url, &listener));
-       ObjectDeleter<BHttpRequest> requestDeleter(request);
-       request->SetHeaders(headers);
-       request->SetMaxRedirections(0);
-       request->SetTimeout(TIMEOUT_MICROSECONDS);
+       thread_id thread;
+
+       {
+               fRequest = dynamic_cast<BHttpRequest *>(
+                       BUrlProtocolRoster::MakeRequest(url, &listener));
+               fRequest->SetHeaders(headers);
+               fRequest->SetMaxRedirections(0);
+               fRequest->SetTimeout(TIMEOUT_MICROSECONDS);
+               thread = fRequest->Run();
+       }
 
-       thread_id thread = request->Run();
        wait_for_thread(thread, NULL);
 
        const BHttpResult& result = dynamic_cast<const BHttpResult&>(
-               request->Result());
-
+               fRequest->Result());
        int32 statusCode = result.StatusCode();
+       const BHttpHeaders responseHeaders = result.Headers();
+       const char *locationC = responseHeaders["Location"];
+       BString location;
+
+       if (locationC != NULL)
+               location.SetTo(locationC);
+
+       delete fRequest;
+       fRequest = NULL;
 
        if (BHttpRequest::IsSuccessStatusCode(statusCode)) {
-               fprintf(stdout, "did complete streaming data\n");
+               fprintf(stdout, "[%s] did complete streaming data [%"
+                       B_PRIdSSIZE " bytes]\n", Name(), 
listener.ContentLength());
                return B_OK;
        } else if (statusCode == HTTP_STATUS_NOT_MODIFIED) {
-               fprintf(stdout, "remote data has not changed since [%s]\n",
-                       ifModifiedSinceHeader.String());
+               fprintf(stdout, "[%s] remote data has not changed since [%s]\n",
+                       Name(), ifModifiedSinceHeader.String());
                return APP_ERR_NOT_MODIFIED;
        } else if (BHttpRequest::IsRedirectionStatusCode(statusCode)) {
-               const BHttpHeaders responseHeaders = result.Headers();
-               const char *locationValue = responseHeaders["Location"];
-
-               if (locationValue != NULL && strlen(locationValue) != 0) {
-                       BUrl location(result.Url(), locationValue);
-                       fprintf(stdout, "will redirect to; %s\n",
-                               location.UrlString().String());
-                               return DownloadToLocalFile(targetFilePath, 
location,
-                                       redirects + 1, 0);
+               if (location.Length() != 0) {
+                       BUrl location(result.Url(), location);
+                       fprintf(stdout, "[%s] will redirect to; %s\n",
+                               Name(), location.UrlString().String());
+                       return DownloadToLocalFile(targetFilePath, location,
+                               redirects + 1, 0);
                }
 
-               fprintf(stdout, "unable to find 'Location' header for 
redirect\n");
+               fprintf(stdout, "[%s] unable to find 'Location' header for 
redirect\n",
+                       Name());
                return B_IO_ERROR;
        } else {
                if (statusCode == 0 || (statusCode / 100) == 5) {
-                       fprintf(stdout, "error response from server; %" 
B_PRId32 " --> "
+                       fprintf(stdout, "error response from server [%" 
B_PRId32 "] --> "
                                "retry...\n", statusCode);
                        return DownloadToLocalFile(targetFilePath, url, 
redirects,
                                failures + 1);
                }
 
-               fprintf(stdout, "unexpected response from server; %" B_PRId32 
"\n",
-                       statusCode);
+               fprintf(stdout, "[%s] unexpected response from server [%" 
B_PRId32 "]\n",
+                       Name(), statusCode);
                return B_IO_ERROR;
        }
 }
 
 
+status_t
+AbstractServerProcess::DeleteLocalFile(const BPath& currentFilePath)
+{
+       if (0 == remove(currentFilePath.Path()))
+               return B_OK;
+
+       return B_IO_ERROR;
+}
+
+
 /*!    When a file is damaged or corrupted in some way, the file should be 
'moved
     aside' so that it is not involved in the next update.  This method will
     create such an alternative 'damaged' file location and move this file to
@@ -263,13 +463,19 @@ AbstractServerProcess::MoveDamagedFileAside(const BPath& 
currentFilePath)
        damagedFilePath.Append(damagedLeaf.String());
 
        if (0 != rename(currentFilePath.Path(), damagedFilePath.Path())) {
-               printf("unable to move damaged file [%s] aside to [%s]\n",
-                       currentFilePath.Path(), damagedFilePath.Path());
+               printf("[%s] unable to move damaged file [%s] aside to [%s]\n",
+                       Name(), currentFilePath.Path(), damagedFilePath.Path());
                return B_IO_ERROR;
        }
 
-       printf("did move damaged file [%s] aside to [%s]\n",
-               currentFilePath.Path(), damagedFilePath.Path());
+       printf("[%s] did move damaged file [%s] aside to [%s]\n",
+               Name(), currentFilePath.Path(), damagedFilePath.Path());
 
        return B_OK;
 }
+
+
+bool
+AbstractServerProcess::IsSuccess(status_t e) {
+       return e == B_OK || e == APP_ERR_NOT_MODIFIED;
+}
diff --git a/src/apps/haikudepot/server/AbstractServerProcess.h 
b/src/apps/haikudepot/server/AbstractServerProcess.h
index de8c0d0..f8288b1 100644
--- a/src/apps/haikudepot/server/AbstractServerProcess.h
+++ b/src/apps/haikudepot/server/AbstractServerProcess.h
@@ -6,26 +6,66 @@
 #ifndef ABSTRACT_SERVER_PROCESS_H
 #define ABSTRACT_SERVER_PROCESS_H
 
+#include <HttpRequest.h>
 #include <Json.h>
 #include <String.h>
 #include <Url.h>
 
 #include "StandardMetaData.h"
+#include "Stoppable.h"
 
 
-#define APP_ERR_NOT_MODIFIED (B_APP_ERROR_BASE + 452)
+#define APP_ERR_NOT_MODIFIED           (B_APP_ERROR_BASE + 452)
+#define APP_ERR_NO_DATA                                (B_APP_ERROR_BASE + 453)
 
 
-class AbstractServerProcess {
+typedef enum process_options {
+       SERVER_PROCESS_NO_NETWORKING    = 1 << 0,
+       SERVER_PROCESS_PREFER_CACHE             = 1 << 1,
+       SERVER_PROCESS_DROP_CACHE               = 1 << 2
+} process_options;
+
+
+typedef enum process_state {
+       SERVER_PROCESS_INITIAL                  = 1,
+       SERVER_PROCESS_RUNNING                  = 2,
+       SERVER_PROCESS_COMPLETE                 = 3
+} process_state;
+
+
+/*! Clients are able to subclass from this 'interface' in order to accept
+    call-backs when a process has exited; either through success or through
+    failure.
+ */
+
+class AbstractServerProcessListener {
 public:
-       virtual status_t                        Run() = 0;
+       virtual void                            ServerProcessExited() = 0;
+};
+
+
+class AbstractServerProcess : public Stoppable {
+public:
+                                                               
AbstractServerProcess(
+                                                                       
AbstractServerProcessListener* listener,
+                                                                       uint32 
options);
+       virtual                                         
~AbstractServerProcess();
+
+       virtual const char*                             Name() = 0;
+                       status_t                        Run();
+                       status_t                        Stop();
+                       status_t                        ErrorStatus();
+                       bool                            IsRunning();
+                       bool                            WasStopped();
 
 protected:
+       virtual status_t                        RunInternal() = 0;
+       virtual status_t                        StopInternal();
+
        virtual void                            GetStandardMetaDataPath(
                                                                        BPath& 
path) const = 0;
        virtual void                            GetStandardMetaDataJsonPath(
                                                                        
BString& jsonPath) const = 0;
-       virtual const char*                     LoggingName() const = 0;
 
                        status_t                        
IfModifiedSinceHeaderValue(
                                                                        
BString& headerValue) const;
@@ -43,15 +83,41 @@ protected:
                                                                        
BJsonEventListener *listener,
                                                                        const 
BPath& path) const;
 
-                       status_t                        DownloadToLocalFile(
+                       status_t                        
DownloadToLocalFileAtomically(
                                                                        const 
BPath& targetFilePath,
-                                                                       const 
BUrl& url,
-                                                                       uint32 
redirects, uint32 failures);
+                                                                       const 
BUrl& url);
+
+                       status_t                        DeleteLocalFile(const 
BPath& currentFilePath);
 
                        status_t                        MoveDamagedFileAside(
                                                                        const 
BPath& currentFilePath);
 
+                       bool                            HasOption(uint32 flag);
+                       bool                            
ShouldAttemptNetworkDownload(
+                                                                       bool 
hasDataAlready);
+
+       static  bool                            IsSuccess(status_t e);
+
 private:
+                       BLocker                         fLock;
+                       AbstractServerProcessListener*
+                                                               fListener;
+                       bool                            fWasStopped;
+                       process_state           fProcessState;
+                       status_t                        fErrorStatus;
+                       uint32                          fOptions;
+
+                       BHttpRequest*           fRequest;
+
+                       process_state           ProcessState();
+                       void                            SetErrorStatus(status_t 
value);
+                       void                            
SetProcessState(process_state value);
+
+                       status_t                        DownloadToLocalFile(
+                                                                       const 
BPath& targetFilePath,
+                                                                       const 
BUrl& url,
+                                                                       uint32 
redirects, uint32 failures);
+
                        bool                            LooksLikeGzip(const 
char *pathStr) const;
 
 };
diff --git a/src/apps/haikudepot/server/PkgDataUpdateProcess.cpp 
b/src/apps/haikudepot/server/PkgDataUpdateProcess.cpp
index 961f8a4..c5d1b23 100644
--- a/src/apps/haikudepot/server/PkgDataUpdateProcess.cpp
+++ b/src/apps/haikudepot/server/PkgDataUpdateProcess.cpp
@@ -11,6 +11,7 @@
 
 #include <Autolock.h>
 #include <FileIO.h>
+#include <support/StopWatch.h>
 #include <Url.h>
 
 #include "Logger.h"
@@ -27,39 +28,46 @@
     packages as they are parsed and processing them.
 */
 
-class PackageFillingPkgListener : public DumpExportPkgListener {
+class PackageFillingPkgListener :
+       public DumpExportPkgListener, public PackageConsumer {
 public:
-                                                               
PackageFillingPkgListener(
-                                                                       const 
PackageList& packages,
-                                                                       const 
CategoryList& categories,
-                                                                       
BLocker& lock);
+                                                               
PackageFillingPkgListener(Model *model,
+                                                                       
BString& depotName, Stoppable* stoppable);
        virtual                                         
~PackageFillingPkgListener();
 
-       virtual void                            Handle(DumpExportPkg* item);
+       virtual bool                            ConsumePackage(const 
PackageInfoRef& package,
+                                                                       void 
*context);
+       virtual bool                            Handle(DumpExportPkg* item);
        virtual void                            Complete();
 
+                       uint32                          Count();
+
 private:
                        int32                           
IndexOfPackageByName(const BString& name) const;
                        int32                           IndexOfCategoryByName(
                                                                        const 
BString& name) const;
                        int32                           IndexOfCategoryByCode(
                                                                        const 
BString& code) const;
-       const   PackageList&            fPackages;
-       const   CategoryList&           fCategories;
-                       BLocker&                        fLock;
-
 
+                       BString                         fDepotName;
+                       Model*                          fModel;
+                       CategoryList            fCategories;
+                       Stoppable*                      fStoppable;
+                       uint32                          fCount;
+                       bool                            fDebugEnabled;
 };
 
 
-PackageFillingPkgListener::PackageFillingPkgListener(
-       const PackageList& packages, const CategoryList& categories,
-       BLocker& lock)
+PackageFillingPkgListener::PackageFillingPkgListener(Model* model,
+       BString& depotName, Stoppable* stoppable)
        :
-       fPackages(packages),
-       fCategories(categories),
-       fLock(lock)
+       fDepotName(depotName),
+       fModel(model),
+       fStoppable(stoppable),
+       fCount(0),
+       fDebugEnabled(Logger::IsDebugEnabled())
 {
+       fCategories = model->Categories();
 }
 
 
@@ -70,16 +78,17 @@ PackageFillingPkgListener::~PackageFillingPkgListener()
 
        // TODO; performance could be improved by not needing the linear search
 
-int32
-PackageFillingPkgListener::IndexOfPackageByName(
+inline int32
+PackageFillingPkgListener::IndexOfCategoryByName(
        const BString& name) const
 {
        int32 i;
+       int32 categoryCount = fCategories.CountItems();
 
-       for (i = 0; i < fPackages.CountItems(); i++) {
-               const PackageInfoRef& packageInfo = fPackages.ItemAt(i);
+       for (i = 0; i < categoryCount; i++) {
+               const CategoryRef categoryRef = fCategories.ItemAtFast(i);
 
-               if (packageInfo->Name() == name)
+               if (categoryRef->Name() == name)
                        return i;
        }
 
@@ -87,100 +96,95 @@ PackageFillingPkgListener::IndexOfPackageByName(
 }
 
 
-       // TODO; performance could be improved by not needing the linear search
-
-int32
-PackageFillingPkgListener::IndexOfCategoryByName(
-       const BString& name) const
+bool
+PackageFillingPkgListener::ConsumePackage(const PackageInfoRef& package,
+       void *context)
 {
+       DumpExportPkg* pkg = static_cast<DumpExportPkg*>(context);
        int32 i;
 
-       for (i = 0; i < fCategories.CountItems(); i++) {
-               const CategoryRef categoryRef = fCategories.ItemAtFast(i);
+       if (0 != pkg->CountPkgVersions()) {
 
-               if (categoryRef->Name() == name)
-                       return i;
-       }
+                       // this makes the assumption that the only version will 
be the
+                       // latest one.
 
-       return -1;
-}
+               DumpExportPkgVersion* pkgVersion = pkg->PkgVersionsItemAt(0);
 
+               if (!pkgVersion->TitleIsNull())
+                       package->SetTitle(*(pkgVersion->Title()));
 
-void
-PackageFillingPkgListener::Handle(DumpExportPkg* pkg)
-{
-       BAutolock locker(fLock); // lock from the model.
-       int32 packageIndex = IndexOfPackageByName(*(pkg->Name()));
+               if (!pkgVersion->SummaryIsNull())
+                       package->SetShortDescription(*(pkgVersion->Summary()));
 
-       if (packageIndex == -1) {
-               printf("unable to find package data for pkg name [%s]\n",
-                       pkg->Name()->String());
-       } else {
-               int32 i;
-               const PackageInfoRef& packageInfo = 
fPackages.ItemAt(packageIndex);
+               if (!pkgVersion->DescriptionIsNull())
+                       
package->SetFullDescription(*(pkgVersion->Description()));
 
-               if (0 != pkg->CountPkgVersions()) {
+               if (!pkgVersion->PayloadLengthIsNull())
+                       package->SetSize(pkgVersion->PayloadLength());
+       }
 
-                               // this makes the assumption that the only 
version will be the
-                               // latest one.
+       int32 countPkgCategories = pkg->CountPkgCategories();
 
-                       DumpExportPkgVersion* pkgVersion = 
pkg->PkgVersionsItemAt(0);
+       for (i = 0; i < countPkgCategories; i++) {
+               BString* categoryCode = pkg->PkgCategoriesItemAt(i)->Code();
+               int categoryIndex = IndexOfCategoryByName(*(categoryCode));
 
-                       if (!pkgVersion->TitleIsNull())
-                               packageInfo->SetTitle(*(pkgVersion->Title()));
+               if (categoryIndex == -1) {
+                       printf("unable to find the category for [%s]\n",
+                               categoryCode->String());
+               } else {
+                       package->AddCategory(
+                               fCategories.ItemAtFast(categoryIndex));
+               }
+       }
 
-                       if (!pkgVersion->SummaryIsNull())
-                               
packageInfo->SetShortDescription(*(pkgVersion->Summary()));
+       if (!pkg->DerivedRatingIsNull()) {
+               RatingSummary summary;
+               summary.averageRating = pkg->DerivedRating();
+               package->SetRatingSummary(summary);
+       }
 
-                       if (!pkgVersion->DescriptionIsNull()) {
-                               
packageInfo->SetFullDescription(*(pkgVersion->Description()));
-                       }
+       if (!pkg->ProminenceOrderingIsNull())
+               package->SetProminence(pkg->ProminenceOrdering());
 
-                       if (!pkgVersion->PayloadLengthIsNull())
-                               
packageInfo->SetSize(pkgVersion->PayloadLength());
-               }
+       if (!pkg->PkgChangelogContentIsNull())
+               package->SetChangelog(*(pkg->PkgChangelogContent()));
 
-               for (i = 0; i < pkg->CountPkgCategories(); i++) {
-                       BString* categoryCode = 
pkg->PkgCategoriesItemAt(i)->Code();
-                       int categoryIndex = 
IndexOfCategoryByName(*(categoryCode));
-
-                       if (categoryIndex == -1) {
-                               printf("unable to find the category for [%s]\n",
-                                       categoryCode->String());
-                       } else {
-                               packageInfo->AddCategory(
-                                       fCategories.ItemAtFast(categoryIndex));
-                       }
-               }
+       int32 countPkgScreenshots = pkg->CountPkgScreenshots();
 
-               if (!pkg->DerivedRatingIsNull()) {
-                       RatingSummary summary;
-                       summary.averageRating = pkg->DerivedRating();
-                       packageInfo->SetRatingSummary(summary);
-               }
+       for (i = 0; i < countPkgScreenshots; i++) {
+               DumpExportPkgScreenshot* screenshot = 
pkg->PkgScreenshotsItemAt(i);
+               package->AddScreenshotInfo(ScreenshotInfo(
+                       *(screenshot->Code()),
+                       static_cast<int32>(screenshot->Width()),
+                       static_cast<int32>(screenshot->Height()),
+                       static_cast<int32>(screenshot->Length())
+               ));
+       }
 
-               if (!pkg->ProminenceOrderingIsNull()) {
-                       packageInfo->SetProminence(pkg->ProminenceOrdering());
-               }
+       if (fDebugEnabled) {
+               printf("did populate data for [%s] (%s)\n", 
pkg->Name()->String(),
+                       fDepotName.String());
+       }
 
-               if (!pkg->PkgChangelogContentIsNull()) {
-                       
packageInfo->SetChangelog(*(pkg->PkgChangelogContent()));
-               }
+       fCount++;
 
-               for (i = 0; i < pkg->CountPkgScreenshots(); i++) {
-                       DumpExportPkgScreenshot* screenshot = 
pkg->PkgScreenshotsItemAt(i);
-                       packageInfo->AddScreenshotInfo(ScreenshotInfo(
-                               *(screenshot->Code()),
-                               static_cast<int32>(screenshot->Width()),
-                               static_cast<int32>(screenshot->Height()),
-                               static_cast<int32>(screenshot->Length())
-                       ));
-               }
+       return !fStoppable->WasStopped();
+}
 
-               if (Logger::IsDebugEnabled()) {
-                       printf("did populate data for [%s]\n", 
pkg->Name()->String());
-               }
-       }
+
+uint32
+PackageFillingPkgListener::Count()
+{
+       return fCount;
+}
+
+
+bool
+PackageFillingPkgListener::Handle(DumpExportPkg* pkg)
+{
+       fModel->ForPackageByNameInDepot(fDepotName, *(pkg->Name()), this, pkg);
+       return !fStoppable->WasStopped();
 }
 
 
@@ -191,20 +195,22 @@ PackageFillingPkgListener::Complete()
 
 
 PkgDataUpdateProcess::PkgDataUpdateProcess(
+       AbstractServerProcessListener* listener,
        const BPath& localFilePath,
-       BLocker& lock,
-       BString repositorySourceCode,
        BString naturalLanguageCode,
-       const PackageList& packages,
-       const CategoryList& categories)
+       BString repositorySourceCode,
+       BString depotName,
+       Model *model,
+       uint32 options)
        :
+       AbstractSingleFileServerProcess(listener, options),
        fLocalFilePath(localFilePath),
        fNaturalLanguageCode(naturalLanguageCode),
        fRepositorySourceCode(repositorySourceCode),
-       fPackages(packages),
-       fCategories(categories),
-       fLock(lock)
+       fModel(model),
+       fDepotName(depotName)
 {
+       fName.SetToFormat("PkgDataUpdateProcess<%s>", depotName.String());
 }
 
 
@@ -213,53 +219,50 @@ PkgDataUpdateProcess::~PkgDataUpdateProcess()
 }
 
 
-status_t
-PkgDataUpdateProcess::Run()
+const char*
+PkgDataUpdateProcess::Name()
 {
-       printf("will fetch packages' data\n");
+       return fName.String();
+}
+
 
+BString
+PkgDataUpdateProcess::UrlPathComponent()
+{
        BString urlPath;
        urlPath.SetToFormat("/__pkg/all-%s-%s.json.gz",
                fRepositorySourceCode.String(),
                fNaturalLanguageCode.String());
+       return urlPath;
+}
 
-       status_t result = DownloadToLocalFile(fLocalFilePath,
-               ServerSettings::CreateFullUrl(urlPath),
-               0, 0);
-
-       if (result == B_OK || result == APP_ERR_NOT_MODIFIED) {
-               printf("did fetch packages' data\n");
-
-                       // now load the data in and process it.
-
-               printf("will process packages' data\n");
-               result = PopulateDataToDepots();
-
-               switch (result) {
-                       case B_OK:
-                               printf("did process packages' data\n");
-                               break;
-                       default:
-                               MoveDamagedFileAside(fLocalFilePath);
-                               break;
-               }
-       }
 
-       return result;
+BPath&
+PkgDataUpdateProcess::LocalPath()
+{
+       return fLocalFilePath;
 }
 
 
 status_t
-PkgDataUpdateProcess::PopulateDataToDepots()
+PkgDataUpdateProcess::ProcessLocalData()
 {
+       BStopWatch watch("PkgDataUpdateProcess::ProcessLocalData", true);
+
        PackageFillingPkgListener* itemListener =
-               new PackageFillingPkgListener(fPackages, fCategories, fLock);
+               new PackageFillingPkgListener(fModel, fDepotName, this);
 
        BulkContainerDumpExportPkgJsonListener* listener =
                new BulkContainerDumpExportPkgJsonListener(itemListener);
 
        status_t result = ParseJsonFromFileWithListener(listener, 
fLocalFilePath);
 
+       if (Logger::IsInfoEnabled()) {
+               double secs = watch.ElapsedTime() / 1000000.0;
+               fprintf(stdout, "[%s] did process %" B_PRIi32 " packages' data "
+                       "in  (%6.3g secs)\n", Name(), itemListener->Count(), 
secs);
+       }
+
        if (B_OK != result)
                return result;
 
@@ -280,10 +283,3 @@ PkgDataUpdateProcess::GetStandardMetaDataJsonPath(
 {
        jsonPath.SetTo("$.info");
 }
-
-
-const char*
-PkgDataUpdateProcess::LoggingName() const
-{
-       return "pkg-data-update";
-}
diff --git a/src/apps/haikudepot/server/PkgDataUpdateProcess.h 
b/src/apps/haikudepot/server/PkgDataUpdateProcess.h
index 749783a..7d1486a 100644
--- a/src/apps/haikudepot/server/PkgDataUpdateProcess.h
+++ b/src/apps/haikudepot/server/PkgDataUpdateProcess.h
@@ -7,8 +7,9 @@
 #define PACKAGE_DATA_UPDATE_PROCESS_H
 
 
-#include "AbstractServerProcess.h"
+#include "AbstractSingleFileServerProcess.h"
 
+#include "Model.h"
 #include "PackageInfo.h"
 
 #include <File.h>
@@ -17,34 +18,37 @@
 #include <Url.h>
 
 
-class PkgDataUpdateProcess : public AbstractServerProcess {
+class PkgDataUpdateProcess : public AbstractSingleFileServerProcess {
 public:
                                                                
PkgDataUpdateProcess(
+                                                                       
AbstractServerProcessListener* listener,
                                                                        const 
BPath& localFilePath,
-                                                                       
BLocker& lock,
                                                                        BString 
naturalLanguageCode,
                                                                        BString 
repositorySourceCode,
-                                                                       const 
PackageList& packages,
-                                                                       const 
CategoryList& categories);
+                                                                       BString 
depotName,
+                                                                       Model 
*model,
+                                                                       uint32 
options);
        virtual                                         ~PkgDataUpdateProcess();
 
-                       status_t                        Run();
+                       const char*                             Name();
 
 protected:
                        void                            
GetStandardMetaDataPath(BPath& path) const;
                        void                            
GetStandardMetaDataJsonPath(
                                                                        
BString& jsonPath) const;
-                       const char*                     LoggingName() const;
+
+                       BString                         UrlPathComponent();
+                       status_t                        ProcessLocalData();
+                       BPath&                          LocalPath();
 
 private:
-                       status_t                        PopulateDataToDepots();
 
                        BPath                           fLocalFilePath;
                        BString                         fNaturalLanguageCode;
                        BString                         fRepositorySourceCode;
-       const   PackageList&            fPackages;
-       const   CategoryList&           fCategories;
-                       BLocker&                        fLock;
+                       Model*                          fModel;
+                       BString                         fDepotName;
+                       BString                         fName;
 
 };
 
diff --git a/src/apps/haikudepot/server/RepositoryDataUpdateProcess.cpp 
b/src/apps/haikudepot/server/RepositoryDataUpdateProcess.cpp
index 3218075..bbd0eac 100644
--- a/src/apps/haikudepot/server/RepositoryDataUpdateProcess.cpp
+++ b/src/apps/haikudepot/server/RepositoryDataUpdateProcess.cpp
@@ -14,6 +14,7 @@
 
 #include "ServerSettings.h"
 #include "StorageUtils.h"
+#include "Logger.h"
 #include "DumpExportRepository.h"
 #include "DumpExportRepositorySource.h"
 #include "DumpExportRepositoryJsonListener.h"
@@ -25,13 +26,15 @@
     from the server with the data about the depot.
 */
 
-class DepotMatchingRepositoryListener : public DumpExportRepositoryListener {
+class DepotMatchingRepositoryListener :
+       public DumpExportRepositoryListener, public DepotMapper {
 public:
-                                                               
DepotMatchingRepositoryListener(
-                                                                       
DepotList* depotList);
+                                                               
DepotMatchingRepositoryListener(Model* model,
+                                                                       
Stoppable* stoppable);
        virtual                                         
~DepotMatchingRepositoryListener();
 
-       virtual void                            Handle(DumpExportRepository* 
item);
+       virtual DepotInfo                       MapDepot(const DepotInfo& 
depot, void *context);
+       virtual bool                            Handle(DumpExportRepository* 
item);
        virtual void                            Complete();
 
 private:
@@ -43,15 +46,17 @@ private:
                        int32                           
IndexOfUnassociatedDepotByUrl(
                                                                        const 
BString& url) const;
 
-                       DepotList*                      fDepotList;
-
+                       Model*                          fModel;
+                       Stoppable*                      fStoppable;
 };
 
 
 DepotMatchingRepositoryListener::DepotMatchingRepositoryListener(
-       DepotList* depotList)
+       Model* model, Stoppable* stoppable)
+       :
+       fModel(model),
+       fStoppable(stoppable)
 {
-       fDepotList = depotList;
 }
 
 
@@ -60,110 +65,86 @@ 
DepotMatchingRepositoryListener::~DepotMatchingRepositoryListener()
 }
 
 
-void
-DepotMatchingRepositoryListener::NormalizeUrl(BUrl& url) const
-{
-       if (url.Protocol() == "https")
-               url.SetProtocol("http");
-
-       BString path(url.Path());
-
-       if (path.EndsWith("/"))
-               url.SetPath(path.Truncate(path.Length() - 1));
-}
-
-
-bool
-DepotMatchingRepositoryListener::IsUnassociatedDepotByUrl(
-       const DepotInfo& depotInfo, const BString& urlStr) const
-{
-       if (depotInfo.BaseURL().Length() > 0
-               && depotInfo.WebAppRepositorySourceCode().Length() == 0) {
-               BUrl depotInfoBaseUrl(depotInfo.BaseURL());
-               BUrl url(urlStr);
-
-               NormalizeUrl(depotInfoBaseUrl);
-               NormalizeUrl(url);
-
-               if (depotInfoBaseUrl == url)
-                       return true;
-       }
+struct repository_and_repository_source {
+       DumpExportRepository* repository;
+       DumpExportRepositorySource* repositorySource;
+};
 
-       return false;
-}
 
+/*! This is invoked as a result of logic in 'Handle(..)' that requests that the
+    model call this method with the requested DepotInfo instance.
+*/
 
-int32
-DepotMatchingRepositoryListener::IndexOfUnassociatedDepotByUrl(
-       const BString& url) const
+DepotInfo
+DepotMatchingRepositoryListener::MapDepot(const DepotInfo& depot, void 
*context)
 {
-       int32 i;
-
-       for (i = 0; i < fDepotList->CountItems(); i++) {
-               const DepotInfo& depot = fDepotList->ItemAt(i);
-
-               if (IsUnassociatedDepotByUrl(depot, url))
-                       return i;
+       repository_and_repository_source* repositoryAndRepositorySource =
+               (repository_and_repository_source*) context;
+       BString* repositoryCode =
+               repositoryAndRepositorySource->repository->Code();
+       BString* repositorySourceCode =
+               repositoryAndRepositorySource->repositorySource->Code();
+
+       DepotInfo modifiedDepotInfo(depot);
+       modifiedDepotInfo.SetWebAppRepositoryCode(BString(*repositoryCode));
+       modifiedDepotInfo.SetWebAppRepositorySourceCode(
+               BString(*repositorySourceCode));
+
+       if (Logger::IsDebugEnabled()) {
+               printf("associated dept [%s] (%s) with server repository "
+                       "source [%s] (%s)\n", modifiedDepotInfo.Name().String(),
+                       modifiedDepotInfo.BaseURL().String(),
+                       repositorySourceCode->String(),
+                       
repositoryAndRepositorySource->repositorySource->Url()->String());
+       } else {
+               printf("associated depot [%s] with server repository source 
[%s]\n",
+                       modifiedDepotInfo.Name().String(), 
repositorySourceCode->String());
        }
 
-       return -1;
+       return modifiedDepotInfo;
 }
 
 
-void
+bool
 DepotMatchingRepositoryListener::Handle(DumpExportRepository* repository)
 {
        int32 i;
 
        for (i = 0; i < repository->CountRepositorySources(); i++) {
-               DumpExportRepositorySource *repositorySource =
+               repository_and_repository_source repositoryAndRepositorySource;
+               repositoryAndRepositorySource.repository = repository;
+               repositoryAndRepositorySource.repositorySource =
                        repository->RepositorySourcesItemAt(i);
-               int32 depotIndex = IndexOfUnassociatedDepotByUrl(
-                       *(repositorySource->Url()));
-
-               if (depotIndex >= 0) {
-                       DepotInfo 
modifiedDepotInfo(fDepotList->ItemAt(depotIndex));
-                       modifiedDepotInfo.SetWebAppRepositoryCode(
-                               BString(*(repository->Code())));
-                       modifiedDepotInfo.SetWebAppRepositorySourceCode(
-                               BString(*(repositorySource->Code())));
-                       fDepotList->Replace(depotIndex, modifiedDepotInfo);
-
-                       printf("associated depot [%s] with server"
-                               " repository source [%s]\n", 
modifiedDepotInfo.Name().String(),
-                               repositorySource->Code()->String());
+
+               // TODO; replace with the repo info url
+               BString* url = 
repositoryAndRepositorySource.repositorySource->Url();
+
+               if (url->Length() > 0) {
+                       fModel->ReplaceDepotByUrl(*url, this,
+                               &repositoryAndRepositorySource);
                }
        }
+
+       return !fStoppable->WasStopped();
 }
 
 
 void
 DepotMatchingRepositoryListener::Complete()
 {
-       int32 i;
-
-       for (i = 0; i < fDepotList->CountItems(); i++) {
-               const DepotInfo& depot = fDepotList->ItemAt(i);
-
-               if (depot.WebAppRepositoryCode().Length() == 0) {
-                       printf("depot [%s]", depot.Name().String());
-
-                       if (depot.BaseURL().Length() > 0)
-                               printf(" (%s)", depot.BaseURL().String());
-
-                       printf(" correlates with no repository in the haiku"
-                               "depot server system\n");
-               }
-       }
 }
 
 
 RepositoryDataUpdateProcess::RepositoryDataUpdateProcess(
+       AbstractServerProcessListener* listener,
        const BPath& localFilePath,
-       DepotList* depotList)
+       Model* model,
+       uint32 options)
+       :
+       AbstractSingleFileServerProcess(listener, options),
+       fLocalFilePath(localFilePath),
+       fModel(model)
 {
-       fLocalFilePath = localFilePath;
-       fDepotList = depotList;
 }
 
 
@@ -172,46 +153,32 @@ 
RepositoryDataUpdateProcess::~RepositoryDataUpdateProcess()
 }
 
 
-status_t
-RepositoryDataUpdateProcess::Run()
+const char*
+RepositoryDataUpdateProcess::Name()
 {
-       printf("will fetch repositories data\n");
-
-               // TODO: add language ISO code to the path; just 'en' for now.
-       status_t result = DownloadToLocalFile(fLocalFilePath,
-               ServerSettings::CreateFullUrl("/__repository/all-en.json.gz"),
-               0, 0);
-
-       if (result == B_OK || result == APP_ERR_NOT_MODIFIED) {
-               printf("did fetch repositories data\n");
-
-                       // now load the data in and process it.
+       return "RepositoryDataUpdateProcess";
+}
 
-               printf("will process repository data and match to depots\n");
-               result = PopulateDataToDepots();
 
-               switch (result) {
-                       case B_OK:
-                               printf("did process repository data and match 
to depots\n");
-                               break;
-                       default:
-                               MoveDamagedFileAside(fLocalFilePath);
-                               break;
-               }
+BString
+RepositoryDataUpdateProcess::UrlPathComponent()
+{
+       return BString("/__repository/all-en.json.gz");
+}
 
-       } else {
-               printf("an error has arisen downloading the repositories' 
data\n");
-       }
 
-       return result;
+BPath&
+RepositoryDataUpdateProcess::LocalPath()
+{
+       return fLocalFilePath;
 }
 
 
 status_t
-RepositoryDataUpdateProcess::PopulateDataToDepots()
+RepositoryDataUpdateProcess::ProcessLocalData()
 {
        DepotMatchingRepositoryListener* itemListener =
-               new DepotMatchingRepositoryListener(fDepotList);
+               new DepotMatchingRepositoryListener(fModel, this);
 
        BulkContainerDumpExportRepositoryJsonListener* listener =
                new BulkContainerDumpExportRepositoryJsonListener(itemListener);
@@ -238,10 +205,3 @@ RepositoryDataUpdateProcess::GetStandardMetaDataJsonPath(
 {
        jsonPath.SetTo("$.info");
 }
-
-
-const char*
-RepositoryDataUpdateProcess::LoggingName() const
-{
-       return "repo-data-update";
-}
\ No newline at end of file
diff --git a/src/apps/haikudepot/server/RepositoryDataUpdateProcess.h 
b/src/apps/haikudepot/server/RepositoryDataUpdateProcess.h
index 1226a2a..5bc4890 100644
--- a/src/apps/haikudepot/server/RepositoryDataUpdateProcess.h
+++ b/src/apps/haikudepot/server/RepositoryDataUpdateProcess.h
@@ -7,8 +7,9 @@
 #define REPOSITORY_DATA_UPDATE_PROCESS_H
 
 
-#include "AbstractServerProcess.h"
+#include "AbstractSingleFileServerProcess.h"
 
+#include "Model.h"
 #include "PackageInfo.h"
 
 #include <File.h>
@@ -17,27 +18,29 @@
 #include <Url.h>
 
 
-class RepositoryDataUpdateProcess : public AbstractServerProcess {
+class RepositoryDataUpdateProcess : public AbstractSingleFileServerProcess {
 public:
 
                                                                
RepositoryDataUpdateProcess(
+                                                                       
AbstractServerProcessListener* listener,
                                                                        const 
BPath& localFilePath,
-                                                                       
DepotList* depotList);
+                                                                       Model* 
model, uint32 options);
        virtual                                         
~RepositoryDataUpdateProcess();
 
-                       status_t                        Run();
+                       const char*                             Name();
 
 protected:
                        void                            
GetStandardMetaDataPath(BPath& path) const;
                        void                            
GetStandardMetaDataJsonPath(
                                                                        
BString& jsonPath) const;
-                       const char*                     LoggingName() const;
 
-private:
-                       status_t                        PopulateDataToDepots();
+                       BString                         UrlPathComponent();
+                       status_t                        ProcessLocalData();
+                       BPath&                          LocalPath();
 
+private:
                        BPath                           fLocalFilePath;
-                       DepotList*                      fDepotList;
+                       Model*                          fModel;
 
 };
 
diff --git a/src/apps/haikudepot/server/ServerIconExportUpdateProcess.cpp 
b/src/apps/haikudepot/server/ServerIconExportUpdateProcess.cpp
index af74cbf..5d434c9 100644
--- a/src/apps/haikudepot/server/ServerIconExportUpdateProcess.cpp
+++ b/src/apps/haikudepot/server/ServerIconExportUpdateProcess.cpp
@@ -9,9 +9,12 @@
 #include <sys/stat.h>
 #include <time.h>
 
+#include <Autolock.h>
 #include <FileIO.h>
+#include <support/StopWatch.h>
 #include <support/ZlibCompressionAlgorithm.h>
 
+#include "Logger.h"
 #include "ServerSettings.h"
 #include "StorageUtils.h"
 #include "TarArchiveService.h"
@@ -20,9 +23,17 @@
 /*! This constructor will locate the cached data in a standardized location */
 
 ServerIconExportUpdateProcess::ServerIconExportUpdateProcess(
-       const BPath& localStorageDirectoryPath)
+       AbstractServerProcessListener* listener,
+       const BPath& localStorageDirectoryPath,
+       Model* model,
+       uint32 options)
+       :
+       AbstractServerProcess(listener, options),
+       fLocalStorageDirectoryPath(localStorageDirectoryPath),
+       fModel(model),
+       fLocalIconStore(LocalIconStore(localStorageDirectoryPath)),
+       fCountIconsSet(0)
 {
-       fLocalStorageDirectoryPath = localStorageDirectoryPath;
 }
 
 
@@ -31,8 +42,106 @@ 
ServerIconExportUpdateProcess::~ServerIconExportUpdateProcess()
 }
 
 
+const char*
+ServerIconExportUpdateProcess::Name()
+{
+       return "ServerIconExportUpdateProcess";
+}
+
+
+status_t
+ServerIconExportUpdateProcess::RunInternal()
+{
+       status_t result = B_OK;
+
+       if (IsSuccess(result) && HasOption(SERVER_PROCESS_DROP_CACHE)) {
+               result = StorageUtils::RemoveDirectoryContents(
+                       fLocalStorageDirectoryPath);
+       }
+
+       if (IsSuccess(result)) {
+               bool hasData;
+
+               result = HasLocalData(&hasData);
+
+               if (IsSuccess(result) && ShouldAttemptNetworkDownload(hasData))
+                       result = DownloadAndUnpack();
+
+               if (IsSuccess(result)) {
+                       status_t hasDataResult = HasLocalData(&hasData);
+
+                       if (IsSuccess(hasDataResult) && !hasData)
+                               result = APP_ERR_NO_DATA;
+               }
+       }
+
+       if (IsSuccess(result) && !WasStopped())
+               result = Populate();
+
+       return result;
+}
+
+
+status_t
+ServerIconExportUpdateProcess::PopulateForPkg(const PackageInfoRef& package)
+{
+       BPath bestIconPath;
+
+       if ( fLocalIconStore.TryFindIconPath(
+               package->Name(), bestIconPath) == B_OK) {
+
+               BFile bestIconFile(bestIconPath.Path(), O_RDONLY);
+               BitmapRef 
bitmapRef(new(std::nothrow)SharedBitmap(bestIconFile), true);
+               // TODO; somehow handle the locking!
+               //BAutolock locker(&fLock);
+               package->SetIcon(bitmapRef);
+
+               if (Logger::IsDebugEnabled()) {
+                       fprintf(stdout, "have set the package icon for [%s] 
from [%s]\n",
+                               package->Name().String(), bestIconPath.Path());
+               }
+
+               fCountIconsSet++;
+
+               return B_OK;
+       }
+
+       if (Logger::IsDebugEnabled()) {
+               fprintf(stdout, "did not set the package icon for [%s]; no 
data\n",
+                       package->Name().String());
+       }
+
+       return B_FILE_NOT_FOUND;
+}
+
+
+bool
+ServerIconExportUpdateProcess::ConsumePackage(
+       const PackageInfoRef& packageInfoRef, void* context)
+{
+       PopulateForPkg(packageInfoRef);
+       return !WasStopped();
+}
+
+
 status_t
-ServerIconExportUpdateProcess::Run()
+ServerIconExportUpdateProcess::Populate()
+{
+       BStopWatch watch("ServerIconExportUpdateProcess::Populate", true);
+       fModel->ForAllPackages(this, NULL);
+
+       if (Logger::IsInfoEnabled()) {
+               double secs = watch.ElapsedTime() / 1000000.0;
+               fprintf(stdout, "did populate %" B_PRId32 " packages' icons"
+                       " (%6.3g secs)\n", fCountIconsSet, secs);
+       }
+
+       return B_OK;
+}
+
+
+status_t
+ServerIconExportUpdateProcess::DownloadAndUnpack()
 {
        BPath tarGzFilePath(tmpnam(NULL));
        status_t result = B_OK;
@@ -59,10 +168,15 @@ ServerIconExportUpdateProcess::Run()
                            zlibDecompressionParameters, tarIn);
 
                if (result == B_OK) {
+                       BStopWatch 
watch("ServerIconExportUpdateProcess::DownloadAndUnpack_Unpack", true);
+
                        result = TarArchiveService::Unpack(*tarIn,
-                           fLocalStorageDirectoryPath);
+                           fLocalStorageDirectoryPath, NULL);
 
                        if (result == B_OK) {
+                               double secs = watch.ElapsedTime() / 1000000.0;
+                               fprintf(stdout, "did unpack icon tgz in (%6.3g 
secs)\n", secs);
+
                                if (0 != remove(tarGzFilePath.Path())) {
                                        fprintf(stdout, "unable to delete the 
temporary tgz path; "
                                            "%s\n", tarGzFilePath.Path());
@@ -79,6 +193,17 @@ ServerIconExportUpdateProcess::Run()
 }
 
 
+status_t
+ServerIconExportUpdateProcess::HasLocalData(bool* result) const
+{
+       BPath path;
+       off_t size;
+       GetStandardMetaDataPath(path);
+       return StorageUtils::ExistsObject(path, result, NULL, &size)
+               && size > 0;
+}
+
+
 void
 ServerIconExportUpdateProcess::GetStandardMetaDataPath(BPath& path) const
 {
@@ -96,16 +221,9 @@ ServerIconExportUpdateProcess::GetStandardMetaDataJsonPath(
 }
 
 
-const char*
-ServerIconExportUpdateProcess::LoggingName() const
-{
-       return "icon-export-update";
-}
-
-
 status_t
 ServerIconExportUpdateProcess::Download(BPath& tarGzFilePath)
 {
-       return DownloadToLocalFile(tarGzFilePath,
-               ServerSettings::CreateFullUrl("/__pkgicon/all.tar.gz"), 0, 0);
+       return DownloadToLocalFileAtomically(tarGzFilePath,
+               ServerSettings::CreateFullUrl("/__pkgicon/all.tar.gz"));
 }
\ No newline at end of file
diff --git a/src/apps/haikudepot/server/ServerIconExportUpdateProcess.h 
b/src/apps/haikudepot/server/ServerIconExportUpdateProcess.h
index f91c806..ceb9c83 100644
--- a/src/apps/haikudepot/server/ServerIconExportUpdateProcess.h
+++ b/src/apps/haikudepot/server/ServerIconExportUpdateProcess.h
@@ -8,6 +8,8 @@
 
 
 #include "AbstractServerProcess.h"
+#include "LocalIconStore.h"
+#include "Model.h"
 
 #include <File.h>
 #include <Path.h>
@@ -15,26 +17,37 @@
 #include <Url.h>
 
 
-class ServerIconExportUpdateProcess : public AbstractServerProcess {
+class ServerIconExportUpdateProcess :
+       public AbstractServerProcess, public PackageConsumer {
 public:
 
                                                                
ServerIconExportUpdateProcess(
-                                                                       const 
BPath& localStorageDirectoryPath);
+                                                                       
AbstractServerProcessListener* listener,
+                                                                       const 
BPath& localStorageDirectoryPath,
+                                                                       Model* 
model, uint32 options);
        virtual                                         
~ServerIconExportUpdateProcess();
 
-                       status_t                        Run();
+                       const char*                             Name();
+                       status_t                        RunInternal();
 
+       virtual bool                            ConsumePackage(
+                                                                       const 
PackageInfoRef& packageInfoRef,
+                                                                       void 
*context);
 protected:
+                       status_t                        PopulateForPkg(const 
PackageInfoRef& package);
+                       status_t                        Populate();
+                       status_t                        DownloadAndUnpack();
+                       status_t                        HasLocalData(bool* 
result) const;
                        void                            
GetStandardMetaDataPath(BPath& path) const;
                        void                            
GetStandardMetaDataJsonPath(
                                                                        
BString& jsonPath) const;
-                       const char*                     LoggingName() const;
-
-
 private:
                        status_t                        Download(BPath& 
tarGzFilePath);
 
                        BPath                           
fLocalStorageDirectoryPath;
+                       Model*                          fModel;
+                       LocalIconStore          fLocalIconStore;
+                       int32                           fCountIconsSet;
 
 };
 
diff --git a/src/apps/haikudepot/server/ServerSettings.cpp 
b/src/apps/haikudepot/server/ServerSettings.cpp
index c3009bd..043fee4 100644
--- a/src/apps/haikudepot/server/ServerSettings.cpp
+++ b/src/apps/haikudepot/server/ServerSettings.cpp
@@ -21,6 +21,9 @@
 BUrl ServerSettings::sBaseUrl = BUrl(BASEURL_DEFAULT);
 BString ServerSettings::sUserAgent = BString();
 pthread_once_t ServerSettings::sUserAgentInitOnce = PTHREAD_ONCE_INIT;
+bool ServerSettings::sPreferCache = false;
+bool ServerSettings::sDropCache = false;
+bool ServerSettings::sForceNoNetwork = false;
 
 
 status_t
@@ -102,6 +105,7 @@ ServerSettings::_GetUserAgentVersionString()
        return result;
 }
 
+
 void
 ServerSettings::AugmentHeaders(BHttpHeaders& headers)
 {
@@ -109,3 +113,43 @@ ServerSettings::AugmentHeaders(BHttpHeaders& headers)
 }
 
 
+bool
+ServerSettings::PreferCache()
+{
+       return sPreferCache;
+}
+
+
+void
+ServerSettings::SetPreferCache(bool value)
+{
+       sPreferCache = value;
+}
+
+
+bool
+ServerSettings::DropCache()
+{
+       return sDropCache;
+}
+
+
+void
+ServerSettings::SetDropCache(bool value)
+{
+       sDropCache = value;
+}
+
+
+bool
+ServerSettings::ForceNoNetwork()
+{
+       return sForceNoNetwork;
+}

[ *** diff truncated: 2336 lines dropped *** ]



Other related posts: