1
|
|
|
#!/usr/bin/php |
2
|
|
|
|
3
|
|
|
<?php |
4
|
|
|
|
5
|
|
|
/* Copyright (C) 2015, Sport Archive Inc. */ |
6
|
|
|
|
7
|
|
|
/* This program is free software; you can redistribute it and/or modify */ |
8
|
|
|
/* it under the terms of the GNU General Public License as published by */ |
9
|
|
|
/* the Free Software Foundation; either version 2 of the License, or */ |
10
|
|
|
/* (at your option) any later version. */ |
11
|
|
|
|
12
|
|
|
/* This program is distributed in the hope that it will be useful, */ |
13
|
|
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ |
14
|
|
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ |
15
|
|
|
/* GNU General Public License for more details. */ |
16
|
|
|
|
17
|
|
|
/* You should have received a copy of the GNU General Public License along */ |
18
|
|
|
/* with this program; if not, write to the Free Software Foundation, Inc., */ |
19
|
|
|
/* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ |
20
|
|
|
|
21
|
|
|
/* Cloud Processing Engine, Copyright (C) 2015, Sport Archive Inc */ |
22
|
|
|
/* Cloud Processing Engine comes with ABSOLUTELY NO WARRANTY; */ |
23
|
|
|
/* This is free software, and you are welcome to redistribute it */ |
24
|
|
|
/* under certain conditions; */ |
25
|
|
|
|
26
|
|
|
/* June 29th 2015 */ |
27
|
|
|
/* Sport Archive Inc. */ |
28
|
|
|
/* [email protected] */ |
29
|
|
|
|
30
|
|
|
|
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* The activity poller listen for "activity tasks" |
34
|
|
|
* Stuff to do for this worker: compute, process, whatever. |
35
|
|
|
* The ActivityPoller can be configued to listen on a particular SWF TaskList (queue) |
36
|
|
|
* It will process tasks only coming in the TaskList |
37
|
|
|
*/ |
38
|
|
|
|
39
|
|
|
require_once __DIR__ . "/../vendor/autoload.php"; |
40
|
|
|
|
41
|
|
|
use Aws\Swf\Exception; |
|
|
|
|
42
|
|
|
use SA\CpeSdk; |
43
|
|
|
|
44
|
|
|
class ActivityPoller |
45
|
|
|
{ |
46
|
|
|
private $debug; |
47
|
|
|
private $cpeSwfHandler; |
48
|
|
|
private $cpeLogger; |
49
|
|
|
|
50
|
|
|
private $domain; |
51
|
|
|
private $taskList; |
52
|
|
|
private $activityName; |
53
|
|
|
private $activityVersion; |
54
|
|
|
private $activityHandler; |
55
|
|
|
private $knownActivities; |
56
|
|
|
|
57
|
|
|
const ACTIVITY_FAILED = "ACTIVITY_FAILED"; |
58
|
|
|
|
59
|
|
|
public function __construct($config) |
60
|
|
|
{ |
61
|
|
|
global $debug; |
62
|
|
|
global $cpeLogger; |
63
|
|
|
|
64
|
|
|
global $domain; |
65
|
|
|
global $taskList; |
66
|
|
|
global $activityName; |
67
|
|
|
global $activityVersion; |
68
|
|
|
|
69
|
|
|
$this->debug = $debug; |
70
|
|
|
$this->cpeLogger = $cpeLogger; |
71
|
|
|
|
72
|
|
|
// SWF related |
73
|
|
|
$this->domain = $domain; |
74
|
|
|
$this->taskList = $taskList; |
75
|
|
|
$this->activityName = $activityName; |
76
|
|
|
$this->activityVersion = $activityVersion; |
77
|
|
|
$this->knownActivities = $config->{'activities'}; |
78
|
|
|
|
79
|
|
|
// For creating SWF object |
80
|
|
|
$this->cpeSwfHandler = new CpeSdk\Swf\CpeSwfHandler($this->debug); |
81
|
|
|
|
82
|
|
|
// Check and load activities to handle |
83
|
|
|
if (!$this->register_activities()) { |
84
|
|
|
$msg = "No activity class registered! Check the logs (/var/tmp/logs/cpe/). Exiting ...\n"; |
85
|
|
|
$cpeLogger->log_out( |
86
|
|
|
"FATAL", |
87
|
|
|
basename(__FILE__), |
88
|
|
|
$msg |
89
|
|
|
); |
90
|
|
|
print $msg; |
91
|
|
|
exit(1); |
92
|
|
|
} |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
// We poll for new activities |
96
|
|
|
// Return true to keep polling even on failure |
97
|
|
|
// Return false will stop process ! |
98
|
|
|
public function poll_for_activities() |
99
|
|
|
{ |
100
|
|
|
// Poll from all the taskList registered for each activities |
101
|
|
|
if ($this->debug) |
102
|
|
|
$this->cpeLogger->log_out("DEBUG", basename(__FILE__), |
103
|
|
|
"Polling activity taskList '" . $this->taskList . "' ... "); |
104
|
|
|
|
105
|
|
|
try { |
106
|
|
|
// Call SWF and poll for incoming tasks |
107
|
|
|
$activityTask = $this->cpeSwfHandler->swf->pollForActivityTask([ |
108
|
|
|
"domain" => $this->domain, |
109
|
|
|
"taskList" => array("name" => $this->taskList) |
110
|
|
|
]); |
111
|
|
|
|
112
|
|
|
// Handle and process the new activity task |
113
|
|
|
$this->process_activity_task($activityTask); |
114
|
|
|
} catch (CpeSdk\CpeException $e) { |
|
|
|
|
115
|
|
|
$this->cpeLogger->log_out("ERROR", basename(__FILE__), |
116
|
|
|
"Unable to poll activity tasks! " . $e->getMessage()); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
return true; |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
// Process the new task using one of the activity handler classes registered |
123
|
|
|
private function process_activity_task($activityTask) |
124
|
|
|
{ |
125
|
|
|
// Get activityType and WorkflowExecution info |
126
|
|
|
if (!($activityType = $activityTask->get("activityType")) || |
127
|
|
|
!($workflowExecution = $activityTask->get("workflowExecution"))) |
128
|
|
|
return false; |
129
|
|
|
|
130
|
|
|
$this->cpeLogger->log_out("INFO", |
131
|
|
|
basename(__FILE__), |
132
|
|
|
"Starting activity: name=" |
133
|
|
|
. $activityType['name'] . ",version=" . $activityType['version'], |
134
|
|
|
$workflowExecution['workflowId']); |
135
|
|
|
|
136
|
|
|
// Has activity handler object been instantiated ? |
137
|
|
|
if (!isset($this->activityHandler)) |
138
|
|
|
{ |
139
|
|
|
$this->cpeLogger->log_out("ERROR", basename(__FILE__), |
140
|
|
|
"The activity handler class for this activity type is not instantiated !", |
141
|
|
|
$workflowExecution['workflowId']); |
142
|
|
|
|
143
|
|
|
return false; |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
$result = null; |
147
|
|
|
$reason = 0; |
148
|
|
|
$details = 0; |
149
|
|
|
try { |
150
|
|
|
// Check activity task |
151
|
|
|
$this->activityHandler->do_task_check($activityTask); |
152
|
|
|
// Perform input validation |
153
|
|
|
$this->activityHandler->do_input_validation(); |
154
|
|
|
|
155
|
|
|
if ($this->debug) |
156
|
|
|
$this->cpeLogger->log_out("DEBUG", basename(__FILE__), |
157
|
|
|
"Activity input:\n" . print_r($this->activityHandler->input, true)); |
158
|
|
|
|
159
|
|
|
// Run activity task |
160
|
|
|
$result = $this->activityHandler->do_activity($activityTask); |
161
|
|
|
|
162
|
|
|
if ($this->debug && $result) |
163
|
|
|
$this->cpeLogger->log_out("DEBUG", basename(__FILE__), |
164
|
|
|
"Activity output:\n" . print_r($result, true)); |
165
|
|
|
|
166
|
|
|
} catch (CpeSdk\CpeException $e) { |
|
|
|
|
167
|
|
|
$reason = $e->ref; |
168
|
|
|
$details = $e->getMessage(); |
169
|
|
|
} catch (Exception $e) { |
|
|
|
|
170
|
|
|
$reason = self::ACTIVITY_FAILED; |
171
|
|
|
$details = $e->getMessage(); |
172
|
|
|
} finally { |
173
|
|
|
if ($reason && $details) |
174
|
|
|
{ |
175
|
|
|
// Activity has failed! |
176
|
|
|
// We send back to SWF the reason and details about the failure |
177
|
|
|
$this->activityHandler->activity_failed( |
178
|
|
|
$activityTask, |
179
|
|
|
$reason, |
180
|
|
|
$details |
181
|
|
|
); |
182
|
|
|
return false; |
183
|
|
|
} |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
// Activity has completed! |
187
|
|
|
$this->activityHandler->activity_completed($activityTask, $result); |
188
|
|
|
return true; |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
// Register and instantiate activities handlers classes |
192
|
|
|
private function register_activities() |
193
|
|
|
{ |
194
|
|
|
foreach ($this->knownActivities as $knownActivity) |
195
|
|
|
{ |
196
|
|
|
if ($this->activityName == $knownActivity->{"name"} && |
197
|
|
|
$this->activityVersion == $knownActivity->{"version"}) |
198
|
|
|
{ |
199
|
|
|
$activityToHandle = $knownActivity; |
200
|
|
|
|
201
|
|
|
if (!file_exists($activityToHandle->{"file"})) |
202
|
|
|
{ |
203
|
|
|
$this->cpeLogger->log_out("ERROR", basename(__FILE__), |
204
|
|
|
"The code file '".$activityToHandle->{"file"}."' for activity: name=" |
205
|
|
|
. $activityToHandle->{"name"} . ",version=" |
206
|
|
|
. $activityToHandle->{"version"}." doesn't exists! Check if the file is accessible and if the path is correct in your config file."); |
207
|
|
|
return false; |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
$this->cpeLogger->log_out("INFO", basename(__FILE__), |
211
|
|
|
"Registering Activity: $this->activityName:$this->activityVersion"); |
212
|
|
|
|
213
|
|
|
// Load the file implementing the activity |
214
|
|
|
require_once $activityToHandle->{"file"}; |
215
|
|
|
|
216
|
|
|
// Instantiate the Activity class that will process Tasks |
217
|
|
|
|
218
|
|
|
if (!isset($this->cpeLogger) || |
219
|
|
|
!$this->cpeLogger) |
220
|
|
|
print "EMPTY !!!\n"; |
221
|
|
|
|
222
|
|
|
$params = [ |
223
|
|
|
"domain" => $this->domain, |
224
|
|
|
"name" => $activityToHandle->{"name"}, |
225
|
|
|
"version" => $activityToHandle->{"version"} |
226
|
|
|
]; |
227
|
|
|
if (isset($activityToHandle->{"defaultTaskStartToCloseTimeout"})) |
228
|
|
|
$params["defaultTaskStartToCloseTimeout"] = |
229
|
|
|
$activityToHandle->{"defaultTaskStartToCloseTimeout"}; |
230
|
|
|
if (isset($activityToHandle->{"defaultTaskHeartbeatTimeout"})) |
231
|
|
|
$params["defaultTaskHeartbeatTimeout"] = |
232
|
|
|
$activityToHandle->{"defaultTaskHeartbeatTimeout"}; |
233
|
|
|
if (isset($activityToHandle->{"defaultTaskScheduleToStartTimeout"})) |
234
|
|
|
$params["defaultTaskScheduleToStartTimeout"] = |
235
|
|
|
$activityToHandle->{"defaultTaskScheduleToStartTimeout"}; |
236
|
|
|
if (isset($activityToHandle->{"defaultTaskScheduleToCloseTimeout"})) |
237
|
|
|
$params["defaultTaskScheduleToCloseTimeout"] = |
238
|
|
|
$activityToHandle->{"defaultTaskScheduleToCloseTimeout"}; |
239
|
|
|
|
240
|
|
|
$this->activityHandler = |
241
|
|
|
new $activityToHandle->{"class"}( |
242
|
|
|
$params, |
243
|
|
|
$this->debug, |
244
|
|
|
$this->cpeLogger |
245
|
|
|
); |
246
|
|
|
|
247
|
|
|
$this->cpeLogger->log_out("INFO", basename(__FILE__), |
248
|
|
|
"Activity handler registered: name=" |
249
|
|
|
. $activityToHandle->{"name"} . ",version=" |
250
|
|
|
. $activityToHandle->{"version"}); |
251
|
|
|
|
252
|
|
|
return true; |
253
|
|
|
} |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
$this->cpeLogger->log_out("ERROR", basename(__FILE__), |
257
|
|
|
"No Activity handler was found for: name=" |
258
|
|
|
. $this->activityName . ",version=" |
259
|
|
|
. $this->activityVersion.". Check your config file and ensure your 'activity' name AND 'version' is there."); |
260
|
|
|
return false; |
261
|
|
|
} |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
|
265
|
|
|
|
266
|
|
|
/* |
267
|
|
|
*************************** |
268
|
|
|
* POLLER START |
269
|
|
|
*************************** |
270
|
|
|
*/ |
271
|
|
|
|
272
|
|
|
// Globals |
273
|
|
|
$debug = false; |
274
|
|
|
$cpeLogger; |
275
|
|
|
|
276
|
|
|
// Usage |
277
|
|
|
function usage($defaultConfigFile) |
|
|
|
|
278
|
|
|
{ |
279
|
|
|
echo("Usage: php ". basename(__FILE__) . " -D <domain> -A <activity_name> -V <activity_version> [-T <task_list>] [-h] [-d] [-c <config_file path>] [-l <log path>]\n"); |
280
|
|
|
echo("-h: Print this help\n"); |
281
|
|
|
echo("-d: Debug mode\n"); |
282
|
|
|
echo("-c <config_file path>: Optional parameter to override the default configuration file: '$defaultConfigFile'.\n"); |
283
|
|
|
echo("-l <log_path>: Location where logs will be dumped in (folder).\n"); |
284
|
|
|
echo("-D <domain>: SWF domain your Workflow runs on.\n"); |
285
|
|
|
echo("-A <activity_name>: Activity name this Poller can process.\n"); |
286
|
|
|
echo("-V <activity_version>: Activity version this Poller can process.\n"); |
287
|
|
|
echo("-T <task list>: Specify the Activity Task List this activity will listen to. An Activity Task list is the queue your Activity poller will listen to for new tasks. If not specified, it will listen to the default channel used by the Decider : 'activity_name-activity_version'.\n"); |
288
|
|
|
exit(0); |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
// Check command line input parameters |
292
|
|
|
function check_input_parameters(&$defaultConfigFile) |
|
|
|
|
293
|
|
|
{ |
294
|
|
|
global $debug; |
295
|
|
|
global $cpeLogger; |
296
|
|
|
|
297
|
|
|
// Filling the globals with command input |
298
|
|
|
global $domain; |
299
|
|
|
global $taskList; |
300
|
|
|
global $activityName; |
301
|
|
|
global $activityVersion; |
302
|
|
|
|
303
|
|
|
// Handle input parameters |
304
|
|
|
if (!($options = getopt("D:T:A:V:c:l:hd"))) |
305
|
|
|
usage($defaultConfigFile); |
|
|
|
|
306
|
|
|
|
307
|
|
|
if (!count($options) || isset($options['h'])) |
308
|
|
|
usage($defaultConfigFile); |
|
|
|
|
309
|
|
|
|
310
|
|
|
// Debug |
311
|
|
|
if (isset($options['d'])) |
312
|
|
|
$debug = true; |
313
|
|
|
|
314
|
|
|
// Domain |
315
|
|
|
if (!isset($options['D'])) |
316
|
|
|
{ |
317
|
|
|
echo "ERROR: You must provide a Domain\n"; |
318
|
|
|
usage($defaultConfigFile); |
|
|
|
|
319
|
|
|
} |
320
|
|
|
$domain = $options['D']; |
321
|
|
|
|
322
|
|
|
// Activity name |
323
|
|
|
if (!isset($options['A'])) |
324
|
|
|
{ |
325
|
|
|
echo "ERROR: You must provide an Activity name\n"; |
326
|
|
|
usage($defaultConfigFile); |
|
|
|
|
327
|
|
|
} |
328
|
|
|
$activityName = $options['A']; |
329
|
|
|
|
330
|
|
|
$logPath = null; |
331
|
|
|
if (isset($options['l'])) |
332
|
|
|
{ |
333
|
|
|
$logPath = $options['l']; |
334
|
|
|
} |
335
|
|
|
$cpeLogger = new CpeSdk\CpeLogger($logPath, $activityName, $debug); |
336
|
|
|
|
337
|
|
|
// Activity version |
338
|
|
|
if (!isset($options['V'])) |
339
|
|
|
{ |
340
|
|
|
echo "ERROR: You must provide an Activity version\n"; |
341
|
|
|
usage($defaultConfigFile); |
|
|
|
|
342
|
|
|
} |
343
|
|
|
$activityVersion = $options['V']; |
344
|
|
|
|
345
|
|
|
// Tasklist |
346
|
|
|
if (!isset($options['T'])) |
347
|
|
|
{ |
348
|
|
|
$taskList = $options['A'].'-'.$options['V']; |
349
|
|
|
} else { |
350
|
|
|
$taskList = $options['T']; |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
// OVerrride config file |
354
|
|
|
if (isset($options['c'])) |
355
|
|
|
{ |
356
|
|
|
$cpeLogger->log_out( |
357
|
|
|
"INFO", |
358
|
|
|
basename(__FILE__), |
359
|
|
|
"Config file: '" . $options['c'] . "'"); |
360
|
|
|
$defaultConfigFile = $options['c']; |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
try { |
364
|
|
|
// Check config file |
365
|
|
|
if (!($config = json_decode(file_get_contents($defaultConfigFile)))) |
366
|
|
|
{ |
367
|
|
|
print "\n[CONFIG ISSUE]\nConfiguration file '$defaultConfigFile' invalid or non-existant.\n\n[EASY FIX]\nGo to the directory mentioned in the error above and rename the template file 'cpeConfigTemplate.json' to 'cpeConfig.json'. Configure your Activities. As example you have Activities for CloudTranscode already setup in the template. You can declare your Activities and start executing tasks in an SWF workflow.\n"; |
368
|
|
|
exit(1); |
369
|
|
|
} |
370
|
|
|
} |
371
|
|
|
catch (Exception $e) { |
|
|
|
|
372
|
|
|
print $e; |
373
|
|
|
} |
374
|
|
|
|
375
|
|
|
|
376
|
|
|
# Validate against JSON Schemas |
377
|
|
|
# if (($err = validate_json($config, "config/mainConfig.json"))) |
|
|
|
|
378
|
|
|
# exit("JSON main configuration file invalid! Details:\n".$err); |
|
|
|
|
379
|
|
|
|
380
|
|
|
return $config; |
|
|
|
|
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
// Get config file |
384
|
|
|
$defaultConfigFile = |
385
|
|
|
realpath(dirname(__FILE__)) . "/../config/cpeConfig.json"; |
386
|
|
|
$config = check_input_parameters($defaultConfigFile); |
|
|
|
|
387
|
|
|
|
388
|
|
|
// Instantiate ActivityPoller |
389
|
|
|
try { |
390
|
|
|
$activityPoller = new ActivityPoller($config); |
391
|
|
|
} |
392
|
|
|
catch (CpeSdk\CpeException $e) { |
|
|
|
|
393
|
|
|
$cpeLogger->log_out("FATAL", |
394
|
|
|
basename(__FILE__), $e->getMessage()); |
395
|
|
|
exit(1); |
396
|
|
|
} |
397
|
|
|
|
398
|
|
|
$cpeLogger->log_out("INFO", basename(__FILE__), "Starting activity tasks polling"); |
399
|
|
|
|
400
|
|
|
// Start polling loop |
401
|
|
|
while (42) |
402
|
|
|
$activityPoller->poll_for_activities(); |
403
|
|
|
|
404
|
|
|
|
Let’s assume that you have a directory layout like this:
and let’s assume the following content of
Bar.php
:If both files
OtherDir/Foo.php
andSomeDir/Foo.php
are loaded in the same runtime, you will see a PHP error such as the following:PHP Fatal error: Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php
However, as
OtherDir/Foo.php
does not necessarily have to be loaded and the error is only triggered if it is loaded beforeOtherDir/Bar.php
, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias: