2 * Copyright 2017 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
10 #include "TaskLooper.h"
13 #include <MessageQueue.h>
14 #include <package/AddRepositoryRequest.h>
15 #include <package/DropRepositoryRequest.h>
16 #include <package/RefreshRepositoryRequest.h>
17 #include <package/PackageRoster.h>
18 #include <package/RepositoryConfig.h>
20 #include "constants.h"
24 #undef B_TRANSLATION_CONTEXT
25 #define B_TRANSLATION_CONTEXT "TaskLooper"
27 static const BString kLogResultIndicator
= "***";
28 static const BString kCompletedText
=
29 B_TRANSLATE_COMMENT("Completed", "Completed task status message");
30 static const BString kFailedText
=
31 B_TRANSLATE_COMMENT("Failed", "Failed task status message");
32 static const BString kAbortedText
=
33 B_TRANSLATE_COMMENT("Aborted", "Aborted task status message");
34 static const BString kDescriptionText
=
35 B_TRANSLATE_COMMENT("Description", "Failed task error description");
36 static const BString kDetailsText
=
37 B_TRANSLATE_COMMENT("Details", "Job log details header");
39 using BSupportKit::BJob
;
43 JobStateListener::JobStarted(BJob
* job
)
45 fJobLog
.Add(job
->Title());
50 JobStateListener::JobSucceeded(BJob
* job
)
52 BString
resultText(kLogResultIndicator
);
53 fJobLog
.Add(resultText
.Append(kCompletedText
));
58 JobStateListener::JobFailed(BJob
* job
)
60 BString
resultText(kLogResultIndicator
);
61 resultText
.Append(kFailedText
).Append(": ")
62 .Append(strerror(job
->Result()));
63 fJobLog
.Add(resultText
);
64 if (job
->ErrorString().Length() > 0) {
65 resultText
.SetTo(kLogResultIndicator
);
66 resultText
.Append(kDescriptionText
).Append(": ")
67 .Append(job
->ErrorString());
68 fJobLog
.Add(resultText
);
74 JobStateListener::JobAborted(BJob
* job
)
76 BString
resultText(kLogResultIndicator
);
77 resultText
.Append(kAbortedText
).Append(": ")
78 .Append(strerror(job
->Result()));
79 fJobLog
.Add(resultText
);
80 if (job
->ErrorString().Length() > 0) {
81 resultText
.SetTo(kLogResultIndicator
);
82 resultText
.Append(kDescriptionText
).Append(": ")
83 .Append(job
->ErrorString());
84 fJobLog
.Add(resultText
);
90 JobStateListener::GetJobLog()
92 return fJobLog
.Join("\n");
96 TaskLooper::TaskLooper(const BMessenger
& target
)
98 BLooper("TaskLooper"),
102 fMessenger
.SetTo(this);
107 TaskLooper::QuitRequested()
109 return MessageQueue()->IsEmpty();
114 TaskLooper::MessageReceived(BMessage
* message
)
116 switch (message
->what
)
121 status_t result
= message
->FindPointer(key_rowptr
, (void**)&rowItem
);
122 if (result
== B_OK
) {
123 // Check to make sure there isn't already an existing task for this
124 int16 queueCount
= fTaskQueue
.CountItems();
125 for (int16 index
= 0; index
<queueCount
; index
++) {
126 Task
* task
= fTaskQueue
.ItemAt(index
);
127 if (rowItem
== task
->rowItem
)
132 Task
* newTask
= new Task();
133 newTask
->rowItem
= rowItem
;
134 newTask
->name
= rowItem
->Name();
135 newTask
->resultName
= newTask
->name
;
136 if (rowItem
->IsEnabled()) {
137 newTask
->taskType
= DISABLE_REPO
;
138 newTask
->taskParam
= newTask
->name
;
140 newTask
->taskType
= ENABLE_REPO
;
141 newTask
->taskParam
= rowItem
->Url();
143 newTask
->owner
= this;
144 newTask
->fTimer
= NULL
;
146 // Add to queue and start
147 fTaskQueue
.AddItem(newTask
);
148 BString
threadName(newTask
->taskType
== ENABLE_REPO
?
149 "enable_task" : "disable_task");
150 newTask
->threadId
= spawn_thread(_DoTask
, threadName
.String(),
151 B_NORMAL_PRIORITY
, (void*)newTask
);
152 status_t threadResult
;
153 if (newTask
->threadId
< B_OK
)
154 threadResult
= B_ERROR
;
156 threadResult
= resume_thread(newTask
->threadId
);
157 if (threadResult
== B_OK
) {
158 newTask
->fTimer
= new TaskTimer(fMessenger
, newTask
);
159 newTask
->fTimer
->Start(newTask
->name
);
161 BMessage
reply(*message
);
162 reply
.what
= TASK_STARTED
;
163 reply
.AddInt16(key_count
, fTaskQueue
.CountItems());
164 fReplyTarget
.SendMessage(&reply
);
166 kill_thread(newTask
->threadId
);
168 if (threadResult
!= B_OK
) {
169 _RemoveAndDelete(newTask
);
176 case TASK_COMPLETED_WITH_ERRORS
:
180 status_t result
= message
->FindPointer(key_taskptr
, (void**)&task
);
181 if (result
== B_OK
&& fTaskQueue
.HasItem(task
)) {
182 task
->fTimer
->Stop(task
->resultName
);
183 BMessage
reply(message
->what
);
184 reply
.AddInt16(key_count
, fTaskQueue
.CountItems()-1);
185 reply
.AddPointer(key_rowptr
, task
->rowItem
);
186 if (message
->what
== TASK_COMPLETED_WITH_ERRORS
)
187 reply
.AddString(key_details
, task
->resultErrorDetails
);
188 if (task
->taskType
== ENABLE_REPO
189 && task
->name
.Compare(task
->resultName
) != 0) {
190 reply
.AddString(key_name
, task
->resultName
);
192 fReplyTarget
.SendMessage(&reply
);
193 _RemoveAndDelete(task
);
198 case TASK_KILL_REQUEST
:
201 status_t result
= message
->FindPointer(key_taskptr
, (void**)&task
);
202 if (result
== B_OK
&& fTaskQueue
.HasItem(task
)) {
203 kill_thread(task
->threadId
);
204 BMessage
reply(TASK_CANCELED
);
205 reply
.AddInt16(key_count
, fTaskQueue
.CountItems()-1);
206 reply
.AddPointer(key_rowptr
, task
->rowItem
);
207 fReplyTarget
.SendMessage(&reply
);
208 _RemoveAndDelete(task
);
217 TaskLooper::_RemoveAndDelete(Task
* task
)
219 fTaskQueue
.RemoveItem(task
);
221 task
->fTimer
->Lock();
222 task
->fTimer
->Quit();
230 TaskLooper::_DoTask(void* data
)
232 Task
* task
= (Task
*)data
;
233 BString errorDetails
, repoName("");
234 status_t returnResult
= B_OK
;
235 DecisionProvider decisionProvider
;
236 JobStateListener listener
;
237 switch (task
->taskType
)
241 BString
nameParam(task
->taskParam
);
242 BPackageKit::BContext
context(decisionProvider
, listener
);
243 BPackageKit::DropRepositoryRequest
dropRequest(context
, nameParam
);
244 status_t result
= dropRequest
.Process();
245 if (result
!= B_OK
) {
246 returnResult
= result
;
247 if (result
!= B_CANCELED
) {
248 errorDetails
.Append(B_TRANSLATE_COMMENT("There was an "
249 "error disabling the repository %name%",
250 "Error message, do not translate %name%"));
251 BString
nameString("\"");
252 nameString
.Append(nameParam
).Append("\"");
253 errorDetails
.ReplaceFirst("%name%", nameString
);
254 _AppendErrorDetails(errorDetails
, &listener
);
262 BString
urlParam(task
->taskParam
);
263 BPackageKit::BContext
context(decisionProvider
, listener
);
265 bool asUserRepository
= false;
266 // TODO does this ever change?
267 BPackageKit::AddRepositoryRequest
addRequest(context
, urlParam
,
269 status_t result
= addRequest
.Process();
270 if (result
!= B_OK
) {
271 returnResult
= result
;
272 if (result
!= B_CANCELED
) {
273 errorDetails
.Append(B_TRANSLATE_COMMENT("There was an "
274 "error enabling the repository %url%",
275 "Error message, do not translate %url%"));
276 errorDetails
.ReplaceFirst("%url%", urlParam
);
277 _AppendErrorDetails(errorDetails
, &listener
);
281 // Continue on to refresh repo cache
282 repoName
= addRequest
.RepositoryName();
283 BPackageKit::BPackageRoster roster
;
284 BPackageKit::BRepositoryConfig repoConfig
;
285 roster
.GetRepositoryConfig(repoName
, &repoConfig
);
286 BPackageKit::BRefreshRepositoryRequest
refreshRequest(context
,
288 result
= refreshRequest
.Process();
289 if (result
!= B_OK
) {
290 returnResult
= result
;
291 if (result
!= B_CANCELED
) {
292 errorDetails
.Append(B_TRANSLATE_COMMENT("There was an "
293 "error refreshing the repository cache for %name%",
294 "Error message, do not translate %name%"));
295 BString
nameString("\"");
296 nameString
.Append(repoName
).Append("\"");
297 errorDetails
.ReplaceFirst("%name%", nameString
);
298 _AppendErrorDetails(errorDetails
, &listener
);
304 // Report completion status
306 if (returnResult
== B_OK
) {
307 reply
.what
= TASK_COMPLETED
;
308 // Add the repo name if we need to update the list row value
309 if (task
->taskType
== ENABLE_REPO
)
310 task
->resultName
= repoName
;
311 } else if (returnResult
== B_CANCELED
)
312 reply
.what
= TASK_CANCELED
;
314 reply
.what
= TASK_COMPLETED_WITH_ERRORS
;
315 task
->resultErrorDetails
= errorDetails
;
316 if (task
->taskType
== ENABLE_REPO
)
317 task
->resultName
= repoName
;
319 reply
.AddPointer(key_taskptr
, task
);
320 task
->owner
->PostMessage(&reply
);
322 if (returnResult
== B_OK
|| returnResult
== B_CANCELED
) {
323 BString
degubDetails("Debug info:\n");
324 degubDetails
.Append(listener
.GetJobLog());
325 (new BAlert("debug", degubDetails
, "OK"))->Go(NULL
);
333 TaskLooper::_AppendErrorDetails(BString
& details
, JobStateListener
* listener
)
335 details
.Append("\n\n").Append(kDetailsText
).Append(":\n");
336 details
.Append(listener
->GetJobLog());