123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375 |
- /**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- var App = require('app');
- /**
- * @typedef {object} initializer
- * @property {string} type initializer type name
- * @property {boolean} [isChecker] determines control flow callback
- */
- /**
- * @typedef {object} initializerType
- * @property {string} name key
- * @property {string} method function's name (prefer to start method-name with '_init' or '_initAs'). Each method here is called with arguments equal to <code>initialValue</code>-call args. Initializer-settings are added as last argument
- */
- /**
- * Minimal fields-list that should be in the config-object
- * There is no way to set value without any of them
- *
- * @typedef {object} configProperty
- * @property {string} name config's name
- * @property {number|string} value current value
- * @property {string} filename file name where this config is
- * @property {number|string} [recommendedValue] value which is recommended
- */
- /**
- * Basic class for config-initializers
- * Each child should fill <code>initializers</code> or <code>uniqueInitializers</code> and <code>initializerTypes</code>
- * Usage:
- * <pre>
- * var myCoolInitializer = App.ConfigInitializerClass.create({
- * initializers: {
- * 'my-cool-config': {
- * type: 'some_type'
- * }
- * },
- *
- * initializerTypes: {
- * some_type: {
- * method: '_initAsCool'
- * }
- * },
- *
- * _initAsCool: function (configProperty, localDB, dependencies, initializer) {
- * // some magic
- * return configProperty;
- * }
- * });
- *
- * var myConfig = { name: 'my-cool-config' };
- * var localDB = getLocalDB();
- * var dependencies = {};
- * myCoolInitializer.initialValue(myConfig, localDB, dependencies);
- * </pre>
- * <code>dependencies</code> - it's an object with almost any information that might be needed to set config's value. It
- * shouldn't contain App.*-flags like 'isHaEnabled' or 'isHadoop2Stack'. They might be accessed directly from App. But something
- * bigger data should be pulled to the <code>dependencies</code>.
- * Information about cluster's hosts, topology of master components should be in the <code>localDB</code>
- *
- * @type {ConfigInitializerClass}
- * @augments {Em.Object}
- */
- App.ConfigInitializerClass = Em.Object.extend({
- _initializerFlowCode: {
- next: 0,
- skipNext: 1,
- skipAll: 2
- },
- concatenatedProperties: ['initializerTypes'],
- /**
- * Map with configurations for config initializers
- * It's used only for initializers which are common for some configs (if not - use <code>uniqueInitializers</code>-map)
- * Key {string} configProperty-name
- * Value {initializer|initializer[]} settings for initializer
- *
- * @type {object}
- */
- initializers: {},
- /**
- * Map with initializers types
- * It's not overridden in the child-classes (@see Ember's concatenatedProperties)
- *
- * @type {initializerType[]}
- */
- initializerTypes: [],
- /**
- * Map with initializers that are used only for one config (are unique)
- * Key: configProperty-name
- * Value: method-name
- * Every method from this map is called with same arguments as <code>initialValue</code> is (prefer to start method-name with '_init' or '_initAs')
- *
- * @type {object}
- */
- uniqueInitializers: {},
- /**
- * Wrapper for common initializers
- * Execute initializer if it is a function or throw an error otherwise
- *
- * @param {configProperty} configProperty
- * @param {topologyLocalDB} localDB
- * @param {object} dependencies
- * @returns {Object}
- * @private
- */
- _defaultInitializer: function (configProperty, localDB, dependencies) {
- var args = [].slice.call(arguments);
- var self = this;
- var initializers = this.get('initializers');
- var initializerTypes = this.get('initializerTypes');
- var initializer = initializers[Em.get(configProperty, 'name')];
- if (initializer) {
- initializer = Em.makeArray(initializer);
- var i = 0;
- while(i < initializer.length) {
- var init = initializer[i];
- var _args = [].slice.call(args);
- var type = initializerTypes.findProperty('name', init.type);
- // add initializer-settings
- _args.push(init);
- var methodName = type.method;
- Em.assert('method-initializer is not a function ' + methodName, 'function' === Em.typeOf(self[methodName]));
- if (init.isChecker) {
- var result = self[methodName].apply(self, _args);
- if (result === this.flowSkipNext()) {
- i++; // skip next
- }
- else {
- if (result === this.flowSkipAll()) {
- break;
- }
- }
- }
- else {
- configProperty = self[methodName].apply(self, _args);
- }
- i++;
- }
- }
- return configProperty;
- },
- /**
- * Entry-point for any config's value initializing
- * Before calling it, be sure that <code>initializers</code> or <code>uniqueInitializers</code>
- * contains record about needed configs
- *
- * @param {configProperty} configProperty
- * @param {topologyLocalDB} localDB
- * @param {object} dependencies
- * @returns {Object}
- * @method initialValue
- */
- initialValue: function (configProperty, localDB, dependencies) {
- var configName = Em.get(configProperty, 'name');
- var initializers = this.get('initializers');
- var initializer = initializers[configName];
- if (initializer) {
- return this._defaultInitializer(configProperty, localDB, dependencies);
- }
- var uniqueInitializers = this.get('uniqueInitializers');
- var uniqueInitializer = uniqueInitializers[configName];
- if (uniqueInitializer) {
- var args = [].slice.call(arguments);
- return this[uniqueInitializer].apply(this, args);
- }
- Em.set(configProperty, 'initialValue', Em.get(configProperty, 'value'));
- return configProperty;
- },
- /**
- * Should do some preparing for initializing-process
- * Shouldn't be redefined without needs
- * Should be used before any <code>initialValue</code>-calls (if needed)
- *
- * @method setup
- */
- setup: Em.K,
- /**
- * Should restore Initializer's to the initial state
- * Basically, should revert changes done in the <code>setup</code>
- *
- * @method cleanup
- */
- cleanup: Em.K,
- /**
- * Setup <code>initializers</code>
- * There are some configs with names based on other config's values.
- * Example: config with name <code>hadoop.proxyuser.{{oozieUser}}.hosts</code>
- * Here <code>{{oozieUser}}</code> is value of the config <code>['oozie-env']['oozie_user']</code>.
- * So, when <code>initializers</code> are manually set up in the Initializer-instance, there is no way to populate initializer
- * for such configs. Reason: each initializer's key is a config-name which is compared strictly to the provided config-name.
- * Solution: use config-names with placeholders. Example: <code>hadoop.proxyuser.${oozieUser}.hosts</code>
- * In this case, before calling <code>initialValue</code> this key should be updated with real value. And it should be done
- * in the common way for the all such configs.
- * Code example:
- * <pre>
- * var mySettings = {
- * oozieUser: 'someDude'
- * };
- *
- * var myCoolInitializer = App.MoveComponentConfigInitializerClass.create({
- * initializers: {
- * 'hadoop.proxyuser.{{oozieUser}}.hosts': {
- * // some setting are here
- * }
- * },
- *
- * setup: function (settings) {
- * this._updateInitializers(settings);
- * },
- *
- * cleanup: function () {
- * this._restoreInitializers();
- * }
- *
- * });
- *
- * myCoolInitializer.setup(mySettings); // after this call `myCoolInitializer.initializers` will contain two keys
- * console.log(myCoolInitializer.initializers); // {'hadoop.proxyuser.{{oozieUser}}.hosts': {}, 'hadoop.proxyuser.someDude.hosts': {}}
- * // it's possible to call `initialValue` now
- * myCoolInitializer.initialValue({name: 'hadoop.proxyuser.someDude.hosts', value: ''}, {}, {});
- * myCoolInitializer.cleanup(); // after updating values for the all configs
- * </pre>
- * Additional key in the <code>initializers</code> won't cause any issues.
- * Keep in mind replacing-rules:
- * <ul>
- * <li>Each field in the <code>settings</code> should be a string or number</li>
- * <li>Substring that will be replaced should be wrapped with '{{', '}}'</li>
- * <li>Each field is searched in the each initializers-key, so try to avoid names-collision</li>
- * <li>Value for 'new' key is the same as for 'old' ('hadoop.proxyuser.{{oozieUser}}.hosts' and 'hadoop.proxyuser.someDude.hosts' will have same initializer)</li>
- * </ul>
- * <b>Important! Be sure, that you call <code>_restoreInitializers</code> before calling <code>_updateInitializers</code> second time</b>
- *
- * @param {object} settings
- * @method _updateInitializers
- */
- _updateInitializers: function (settings) {
- settings = settings || {};
- var originalInitializers = this.get('initializers');
- var copyInitializers = Em.copy(originalInitializers, true);
- this.set('__copyInitializers', copyInitializers);
- var initializers = this._updateNames('initializers', settings);
- this._setForComputed('initializers', initializers);
- var originalUniqueInitializers = this.get('uniqueInitializers');
- var copyUniqueInitializers = Em.copy(originalUniqueInitializers, true);
- this.set('__copyUniqueInitializers', copyUniqueInitializers);
- var uniqueInitializers = this._updateNames('uniqueInitializers', settings);
- this._setForComputed('uniqueInitializers', uniqueInitializers);
- },
- /**
- * Revert names changes done in the <code>_updateInitializers</code>
- *
- * @method _restoreInitializers
- */
- _restoreInitializers: function() {
- var copyInitializers = this.get('__copyInitializers');
- var copyUniqueInitializers = this.get('__copyUniqueInitializers');
- if ('object' === Em.typeOf(copyInitializers)) {
- this._setForComputed('initializers', Em.copy(copyInitializers, true));
- }
- if ('object' === Em.typeOf(copyUniqueInitializers)) {
- this._setForComputed('uniqueInitializers', Em.copy(copyUniqueInitializers, true));
- }
- },
- /**
- * Replace list of <code>settings</code> in the sourceKey
- * Example:
- * <pre>
- * var sourceKey = '{{A}},{{B}},{{C}}';
- * var settings = {A: 'a', B: 'b', C: 'c'};
- * sourceKey = _updateNames(sourceKey, settings);
- * console.log(sourceKey); // 'a,b,c'
- * </pre>
- *
- * @param {string} sourceKey
- * @param {object} settings
- * @returns {object}
- * @private
- * @method _updateNames
- */
- _updateNames: function (sourceKey, settings) {
- settings = settings || {};
- var source = this.get(sourceKey);
- Object.keys(source).forEach(function (configName) {
- var initializer = source[configName];
- Object.keys(settings).forEach(function (key) {
- var replaceWith = settings[key];
- var toReplace = '{{' + key + '}}';
- configName = configName.replace(toReplace, replaceWith);
- });
- source[configName] = initializer;
- });
- return source;
- },
- flowNext: function() {
- return this.get('_initializerFlowCode.next');
- },
- flowSkipNext: function() {
- return this.get('_initializerFlowCode.skipNext');
- },
- flowSkipAll: function() {
- return this.get('_initializerFlowCode.skipAll');
- },
- /**
- * Set value for computed property using `reopen`. Currently used to update 'initializers'
- * and 'uniqueInitializers'.
- * Used to set value for props like:
- * <code>cp: function() { }.property()</code>
- * <code>
- * var obj = App.ConfigInitializerClass.create({
- * cp: function() {
- * return {
- * key: "value"
- * }
- * }.property(),
- * setProp: function() {
- * this.set('cp', {newKey: "new_value"}); // will not change `cp` value
- * },
- * updateProp: function() {
- * this._setForComputed('cp', { newKey: "new_value"}); // will update
- * }
- * });
- *
- * obj.get('cp'); // {key: "value"}
- * obj.setProp();
- * obj.get('cp'); // {key: "value"}
- * obj.updateProp();
- * obj.get('cp'); // {newKey: "new_value"}
- * </code>
- * @private
- * @param {string} key
- * @param {*} value
- */
- _setForComputed: function(key, value) {
- var obj = {};
- obj[key] = function() {
- return value;
- }.property();
- this.reopen(obj);
- }
- });
|