API Docs for: 0.9.3
Show:

File: lib/abstractForkTask.js

// Copyright 2014, Yahoo! Inc.
// Copyrights licensed under the Mit License. See the accompanying LICENSE file for terms.

var childProcess = require('child_process');
var AbstractTask = require('./abstractTask');
var _ = require('underscore');
var Promise = require('promise');
var path = require('path');

var logger = require('log4js').getLogger(__filename);

/**
 * @class AbstractForkTask
 * @extends AbstractTask
 * @constructor
 */
var AbstractForkTask = AbstractTask.extend(

	{
		/**
		 * Validates the given data
		 *
		 * @Method validate
		 */
		validate: function () {
			this.__super();
			if (!_.isBoolean(this.shouldFailOnError())) {
				throw new Error('The "failOnError" parameter is not a boolean.');
			}
		},


		/**
		 * Should task fail on error?
		 *
		 * @method shouldFailOnError
		 * @return {boolean}
		 */
		shouldFailOnError: function () {
			return this.getOptions().failOnError;
		},

		/**
		 * Runs the client in a forked environment
		 *
		 * @method runClient
		 * @param {string} parentId
		 * @param {string} clientPath
		 * @return {Promise}
		 */
		runClient: function (parentId, clientPath) {

			var options,
				shouldFailOnError = this.shouldFailOnError(),
				message;

			options = {
				type: "run",
				coverage: this.getOptions().coverage,
				clientPath: clientPath,
				parentId: parentId,
				globalConfig: this.getGlobalConfig().toObject(),
				configuration: this.getConfiguration(),
				decorators: this.getDecorators(),
				decoratorPlugins: this.getClientDecoratorPlugins()
			};

			return new Promise(function (resolve, reject) {

				var ClientClass,
					clientInstance;

				if (this.inDebug()) {
					logger.debug('Fork task-client in debug mode', clientPath);

					ClientClass = require(clientPath);
					clientInstance = new ClientClass(options.decorators, options.decoratorPlugins, options.configuration);

					clientInstance.on('reportMessage', function (messageType, data) {
						if (this.shouldReport()) {
							this.getReportManager().processMessage(messageType, data);
						}
					}.bind(this));

					// Make global configuration available
					global.PRECEPTOR = {
						config: options.globalConfig
					};

					clientInstance.run(parentId).then(function () {
						this.getCoverageCollector().add(global.__preceptorCoverage__ || {});
						global.__preceptorCoverage__ = {};
						resolve({ success: true });
					}, function (err) {
						this.getCoverageCollector().add(global.__preceptorCoverage__ || {});
						global.__preceptorCoverage__ = {};
						throw err;
					}).then(null, function (err) {
						if (shouldFailOnError) {
							reject(err);
						} else {
							logger.error(err.message);
							resolve();
						}
					});
					// In debug-mode, the client cannot be parsed

				} else {
					logger.debug('Fork task-client', clientPath, path.resolve(path.join(__dirname, 'client.js')));
					clientInstance = childProcess.fork(path.resolve(path.join(__dirname, 'client.js')), {
						silent: true
					});

					clientInstance.on('message', function (dataPackage) {
						var err;

						logger.debug('Received message from task-client', dataPackage);

						switch(dataPackage.type) {

							case 'exception':
								throw new Error(dataPackage.message);
								break;

							case 'completion':

								if (dataPackage.data.coverage) {
									this.getCoverageCollector().add(dataPackage.data.coverage);
								}

								if (dataPackage.data.success) {
									resolve(dataPackage.data);

								} else {
									err = new Error('Client failed with: ' + JSON.stringify(dataPackage.data.context));
									if (shouldFailOnError) {
										reject(err);
									} else {
										logger.error(err.message);
										resolve();
									}
								}
								break;

							case 'reportMessage':
								if (this.shouldReport()) {
									this.getReportManager().processMessage(dataPackage.messageType, dataPackage.data);
								}
								break;
						}
					}.bind(this));

					clientInstance.stdout.on('data', function (data) {
						if (this.shouldReport()) {
							data = this.getReportManager().parse(data.toString('utf-8'), { "#TASK#": parentId });
							if (this.shouldEchoStdOut()) {
								process.stdout.write((this.isVerbose() ? this.getName() + ": " : '') + data);
							}
						}
					}.bind(this));

					clientInstance.stderr.on('data', function (data) {
						if (this.shouldReport()) {
							data = this.getReportManager().parse(data.toString('utf-8'), { "#TASK#": parentId });
							if (this.shouldEchoStdErr()) {
								process.stderr.write((this.isVerbose() ? this.getName() + ": " : '') + data);
							}
						}
					}.bind(this));

					clientInstance.send(options);
				}

			}.bind(this));
		}
	});

module.exports = AbstractForkTask;