|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* FullTextSearch - Full text search framework for Nextcloud |
|
4
|
|
|
* |
|
5
|
|
|
* This file is licensed under the Affero General Public License version 3 or |
|
6
|
|
|
* later. See the COPYING file. |
|
7
|
|
|
* |
|
8
|
|
|
* @author Maxence Lange <[email protected]> |
|
9
|
|
|
* @copyright 2018 |
|
10
|
|
|
* @license GNU AGPL version 3 or any later version |
|
11
|
|
|
* |
|
12
|
|
|
* This program is free software: you can redistribute it and/or modify |
|
13
|
|
|
* it under the terms of the GNU Affero General Public License as |
|
14
|
|
|
* published by the Free Software Foundation, either version 3 of the |
|
15
|
|
|
* License, or (at your option) any later version. |
|
16
|
|
|
* |
|
17
|
|
|
* This program is distributed in the hope that it will be useful, |
|
18
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20
|
|
|
* GNU Affero General Public License for more details. |
|
21
|
|
|
* |
|
22
|
|
|
* You should have received a copy of the GNU Affero General Public License |
|
23
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
24
|
|
|
* |
|
25
|
|
|
*/ |
|
26
|
|
|
|
|
27
|
|
|
|
|
28
|
|
|
namespace OCA\FullTextSearch\Model; |
|
29
|
|
|
|
|
30
|
|
|
use OCA\FullTextSearch\Exceptions\RunnerAlreadyUpException; |
|
31
|
|
|
use OCA\FullTextSearch\Exceptions\TickDoesNotExistException; |
|
32
|
|
|
use OCA\FullTextSearch\Exceptions\TickIsNotAliveException; |
|
33
|
|
|
use OCA\FullTextSearch\Service\MiscService; |
|
34
|
|
|
use OCA\FullTextSearch\Service\RunningService; |
|
35
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
|
36
|
|
|
|
|
37
|
|
|
|
|
38
|
|
|
class Runner { |
|
39
|
|
|
|
|
40
|
|
|
const TICK_TTL = 1800; |
|
41
|
|
|
|
|
42
|
|
|
const TICK_MINIMUM = 2; |
|
43
|
|
|
const TICK_UPDATE = 10; |
|
44
|
|
|
const MEMORY_UPDATE = 5; |
|
45
|
|
|
|
|
46
|
|
|
const RESULT_TYPE_SUCCESS = 1; |
|
47
|
|
|
const RESULT_TYPE_WARNING = 4; |
|
48
|
|
|
const RESULT_TYPE_FAIL = 9; |
|
49
|
|
|
|
|
50
|
|
|
/** @var RunningService */ |
|
51
|
|
|
private $runningService; |
|
52
|
|
|
|
|
53
|
|
|
/** @var string */ |
|
54
|
|
|
private $source; |
|
55
|
|
|
|
|
56
|
|
|
// /** @var bool */ |
|
57
|
|
|
// private $strict = false; |
|
58
|
|
|
|
|
59
|
|
|
/** @var int */ |
|
60
|
|
|
private $tickId; |
|
61
|
|
|
|
|
62
|
|
|
/** @var ExtendedBase */ |
|
63
|
|
|
private $commandBase = null; |
|
64
|
|
|
|
|
65
|
|
|
/** @var OutputInterface */ |
|
66
|
|
|
private $outputInterface = null; |
|
67
|
|
|
|
|
68
|
|
|
/** @var array */ |
|
69
|
|
|
private $info = []; |
|
70
|
|
|
|
|
71
|
|
|
/** @var int */ |
|
72
|
|
|
private $oldTick = 0; |
|
73
|
|
|
|
|
74
|
|
|
/** @var string */ |
|
75
|
|
|
private $oldAction = ''; |
|
76
|
|
|
|
|
77
|
|
|
/** @var int */ |
|
78
|
|
|
private $ramUpdate = 0; |
|
79
|
|
|
|
|
80
|
|
|
/** @var int */ |
|
81
|
|
|
private $tickUpdate = 0; |
|
82
|
|
|
|
|
83
|
|
|
/** @var array */ |
|
84
|
|
|
private $methodOnKeyPress = []; |
|
85
|
|
|
|
|
86
|
|
|
/** @var array */ |
|
87
|
|
|
private $methodOnInfoUpdate = []; |
|
88
|
|
|
|
|
89
|
|
|
/** @var array */ |
|
90
|
|
|
private $methodOnIndexError = []; |
|
91
|
|
|
|
|
92
|
|
|
/** @var array */ |
|
93
|
|
|
private $methodOnIndexResult = []; |
|
94
|
|
|
|
|
95
|
|
|
/** @var bool */ |
|
96
|
|
|
private $paused = false; |
|
97
|
|
|
|
|
98
|
|
|
/** @var bool */ |
|
99
|
|
|
private $pauseRunning = false; |
|
100
|
|
|
|
|
101
|
|
|
/** @var array */ |
|
102
|
|
|
private $keys = ['nextStep' => 'n']; |
|
103
|
|
|
|
|
104
|
|
|
|
|
105
|
|
|
/** |
|
106
|
|
|
* Runner constructor. |
|
107
|
|
|
* |
|
108
|
|
|
* @param RunningService $runningService |
|
109
|
|
|
* @param string $source |
|
110
|
|
|
* @param array $keys |
|
111
|
|
|
*/ |
|
112
|
|
|
public function __construct(RunningService $runningService, $source, $keys = []) { |
|
113
|
|
|
$this->runningService = $runningService; |
|
114
|
|
|
$this->source = $source; |
|
115
|
|
|
|
|
116
|
|
|
if (sizeof($keys) > 0) { |
|
117
|
|
|
$this->keys = $keys; |
|
118
|
|
|
} |
|
119
|
|
|
} |
|
120
|
|
|
|
|
121
|
|
|
|
|
122
|
|
|
/** |
|
123
|
|
|
* @throws RunnerAlreadyUpException |
|
124
|
|
|
*/ |
|
125
|
|
|
public function start() { |
|
126
|
|
|
// $this->strict = $strict; |
|
127
|
|
|
$this->tickId = $this->runningService->start($this->source); |
|
128
|
|
|
} |
|
129
|
|
|
|
|
130
|
|
|
|
|
131
|
|
|
/** |
|
132
|
|
|
* @param string $action |
|
133
|
|
|
* @param bool $force |
|
134
|
|
|
* |
|
135
|
|
|
* @return string |
|
136
|
|
|
* @throws \Exception |
|
137
|
|
|
*/ |
|
138
|
|
|
public function updateAction($action = '', $force = false) { |
|
139
|
|
|
$n = ''; |
|
140
|
|
|
if (sizeof($this->methodOnKeyPress) > 0) { |
|
141
|
|
|
$n = fread(STDIN, 9999); |
|
142
|
|
|
if ($n !== '') { |
|
143
|
|
|
$n = substr($n, 0, 1); |
|
144
|
|
|
$this->keyPressed($n); |
|
145
|
|
|
} |
|
146
|
|
|
} |
|
147
|
|
|
|
|
148
|
|
|
if ($action === '') { |
|
149
|
|
|
return $n; |
|
150
|
|
|
} |
|
151
|
|
|
|
|
152
|
|
|
$tick = time(); |
|
153
|
|
|
// try { |
|
154
|
|
|
// $this->hasBeenInterrupted(); |
|
155
|
|
|
// } catch (InterruptException $e) { |
|
156
|
|
|
// $this->stop(); |
|
157
|
|
|
// throw $e; |
|
158
|
|
|
// } |
|
159
|
|
|
|
|
160
|
|
|
if ($this->oldAction !== $action || $force) { |
|
161
|
|
|
while (true) { |
|
162
|
|
|
if (!$this->isPaused()) { |
|
163
|
|
|
break; |
|
164
|
|
|
} |
|
165
|
|
|
|
|
166
|
|
|
$this->pauseRunning(true); |
|
167
|
|
|
$pressed = strtolower($this->updateAction('')); |
|
168
|
|
|
if ($pressed === $this->keys['nextStep']) { |
|
169
|
|
|
break; |
|
170
|
|
|
} |
|
171
|
|
|
usleep(300000); |
|
172
|
|
|
} |
|
173
|
|
|
|
|
174
|
|
|
$this->pauseRunning(false); |
|
175
|
|
|
} |
|
176
|
|
|
|
|
177
|
|
|
if ($this->oldAction === $action && ($this->oldTick + self::TICK_MINIMUM > $tick)) { |
|
178
|
|
|
return ''; |
|
179
|
|
|
} |
|
180
|
|
|
|
|
181
|
|
|
$this->setInfo('action', $action); |
|
182
|
|
|
|
|
183
|
|
|
$this->updateTick($tick, $action); |
|
184
|
|
|
$this->updateRamInfo($tick); |
|
185
|
|
|
|
|
186
|
|
|
$this->oldAction = $action; |
|
187
|
|
|
$this->oldTick = $tick; |
|
188
|
|
|
|
|
189
|
|
|
return ''; |
|
190
|
|
|
} |
|
191
|
|
|
|
|
192
|
|
|
|
|
193
|
|
|
/** |
|
194
|
|
|
* @param string $info |
|
195
|
|
|
* @param string $value |
|
196
|
|
|
* @param int $type |
|
197
|
|
|
*/ |
|
198
|
|
|
public function setInfo($info, $value, $type = 0) { |
|
199
|
|
|
$this->info[$info] = $value; |
|
200
|
|
|
$this->setInfoColored($info, $type); |
|
201
|
|
|
$this->infoUpdated(); |
|
202
|
|
|
} |
|
203
|
|
|
|
|
204
|
|
|
/** |
|
205
|
|
|
* @param array $data |
|
206
|
|
|
*/ |
|
207
|
|
|
public function setInfoArray($data) { |
|
208
|
|
|
$keys = array_keys($data); |
|
209
|
|
|
//$this->info['info'] = ''; |
|
210
|
|
|
foreach ($keys as $k) { |
|
211
|
|
|
$this->info[$k] = $data[$k]; |
|
212
|
|
|
} |
|
213
|
|
|
|
|
214
|
|
|
$this->infoUpdated(); |
|
215
|
|
|
} |
|
216
|
|
|
|
|
217
|
|
|
|
|
218
|
|
|
/** |
|
219
|
|
|
* @param string $info |
|
220
|
|
|
* @param int $level |
|
221
|
|
|
*/ |
|
222
|
|
|
public function setInfoColored($info, $level) { |
|
223
|
|
|
|
|
224
|
|
|
$value = $this->getInfo($info); |
|
225
|
|
|
if ($value === '') { |
|
226
|
|
|
return; |
|
227
|
|
|
} |
|
228
|
|
|
|
|
229
|
|
|
$color = ''; |
|
230
|
|
|
switch ($level) { |
|
231
|
|
|
case self::RESULT_TYPE_SUCCESS: |
|
232
|
|
|
$color = 'info'; |
|
233
|
|
|
break; |
|
234
|
|
|
|
|
235
|
|
|
case self::RESULT_TYPE_WARNING: |
|
236
|
|
|
$color = 'comment'; |
|
237
|
|
|
break; |
|
238
|
|
|
|
|
239
|
|
|
case self::RESULT_TYPE_FAIL: |
|
240
|
|
|
$color = 'error'; |
|
241
|
|
|
break; |
|
242
|
|
|
} |
|
243
|
|
|
|
|
244
|
|
|
if ($color !== '') { |
|
245
|
|
|
$this->info[$info . 'Colored'] = '<' . $color . '>' . $value . '</' . $color . '>'; |
|
246
|
|
|
} |
|
247
|
|
|
|
|
248
|
|
|
|
|
249
|
|
|
} |
|
250
|
|
|
|
|
251
|
|
|
/** |
|
252
|
|
|
* @return array |
|
253
|
|
|
*/ |
|
254
|
|
|
public function getInfoAll() { |
|
255
|
|
|
return $this->info; |
|
256
|
|
|
} |
|
257
|
|
|
|
|
258
|
|
|
|
|
259
|
|
|
/** |
|
260
|
|
|
* @param string $k |
|
261
|
|
|
* |
|
262
|
|
|
* @return string |
|
|
|
|
|
|
263
|
|
|
*/ |
|
264
|
|
|
public function getInfo($k) { |
|
265
|
|
|
return MiscService::get($k, $this->info, ''); |
|
266
|
|
|
} |
|
267
|
|
|
|
|
268
|
|
|
|
|
269
|
|
|
/** |
|
270
|
|
|
* @param array $method |
|
271
|
|
|
*/ |
|
272
|
|
|
public function onKeyPress($method) { |
|
273
|
|
|
$this->methodOnKeyPress[] = $method; |
|
274
|
|
|
} |
|
275
|
|
|
|
|
276
|
|
|
/** |
|
277
|
|
|
* @param $key |
|
278
|
|
|
*/ |
|
279
|
|
|
public function keyPressed($key) { |
|
280
|
|
|
foreach ($this->methodOnKeyPress as $method) { |
|
281
|
|
|
call_user_func($method, $key); |
|
282
|
|
|
} |
|
283
|
|
|
} |
|
284
|
|
|
|
|
285
|
|
|
|
|
286
|
|
|
/** |
|
287
|
|
|
* @param array $method |
|
288
|
|
|
*/ |
|
289
|
|
|
public function onInfoUpdate($method) { |
|
290
|
|
|
$this->methodOnInfoUpdate[] = $method; |
|
291
|
|
|
} |
|
292
|
|
|
|
|
293
|
|
|
|
|
294
|
|
|
public function infoUpdated() { |
|
295
|
|
|
foreach ($this->methodOnInfoUpdate as $method) { |
|
296
|
|
|
call_user_func($method, $this->info); |
|
297
|
|
|
} |
|
298
|
|
|
} |
|
299
|
|
|
|
|
300
|
|
|
|
|
301
|
|
|
/** |
|
302
|
|
|
* @param array $method |
|
303
|
|
|
*/ |
|
304
|
|
|
public function onNewIndexError($method) { |
|
305
|
|
|
$this->methodOnIndexError[] = $method; |
|
306
|
|
|
} |
|
307
|
|
|
|
|
308
|
|
|
/** |
|
309
|
|
|
* @param Index $index |
|
310
|
|
|
* @param string $message |
|
311
|
|
|
* @param string $class |
|
312
|
|
|
* @param int $sev |
|
313
|
|
|
*/ |
|
314
|
|
|
public function newIndexError($index, $message, $class = '', $sev = 3) { |
|
315
|
|
|
$error = [ |
|
316
|
|
|
'index' => $index, |
|
317
|
|
|
'message' => $message, |
|
318
|
|
|
'exception' => $class, |
|
319
|
|
|
'severity' => $sev |
|
320
|
|
|
]; |
|
321
|
|
|
|
|
322
|
|
|
foreach ($this->methodOnIndexError as $method) { |
|
323
|
|
|
call_user_func($method, $error); |
|
324
|
|
|
} |
|
325
|
|
|
} |
|
326
|
|
|
|
|
327
|
|
|
|
|
328
|
|
|
/** |
|
329
|
|
|
* @param array $method |
|
330
|
|
|
*/ |
|
331
|
|
|
public function onNewIndexResult($method) { |
|
332
|
|
|
$this->methodOnIndexResult[] = $method; |
|
333
|
|
|
} |
|
334
|
|
|
|
|
335
|
|
|
|
|
336
|
|
|
/** |
|
337
|
|
|
* @param Index $index |
|
338
|
|
|
* @param string $message |
|
339
|
|
|
* @param string $status |
|
340
|
|
|
* @param int $type |
|
341
|
|
|
*/ |
|
342
|
|
|
public function newIndexResult($index, $message, $status, $type) { |
|
343
|
|
|
$result = [ |
|
344
|
|
|
'index' => $index, |
|
345
|
|
|
'message' => $message, |
|
346
|
|
|
'status' => $status, |
|
347
|
|
|
'type' => $type |
|
348
|
|
|
]; |
|
349
|
|
|
|
|
350
|
|
|
foreach ($this->methodOnIndexResult as $method) { |
|
351
|
|
|
call_user_func($method, $result); |
|
352
|
|
|
} |
|
353
|
|
|
} |
|
354
|
|
|
|
|
355
|
|
|
|
|
356
|
|
|
// /** |
|
357
|
|
|
// * @throws InterruptException |
|
358
|
|
|
// */ |
|
359
|
|
|
// private function hasBeenInterrupted() { |
|
360
|
|
|
// if ($this->commandBase === null) { |
|
361
|
|
|
// return; |
|
362
|
|
|
// } |
|
363
|
|
|
// $this->commandBase->hasBeenInterrupted(); |
|
364
|
|
|
// } |
|
365
|
|
|
|
|
366
|
|
|
|
|
367
|
|
|
/** |
|
368
|
|
|
* @param int $tick |
|
369
|
|
|
* @param string $action |
|
370
|
|
|
* |
|
371
|
|
|
* @throws TickDoesNotExistException |
|
372
|
|
|
*/ |
|
373
|
|
|
private function updateTick($tick, $action) { |
|
374
|
|
|
if ($this->oldAction === $action && ($this->tickUpdate + self::TICK_UPDATE > $tick)) { |
|
375
|
|
|
return; |
|
376
|
|
|
} |
|
377
|
|
|
|
|
378
|
|
|
try { |
|
379
|
|
|
$this->runningService->update($this->tickId, $action); |
|
380
|
|
|
} catch (TickIsNotAliveException $e) { |
|
381
|
|
|
$this->output('Force Quit'); |
|
382
|
|
|
exit(); |
|
383
|
|
|
} |
|
384
|
|
|
|
|
385
|
|
|
$this->tickUpdate = $tick; |
|
386
|
|
|
} |
|
387
|
|
|
|
|
388
|
|
|
|
|
389
|
|
|
/** |
|
390
|
|
|
* @param $tick |
|
391
|
|
|
*/ |
|
392
|
|
|
private function updateRamInfo($tick) { |
|
393
|
|
|
if (($this->ramUpdate + self::MEMORY_UPDATE) > $tick) { |
|
394
|
|
|
return; |
|
395
|
|
|
} |
|
396
|
|
|
|
|
397
|
|
|
$this->setInfo('_memory', round((memory_get_usage() / 1024 / 1024)) . ' MB'); |
|
398
|
|
|
$this->ramUpdate = $tick; |
|
399
|
|
|
} |
|
400
|
|
|
|
|
401
|
|
|
|
|
402
|
|
|
/** |
|
403
|
|
|
* @deprecated - verifier l'interet !? |
|
404
|
|
|
* |
|
405
|
|
|
* @param $reason |
|
406
|
|
|
* @param $stop |
|
407
|
|
|
*/ |
|
408
|
|
|
public function exception($reason, $stop) { |
|
409
|
|
|
if (!$stop) { |
|
410
|
|
|
$this->output('Exception: ' . $reason); |
|
411
|
|
|
// TODO: feed an array of exceptions for log; |
|
412
|
|
|
} |
|
413
|
|
|
$this->runningService->exception($this->tickId, $reason, $stop); |
|
|
|
|
|
|
414
|
|
|
} |
|
415
|
|
|
|
|
416
|
|
|
|
|
417
|
|
|
/** |
|
418
|
|
|
* @throws TickDoesNotExistException |
|
419
|
|
|
*/ |
|
420
|
|
|
public function stop() { |
|
421
|
|
|
$this->runningService->stop($this->tickId); |
|
422
|
|
|
} |
|
423
|
|
|
|
|
424
|
|
|
|
|
425
|
|
|
/** |
|
426
|
|
|
* @param ExtendedBase $base |
|
427
|
|
|
* @param OutputInterface $output |
|
428
|
|
|
*/ |
|
429
|
|
|
public function sourceIsCommandLine(ExtendedBase $base, OutputInterface $output) { |
|
430
|
|
|
$this->outputInterface = $output; |
|
431
|
|
|
$this->commandBase = $base; |
|
432
|
|
|
} |
|
433
|
|
|
|
|
434
|
|
|
|
|
435
|
|
|
/** |
|
436
|
|
|
* @param bool $pause |
|
437
|
|
|
*/ |
|
438
|
|
|
public function pause($pause) { |
|
439
|
|
|
$this->paused = $pause; |
|
440
|
|
|
$this->infoUpdated(); |
|
441
|
|
|
} |
|
442
|
|
|
|
|
443
|
|
|
/** |
|
444
|
|
|
* @return bool |
|
445
|
|
|
*/ |
|
446
|
|
|
public function isPaused() { |
|
447
|
|
|
return $this->paused; |
|
448
|
|
|
} |
|
449
|
|
|
|
|
450
|
|
|
|
|
451
|
|
|
/** |
|
452
|
|
|
* @param bool $running |
|
453
|
|
|
*/ |
|
454
|
|
|
public function pauseRunning($running) { |
|
455
|
|
|
$this->pauseRunning = $running; |
|
456
|
|
|
$this->infoUpdated(); |
|
457
|
|
|
} |
|
458
|
|
|
|
|
459
|
|
|
|
|
460
|
|
|
public function isPauseRunning() { |
|
461
|
|
|
return $this->pauseRunning; |
|
462
|
|
|
} |
|
463
|
|
|
|
|
464
|
|
|
|
|
465
|
|
|
// /** |
|
466
|
|
|
// * @return bool |
|
467
|
|
|
// */ |
|
468
|
|
|
// public function isStrict() { |
|
469
|
|
|
// return $this->strict; |
|
470
|
|
|
// } |
|
471
|
|
|
|
|
472
|
|
|
/** |
|
473
|
|
|
* @param string $line |
|
474
|
|
|
*/ |
|
475
|
|
|
public function output($line) { |
|
476
|
|
|
if ($this->outputInterface === null) { |
|
477
|
|
|
return; |
|
478
|
|
|
} |
|
479
|
|
|
|
|
480
|
|
|
$this->outputInterface->writeln($line); |
|
481
|
|
|
} |
|
482
|
|
|
|
|
483
|
|
|
|
|
484
|
|
|
} |
|
485
|
|
|
|
This check compares the return type specified in the
@returnannotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.If the return type contains the type array, this check recommends the use of a more specific type like
String[]orarray<String>.