* Copyright 2017-2021, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "ServerPkgDataUpdateProcess.h"
#include <stdio.h>
#include <sys/stat.h>
#include <time.h>
#include <AutoDeleter.h>
#include <AutoLocker.h>
#include <Catalog.h>
#include <FileIO.h>
#include <support/StopWatch.h>
#include <Url.h>
#include "Logger.h"
#include "ServerSettings.h"
#include "StorageUtils.h"
#include "DumpExportPkg.h"
#include "DumpExportPkgCategory.h"
#include "DumpExportPkgJsonListener.h"
#include "DumpExportPkgScreenshot.h"
#include "DumpExportPkgVersion.h"
#include "HaikuDepotConstants.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ServerPkgDataUpdateProcess"
packages as they are parsed and processing them.
*/
class PackageFillingPkgListener : public DumpExportPkgListener {
public:
PackageFillingPkgListener(Model *model,
BString& depotName, Stoppable* stoppable);
virtual ~PackageFillingPkgListener();
virtual bool ConsumePackage(const PackageInfoRef& package,
DumpExportPkg* pkg);
virtual bool Handle(DumpExportPkg* item);
virtual void Complete();
uint32 Count();
private:
int32 IndexOfPackageByName(const BString& name) const;
private:
BString fDepotName;
Model* fModel;
std::vector<CategoryRef>
fCategories;
Stoppable* fStoppable;
uint32 fCount;
bool fDebugEnabled;
};
PackageFillingPkgListener::PackageFillingPkgListener(Model* model,
BString& depotName, Stoppable* stoppable)
:
fDepotName(depotName),
fModel(model),
fStoppable(stoppable),
fCount(0),
fDebugEnabled(Logger::IsDebugEnabled())
{
}
PackageFillingPkgListener::~PackageFillingPkgListener()
{
}
bool
PackageFillingPkgListener::ConsumePackage(const PackageInfoRef& package,
DumpExportPkg* pkg)
{
int32 i;
package->StartCollatingChanges();
if (0 != pkg->CountPkgVersions()) {
DumpExportPkgVersion* pkgVersion = pkg->PkgVersionsItemAt(0);
if (!pkgVersion->TitleIsNull())
package->SetTitle(*(pkgVersion->Title()));
if (!pkgVersion->SummaryIsNull())
package->SetShortDescription(*(pkgVersion->Summary()));
if (!pkgVersion->DescriptionIsNull())
package->SetFullDescription(*(pkgVersion->Description()));
if (!pkgVersion->PayloadLengthIsNull())
package->SetSize(static_cast<off_t>(pkgVersion->PayloadLength()));
if (!pkgVersion->CreateTimestampIsNull())
package->SetVersionCreateTimestamp(pkgVersion->CreateTimestamp());
}
int32 countPkgCategories = pkg->CountPkgCategories();
for (i = 0; i < countPkgCategories; i++) {
BString* categoryCode = pkg->PkgCategoriesItemAt(i)->Code();
CategoryRef category = fModel->CategoryByCode(*categoryCode);
if (!category.IsSet()) {
HDERROR("unable to find the category for [%s]",
categoryCode->String());
} else
package->AddCategory(category);
}
RatingSummary summary;
summary.averageRating = RATING_MISSING;
if (!pkg->DerivedRatingIsNull())
summary.averageRating = pkg->DerivedRating();
package->SetRatingSummary(summary);
package->SetHasChangelog(pkg->HasChangelog());
if (!pkg->ProminenceOrderingIsNull())
package->SetProminence(pkg->ProminenceOrdering());
int32 countPkgScreenshots = pkg->CountPkgScreenshots();
for (i = 0; i < countPkgScreenshots; i++) {
DumpExportPkgScreenshot* screenshot = pkg->PkgScreenshotsItemAt(i);
package->AddScreenshotInfo(ScreenshotInfoRef(new ScreenshotInfo(
*(screenshot->Code()),
static_cast<int32>(screenshot->Width()),
static_cast<int32>(screenshot->Height()),
static_cast<int32>(screenshot->Length())
), true));
}
HDDEBUG("did populate data for [%s] (%s)", pkg->Name()->String(),
fDepotName.String());
fCount++;
package->EndCollatingChanges();
return !fStoppable->WasStopped();
}
uint32
PackageFillingPkgListener::Count()
{
return fCount;
}
bool
PackageFillingPkgListener::Handle(DumpExportPkg* pkg)
{
AutoLocker<BLocker> locker(fModel->Lock());
DepotInfoRef depot = fModel->DepotForName(fDepotName);
if (depot.Get() != NULL) {
const BString packageName = *(pkg->Name());
PackageInfoRef package = depot->PackageByName(packageName);
if (package.Get() != NULL)
ConsumePackage(package, pkg);
else {
HDINFO("[PackageFillingPkgListener] unable to find the pkg [%s]",
packageName.String());
}
} else {
HDINFO("[PackageFillingPkgListener] unable to find the depot [%s]",
fDepotName.String());
}
return !fStoppable->WasStopped();
}
void
PackageFillingPkgListener::Complete()
{
}
ServerPkgDataUpdateProcess::ServerPkgDataUpdateProcess(
BString naturalLanguageCode,
BString depotName,
Model *model,
uint32 serverProcessOptions)
:
AbstractSingleFileServerProcess(serverProcessOptions),
fNaturalLanguageCode(naturalLanguageCode),
fModel(model),
fDepotName(depotName)
{
fName.SetToFormat("ServerPkgDataUpdateProcess<%s>", depotName.String());
fDescription.SetTo(
B_TRANSLATE("Synchronizing package data for repository "
"'%REPO_NAME%'"));
fDescription.ReplaceAll("%REPO_NAME%", depotName.String());
}
ServerPkgDataUpdateProcess::~ServerPkgDataUpdateProcess()
{
}
const char*
ServerPkgDataUpdateProcess::Name() const
{
return fName.String();
}
const char*
ServerPkgDataUpdateProcess::Description() const
{
return fDescription.String();
}
BString
ServerPkgDataUpdateProcess::UrlPathComponent()
{
BString urlPath;
urlPath.SetToFormat("/__pkg/all-%s-%s.json.gz",
_DeriveWebAppRepositorySourceCode().String(),
fNaturalLanguageCode.String());
return urlPath;
}
status_t
ServerPkgDataUpdateProcess::GetLocalPath(BPath& path) const
{
BString webAppRepositorySourceCode = _DeriveWebAppRepositorySourceCode();
if (!webAppRepositorySourceCode.IsEmpty()) {
AutoLocker<BLocker> locker(fModel->Lock());
return fModel->DumpExportPkgDataPath(path, webAppRepositorySourceCode);
}
return B_ERROR;
}
status_t
ServerPkgDataUpdateProcess::ProcessLocalData()
{
BStopWatch watch("ServerPkgDataUpdateProcess::ProcessLocalData", true);
PackageFillingPkgListener* itemListener =
new PackageFillingPkgListener(fModel, fDepotName, this);
ObjectDeleter<PackageFillingPkgListener>
itemListenerDeleter(itemListener);
BulkContainerDumpExportPkgJsonListener* listener =
new BulkContainerDumpExportPkgJsonListener(itemListener);
ObjectDeleter<BulkContainerDumpExportPkgJsonListener>
listenerDeleter(listener);
BPath localPath;
status_t result = GetLocalPath(localPath);
if (result != B_OK)
return result;
result = ParseJsonFromFileWithListener(listener, localPath);
if (B_OK != result)
return result;
if (Logger::IsInfoEnabled()) {
double secs = watch.ElapsedTime() / 1000000.0;
HDINFO("[%s] did process %" B_PRIi32 " packages' data "
"in (%6.3g secs)", Name(), itemListener->Count(), secs);
}
return listener->ErrorStatus();
}
status_t
ServerPkgDataUpdateProcess::GetStandardMetaDataPath(BPath& path) const
{
return GetLocalPath(path);
}
void
ServerPkgDataUpdateProcess::GetStandardMetaDataJsonPath(
BString& jsonPath) const
{
jsonPath.SetTo("$.info");
}
BString
ServerPkgDataUpdateProcess::_DeriveWebAppRepositorySourceCode() const
{
const DepotInfo* depot = fModel->DepotForName(fDepotName);
if (depot == NULL) {
return BString();
}
return depot->WebAppRepositorySourceCode();
}
status_t
ServerPkgDataUpdateProcess::RunInternal()
{
if (_DeriveWebAppRepositorySourceCode().IsEmpty()) {
HDINFO("[%s] am not updating data for depot [%s] as there is no"
" web app repository source code available",
Name(), fDepotName.String());
return B_OK;
}
return AbstractSingleFileServerProcess::RunInternal();
}