dl
Version:
DreamLab Libs
491 lines (402 loc) • 16.7 kB
JavaScript
var core = require('core');
var flat = require('flat');
var CredentialsProvider = core.credentials.CredentialsProvider;
var Event = core.event.Event;
var EventDispatcher = core.event.EventDispatcher;
var Random = core.common.Random;
var Types = core.common.Types;
var PackageJson = require('../../package.json');
var RbmqMessage = core.client.rabbitmq.RbmqMessage;
var QueueProduct = require('./QueueVersion').QueueProduct;
var QueueVersion = require('./QueueVersion').QueueVersion;
var QueueIncomingMessageProcessor = require('./QueueIncomingMessageProcessor.js').QueueIncomingMessageProcessor;
// helpers
var configValue = function (config, path, defaultValue) {
var val = config;
var pathArr = path.split('.');
for (var i = 0, l = pathArr.length; i < l; i++) {
if (val.hasOwnProperty(pathArr[i])) {
val = val[pathArr[i]];
} else {
val = undefined;
break;
}
}
return (val !== undefined) ? val : defaultValue;
};
var ensureSimpleValue = function (value, errorMessage) {
if (value === undefined) {
throw new Error(errorMessage);
}
return value;
};
var ensureConfigValue = function (config, path, errorMessage) {
return ensureSimpleValue(configValue(config, path), errorMessage);
};
var filterUndefined = function (config) {
if (!config) {
return;
}
var keys = Object.keys(config);
for (var i = 0, l = keys.length; i < l; i++) {
if (config[keys[i]] === undefined) {
delete config[keys[i]];
}
}
return config;
};
var hostMapFnc = function (item) {
return [item.host, item.port].join('_');
};
// end of helpers
var QueueCredentials = function(credentialsProvider) {
EventDispatcher.call(this);
this._cp = credentialsProvider;
this._credentials = null;
this._parsedConfig = null;
};
QueueCredentials.prototype = Object.create(EventDispatcher.prototype);
QueueCredentials.prototype.getCredentialId = function() {
return this._cp.getCredentialId();
};
QueueCredentials.prototype.load = function() {
var that = this;
function attachListeners() {
that._cp.addEventListener(CredentialsProvider.Event.LOAD, that._onCredentialLoaded, that);
that._cp.addEventListener(CredentialsProvider.Event.ERROR, that._onCredentialError, that);
that._cp.addEventListener(CredentialsProvider.Event.TIMEOUT, that._onCredentialTimeout, that);
}
if (this._cp._refreshInProgress) {
attachListeners();
} else {
this._cp.get(function(err, data) {
attachListeners();
if (!err && data && !that._credentials) {
that._onCredentialLoaded({data: data});
} else {
that._cp.refresh();
}
});
}
};
QueueCredentials.prototype._onCredentialLoaded = function(ev) {
ev.data = flat.unflatten(ev.data);
if (!this._credentials) {
this._credentials = {};
}
var reload = false;
var data = JSON.parse(JSON.stringify(ev.data));
if (!data.hosts || !Types.isArray(data.hosts) || data.hosts.length === 0) {
console.error('QueueCredentials/_onCredentialLoaded null or empty hosts list!');
return;
}
var hosts = data.hosts.map(hostMapFnc).sort();
/* usuwam klucz z konfiguracji */
delete data.hosts;
/* dane kolejki */
var config = data;
/* sprawdzanie hostow */
if (!this._credentials.hosts) {
/* connect */
reload = true;
} else if (this._credentials.hosts != hosts) {
/* hosts changed */
reload = true;
}
//FIXME: sometime...
var configChanged = false;
/* sprawdzanie konfiguracji kolejki/exchange */
if (!this._credentials.config) {
reload = true;
} else if (JSON.stringify(this._credentials.config) != JSON.stringify(config)) {
reload = true;
configChanged = true;
}
/* wpisanie nowych wartosci */
this._credentials.hosts = hosts;
this._credentials.config = config;
console.log('QueueCredentials/_onCredentialLoaded: reload=%s', reload, this.getCredentialId());
if (reload) {
try {
this._parsedConfig = this._parseConfig(this._credentials.config);
} catch (err) {
console.error('QueueCredentials/_onCredentialLoaded failed to parse configuration:', err.message);
return;
}
//FIXME: sometime...
if (configChanged) {
this._parsedConfig.connection.forceReconnect = true;
} else {
this._parsedConfig.connection.forceReconnect = false;
}
this.dispatchEvent(new Event(QueueCredentials.Event.LOADED));
}
};
QueueCredentials.prototype._onCredentialError = function(ev) {
console.error('QueueCredentials/_onCredentialsError:', ev.data);
};
QueueCredentials.prototype._onCredentialTimeout = function(ev) {
console.error('QueueCredentials/_onCredentialTimeout:', ev.data);
};
QueueCredentials.prototype._parseConfig = function(config) {
if (config.hasOwnProperty('qaas') && config.qaas.hasOwnProperty('version')) {
return this['_parseConfigV' + config.qaas.version](config);
}
return this._parseLegacyConfig(config);
};
QueueCredentials.prototype._parseMonitoringConfig = function (config) {
return {
key: configValue(config, 'monitoring.key', null),
prefix: configValue(config, 'monitoring.prefix', null)
};
};
QueueCredentials.prototype._parseLoggingConfig = function(config) {
return {
connection: configValue(config, 'logging.connection', true),
subscribe: configValue(config, 'logging.subscribe', false),
publish: configValue(config, 'logging.publish', false)
};
};
QueueCredentials.prototype._parseClientProperties = function(config) {
return {
product: QueueProduct,
version: QueueVersion,
dl: PackageJson.version,
'x-onet-app': ensureSimpleValue(process.env['OPAL_IDENTITY'], 'Cannot determine OPAL_IDENTITY environment variable. Forgot to set?'),
'x-onet-segment': ensureSimpleValue(process.env['ONET_SEGMENT'], 'Cannot determine ONET_SEGMENT environment variable. Forgot to set?'),
datasource: this.getCredentialId(),
configVersion: ensureConfigValue(config, 'qaas.version', 'Cannot determine config version. It should never happen!'),
capabilities: {
exchange_exchange_bindings: true,
consumer_cancel_notify: true,
'connection.blocked': true
}
};
};
QueueCredentials.prototype._parseConnectionConfig = function(config) {
return {
login: ensureConfigValue(config, 'connection.login', 'Cannot find connection.login in configuration'),
password: ensureConfigValue(config, 'connection.password', 'Cannot find connection.password in configuration'),
vhost: configValue(config, 'connection.vhost', QueueCredentials.DEFAULT_VHOST),
heartbeat: configValue(config, 'connection.heartbeat', QueueCredentials.DEFAULT_HEARTBEAT),
timeout: configValue(config, 'connection.timeout', QueueCredentials.DEFAULT_CONNECTION_TIMEOUT),
noDelay: configValue(config, 'connection.noDelay', true),
clientProperties: this._parseClientProperties(config)
};
};
QueueCredentials.prototype._parseExchangeConfig = function(config) {
if (!config) {
return;
}
return filterUndefined({
name: ensureConfigValue(config, 'name', 'Cannot find exchange.name in configuration'),
durable: configValue(config, 'durable', true),
autoDelete: configValue(config, 'autoDelete', false),
passive: configValue(config, 'passive', false),
type: configValue(config, 'type', QueueCredentials.DEFAULT_EXCHANGE_TYPE),
noDeclare: configValue(config, 'noDeclare', false),
bindings: configValue(config, 'bindings', []),
alternateExchange: configValue(config, 'alternateExchange'),
publish: {
confirm: configValue(config, 'publish.confirm', true),
routingKey: configValue(config, 'publish.routingKey', QueueCredentials.DEFAULT_EXCHANGE_PUBLISH_ROUTING_KEY),
deliveryMode: configValue(config, 'publish.deliveryMode', RbmqMessage.Options.DMODE_PERSISTENT),
messageSizeLimit: configValue(config, 'publish.messageSizeLimit', QueueCredentials.DEFAULT_MESSAGE_SIZE_LIMIT)
}
});
};
QueueCredentials.prototype._parseQueueConfig = function(config) {
if (!config) {
return;
}
var result = filterUndefined({
name: ensureConfigValue(config, 'name', 'Cannot find queue.name in configuration'),
durable: configValue(config, 'durable', true),
autoDelete: configValue(config, 'autoDelete', false),
passive: configValue(config, 'passive', false),
exclusive: configValue(config, 'exclusive', false),
subscribe: {
ack: configValue(config, 'subscribe.ack', true),
prefetchCount: configValue(config, 'subscribe.prefetchCount', 0)
},
bindings: configValue(config, 'bindings', []),
noDeclare: configValue(config, 'noDeclare', false),
ttl: configValue(config, 'ttl'),
});
if (result.exclusive) {
result.name = [result.name, Random.randomString(10, '0123456789')].join('.');
}
if (config.hasOwnProperty('postpone')) {
result.postpone = filterUndefined({
method: configValue(config, 'postpone.method', QueueCredentials.DEFAULT_QUEUE_POSTPONE_METHOD),
skip: configValue(config, 'postpone.skip', []),
strategy: configValue(config, 'postpone.strategy'),
factor: configValue(config, 'postpone.factor'),
exchange: filterUndefined(this._parseExchangeConfig(config.postpone.exchange))
});
if (result.postpone.method == QueueIncomingMessageProcessor.POSTPONE_METHOD.DLE) {
result.deadLetterExchange = configValue(result, 'postpone.exchange.name');
result.deadLetterRoutingKey = configValue(result, 'postpone.exchange.publish.routingKey');
}
}
return filterUndefined(result);
};
QueueCredentials.prototype._parseConfigV3 = function(config) {
console.log('QueueCredentials/_parseConfigV3', this.getCredentialId());
if (config.qaas.version == 3 && config.qaas.hasOwnProperty('v3')) {
config = config.qaas.v3;
}
var result = filterUndefined({
connection: this._parseConnectionConfig(config),
logging: this._parseLoggingConfig(config),
monitoring: this._parseMonitoringConfig(config),
exchange: this._parseExchangeConfig(config.exchange),
queue: this._parseQueueConfig(config.queue)
});
if (result.hasOwnProperty('exchange') && result.exchange.publish.confirm) {
result.connection.clientProperties.capabilities['publisher_confirms'] = true;
}
return result;
};
//FIXME: remove when legacy cluster is dead
QueueCredentials.prototype._parseConfigV2 = function(config) {
console.log('QueueCredentials/_parseConfigV2');
if (config.qaas.hasOwnProperty('v2')) {
config = config.qaas.v2;
}
if (config.hasOwnProperty('queue') && config.queue.hasOwnProperty('postponer') && !Types.isObject(config.queue.postponer)) {
if (!config.queue.hasOwnProperty('postpone')) {
config.queue.postpone = {};
}
config.queue.postpone.exchange = {
name: 'postponer.' + config.queue.postponer
};
}
var result = this._parseConfigV3(config);
if (result.hasOwnProperty('exchange') && config.exchange.hasOwnProperty('policy')) {
result.exchange.name = [config.exchange.policy, result.exchange.name].join('.');
}
if (result.hasOwnProperty('queue') && config.queue.hasOwnProperty('policy')) {
result.queue.name = [config.queue.policy, result.queue.name].join('.');
if (result.queue.hasOwnProperty('postpone')) {
if (Types.isObject(config.queue.postponer) && config.queue.postponer.hasOwnProperty('policy')) {
result.queue.postpone.exchange.name = [config.queue.postponer.policy, result.queue.postpone.exchange.name].join('.');
}
if (!config.queue.hasOwnProperty('postponer') || !config.queue.postponer.hasOwnProperty('routingKey')) {
result.queue.postpone.exchange.publish.routingKey = '_postpone_' + result.queue.name + '_';
}
result.queue.bindings.push({
exchange: 'postponer.postpone',
routingKey: result.queue.postpone.exchange.publish.routingKey
});
}
}
return result;
};
//FIXME: remove when legacy cluster is dead
QueueCredentials.prototype._parseLegacyConfig = function(config) {
console.log('QueueCredentials/_parseLegacyConfig');
var result = {
connection: {
login: config.user,
password: config.pass,
vhost: config.vhost || QueueCredentials.DEFAULT_VHOST,
heartbeat: config.heartbeat || QueueCredentials.DEFAULT_HEARTBEAT,
clientProperties: {
product: 'dl-queue-legacy',
version: QueueVersion + '-' + PackageJson.version,
'x-onet-app': process.env['OPAL_IDENTITY'] || 'unauthorized-app.unknown.onetapi.pl',
'x-onet-segment': process.env['ONET_SEGMENT'] || 'UNKNOWN',
datasource: this.getCredentialId(),
configVersion: 'legacy'
}
},
logging: {
connection: true,
subscribe: true,
publish: true
}
};
result.exchange = {
name: config.exchange,
durable: (config.exchange_durable === 0) ? false : true,
autoDelete: config.exchange_autodelete ? true : false,
passive: config.exchange_passive ? true : false,
type: config.exchange_type || 'topic',
bindings: [],
publish: {
confirm: true,
deliveryMode: RbmqMessage.Options.DMODE_PERSISTENT
}
};
var queueSuffix = config.queue_suffix;
var queueName = config.exchange + '_' + (queueSuffix ? queueSuffix : Random.randomString(10));
result.queue = {
name: queueName,
durable: (config.queue_durable === 0) ? false : true,
autoDelete: config.queue_auto_delete ? true : false,
passive: config.queue_passive ? true : false,
exclusive: config.queue_exclusive ? true : false,
subscribe: {
ack: true,
prefetchCount: 0
},
bindings: []
};
if (Types.isArray(config.routing_key)) {
for (var i = 0, l = config.routing_key.length; i < l; i++) {
result.queue.bindings.push({
exchange: config.exchange,
routingKey: config.routing_key[i]
});
}
} else {
result.queue.bindings = [{
exchange: config.exchange,
routingKey: config.routing_key
}];
result.exchange.publish.routingKey = config.routing_key;
}
if (config.prefetch_count) {
result.queue.subscribe.prefetchCount = config.prefetch_count;
}
return result;
};
QueueCredentials.prototype.getConnectionConfig = function() {
var hosts = this._credentials.hosts;
var config = this._parsedConfig.connection;
config.hosts = hosts.map(function(hostStr) {
var hostArr = hostStr.split('_');
return {
host: hostArr[0],
port: hostArr[1]
};
});
return config;
};
QueueCredentials.prototype.getMonitoringConfig = function () {
return this._parsedConfig.monitoring;
};
QueueCredentials.prototype.getLoggingConfig = function () {
return this._parsedConfig.logging;
};
QueueCredentials.prototype.getExchangeConfig = function () {
return this._parsedConfig.exchange;
};
QueueCredentials.prototype.getQueueConfig = function () {
return this._parsedConfig.queue;
};
QueueCredentials.prototype.destroy = function () {
this.removeAllEventListeners();
this._cp.destroy();
};
QueueCredentials.Event = {};
QueueCredentials.Event.LOADED = 'QueueCredentials.Event.LOADED';
QueueCredentials.DEFAULT_MESSAGE_SIZE_LIMIT = 50 * 1024;
QueueCredentials.DEFAULT_VHOST = '/';
QueueCredentials.DEFAULT_HEARTBEAT = 5;
QueueCredentials.DEFAULT_CONNECTION_TIMEOUT = 500;
QueueCredentials.DEFAULT_EXCHANGE_TYPE = 'topic';
QueueCredentials.DEFAULT_EXCHANGE_PUBLISH_ROUTING_KEY = '';
QueueCredentials.DEFAULT_QUEUE_POSTPONE_METHOD = 'reject';
exports.QueueCredentials = QueueCredentials;