xref: /haiku/src/apps/haikudepot/process/ProcessCoordinatorFactory.cpp (revision 909af08f4328301fbdef1ffb41f566c3b5bec0c7)
1 /*
2  * Copyright 2018-2024, Andrew Lindesay <apl@lindesay.co.nz>.
3  * All rights reserved. Distributed under the terms of the MIT License.
4  */
5 #include "ProcessCoordinatorFactory.h"
6 
7 #include <Autolock.h>
8 #include <AutoLocker.h>
9 
10 #include <package/Context.h>
11 #include <package/PackageRoster.h>
12 
13 #include "AbstractServerProcess.h"
14 #include "CacheScreenshotProcess.h"
15 #include "DeskbarLink.h"
16 #include "HaikuDepotConstants.h"
17 #include "IncrementViewCounterProcess.h"
18 #include "InstallPackageProcess.h"
19 #include "LocalPkgDataLoadProcess.h"
20 #include "LocalRepositoryUpdateProcess.h"
21 #include "Logger.h"
22 #include "Model.h"
23 #include "OpenPackageProcess.h"
24 #include "PackageInfoListener.h"
25 #include "PopulatePkgChangelogFromServerProcess.h"
26 #include "PopulatePkgSizesProcess.h"
27 #include "PopulatePkgUserRatingsFromServerProcess.h"
28 #include "ProcessCoordinator.h"
29 #include "ServerHelper.h"
30 #include "ServerIconExportUpdateProcess.h"
31 #include "ServerPkgDataUpdateProcess.h"
32 #include "ServerReferenceDataUpdateProcess.h"
33 #include "ServerRepositoryDataUpdateProcess.h"
34 #include "ServerSettings.h"
35 #include "StorageUtils.h"
36 #include "ThreadedProcessNode.h"
37 #include "UninstallPackageProcess.h"
38 #include "UserDetailVerifierProcess.h"
39 
40 
41 using namespace BPackageKit;
42 
43 
44 /*static*/ ProcessCoordinator*
45 ProcessCoordinatorFactory::CreateIncrementViewCounter(
46 	Model* model, const PackageInfoRef package)
47 {
48 	ProcessCoordinator* processCoordinator = new ProcessCoordinator(
49 		"IncrementViewCounter");
50 	AbstractProcessNode* node = new ThreadedProcessNode(
51 		new IncrementViewCounterProcess(model, package));
52 	processCoordinator->AddNode(node);
53 	return processCoordinator;
54 }
55 
56 
57 /*static*/ ProcessCoordinator*
58 ProcessCoordinatorFactory::CreateUserDetailVerifierCoordinator(
59 	UserDetailVerifierListener* userDetailVerifierListener,
60 	Model* model)
61 {
62 	ProcessCoordinator* processCoordinator = new ProcessCoordinator(
63 		"UserDetailVerifier");
64 	AbstractProcessNode* userDetailVerifier = new ThreadedProcessNode(
65 		new UserDetailVerifierProcess(model, userDetailVerifierListener));
66 	processCoordinator->AddNode(userDetailVerifier);
67 	return processCoordinator;
68 }
69 
70 
71 /* static */ ProcessCoordinator*
72 ProcessCoordinatorFactory::CreateBulkLoadCoordinator(
73 	PackageInfoListenerRef packageInfoListener,
74 	Model* model, bool forceLocalUpdate)
75 {
76 	bool areWorkingFilesAvailable = StorageUtils::AreWorkingFilesAvailable();
77 	uint32 serverProcessOptions = _CalculateServerProcessOptions();
78 	BAutolock locker(model->Lock());
79 	ProcessCoordinator* processCoordinator = new ProcessCoordinator(
80 		"BulkLoad", new BMessage(MSG_BULK_LOAD_DONE));
81 
82 	AbstractProcessNode *localRepositoryUpdate =
83 		new ThreadedProcessNode(new LocalRepositoryUpdateProcess(model,
84 			forceLocalUpdate));
85 	processCoordinator->AddNode(localRepositoryUpdate);
86 
87 	AbstractProcessNode *localPkgDataLoad =
88 		new ThreadedProcessNode(new LocalPkgDataLoadProcess(
89 			packageInfoListener, model, forceLocalUpdate));
90 	localPkgDataLoad->AddPredecessor(localRepositoryUpdate);
91 	processCoordinator->AddNode(localPkgDataLoad);
92 
93 	if (areWorkingFilesAvailable) {
94 		AbstractProcessNode *serverIconExportUpdate =
95 			new ThreadedProcessNode(new ServerIconExportUpdateProcess(model,
96 				serverProcessOptions));
97 		serverIconExportUpdate->AddPredecessor(localPkgDataLoad);
98 		processCoordinator->AddNode(serverIconExportUpdate);
99 
100 		AbstractProcessNode *serverRepositoryDataUpdate =
101 			new ThreadedProcessNode(new ServerRepositoryDataUpdateProcess(model,
102 				serverProcessOptions));
103 		serverRepositoryDataUpdate->AddPredecessor(localPkgDataLoad);
104 		processCoordinator->AddNode(serverRepositoryDataUpdate);
105 
106 		AbstractProcessNode *serverReferenceDataUpdate =
107 			new ThreadedProcessNode(new ServerReferenceDataUpdateProcess(model,
108 				serverProcessOptions));
109 		processCoordinator->AddNode(serverReferenceDataUpdate);
110 
111 		// This one has to run after the server data is taken up because it
112 		// will fill in the gaps based on local data that was not able to be
113 		// sourced from the server. It has all of the
114 		// `ServerPkgDataUpdateProcess` nodes configured as its predecessors.
115 
116 		AbstractProcessNode* populatePkgSizes =
117 			new ThreadedProcessNode(new PopulatePkgSizesProcess(model));
118 
119 		// create a process for each of the repositories that are configured on
120 		// the local system.  Later, only those that have a web-app repository
121 		// server code will be actually processed, but this means that the
122 		// creation of the 'processes' does not need to be dynamic as the
123 		// process coordinator runs.
124 
125 		BPackageRoster roster;
126 		BStringList repoNames;
127 		status_t repoNamesResult = roster.GetRepositoryNames(repoNames);
128 
129 		if (repoNamesResult == B_OK) {
130 			AutoLocker<BLocker> locker(model->Lock());
131 
132 			for (int32 i = 0; i < repoNames.CountStrings(); i++) {
133 				AbstractProcessNode* processNode = new ThreadedProcessNode(
134 					new ServerPkgDataUpdateProcess(
135 						repoNames.StringAt(i), model, serverProcessOptions));
136 				processNode->AddPredecessor(serverRepositoryDataUpdate);
137 				processNode->AddPredecessor(serverReferenceDataUpdate);
138 				processCoordinator->AddNode(processNode);
139 
140 				populatePkgSizes->AddPredecessor(processNode);
141 			}
142 		} else
143 			HDERROR("a problem has arisen getting the repository names.");
144 
145 		processCoordinator->AddNode(populatePkgSizes);
146 	}
147 
148 	return processCoordinator;
149 }
150 
151 
152 /*static*/ ProcessCoordinator*
153 ProcessCoordinatorFactory::CreatePackageActionCoordinator(
154 	Model* model, BMessage* message)
155 {
156 	switch (message->what) {
157 		case MSG_PKG_INSTALL:
158 			return _CreateInstallPackageActionCoordinator(model, message);
159 		case MSG_PKG_UNINSTALL:
160 			return _CreateUninstallPackageActionCoordinator(model, message);
161 		case MSG_PKG_OPEN:
162 			return _CreateOpenPackageActionCoordinator(model, message);
163 		default:
164 			HDFATAL("unexpected package action message what");
165 	}
166 }
167 
168 
169 /*static*/ ProcessCoordinator*
170 ProcessCoordinatorFactory::CacheScreenshotCoordinator(Model* model,
171 	ScreenshotCoordinate& screenshotCoordinate)
172 {
173 	return _CreateSingleProcessCoordinator("CacheScreenshot",
174 		new CacheScreenshotProcess(model, screenshotCoordinate));
175 }
176 
177 
178 /*static*/ ProcessCoordinator*
179 ProcessCoordinatorFactory::PopulatePkgChangelogCoordinator(Model* model, PackageInfoRef package)
180 {
181 	return _CreateSingleProcessCoordinator("PopulatePkgChangelog",
182 		new PopulatePkgChangelogFromServerProcess(package, model));
183 }
184 
185 
186 /*static*/ ProcessCoordinator*
187 ProcessCoordinatorFactory::PopulatePkgUserRatingsCoordinator(Model* model, PackageInfoRef package)
188 {
189 	return _CreateSingleProcessCoordinator("PopulatePkgUserRatings",
190 		new PopulatePkgUserRatingsFromServerProcess(package, model));
191 }
192 
193 
194 /*static*/ PackageInfoRef
195 ProcessCoordinatorFactory::_ExtractPackageFromMessage(
196 	Model* model, BMessage* message)
197 {
198 	BString pkgName;
199 	if (message->FindString(KEY_PACKAGE_NAME, &pkgName) != B_OK)
200 		HDFATAL("malformed message missing key [%s]", KEY_PACKAGE_NAME);
201 	return model->PackageForName(pkgName);
202 }
203 
204 
205 /*static*/ ProcessCoordinator*
206 ProcessCoordinatorFactory::_CreateInstallPackageActionCoordinator(
207 	Model* model, BMessage* message)
208 {
209 	ProcessCoordinator* processCoordinator = new ProcessCoordinator(
210 		"InstallPackage", new BMessage(MSG_PACKAGE_ACTION_DONE));
211 	PackageInfoRef package = _ExtractPackageFromMessage(model, message);
212 	if (package.IsSet()) {
213 		AbstractProcessNode *processNode =
214 			new ThreadedProcessNode(
215 				new InstallPackageProcess(package, model), 10);
216 		processCoordinator->AddNode(processNode);
217 	} else {
218 		HDERROR("unable to find the package");
219 	}
220 	return processCoordinator;
221 }
222 
223 
224 /*static*/ ProcessCoordinator*
225 ProcessCoordinatorFactory::_CreateUninstallPackageActionCoordinator(
226 	Model* model, BMessage* message)
227 {
228 	ProcessCoordinator* processCoordinator = new ProcessCoordinator(
229 		"UninstallPackage", new BMessage(MSG_PACKAGE_ACTION_DONE));
230 	PackageInfoRef package = _ExtractPackageFromMessage(model, message);
231 	if (package.IsSet()) {
232 		AbstractProcessNode *processNode =
233 			new ThreadedProcessNode(
234 				new UninstallPackageProcess(package, model), 10);
235 		processCoordinator->AddNode(processNode);
236 	} else {
237 		HDERROR("unable to find the package");
238 	}
239 	return processCoordinator;
240 }
241 
242 
243 /*static*/ ProcessCoordinator*
244 ProcessCoordinatorFactory::_CreateOpenPackageActionCoordinator(
245 	Model* model, BMessage* message)
246 {
247 	ProcessCoordinator* processCoordinator = new ProcessCoordinator(
248 		"OpenPackage", new BMessage(MSG_PACKAGE_ACTION_DONE));
249 	PackageInfoRef package = _ExtractPackageFromMessage(model, message);
250 	if (package.IsSet()) {
251 		BMessage deskbarLinkMessage;
252 		if (message->FindMessage(KEY_DESKBAR_LINK, &deskbarLinkMessage) != B_OK)
253 			HDFATAL("malformed message missing key [%s]", KEY_DESKBAR_LINK);
254 		DeskbarLink deskbarLink(&deskbarLinkMessage);
255 		AbstractProcessNode *processNode =
256 			new ThreadedProcessNode(new OpenPackageProcess(
257 				package, model, deskbarLink));
258 		processCoordinator->AddNode(processNode);
259 	} else {
260 		HDERROR("unable to find the package");
261 	}
262 	return processCoordinator;
263 }
264 
265 
266 /* static */ uint32
267 ProcessCoordinatorFactory::_CalculateServerProcessOptions()
268 {
269 	uint32 processOptions = 0;
270 
271 	if (ServerSettings::IsClientTooOld()) {
272 		HDINFO("bulk load proceeding without network communications "
273 			"because the client is too old");
274 		processOptions |= SERVER_PROCESS_NO_NETWORKING;
275 	}
276 
277 	if (!ServerHelper::IsNetworkAvailable())
278 		processOptions |= SERVER_PROCESS_NO_NETWORKING;
279 
280 	if (ServerSettings::PreferCache())
281 		processOptions |= SERVER_PROCESS_PREFER_CACHE;
282 
283 	if (ServerSettings::DropCache())
284 		processOptions |= SERVER_PROCESS_DROP_CACHE;
285 
286 	return processOptions;
287 }
288 
289 
290 /*static*/ ProcessCoordinator*
291 ProcessCoordinatorFactory::_CreateSingleProcessCoordinator(const char* name,
292 	AbstractProcess* process)
293 {
294 	ProcessCoordinator* processCoordinator = new ProcessCoordinator(name);
295 	AbstractProcessNode* cacheScreenshotNode = new ThreadedProcessNode(process);
296 	processCoordinator->AddNode(cacheScreenshotNode);
297 	return processCoordinator;
298 }
299