1 /* 2 * Copyright 2017-2021, Andrew Lindesay <apl@lindesay.co.nz>. 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "ServerPkgDataUpdateProcess.h" 8 9 #include <stdio.h> 10 #include <sys/stat.h> 11 #include <time.h> 12 13 #include <AutoDeleter.h> 14 #include <AutoLocker.h> 15 #include <Catalog.h> 16 #include <FileIO.h> 17 #include <support/StopWatch.h> 18 #include <Url.h> 19 20 #include "Logger.h" 21 #include "ServerSettings.h" 22 #include "StorageUtils.h" 23 #include "DumpExportPkg.h" 24 #include "DumpExportPkgCategory.h" 25 #include "DumpExportPkgJsonListener.h" 26 #include "DumpExportPkgScreenshot.h" 27 #include "DumpExportPkgVersion.h" 28 #include "HaikuDepotConstants.h" 29 30 31 #undef B_TRANSLATION_CONTEXT 32 #define B_TRANSLATION_CONTEXT "ServerPkgDataUpdateProcess" 33 34 35 /*! This package listener (not at the JSON level) is feeding in the 36 packages as they are parsed and processing them. 37 */ 38 39 class PackageFillingPkgListener : public DumpExportPkgListener { 40 public: 41 PackageFillingPkgListener(Model *model, 42 BString& depotName, Stoppable* stoppable); 43 virtual ~PackageFillingPkgListener(); 44 45 virtual bool ConsumePackage(const PackageInfoRef& package, 46 DumpExportPkg* pkg); 47 virtual bool Handle(DumpExportPkg* item); 48 virtual void Complete(); 49 50 uint32 Count(); 51 52 private: 53 int32 IndexOfPackageByName(const BString& name) const; 54 55 private: 56 BString fDepotName; 57 Model* fModel; 58 std::vector<CategoryRef> 59 fCategories; 60 Stoppable* fStoppable; 61 uint32 fCount; 62 bool fDebugEnabled; 63 }; 64 65 66 PackageFillingPkgListener::PackageFillingPkgListener(Model* model, 67 BString& depotName, Stoppable* stoppable) 68 : 69 fDepotName(depotName), 70 fModel(model), 71 fStoppable(stoppable), 72 fCount(0), 73 fDebugEnabled(Logger::IsDebugEnabled()) 74 { 75 } 76 77 78 PackageFillingPkgListener::~PackageFillingPkgListener() 79 { 80 } 81 82 83 bool 84 PackageFillingPkgListener::ConsumePackage(const PackageInfoRef& package, 85 DumpExportPkg* pkg) 86 { 87 int32 i; 88 89 // Collects all of the changes here into one set of notifications to 90 // the package's listeners. This way the quantity of BMessages 91 // communicated back to listeners is considerably reduced. See stop 92 // invocation later in this method. 93 94 package->StartCollatingChanges(); 95 96 if (0 != pkg->CountPkgVersions()) { 97 98 // this makes the assumption that the only version will be the 99 // latest one. 100 101 DumpExportPkgVersion* pkgVersion = pkg->PkgVersionsItemAt(0); 102 103 if (!pkgVersion->TitleIsNull()) 104 package->SetTitle(*(pkgVersion->Title())); 105 106 if (!pkgVersion->SummaryIsNull()) 107 package->SetShortDescription(*(pkgVersion->Summary())); 108 109 if (!pkgVersion->DescriptionIsNull()) 110 package->SetFullDescription(*(pkgVersion->Description())); 111 112 if (!pkgVersion->PayloadLengthIsNull()) 113 package->SetSize(pkgVersion->PayloadLength()); 114 } 115 116 int32 countPkgCategories = pkg->CountPkgCategories(); 117 118 for (i = 0; i < countPkgCategories; i++) { 119 BString* categoryCode = pkg->PkgCategoriesItemAt(i)->Code(); 120 CategoryRef category = fModel->CategoryByCode(*categoryCode); 121 122 if (!category.IsSet()) { 123 HDERROR("unable to find the category for [%s]", 124 categoryCode->String()); 125 } else 126 package->AddCategory(category); 127 } 128 129 RatingSummary summary; 130 summary.averageRating = RATING_MISSING; 131 132 if (!pkg->DerivedRatingIsNull()) 133 summary.averageRating = pkg->DerivedRating(); 134 135 package->SetRatingSummary(summary); 136 137 package->SetHasChangelog(pkg->HasChangelog()); 138 139 if (!pkg->ProminenceOrderingIsNull()) 140 package->SetProminence(pkg->ProminenceOrdering()); 141 142 int32 countPkgScreenshots = pkg->CountPkgScreenshots(); 143 144 for (i = 0; i < countPkgScreenshots; i++) { 145 DumpExportPkgScreenshot* screenshot = pkg->PkgScreenshotsItemAt(i); 146 package->AddScreenshotInfo(ScreenshotInfoRef(new ScreenshotInfo( 147 *(screenshot->Code()), 148 static_cast<int32>(screenshot->Width()), 149 static_cast<int32>(screenshot->Height()), 150 static_cast<int32>(screenshot->Length()) 151 ), true)); 152 } 153 154 HDDEBUG("did populate data for [%s] (%s)", pkg->Name()->String(), 155 fDepotName.String()); 156 157 fCount++; 158 159 package->EndCollatingChanges(); 160 161 return !fStoppable->WasStopped(); 162 } 163 164 165 uint32 166 PackageFillingPkgListener::Count() 167 { 168 return fCount; 169 } 170 171 172 bool 173 PackageFillingPkgListener::Handle(DumpExportPkg* pkg) 174 { 175 AutoLocker<BLocker> locker(fModel->Lock()); 176 DepotInfoRef depot = fModel->DepotForName(fDepotName); 177 178 if (depot.Get() != NULL) { 179 const BString packageName = *(pkg->Name()); 180 PackageInfoRef package = depot->PackageByName(packageName); 181 if (package.Get() != NULL) 182 ConsumePackage(package, pkg); 183 else { 184 HDINFO("[PackageFillingPkgListener] unable to find the pkg [%s]", 185 packageName.String()); 186 } 187 } else { 188 HDINFO("[PackageFillingPkgListener] unable to find the depot [%s]", 189 fDepotName.String()); 190 } 191 192 return !fStoppable->WasStopped(); 193 } 194 195 196 void 197 PackageFillingPkgListener::Complete() 198 { 199 } 200 201 202 ServerPkgDataUpdateProcess::ServerPkgDataUpdateProcess( 203 BString naturalLanguageCode, 204 BString depotName, 205 Model *model, 206 uint32 serverProcessOptions) 207 : 208 AbstractSingleFileServerProcess(serverProcessOptions), 209 fNaturalLanguageCode(naturalLanguageCode), 210 fModel(model), 211 fDepotName(depotName) 212 { 213 fName.SetToFormat("ServerPkgDataUpdateProcess<%s>", depotName.String()); 214 fDescription.SetTo( 215 B_TRANSLATE("Synchronizing package data for repository " 216 "'%REPO_NAME%'")); 217 fDescription.ReplaceAll("%REPO_NAME%", depotName.String()); 218 } 219 220 221 ServerPkgDataUpdateProcess::~ServerPkgDataUpdateProcess() 222 { 223 } 224 225 226 const char* 227 ServerPkgDataUpdateProcess::Name() const 228 { 229 return fName.String(); 230 } 231 232 233 const char* 234 ServerPkgDataUpdateProcess::Description() const 235 { 236 return fDescription.String(); 237 } 238 239 240 BString 241 ServerPkgDataUpdateProcess::UrlPathComponent() 242 { 243 BString urlPath; 244 urlPath.SetToFormat("/__pkg/all-%s-%s.json.gz", 245 _DeriveWebAppRepositorySourceCode().String(), 246 fNaturalLanguageCode.String()); 247 return urlPath; 248 } 249 250 251 status_t 252 ServerPkgDataUpdateProcess::GetLocalPath(BPath& path) const 253 { 254 BString webAppRepositorySourceCode = _DeriveWebAppRepositorySourceCode(); 255 256 if (!webAppRepositorySourceCode.IsEmpty()) { 257 AutoLocker<BLocker> locker(fModel->Lock()); 258 return fModel->DumpExportPkgDataPath(path, webAppRepositorySourceCode); 259 } 260 261 return B_ERROR; 262 } 263 264 265 status_t 266 ServerPkgDataUpdateProcess::ProcessLocalData() 267 { 268 BStopWatch watch("ServerPkgDataUpdateProcess::ProcessLocalData", true); 269 270 PackageFillingPkgListener* itemListener = 271 new PackageFillingPkgListener(fModel, fDepotName, this); 272 ObjectDeleter<PackageFillingPkgListener> 273 itemListenerDeleter(itemListener); 274 275 BulkContainerDumpExportPkgJsonListener* listener = 276 new BulkContainerDumpExportPkgJsonListener(itemListener); 277 ObjectDeleter<BulkContainerDumpExportPkgJsonListener> 278 listenerDeleter(listener); 279 280 BPath localPath; 281 status_t result = GetLocalPath(localPath); 282 283 if (result != B_OK) 284 return result; 285 286 result = ParseJsonFromFileWithListener(listener, localPath); 287 288 if (B_OK != result) 289 return result; 290 291 if (Logger::IsInfoEnabled()) { 292 double secs = watch.ElapsedTime() / 1000000.0; 293 HDINFO("[%s] did process %" B_PRIi32 " packages' data " 294 "in (%6.3g secs)", Name(), itemListener->Count(), secs); 295 } 296 297 return listener->ErrorStatus(); 298 } 299 300 301 status_t 302 ServerPkgDataUpdateProcess::GetStandardMetaDataPath(BPath& path) const 303 { 304 return GetLocalPath(path); 305 } 306 307 308 void 309 ServerPkgDataUpdateProcess::GetStandardMetaDataJsonPath( 310 BString& jsonPath) const 311 { 312 jsonPath.SetTo("$.info"); 313 } 314 315 316 BString 317 ServerPkgDataUpdateProcess::_DeriveWebAppRepositorySourceCode() const 318 { 319 const DepotInfo* depot = fModel->DepotForName(fDepotName); 320 321 if (depot == NULL) { 322 return BString(); 323 } 324 325 return depot->WebAppRepositorySourceCode(); 326 } 327 328 329 status_t 330 ServerPkgDataUpdateProcess::RunInternal() 331 { 332 if (_DeriveWebAppRepositorySourceCode().IsEmpty()) { 333 HDINFO("[%s] am not updating data for depot [%s] as there is no" 334 " web app repository source code available", 335 Name(), fDepotName.String()); 336 return B_OK; 337 } 338 339 return AbstractSingleFileServerProcess::RunInternal(); 340 } 341