1 | var define;
|
2 | if (typeof define === "undefined")
|
3 | define = function (classInstance) {
|
4 | classInstance (require, exports, module);
|
5 | }
|
6 |
|
7 | define (function (require, exports, module) {
|
8 |
|
9 | "use strict";
|
10 |
|
11 | var EventEmitter = require ('events').EventEmitter,
|
12 | util = require ('util'),
|
13 | dataflows = require ('../'),
|
14 | common = dataflows.common;
|
15 |
|
16 | var taskStateList = [
|
17 | 'scarce', 'ready', 'running', 'idle',
|
18 | 'complete', 'failed', 'skipped', 'exception'
|
19 | ];
|
20 |
|
21 | var taskStateNames = {};
|
22 |
|
23 | var taskStateMethods = {};
|
24 |
|
25 | for (var stateNum = 0; stateNum < taskStateList.length; stateNum++) {
|
26 | taskStateNames[taskStateList[stateNum]] = stateNum;
|
27 |
|
28 | var fName = 'is' + taskStateList[stateNum].toLowerCase().replace(
|
29 | /\b([a-z])/i, function(c) {return c.toUpperCase()}
|
30 | );
|
31 | taskStateMethods[fName] = function (x) {
|
32 | return function () {return this.state == x};
|
33 | } (stateNum);
|
34 | }
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 |
|
95 |
|
96 | function task (config) {
|
97 |
|
98 | }
|
99 |
|
100 | module.exports = task;
|
101 |
|
102 | util.inherits (task, EventEmitter);
|
103 |
|
104 | util.extend (task.prototype, taskStateMethods, {
|
105 |
|
106 | _launch: function () {
|
107 |
|
108 |
|
109 | if (this.state != 1) return;
|
110 |
|
111 | this.state = 2;
|
112 |
|
113 | var method = this[this.method || 'run'];
|
114 | if (!method) {
|
115 | this.state = 5;
|
116 | this.emit ('error', 'no method named "' + (this.method || 'run') + "\" in current task's class");
|
117 |
|
118 | return;
|
119 | }
|
120 |
|
121 | method.call (this);
|
122 |
|
123 | if (this.timeout) {
|
124 | this.timeoutId = setTimeout (function () {
|
125 | this.state = 5;
|
126 | this.emit ('log', 'timeout is over for task');
|
127 | this._cancel();
|
128 | }.bind (this), this.timeout);
|
129 | }
|
130 | },
|
131 |
|
132 | run: function () {
|
133 | var failed = false;
|
134 |
|
135 | |
136 |
|
137 |
|
138 | var origin = null;
|
139 |
|
140 | var taskFnName = this.functionName;
|
141 | var fnPath = taskFnName.split('#', 2);
|
142 |
|
143 | if (fnPath.length == 2 && $global.project) {
|
144 | origin = $global.project.require(fnPath[0]);
|
145 | taskFnName = fnPath[1];
|
146 | } else if (this.$origin) {
|
147 | origin = this.$origin;
|
148 | } else {
|
149 | origin = $global.$mainModule.exports;
|
150 | }
|
151 |
|
152 | var method;
|
153 | method = common.getByPath (taskFnName, origin);
|
154 |
|
155 | |
156 |
|
157 |
|
158 | if (!method || 'function' !== typeof method.value) {
|
159 | method = common.getByPath (taskFnName);
|
160 | }
|
161 |
|
162 | if (!method || 'function' !== typeof method.value) {
|
163 | method = dataflows.task (taskFnName);
|
164 | }
|
165 |
|
166 | if (!method || ('function' !== typeof method && 'function' !== typeof method.value)) {
|
167 | failed = taskFnName + ' is not a function';
|
168 | this.failed(failed);
|
169 | }
|
170 |
|
171 | var fn = 'function' === typeof method ? method : method.value;
|
172 | var ctx = this.$scope || method.scope;
|
173 |
|
174 | var args = this.$args;
|
175 | var argsType = Object.typeOf(args);
|
176 |
|
177 | if (null == args) {
|
178 | args = [ this ];
|
179 | } else if (
|
180 | this.originalConfig.$args &&
|
181 | Object.typeOf (this.originalConfig.$args) != 'Array' &&
|
182 | Object.typeOf (this.originalConfig.$args) != 'Arguments'
|
183 | ) {
|
184 | args = [ args ];
|
185 | }
|
186 |
|
187 |
|
188 |
|
189 | if (this.type === "errback") {
|
190 | args.push ((function (err) {
|
191 | var cbArgs = [].slice.call (arguments, 1);
|
192 | if (err) {
|
193 | this.failed.apply (this, arguments);
|
194 | return;
|
195 | };
|
196 | this.completed.apply (this, cbArgs);
|
197 | }).bind(this));
|
198 | }
|
199 |
|
200 | try {
|
201 | var returnVal = fn.apply(ctx, args);
|
202 | } catch (e) {
|
203 | failed = e;
|
204 | this.failed(failed);
|
205 | }
|
206 |
|
207 | if (this.type === "promise") {
|
208 | returnVal.then (
|
209 | this.completed.bind (this),
|
210 | this.failed.bind (this)
|
211 | );
|
212 | } else if (this.type === "errback") {
|
213 |
|
214 | } else if (failed) {
|
215 | throw failed;
|
216 | } else {
|
217 | this.completed(returnVal);
|
218 |
|
219 |
|
220 |
|
221 |
|
222 |
|
223 | }
|
224 | },
|
225 |
|
226 | |
227 |
|
228 |
|
229 |
|
230 |
|
231 |
|
232 |
|
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 | _cancel: function (value) {
|
239 |
|
240 | this.attempts ++;
|
241 |
|
242 | if (this.state == 2) return;
|
243 |
|
244 | this.state = 5;
|
245 |
|
246 | if (this.cancel) this.cancel.apply (this, arguments);
|
247 |
|
248 | this.clearOperationTimeout();
|
249 |
|
250 |
|
251 |
|
252 | if (this.attempts - this.retries - 1 < 0) {
|
253 |
|
254 | this.state = 1;
|
255 |
|
256 | setTimeout (this._launch.bind (this), this.delay || 0);
|
257 |
|
258 | return;
|
259 | }
|
260 |
|
261 | |
262 |
|
263 |
|
264 |
|
265 | this.emit ('cancel', value);
|
266 |
|
267 | },
|
268 |
|
269 | init: function (config) {
|
270 |
|
271 | this.require = config.require || null;
|
272 | this.mustProduce = config.mustProduce;
|
273 | this.cb = config.cb;
|
274 | this.cbScope = config.cbScope;
|
275 | this.className = config.className;
|
276 | this.functionName = config.functionName;
|
277 | this.originalConfig = config.originalConfig;
|
278 | this.flowId = config.flowId;
|
279 | this.flowLogId = config.flowLogId;
|
280 | this.getDict = config.getDict;
|
281 | this.type = config.type;
|
282 |
|
283 | this.method = config.method;
|
284 | if (this.className && !this.method)
|
285 | this.method = 'run';
|
286 |
|
287 | this.id = "" + this.flowId + ":" + config.idx;
|
288 |
|
289 | var idxLog = (config.idx < 10 ? " " : "") + config.idx;
|
290 | if ($isServerSide) {
|
291 | idxLog = "\x1B[0;3" + (parseInt(config.idx) % 8) + "m" + idxLog + "\x1B[0m";
|
292 | }
|
293 |
|
294 |
|
295 | this.dfTaskNo = config.idx;
|
296 | this.dfTaskLogNum = idxLog;
|
297 |
|
298 | if (!this.logTitle) {
|
299 | if (this.className) {
|
300 | this.logTitle = this.className + '.' + this.method;
|
301 | } else {
|
302 | this.logTitle = this.functionName;
|
303 | }
|
304 | }
|
305 |
|
306 | var stateList = taskStateList;
|
307 |
|
308 | var self = this;
|
309 |
|
310 | this.state = 0;
|
311 |
|
312 |
|
313 |
|
314 |
|
315 | self.timeout = config.timeout;
|
316 | self.retries = config.retries || null;
|
317 |
|
318 | self.attempts = 0;
|
319 |
|
320 | self.important = config.important || void 0;
|
321 |
|
322 |
|
323 | if (self.DEFAULT_CONFIG) {
|
324 | util.shallowMerge(self, self.DEFAULT_CONFIG);
|
325 | }
|
326 |
|
327 | var state = this.checkState ();
|
328 |
|
329 |
|
330 | },
|
331 |
|
332 | |
333 |
|
334 |
|
335 |
|
336 |
|
337 |
|
338 |
|
339 |
|
340 |
|
341 | completed: function (result) {
|
342 | this.state = 4;
|
343 |
|
344 | var mustProduce = this.mustProduce;
|
345 |
|
346 | if (mustProduce) {
|
347 | var checkString = (mustProduce instanceof Array ? mustProduce.join (' && ') : mustProduce);
|
348 | var satisfy = 0;
|
349 | try {satisfy = eval ("if ("+ checkString +") 1") } catch (e) {};
|
350 | if (!satisfy) {
|
351 |
|
352 | console.error ("task " + this.url + " must produce " + checkString + " but it doesn't");
|
353 |
|
354 | }
|
355 | }
|
356 |
|
357 |
|
358 | if (typeof this.cb == 'function') {
|
359 |
|
360 |
|
361 | if (this.cbScope) {
|
362 | this.cb.call (this.cbScope, this);
|
363 | } else {
|
364 | this.cb (this);
|
365 | }
|
366 | }
|
367 |
|
368 |
|
369 | if (common.isEmpty (result)) {
|
370 | this.empty();
|
371 | }
|
372 |
|
373 | |
374 |
|
375 |
|
376 |
|
377 |
|
378 |
|
379 |
|
380 | this.emit ("complete", this, result);
|
381 | },
|
382 |
|
383 | |
384 |
|
385 |
|
386 |
|
387 |
|
388 |
|
389 |
|
390 |
|
391 |
|
392 |
|
393 | skipped: function (result) {
|
394 | this.state = 6;
|
395 |
|
396 | |
397 |
|
398 |
|
399 |
|
400 |
|
401 |
|
402 |
|
403 | this.emit ("skip", this, result);
|
404 | },
|
405 |
|
406 | |
407 |
|
408 |
|
409 |
|
410 |
|
411 |
|
412 |
|
413 |
|
414 | empty: function () {
|
415 | this.state = 6;
|
416 | this.emit('empty', this);
|
417 | },
|
418 |
|
419 | |
420 |
|
421 |
|
422 |
|
423 |
|
424 |
|
425 | mapFields: function (item) {
|
426 | var self = this;
|
427 |
|
428 | for (var k in self.mapping) {
|
429 | if (item[self.mapping[k]])
|
430 | item[k] = item[self.mapping[k]];
|
431 | }
|
432 |
|
433 | return item;
|
434 | },
|
435 |
|
436 | |
437 |
|
438 |
|
439 |
|
440 |
|
441 |
|
442 | checkState: function () {
|
443 |
|
444 | var self = this;
|
445 |
|
446 | if (!self.require && this.state == 0) {
|
447 | this.state = 1;
|
448 | }
|
449 |
|
450 | if (this.state >= 1)
|
451 | return this.state;
|
452 |
|
453 | var satisfy = 0;
|
454 | if (typeof self.require == 'function') {
|
455 | satisfy = self.require ();
|
456 | } else {
|
457 | try {
|
458 | satisfy = eval ("if ("+ (
|
459 | self.require instanceof Array
|
460 | ? self.require.join (' && ')
|
461 | : self.require)+") 1")
|
462 | } catch (e) {
|
463 |
|
464 | };
|
465 | }
|
466 | if (satisfy) {
|
467 | this.state = 1;
|
468 | return this.state;
|
469 | }
|
470 |
|
471 | return this.state;
|
472 | },
|
473 |
|
474 | |
475 |
|
476 |
|
477 | clearOperationTimeout: function() {
|
478 | if (this.timeoutId) {
|
479 | clearTimeout (this.timeoutId);
|
480 | this.timeoutId = undefined;
|
481 | }
|
482 |
|
483 | },
|
484 |
|
485 | |
486 |
|
487 |
|
488 |
|
489 | activityCheck: function (place, breakOnly) {
|
490 |
|
491 | if (place!=="model.fetch data") {
|
492 |
|
493 | }
|
494 | var self = this;
|
495 |
|
496 | if (breakOnly === void (0)) {
|
497 | breakOnly = false;
|
498 | }
|
499 |
|
500 | self.clearOperationTimeout();
|
501 |
|
502 | if (!breakOnly)
|
503 | {
|
504 | self.timeoutId = setTimeout(function () {
|
505 | self.state = 5;
|
506 | self.emit (
|
507 | 'log', 'timeout is over for ' + place + ' operation'
|
508 | );
|
509 | self.model.stop();
|
510 | self._cancel();
|
511 |
|
512 | }, self.timeout);
|
513 | }
|
514 | },
|
515 |
|
516 | |
517 |
|
518 |
|
519 |
|
520 |
|
521 |
|
522 |
|
523 |
|
524 |
|
525 |
|
526 |
|
527 |
|
528 |
|
529 |
|
530 |
|
531 |
|
532 | stateNames: taskStateNames,
|
533 |
|
534 | |
535 |
|
536 |
|
537 |
|
538 |
|
539 |
|
540 |
|
541 |
|
542 |
|
543 |
|
544 |
|
545 |
|
546 |
|
547 |
|
548 |
|
549 | failed: function (e, data) {
|
550 | var prevState = this.state;
|
551 | this.state = 5;
|
552 |
|
553 | |
554 |
|
555 |
|
556 |
|
557 |
|
558 |
|
559 | this.emit('error', e);
|
560 |
|
561 | if (prevState)
|
562 | this._cancel (data || e)
|
563 | else
|
564 | this.emit ('cancel', data || e);
|
565 | return;
|
566 | }
|
567 |
|
568 |
|
569 | });
|
570 |
|
571 |
|
572 |
|
573 |
|
574 |
|
575 |
|
576 |
|
577 |
|
578 |
|
579 |
|
580 |
|
581 |
|
582 |
|
583 | task.prepare = function (flow, dataflows, gen, taskParams, idx, array) {
|
584 | var theTask;
|
585 |
|
586 | var actualTaskParams = {
|
587 | };
|
588 | var taskTemplateName = taskParams.$template;
|
589 | if (taskTemplateName && flow.templates && flow.templates[taskTemplateName]) {
|
590 | util.extend (true, actualTaskParams, flow.templates[taskTemplateName]);
|
591 | delete actualTaskParams.$template;
|
592 | }
|
593 |
|
594 |
|
595 |
|
596 | util.extend (true, actualTaskParams, taskParams);
|
597 |
|
598 | if (actualTaskParams.$every) {
|
599 | actualTaskParams.$class = 'every';
|
600 | if (!actualTaskParams.$tasks) {
|
601 | flow.logError ('missing $tasks property for $every task');
|
602 | flow.ready = false;
|
603 | return;
|
604 | }
|
605 | actualTaskParams.$tasks.forEach (function (everyTaskConf, idx) {
|
606 | var taskTemplateName = everyTaskConf.$template;
|
607 | if (taskTemplateName && flow.templates && flow.templates[taskTemplateName]) {
|
608 | var newEveryTaskConf = util.extend (true, {}, flow.templates[taskTemplateName]);
|
609 |
|
610 | util.extend (true, newEveryTaskConf, everyTaskConf);
|
611 | util.extend (true, everyTaskConf, newEveryTaskConf);
|
612 | delete everyTaskConf.$template;
|
613 |
|
614 | }
|
615 |
|
616 | });
|
617 |
|
618 | actualTaskParams.flowConfig = {
|
619 | logger: flow.logger
|
620 | };
|
621 | }
|
622 |
|
623 |
|
624 | var originalTaskConfig = util.extend (true, {}, actualTaskParams);
|
625 |
|
626 |
|
627 |
|
628 |
|
629 |
|
630 | var taskClassName = actualTaskParams.className || actualTaskParams.$class || actualTaskParams.task;
|
631 | var taskFnName = actualTaskParams.functionName || actualTaskParams.$function || actualTaskParams.fn;
|
632 | var taskPromise = actualTaskParams.promise || actualTaskParams.$promise;
|
633 | var taskErrBack = actualTaskParams.errback || actualTaskParams.$errback;
|
634 |
|
635 | // console.log ('task:', taskClassName, 'function:', taskFnName, 'promise:', taskPromise, 'errback:', taskErrBack);
|
636 |
|
637 | if (taskClassName && taskFnName)
|
638 | flow.logError ('defined both className and functionName, using className');
|
639 |
|
640 | if (taskClassName) {
|
641 |
|
642 | try {
|
643 | var taskModule = dataflows.task (taskClassName);
|
644 | theTask = new taskModule ({
|
645 | originalConfig: originalTaskConfig,
|
646 | className: taskClassName,
|
647 | method: actualTaskParams.method || actualTaskParams.$method,
|
648 | require: gen ('checkRequirements', actualTaskParams),
|
649 | important: actualTaskParams.important || actualTaskParams.$important,
|
650 | flowLogId: flow.coloredId,
|
651 | flowId: flow.id,
|
652 | getDict: gen ('createDict'),
|
653 | timeout: actualTaskParams.timeout,
|
654 | retries: actualTaskParams.retries,
|
655 | idx: idx
|
656 | });
|
657 | } catch (e) {
|
658 | flow.logError ('instance of "'+taskClassName+'" creation failed:');
|
659 | flow.logError (e.stack);
|
660 |
|
661 | flow.ready = false;
|
662 | }
|
663 |
|
664 | } else if (taskFnName || taskPromise || taskErrBack) {
|
665 |
|
666 | var xTaskClass = function (config) {
|
667 | this.init (config);
|
668 | };
|
669 |
|
670 | var taskType;
|
671 |
|
672 | if (taskPromise) {
|
673 |
|
674 | taskFnName = taskPromise;
|
675 | taskType = "promise";
|
676 | }
|
677 |
|
678 | if (taskErrBack) {
|
679 |
|
680 | taskFnName = taskErrBack;
|
681 | taskType = "errback";
|
682 | }
|
683 |
|
684 | util.inherits (xTaskClass, task);
|
685 |
|
686 | theTask = new xTaskClass ({
|
687 | originalConfig: originalTaskConfig,
|
688 | functionName: taskFnName || taskPromise || taskErrBack,
|
689 | type: taskType,
|
690 | logTitle: actualTaskParams.logTitle || actualTaskParams.$logTitle || actualTaskParams.displayName,
|
691 | require: gen ('checkRequirements', actualTaskParams),
|
692 | important: actualTaskParams.important || actualTaskParams.$important,
|
693 | flowLogId: flow.coloredId,
|
694 | flowId: flow.id,
|
695 | timeout: actualTaskParams.timeout,
|
696 | retries: actualTaskParams.retries,
|
697 | idx: idx
|
698 | });
|
699 |
|
700 | } else {
|
701 | flow.logError ("cannot create task from structure:\n", taskParams);
|
702 | flow.logError ('you must define $task, $function or $promise field');
|
703 |
|
704 | flow.ready = false;
|
705 | }
|
706 |
|
707 | return theTask;
|
708 | }
|
709 |
|
710 | task.prototype.EmitError = task.prototype.failed;
|
711 |
|
712 | return task;
|
713 |
|
714 | });
|