You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by vo...@apache.org on 2016/11/09 08:38:41 UTC

[27/50] [abbrv] ignite git commit: Web console beta-5.

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/states/configuration/summary/summary.controller.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration/summary/summary.controller.js b/modules/web-console/frontend/app/modules/states/configuration/summary/summary.controller.js
index 4aad7e2..4bfbf48 100644
--- a/modules/web-console/frontend/app/modules/states/configuration/summary/summary.controller.js
+++ b/modules/web-console/frontend/app/modules/states/configuration/summary/summary.controller.js
@@ -20,8 +20,8 @@ import JSZip from 'jszip';
 import saver from 'file-saver';
 
 export default [
-    '$rootScope', '$scope', '$http', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteLoading', '$filter', 'igniteConfigurationResource', 'JavaTypes', 'IgniteVersion', 'GeneratorDocker', 'GeneratorPom', 'IgniteFormUtils',
-    function($root, $scope, $http, LegacyUtils, Messages, Loading, $filter, Resource, JavaTypes, Version, docker, pom, FormUtils) {
+    '$rootScope', '$scope', '$http', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteLoading', '$filter', 'IgniteConfigurationResource', 'JavaTypes', 'IgniteVersion', 'IgniteConfigurationGenerator', 'SpringTransformer', 'JavaTransformer', 'GeneratorDocker', 'GeneratorPom', 'IgnitePropertiesGenerator', 'IgniteReadmeGenerator', 'IgniteFormUtils',
+    function($root, $scope, $http, LegacyUtils, Messages, Loading, $filter, Resource, JavaTypes, Version, generator, spring, java, docker, pom, propsGenerator, readme, FormUtils) {
         const ctrl = this;
 
         $scope.ui = { ready: false };
@@ -108,11 +108,18 @@ export default [
             ]
         };
 
+        const clnCfg = { type: 'file', name: 'client.xml' };
+        const srvCfg = { type: 'file', name: 'server.xml' };
+
         const resourcesFolder = {
             type: 'folder',
             name: 'resources',
             children: [
-                { type: 'file', name: 'secret.properties' }
+                {
+                    type: 'folder',
+                    name: 'META-INF',
+                    children: [clnCfg, srvCfg]
+                }
             ]
         };
 
@@ -131,10 +138,6 @@ export default [
             ]
         };
 
-        const clnCfg = { type: 'file', name: 'client.xml' };
-
-        const srvCfg = { type: 'file', name: 'server.xml' };
-
         const mainFolder = {
             type: 'folder',
             name: 'main',
@@ -147,11 +150,6 @@ export default [
             children: [
                 {
                     type: 'folder',
-                    name: 'config',
-                    children: [clnCfg, srvCfg]
-                },
-                {
-                    type: 'folder',
                     name: 'jdbc-drivers',
                     children: [
                         { type: 'file', name: 'README.txt' }
@@ -215,6 +213,16 @@ export default [
                 folder.children.push(leaf);
         }
 
+        function cacheHasDatasource(cache) {
+            if (cache.cacheStoreFactory && cache.cacheStoreFactory.kind) {
+                const storeFactory = cache.cacheStoreFactory[cache.cacheStoreFactory.kind];
+
+                return !!(storeFactory && (storeFactory.connectVia ? (storeFactory.connectVia === 'DataSource' ? storeFactory.dialect : false) : storeFactory.dialect)); // eslint-disable-line no-nested-ternary
+            }
+
+            return false;
+        }
+
         $scope.selectItem = (cluster) => {
             delete ctrl.cluster;
 
@@ -231,17 +239,17 @@ export default [
 
             sessionStorage.summarySelectedId = $scope.clusters.indexOf(cluster);
 
-            mainFolder.children = [javaFolder];
+            mainFolder.children = [javaFolder, resourcesFolder];
 
             if (_.find(cluster.caches, (cache) => !_.isNil(cache.cacheStoreFactory)))
                 javaFolder.children = [javaConfigFolder, loadFolder, javaStartupFolder];
             else
                 javaFolder.children = [javaConfigFolder, javaStartupFolder];
 
-            if ($generatorCommon.secretPropertiesNeeded(cluster))
-                mainFolder.children.push(resourcesFolder);
+            if (_.nonNil(_.find(cluster.caches, cacheHasDatasource)) || cluster.sslEnabled)
+                resourcesFolder.children.push({ type: 'file', name: 'secret.properties' });
 
-            if ($generatorJava.isDemoConfigured(cluster, $root.IgniteDemoMode))
+            if (java.isDemoConfigured(cluster, $root.IgniteDemoMode))
                 javaFolder.children.push(demoFolder);
 
             if (cluster.discovery.kind === 'Jdbc' && cluster.discovery.Jdbc.dialect)
@@ -286,7 +294,6 @@ export default [
         // TODO IGNITE-2114: implemented as independent logic for download.
         $scope.downloadConfiguration = function() {
             const cluster = $scope.cluster;
-            const clientNearCfg = cluster.clientNearCfg;
 
             const zip = new JSZip();
 
@@ -299,54 +306,65 @@ export default [
             zip.file('Dockerfile', ctrl.data.docker);
             zip.file('.dockerignore', docker.ignoreFile());
 
-            const builder = $generatorProperties.generateProperties(cluster);
+            const cfg = generator.igniteConfiguration(cluster, false);
+            const clientCfg = generator.igniteConfiguration(cluster, true);
+            const clientNearCaches = _.filter(cluster.caches, (cache) => _.get(cache, 'clientNearConfiguration.enabled'));
+
+            const secProps = propsGenerator.generate(cfg);
 
-            if (builder)
-                zip.file('src/main/resources/secret.properties', builder.asString());
+            if (secProps)
+                zip.file('src/main/resources/secret.properties', secProps);
 
-            const srcPath = 'src/main/java/';
+            const srcPath = 'src/main/java';
+            const resourcesPath = 'src/main/resources';
 
-            const serverXml = 'config/' + cluster.name + '-server.xml';
-            const clientXml = 'config/' + cluster.name + '-client.xml';
+            const serverXml = `${cluster.name}-server.xml`;
+            const clientXml = `${cluster.name}-client.xml`;
 
-            zip.file(serverXml, $generatorXml.cluster(cluster));
-            zip.file(clientXml, $generatorXml.cluster(cluster, clientNearCfg));
+            const metaPath = `${resourcesPath}/META-INF`;
 
-            zip.file(srcPath + 'config/ServerConfigurationFactory.java', $generatorJava.cluster(cluster, 'config', 'ServerConfigurationFactory', null));
-            zip.file(srcPath + 'config/ClientConfigurationFactory.java', $generatorJava.cluster(cluster, 'config', 'ClientConfigurationFactory', clientNearCfg));
+            zip.file(`${metaPath}/${serverXml}`, spring.igniteConfiguration(cfg).asString());
+            zip.file(`${metaPath}/${clientXml}`, spring.igniteConfiguration(clientCfg, clientNearCaches).asString());
 
-            if ($generatorJava.isDemoConfigured(cluster, $root.IgniteDemoMode)) {
-                zip.file(srcPath + 'demo/DemoStartup.java', $generatorJava.nodeStartup(cluster, 'demo', 'DemoStartup',
+            const cfgPath = `${srcPath}/config`;
+
+            zip.file(`${cfgPath}/ServerConfigurationFactory.java`, java.igniteConfiguration(cfg, 'config', 'ServerConfigurationFactory').asString());
+            zip.file(`${cfgPath}/ClientConfigurationFactory.java`, java.igniteConfiguration(cfg, 'config', 'ClientConfigurationFactory', clientNearCaches).asString());
+
+            if (java.isDemoConfigured(cluster, $root.IgniteDemoMode)) {
+                zip.file(`${srcPath}/demo/DemoStartup.java`, java.nodeStartup(cluster, 'demo.DemoStartup',
                     'ServerConfigurationFactory.createConfiguration()', 'config.ServerConfigurationFactory'));
             }
 
             // Generate loader for caches with configured store.
-            const cachesToLoad = _.filter(cluster.caches, (cache) => !_.isNil(cache.cacheStoreFactory));
+            const cachesToLoad = _.filter(cluster.caches, (cache) => _.nonNil(cache.cacheStoreFactory));
+
+            if (_.nonEmpty(cachesToLoad))
+                zip.file(`${srcPath}/load/LoadCaches.java`, java.loadCaches(cachesToLoad, 'load', 'LoadCaches', `"${clientXml}"`));
 
-            if (!_.isEmpty(cachesToLoad))
-                zip.file(srcPath + 'load/LoadCaches.java', $generatorJava.loadCaches(cachesToLoad, 'load', 'LoadCaches', '"' + clientXml + '"'));
+            const startupPath = `${srcPath}/startup`;
 
-            zip.file(srcPath + 'startup/ServerNodeSpringStartup.java', $generatorJava.nodeStartup(cluster, 'startup', 'ServerNodeSpringStartup', '"' + serverXml + '"'));
-            zip.file(srcPath + 'startup/ClientNodeSpringStartup.java', $generatorJava.nodeStartup(cluster, 'startup', 'ClientNodeSpringStartup', '"' + clientXml + '"'));
+            zip.file(`${startupPath}/ServerNodeSpringStartup.java`, java.nodeStartup(cluster, 'startup.ServerNodeSpringStartup', `"${serverXml}"`));
+            zip.file(`${startupPath}/ClientNodeSpringStartup.java`, java.nodeStartup(cluster, 'startup.ClientNodeSpringStartup', `"${clientXml}"`));
 
-            zip.file(srcPath + 'startup/ServerNodeCodeStartup.java', $generatorJava.nodeStartup(cluster, 'startup', 'ServerNodeCodeStartup',
+            zip.file(`${startupPath}/ServerNodeCodeStartup.java`, java.nodeStartup(cluster, 'startup.ServerNodeCodeStartup',
                 'ServerConfigurationFactory.createConfiguration()', 'config.ServerConfigurationFactory'));
-            zip.file(srcPath + 'startup/ClientNodeCodeStartup.java', $generatorJava.nodeStartup(cluster, 'startup', 'ClientNodeCodeStartup',
-                'ClientConfigurationFactory.createConfiguration()', 'config.ClientConfigurationFactory', clientNearCfg));
+            zip.file(`${startupPath}/ClientNodeCodeStartup.java`, java.nodeStartup(cluster, 'startup.ClientNodeCodeStartup',
+                'ClientConfigurationFactory.createConfiguration()', 'config.ClientConfigurationFactory', clientNearCaches));
 
-            zip.file('pom.xml', pom.generate(cluster, Version.ignite).asString());
+            zip.file('pom.xml', pom.generate(cluster, Version.productVersion().ignite).asString());
 
-            zip.file('README.txt', $generatorReadme.readme().asString());
-            zip.file('jdbc-drivers/README.txt', $generatorReadme.readmeJdbc().asString());
+            zip.file('README.txt', readme.generate());
+            zip.file('jdbc-drivers/README.txt', readme.generateJDBC());
 
-            if (!ctrl.data.pojos)
-                ctrl.data.pojos = $generatorJava.pojos(cluster.caches);
+            if (_.isEmpty(ctrl.data.pojos))
+                ctrl.data.pojos = java.pojos(cluster.caches);
 
             for (const pojo of ctrl.data.pojos) {
                 if (pojo.keyClass && JavaTypes.nonBuiltInClass(pojo.keyType))
-                    zip.file(srcPath + pojo.keyType.replace(/\./g, '/') + '.java', pojo.keyClass);
+                    zip.file(`${srcPath}/${pojo.keyType.replace(/\./g, '/')}.java`, pojo.keyClass);
 
-                zip.file(srcPath + pojo.valueType.replace(/\./g, '/') + '.java', pojo.valueClass);
+                zip.file(`${srcPath}/${pojo.valueType.replace(/\./g, '/')}.java`, pojo.valueClass);
             }
 
             $generatorOptional.optionalContent(zip, cluster);

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/services/ErrorPopover.service.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/services/ErrorPopover.service.js b/modules/web-console/frontend/app/services/ErrorPopover.service.js
index 3130431..5132d50 100644
--- a/modules/web-console/frontend/app/services/ErrorPopover.service.js
+++ b/modules/web-console/frontend/app/services/ErrorPopover.service.js
@@ -108,7 +108,7 @@ export default class ErrorPopover {
         if (this._popover)
             this._popover.hide();
 
-        if (ui) {
+        if (ui && ui.isPanelLoaded) {
             this.FormUtils.ensureActivePanel(ui, panelId, id);
 
             this.$timeout(() => this._show(id, message, showTime), ui.isPanelLoaded(panelId) ? 200 : 500);

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/services/FormUtils.service.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/services/FormUtils.service.js b/modules/web-console/frontend/app/services/FormUtils.service.js
index 5e7943a..6ccc3c6 100644
--- a/modules/web-console/frontend/app/services/FormUtils.service.js
+++ b/modules/web-console/frontend/app/services/FormUtils.service.js
@@ -17,7 +17,7 @@
 
 export default ['IgniteFormUtils', ['$window', 'IgniteFocus', ($window, Focus) => {
     function ensureActivePanel(ui, pnl, focusId) {
-        if (ui) {
+        if (ui && ui.loadPanel) {
             const collapses = $('div.panel-collapse');
 
             ui.loadPanel(pnl);
@@ -430,6 +430,10 @@ export default ['IgniteFormUtils', ['$window', 'IgniteFocus', ($window, Focus) =
                     return _.includes(this.loadedPanels, pnl);
                 }
             };
+        },
+        markPristineInvalidAsDirty(ngModelCtrl) {
+            if (ngModelCtrl && ngModelCtrl.$invalid && ngModelCtrl.$pristine)
+                ngModelCtrl.$setDirty();
         }
     };
 }]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/services/JavaTypes.service.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/services/JavaTypes.service.js b/modules/web-console/frontend/app/services/JavaTypes.service.js
index 8cb87be..679914f 100644
--- a/modules/web-console/frontend/app/services/JavaTypes.service.js
+++ b/modules/web-console/frontend/app/services/JavaTypes.service.js
@@ -15,8 +15,6 @@
  * limitations under the License.
  */
 
-import _ from 'lodash';
-
 // Java built-in class names.
 import JAVA_CLASSES from '../data/java-classes.json';
 
@@ -42,6 +40,42 @@ const VALID_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-
  * Utility service for various check on java types.
  */
 export default class JavaTypes {
+    static $inject = ['igniteClusterDefaults', 'igniteCacheDefaults', 'igniteIgfsDefaults'];
+
+    constructor(clusterDflts, cacheDflts, igfsDflts) {
+        this.enumClasses = _.uniq(this._enumClassesAcc(_.merge(clusterDflts, cacheDflts, igfsDflts), []));
+        this.shortEnumClasses = _.map(this.enumClasses, (cls) => this.shortClassName(cls));
+    }
+
+    /**
+     * Collects recursive enum classes.
+     *
+     * @param root Root object.
+     * @param classes Collected classes.
+     * @return {Array.<String>}
+     * @private
+     */
+    _enumClassesAcc(root, classes) {
+        return _.reduce(root, (acc, val, key) => {
+            if (key === 'clsName')
+                acc.push(val);
+            else if (_.isObject(val))
+                this._enumClassesAcc(val, acc);
+
+            return acc;
+        }, classes);
+    }
+
+    /**
+     * Check if class name is non enum class in Ignite configuration.
+     *
+     * @param clsName
+     * @return {boolean}
+     */
+    nonEnum(clsName) {
+        return !_.includes(this.shortEnumClasses, clsName) && !_.includes(this.enumClasses, clsName);
+    }
+
     /**
      * @param clsName {String} Class name to check.
      * @returns {boolean} 'true' if provided class name is a not Java built in class.
@@ -52,7 +86,7 @@ export default class JavaTypes {
 
     /**
      * @param clsName Class name to check.
-     * @returns Full class name for java build-in types or source class otherwise.
+     * @returns {String} Full class name for java build-in types or source class otherwise.
      */
     fullClassName(clsName) {
         const type = _.find(JAVA_CLASSES, (clazz) => clsName === clazz.short);
@@ -61,6 +95,23 @@ export default class JavaTypes {
     }
 
     /**
+     * Extract class name from full class name.
+     *
+     * @param clsName full class name.
+     * @return {String} Class name.
+     */
+    shortClassName(clsName) {
+        if (this.isJavaPrimitive(clsName))
+            return clsName;
+
+        const fullClsName = this.fullClassName(clsName);
+
+        const dotIdx = fullClsName.lastIndexOf('.');
+
+        return dotIdx > 0 ? fullClsName.substr(dotIdx + 1) : fullClsName;
+    }
+
+    /**
      * @param value {String} Value text to check.
      * @returns {boolean} 'true' if given text is valid Java class name.
      */
@@ -115,4 +166,17 @@ export default class JavaTypes {
     isJavaPrimitive(clsName) {
         return _.includes(JAVA_PRIMITIVES, clsName);
     }
+
+    /**
+     * Convert some name to valid java name.
+     *
+     * @param prefix To append to java name.
+     * @param name to convert.
+     * @returns {string} Valid java name.
+     */
+    toJavaName(prefix, name) {
+        const javaName = name ? this.shortClassName(name).replace(/[^A-Za-z_0-9]+/g, '_') : 'dflt';
+
+        return prefix + javaName.charAt(0).toLocaleUpperCase() + javaName.slice(1);
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/services/LegacyTable.service.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/services/LegacyTable.service.js b/modules/web-console/frontend/app/services/LegacyTable.service.js
index 5d9ec9d..a024a3b 100644
--- a/modules/web-console/frontend/app/services/LegacyTable.service.js
+++ b/modules/web-console/frontend/app/services/LegacyTable.service.js
@@ -19,7 +19,27 @@
 export default ['IgniteLegacyTable',
     ['IgniteLegacyUtils', 'IgniteFocus', 'IgniteErrorPopover', (LegacyUtils, Focus, ErrorPopover) => {
         function _model(item, field) {
-            return LegacyUtils.getModel(item, field);
+            let path = field.path;
+
+            if (_.isNil(path) || _.isNil(item))
+                return item;
+
+            path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
+            path = path.replace(/^\./, '');           // strip a leading dot
+
+            const segs = path.split('.');
+            let root = item;
+
+            while (segs.length > 0) {
+                const pathStep = segs.shift();
+
+                if (typeof root[pathStep] === 'undefined')
+                    root[pathStep] = {};
+
+                root = root[pathStep];
+            }
+
+            return root;
         }
 
         const table = {name: 'none', editIndex: -1};
@@ -190,7 +210,7 @@ export default ['IgniteLegacyTable',
                     }
                 }
 
-                return valid;
+                return valid || stopEdit;
             },
             tablePairSaveVisible(field, index) {
                 const pairValue = _tablePairValue(field, index);

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/services/LegacyUtils.service.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/services/LegacyUtils.service.js b/modules/web-console/frontend/app/services/LegacyUtils.service.js
index dcf0bc8..e7c064b 100644
--- a/modules/web-console/frontend/app/services/LegacyUtils.service.js
+++ b/modules/web-console/frontend/app/services/LegacyUtils.service.js
@@ -182,43 +182,19 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => {
 
     const VALID_JAVA_IDENTIFIER = new RegExp('^[a-zA-Z_$][a-zA-Z\\d_$]*$');
 
-    function isValidJavaIdentifier(msg, ident, elemId, panels, panelId) {
+    function isValidJavaIdentifier(msg, ident, elemId, panels, panelId, stopEdit) {
         if (isEmptyString(ident))
-            return ErrorPopover.show(elemId, msg + ' is invalid!', panels, panelId);
+            return !stopEdit && ErrorPopover.show(elemId, msg + ' is invalid!', panels, panelId);
 
         if (_.includes(JAVA_KEYWORDS, ident))
-            return ErrorPopover.show(elemId, msg + ' could not contains reserved java keyword: "' + ident + '"!', panels, panelId);
+            return !stopEdit && ErrorPopover.show(elemId, msg + ' could not contains reserved java keyword: "' + ident + '"!', panels, panelId);
 
         if (!VALID_JAVA_IDENTIFIER.test(ident))
-            return ErrorPopover.show(elemId, msg + ' contains invalid identifier: "' + ident + '"!', panels, panelId);
+            return !stopEdit && ErrorPopover.show(elemId, msg + ' contains invalid identifier: "' + ident + '"!', panels, panelId);
 
         return true;
     }
 
-    function getModel(obj, field) {
-        let path = field.path;
-
-        if (!isDefined(path) || !isDefined(obj))
-            return obj;
-
-        path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
-        path = path.replace(/^\./, '');           // strip a leading dot
-
-        const segs = path.split('.');
-        let root = obj;
-
-        while (segs.length > 0) {
-            const pathStep = segs.shift();
-
-            if (typeof root[pathStep] === 'undefined')
-                root[pathStep] = {};
-
-            root = root[pathStep];
-        }
-
-        return root;
-    }
-
     /**
      * Extract datasource from cache or cluster.
      *
@@ -226,18 +202,26 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => {
      * @returns {*} Datasource object or null if not set.
      */
     function extractDataSource(object) {
+        let datasource = null;
+
         // Extract from cluster object
         if (_.get(object, 'discovery.kind') === 'Jdbc') {
-            const datasource = object.discovery.Jdbc;
+            datasource = object.discovery.Jdbc;
+
+            if (datasource.dataSourceBean && datasource.dialect)
+                return datasource;
+        } // Extract from JDBC checkpoint configuration.
+        else if (_.get(object, 'kind') === 'JDBC') {
+            datasource = object.JDBC;
 
             if (datasource.dataSourceBean && datasource.dialect)
                 return datasource;
         } // Extract from cache object
         else if (_.get(object, 'cacheStoreFactory.kind')) {
-            const storeFactory = object.cacheStoreFactory[object.cacheStoreFactory.kind];
+            datasource = object.cacheStoreFactory[object.cacheStoreFactory.kind];
 
-            if (storeFactory.dialect || (storeFactory.connectVia === 'DataSource'))
-                return storeFactory;
+            if (datasource.dialect || (datasource.connectVia === 'DataSource'))
+                return datasource;
         }
 
         return null;
@@ -268,10 +252,13 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => {
      * Compare datasources of caches or clusters.
      *
      * @param firstObj First cache or cluster.
+     * @param firstType Type of first object to compare.
      * @param secondObj Second cache or cluster.
+     * @param secondType Type of first object to compare.
+     * @param index Index of invalid object when check is failed.
      * @returns {*} Check result object.
      */
-    function compareDataSources(firstObj, secondObj) {
+    function compareDataSources(firstObj, firstType, secondObj, secondType, index) {
         const firstDs = extractDataSource(firstObj);
         const secondDs = extractDataSource(secondObj);
 
@@ -280,7 +267,7 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => {
             const secondDB = secondDs.dialect;
 
             if (firstDs.dataSourceBean === secondDs.dataSourceBean && firstDB !== secondDB)
-                return {checked: false, firstObj, firstDB, secondObj, secondDB};
+                return {checked: false, firstObj, firstDs, firstType, secondObj, secondDs, secondType, index};
         }
 
         return DS_CHECK_SUCCESS;
@@ -303,7 +290,6 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => {
     }
 
     return {
-        getModel,
         mkOptions(options) {
             return _.map(options, (option) => {
                 return {value: option, label: isDefined(option) ? option : 'Not set'};
@@ -326,24 +312,24 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => {
         javaBuiltInTypes,
         isJavaBuiltInClass,
         isValidJavaIdentifier,
-        isValidJavaClass(msg, ident, allowBuiltInClass, elemId, packageOnly, panels, panelId) {
+        isValidJavaClass(msg, ident, allowBuiltInClass, elemId, packageOnly, panels, panelId, stopEdit = false) {
             if (isEmptyString(ident))
-                return ErrorPopover.show(elemId, msg + ' could not be empty!', panels, panelId);
+                return !stopEdit && ErrorPopover.show(elemId, msg + ' could not be empty!', panels, panelId);
 
             const parts = ident.split('.');
 
             const len = parts.length;
 
             if (!allowBuiltInClass && isJavaBuiltInClass(ident))
-                return ErrorPopover.show(elemId, msg + ' should not be the Java build-in class!', panels, panelId);
+                return !stopEdit && ErrorPopover.show(elemId, msg + ' should not be the Java build-in class!', panels, panelId);
 
             if (len < 2 && !isJavaBuiltInClass(ident) && !packageOnly)
-                return ErrorPopover.show(elemId, msg + ' does not have package specified!', panels, panelId);
+                return !stopEdit && ErrorPopover.show(elemId, msg + ' does not have package specified!', panels, panelId);
 
             for (let i = 0; i < parts.length; i++) {
                 const part = parts[i];
 
-                if (!isValidJavaIdentifier(msg, part, elemId, panels, panelId))
+                if (!isValidJavaIdentifier(msg, part, elemId, panels, panelId, stopEdit))
                     return false;
             }
 
@@ -394,14 +380,25 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => {
             let res = DS_CHECK_SUCCESS;
 
             _.find(caches, (curCache, curIx) => {
-                res = compareDataSources(curCache, cluster);
+                // Check datasources of cluster JDBC ip finder and cache store factory datasource.
+                res = compareDataSources(curCache, 'cache', cluster, 'cluster');
+
+                if (!res.checked)
+                    return true;
+
+                _.find(cluster.checkpointSpi, (spi, spiIx) => {
+                    res = compareDataSources(curCache, 'cache', spi, 'checkpoint', spiIx);
+
+                    return !res.checked;
+                });
 
                 if (!res.checked)
                     return true;
 
+                // Check datasource of current saved cache and datasource of other cache in cluster.
                 if (isDefined(checkCacheExt)) {
                     if (checkCacheExt._id !== curCache._id) {
-                        res = compareDataSources(checkCacheExt, curCache);
+                        res = compareDataSources(checkCacheExt, 'cache', curCache, 'cache');
 
                         return !res.checked;
                     }
@@ -409,9 +406,10 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => {
                     return false;
                 }
 
+                // Check datasources of specified list of caches.
                 return _.find(caches, (checkCache, checkIx) => {
                     if (checkIx < curIx) {
-                        res = compareDataSources(checkCache, curCache);
+                        res = compareDataSources(checkCache, 'cache', curCache, 'cache');
 
                         return !res.checked;
                     }
@@ -420,6 +418,26 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => {
                 });
             });
 
+            if (res.checked) {
+                _.find(cluster.checkpointSpi, (curSpi, curIx) => {
+                    // Check datasources of cluster JDBC ip finder and cache store factory datasource.
+                    res = compareDataSources(cluster, 'cluster', curSpi, 'checkpoint', curIx);
+
+                    if (!res.checked)
+                        return true;
+
+                    _.find(cluster.checkpointSpi, (spi, spiIx) => {
+                        if (spiIx < curIx) {
+                            res = compareDataSources(curSpi, 'checkpoint', spi, 'checkpoint', curIx);
+
+                            return !res.checked;
+                        }
+
+                        return false;
+                    });
+                });
+            }
+
             return res;
         },
         checkCacheSQLSchemas(caches, checkCacheExt) {
@@ -469,14 +487,8 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => {
                     writeThrough: dflt || cache.writeThrough
                 };
             }
-        },
-        autoClusterSwapSpiConfiguration(cluster, caches) {
-            const swapConfigured = cluster.swapSpaceSpi && cluster.swapSpaceSpi.kind;
-
-            if (!swapConfigured && _.find(caches, (cache) => cache.swapEnabled))
-                return {swapSpaceSpi: {kind: 'FileSwapSpaceSpi'}};
 
-            return null;
+            return {};
         },
         randomString(len) {
             const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
@@ -498,7 +510,10 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => {
                 const firstErrorKey = errKeys[0];
 
                 const firstError = errors[firstErrorKey][0];
-                const actualError = firstError.$error[firstErrorKey][0];
+
+                const err = firstError.$error[firstErrorKey];
+
+                const actualError = _.isArray(err) ? err[0] : firstError;
 
                 const errNameFull = actualError.$name;
                 const errNameShort = errNameFull.endsWith('TextInput') ? errNameFull.substring(0, errNameFull.length - 9) : errNameFull;
@@ -507,12 +522,17 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => {
                     try {
                         return errors[firstErrorKey][0].$errorMessages[errName][firstErrorKey];
                     }
-                    catch (ignored) {
+                    catch (ignored1) {
                         try {
                             return form[firstError.$name].$errorMessages[errName][firstErrorKey];
                         }
-                        catch (ignited) {
-                            return false;
+                        catch (ignored2) {
+                            try {
+                                return form.$errorMessages[errName][firstErrorKey];
+                            }
+                            catch (ignored3) {
+                                return false;
+                            }
                         }
                     }
                 };

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/services/SqlTypes.service.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/services/SqlTypes.service.js b/modules/web-console/frontend/app/services/SqlTypes.service.js
index 2a16e9d..e42d903 100644
--- a/modules/web-console/frontend/app/services/SqlTypes.service.js
+++ b/modules/web-console/frontend/app/services/SqlTypes.service.js
@@ -15,13 +15,11 @@
  * limitations under the License.
  */
 
-import _ from 'lodash';
-
 // List of H2 reserved SQL keywords.
-import H2_SQL_KEYWORDS from '../data/sql-keywords.json';
+import H2_SQL_KEYWORDS from 'app/data/sql-keywords.json';
 
 // List of JDBC type descriptors.
-import JDBC_TYPES from '../data/jdbc-types.json';
+import JDBC_TYPES from 'app/data/jdbc-types.json';
 
 // Regular expression to check H2 SQL identifier.
 const VALID_IDENTIFIER = /^[a-zA-Z_][a-zA-Z0-9_$]*$/im;

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/vendor.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/vendor.js b/modules/web-console/frontend/app/vendor.js
index 0322887..a9e8844 100644
--- a/modules/web-console/frontend/app/vendor.js
+++ b/modules/web-console/frontend/app/vendor.js
@@ -38,6 +38,7 @@ import 'brace';
 import 'brace/mode/xml';
 import 'brace/mode/sql';
 import 'brace/mode/java';
+import 'brace/mode/csharp';
 import 'brace/mode/dockerfile';
 import 'brace/mode/snippets';
 import 'brace/theme/chrome';
@@ -46,7 +47,7 @@ import 'brace/ext/searchbox';
 import 'file-saver';
 import 'jszip';
 import 'nvd3';
-import 'query-command-supported';
+import 'lodash';
 import 'angular-gridster/dist/angular-gridster.min.css';
 import 'angular-tree-control/css/tree-control-attribute.css';
 import 'angular-tree-control/css/tree-control.css';

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/controllers/caches-controller.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/controllers/caches-controller.js b/modules/web-console/frontend/controllers/caches-controller.js
index 8c32906..8c01173 100644
--- a/modules/web-console/frontend/controllers/caches-controller.js
+++ b/modules/web-console/frontend/controllers/caches-controller.js
@@ -17,8 +17,8 @@
 
 // Controller for Caches screen.
 export default ['cachesController', [
-    '$scope', '$http', '$state', '$filter', '$timeout', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteConfirm', 'IgniteClone', 'IgniteLoading', 'IgniteModelNormalizer', 'IgniteUnsavedChangesGuard', 'igniteConfigurationResource', 'IgniteErrorPopover', 'IgniteFormUtils',
-    function($scope, $http, $state, $filter, $timeout, LegacyUtils, Messages, Confirm, Clone, Loading, ModelNormalizer, UnsavedChangesGuard, Resource, ErrorPopover, FormUtils) {
+    '$scope', '$http', '$state', '$filter', '$timeout', '$modal', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteConfirm', 'IgniteClone', 'IgniteLoading', 'IgniteModelNormalizer', 'IgniteUnsavedChangesGuard', 'IgniteConfigurationResource', 'IgniteErrorPopover', 'IgniteFormUtils', 'IgniteLegacyTable',
+    function($scope, $http, $state, $filter, $timeout, $modal, LegacyUtils, Messages, Confirm, Clone, Loading, ModelNormalizer, UnsavedChangesGuard, Resource, ErrorPopover, FormUtils, LegacyTable) {
         UnsavedChangesGuard.install($scope);
 
         const emptyCache = {empty: true};
@@ -96,6 +96,73 @@ export default ['cachesController', [
             item.offHeapMaxMemory = item.offHeapMaxMemory > 0 ? item.offHeapMaxMemory : null;
         };
 
+        $scope.tablePairSave = LegacyTable.tablePairSave;
+        $scope.tablePairSaveVisible = LegacyTable.tablePairSaveVisible;
+        $scope.tableNewItem = LegacyTable.tableNewItem;
+        $scope.tableNewItemActive = LegacyTable.tableNewItemActive;
+
+        $scope.tableStartEdit = function(item, field, index) {
+            if ($scope.tableReset(true))
+                LegacyTable.tableStartEdit(item, field, index, $scope.tableSave);
+        };
+
+        $scope.tableEditing = LegacyTable.tableEditing;
+
+        $scope.tableSave = function(field, index, stopEdit) {
+            if (LegacyTable.tablePairSaveVisible(field, index))
+                return LegacyTable.tablePairSave($scope.tablePairValid, $scope.backupItem, field, index, stopEdit);
+
+            return true;
+        };
+
+        $scope.tableRemove = function(item, field, index) {
+            if ($scope.tableReset(true))
+                LegacyTable.tableRemove(item, field, index);
+        };
+
+        $scope.tableReset = (trySave) => {
+            const field = LegacyTable.tableField();
+
+            if (trySave && LegacyUtils.isDefined(field) && !$scope.tableSave(field, LegacyTable.tableEditedRowIndex(), true))
+                return false;
+
+            LegacyTable.tableReset();
+
+            return true;
+        };
+
+        $scope.hibernatePropsTbl = {
+            type: 'hibernate',
+            model: 'cacheStoreFactory.CacheHibernateBlobStoreFactory.hibernateProperties',
+            focusId: 'Property',
+            ui: 'table-pair',
+            keyName: 'name',
+            valueName: 'value',
+            save: $scope.tableSave
+        };
+
+        $scope.tablePairValid = function(item, field, index, stopEdit) {
+            const pairValue = LegacyTable.tablePairValue(field, index);
+
+            const model = _.get(item, field.model);
+
+            if (!_.isNil(model)) {
+                const idx = _.findIndex(model, (pair) => {
+                    return pair.name === pairValue.key;
+                });
+
+                // Found duplicate by key.
+                if (idx >= 0 && idx !== index) {
+                    if (stopEdit)
+                        return false;
+
+                    return ErrorPopover.show(LegacyTable.tableFieldId(index, 'KeyProperty'), 'Property with such name already exists!', $scope.ui, 'query');
+                }
+            }
+
+            return true;
+        };
+
         Loading.start('loadingCachesScreen');
 
         // When landing on the page, get caches and show them.
@@ -117,6 +184,7 @@ export default ['cachesController', [
                     value: cluster._id,
                     label: cluster.name,
                     discovery: cluster.discovery,
+                    checkpointSpi: cluster.checkpointSpi,
                     caches: cluster.caches
                 }));
 
@@ -204,7 +272,7 @@ export default ['cachesController', [
                 else
                     $scope.backupItem = emptyCache;
 
-                $scope.backupItem = angular.merge({}, blank, $scope.backupItem);
+                $scope.backupItem = _.merge({}, blank, $scope.backupItem);
 
                 if ($scope.ui.inputForm) {
                     $scope.ui.inputForm.$error = {};
@@ -258,6 +326,15 @@ export default ['cachesController', [
             return caches;
         }
 
+        const _objToString = (type, name, prefix = '') => {
+            if (type === 'checkpoint')
+                return `${prefix} checkpoint configuration in cluster "${name}"`;
+            if (type === 'cluster')
+                return `${prefix} discovery IP finder in cluster "${name}"`;
+
+            return `${prefix} ${type} "${name}"`;
+        };
+
         function checkDataSources() {
             const clusters = cacheClusters();
 
@@ -272,20 +349,11 @@ export default ['cachesController', [
             });
 
             if (!checkRes.checked) {
-                if (_.get(checkRes.secondObj, 'discovery.kind') === 'Jdbc') {
-                    return ErrorPopover.show(checkRes.firstObj.cacheStoreFactory.kind === 'CacheJdbcPojoStoreFactory' ? 'pojoDialectInput' : 'blobDialectInput',
-                        'Found cluster "' + failCluster.label + '" with the same data source bean name "' +
-                        checkRes.secondObj.discovery.Jdbc.dataSourceBean + '" and different database: "' +
-                        LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.firstDB) + '" in current cache and "' +
-                        LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.secondDB) + '" in"' + checkRes.secondObj.label + '" cluster',
-                        $scope.ui, 'store', 10000);
-                }
-
                 return ErrorPopover.show(checkRes.firstObj.cacheStoreFactory.kind === 'CacheJdbcPojoStoreFactory' ? 'pojoDialectInput' : 'blobDialectInput',
-                    'Found cache "' + checkRes.secondObj.name + '" in cluster "' + failCluster.label + '" ' +
-                    'with the same data source bean name "' + checkRes.firstObj.cacheStoreFactory[checkRes.firstObj.cacheStoreFactory.kind].dataSourceBean +
-                    '" and different database: "' + LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.firstDB) + '" in current cache and "' +
-                    LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.secondDB) + '" in "' + checkRes.secondObj.name + '" cache',
+                    'Found ' + _objToString(checkRes.secondType, checkRes.secondObj.name || failCluster.label) + ' with the same data source bean name "' +
+                    checkRes.firstDs.dataSourceBean + '" and different database: "' +
+                    LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.firstDs.dialect) + '" in ' + _objToString(checkRes.firstType, checkRes.firstObj.name, 'current') + ' and "' +
+                    LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.secondDs.dialect) + '" in ' + _objToString(checkRes.secondType, checkRes.secondObj.name || failCluster.label),
                     $scope.ui, 'store', 10000);
             }
 
@@ -296,7 +364,7 @@ export default ['cachesController', [
             if (evictionPlc && evictionPlc.kind) {
                 const plc = evictionPlc[evictionPlc.kind];
 
-                if (plc.maxMemorySize === 0 && plc.maxSize === 0)
+                if (plc && !plc.maxMemorySize && !plc.maxSize)
                     return ErrorPopover.show('evictionPolicymaxMemorySizeInput', 'Either maximum memory size or maximum size should be great than 0!', $scope.ui, 'memory');
             }
 
@@ -409,7 +477,7 @@ export default ['cachesController', [
                     });
 
                     if (idx >= 0)
-                        angular.merge($scope.caches[idx], item);
+                        _.assign($scope.caches[idx], item);
                     else {
                         item._id = _id;
                         $scope.caches.push(item);
@@ -440,7 +508,7 @@ export default ['cachesController', [
         $scope.saveItem = function() {
             const item = $scope.backupItem;
 
-            angular.extend(item, LegacyUtils.autoCacheStoreConfiguration(item, cacheDomains(item)));
+            _.merge(item, LegacyUtils.autoCacheStoreConfiguration(item, cacheDomains(item)));
 
             if (validate(item))
                 save(item);
@@ -462,7 +530,20 @@ export default ['cachesController', [
 
                     item.name = newName;
 
-                    delete item.sqlSchema;
+                    if (!_.isEmpty(item.clusters) && !_.isNil(item.sqlSchema)) {
+                        delete item.sqlSchema;
+
+                        const scope = $scope.$new();
+
+                        scope.title = 'Info';
+                        scope.content = [
+                            'Use the same SQL schema name in one cluster in not allowed',
+                            'SQL schema name will be reset'
+                        ];
+
+                        // Show a basic modal from a controller
+                        $modal({scope, template: '/templates/message.html', placement: 'center', show: true});
+                    }
 
                     save(item);
                 });

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/controllers/clusters-controller.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/controllers/clusters-controller.js b/modules/web-console/frontend/controllers/clusters-controller.js
index 5a3c7e2..f9096f7 100644
--- a/modules/web-console/frontend/controllers/clusters-controller.js
+++ b/modules/web-console/frontend/controllers/clusters-controller.js
@@ -17,7 +17,7 @@
 
 // Controller for Clusters screen.
 export default ['clustersController', [
-    '$rootScope', '$scope', '$http', '$state', '$timeout', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteConfirm', 'IgniteClone', 'IgniteLoading', 'IgniteModelNormalizer', 'IgniteUnsavedChangesGuard', 'igniteEventGroups', 'DemoInfo', 'IgniteLegacyTable', 'igniteConfigurationResource', 'IgniteErrorPopover', 'IgniteFormUtils',
+    '$rootScope', '$scope', '$http', '$state', '$timeout', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteConfirm', 'IgniteClone', 'IgniteLoading', 'IgniteModelNormalizer', 'IgniteUnsavedChangesGuard', 'igniteEventGroups', 'DemoInfo', 'IgniteLegacyTable', 'IgniteConfigurationResource', 'IgniteErrorPopover', 'IgniteFormUtils',
     function($root, $scope, $http, $state, $timeout, LegacyUtils, Messages, Confirm, Clone, Loading, ModelNormalizer, UnsavedChangesGuard, igniteEventGroups, DemoInfo, LegacyTable, Resource, ErrorPopover, FormUtils) {
         UnsavedChangesGuard.install($scope);
 
@@ -103,6 +103,46 @@ export default ['clustersController', [
                     else
                         $scope.backupItem.failoverSpi = {};
                 }
+                else if (field.type === 'loadBalancingSpi') {
+                    const newLoadBalancing = {Adaptive: {
+                        loadProbe: {
+                            Job: {useAverage: true},
+                            CPU: {
+                                useAverage: true,
+                                useProcessors: true
+                            },
+                            ProcessingTime: {useAverage: true}
+                        }
+                    }};
+
+                    if (LegacyUtils.isDefined($scope.backupItem.loadBalancingSpi))
+                        $scope.backupItem.loadBalancingSpi.push(newLoadBalancing);
+                    else
+                        $scope.backupItem.loadBalancingSpi = [newLoadBalancing];
+                }
+                else if (field.type === 'checkpointSpi') {
+                    const newCheckpointCfg = {
+                        FS: {
+                            directoryPaths: []
+                        },
+                        S3: {
+                            awsCredentials: {
+                                kind: 'Basic'
+                            },
+                            clientConfiguration: {
+                                retryPolicy: {
+                                    kind: 'Default'
+                                },
+                                useReaper: true
+                            }
+                        }
+                    };
+
+                    if (LegacyUtils.isDefined($scope.backupItem.checkpointSpi))
+                        $scope.backupItem.checkpointSpi.push(newCheckpointCfg);
+                    else
+                        $scope.backupItem.checkpointSpi = [newCheckpointCfg];
+                }
                 else
                     LegacyTable.tableNewItem(field);
             }
@@ -149,6 +189,8 @@ export default ['clustersController', [
             $scope.backupItem.failoverSpi.splice(idx, 1);
         };
 
+        $scope.supportedJdbcTypes = LegacyUtils.mkOptions(LegacyUtils.SUPPORTED_JDBC_TYPES);
+
         // We need to initialize backupItem with empty object in order to properly used from angular directives.
         $scope.backupItem = emptyCluster;
 
@@ -204,11 +246,20 @@ export default ['clustersController', [
 
         // When landing on the page, get clusters and show them.
         Resource.read()
-            .then(({spaces, clusters, caches, igfss}) => {
+            .then(({spaces, clusters, caches, domains, igfss}) => {
                 $scope.spaces = spaces;
+
                 $scope.clusters = clusters;
 
-                $scope.caches = _.map(caches, (cache) => ({value: cache._id, label: cache.name, cache}));
+                $scope.caches = _.map(caches, (cache) => {
+                    cache.domains = _.filter(domains, ({_id}) => _.includes(cache.domains, _id));
+
+                    if (_.get(cache, 'nodeFilter.kind') === 'IGFS')
+                        cache.nodeFilter.IGFS.instance = _.find(igfss, {_id: cache.nodeFilter.IGFS.igfs});
+
+                    return {value: cache._id, label: cache.name, cache};
+                });
+
                 $scope.igfss = _.map(igfss, (igfs) => ({value: igfs._id, label: igfs.name, igfs}));
 
                 _.forEach($scope.clusters, (cluster) => {
@@ -222,6 +273,9 @@ export default ['clustersController', [
 
                     if (!cluster.logger)
                         cluster.logger = {Log4j: { mode: 'Default'}};
+
+                    if (!cluster.eventStorage)
+                        cluster.eventStorage = { kind: 'Memory' };
                 });
 
                 if ($state.params.linkId)
@@ -259,6 +313,12 @@ export default ['clustersController', [
                         form.$setPristine();
                     else
                         form.$setDirty();
+
+                    $scope.clusterCaches = _.filter($scope.caches,
+                        (cache) => _.find($scope.backupItem.caches,
+                            (selCache) => selCache === cache.value
+                        )
+                    );
                 }, true);
 
                 $scope.$watch('ui.activePanels.length', () => {
@@ -279,6 +339,8 @@ export default ['clustersController', [
                 Loading.finish('loadingClustersScreen');
             });
 
+        $scope.clusterCaches = [];
+
         $scope.selectItem = function(item, backup) {
             function selectItem() {
                 $scope.selectedItem = item;
@@ -300,7 +362,7 @@ export default ['clustersController', [
                 else
                     $scope.backupItem = emptyCluster;
 
-                $scope.backupItem = angular.merge({}, blank, $scope.backupItem);
+                $scope.backupItem = _.merge({}, blank, $scope.backupItem);
 
                 if ($scope.ui.inputForm) {
                     $scope.ui.inputForm.$error = {};
@@ -319,7 +381,7 @@ export default ['clustersController', [
         $scope.linkId = () => $scope.backupItem._id ? $scope.backupItem._id : 'create';
 
         function prepareNewItem(linkId) {
-            return angular.merge({}, blank, {
+            return _.merge({}, blank, {
                 space: $scope.spaces[0]._id,
                 discovery: {
                     kind: 'Multicast',
@@ -331,6 +393,7 @@ export default ['clustersController', [
                 communication: {tcpNoDelay: true},
                 connector: {noDelay: true},
                 collision: {kind: 'Noop', JobStealing: {stealingEnabled: true}, PriorityQueue: {starvationPreventionEnabled: true}},
+                eventStorage: {kind: 'Memory'},
                 failoverSpi: [],
                 logger: {Log4j: { mode: 'Default'}},
                 caches: linkId && _.find($scope.caches, {value: linkId}) ? [linkId] : [],
@@ -354,27 +417,45 @@ export default ['clustersController', [
                 (cache) => _.includes(item.caches, cache._id));
         }
 
+        const _objToString = (type, name, prefix = '') => {
+            if (type === 'checkpoint')
+                return prefix + ' checkpoint configuration';
+            if (type === 'cluster')
+                return prefix + ' discovery IP finder';
+
+            return `${prefix} ${type} "${name}"`;
+        };
+
         function checkCacheDatasources(item) {
             const caches = clusterCaches(item);
 
             const checkRes = LegacyUtils.checkDataSources(item, caches);
 
             if (!checkRes.checked) {
-                if (_.get(checkRes.secondObj, 'discovery.kind') === 'Jdbc') {
-                    return ErrorPopover.show('dialectInput',
-                        'Found cache "' + checkRes.firstObj.name + '" with the same data source bean name "' +
-                        item.discovery.Jdbc.dataSourceBean + '" and different database: "' +
-                        LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.secondDB) + '" in current cluster and "' +
-                        LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.firstDB) + '" in "' + checkRes.firstObj.name + '" cache',
-                        $scope.ui, 'general', 10000);
+                let ids;
+
+                if (checkRes.secondType === 'cluster')
+                    ids = { section: 'general', fieldId: 'dialectInput' };
+                else if (checkRes.secondType === 'cache')
+                    ids = { section: 'general', fieldId: 'cachesInput' };
+                else if (checkRes.secondType === 'checkpoint')
+                    ids = { section: 'checkpoint', fieldId: `checkpointJdbcDialect${checkRes.index}Input` };
+                else
+                    return true;
+
+                if (checkRes.firstType === checkRes.secondType && checkRes.firstType === 'cache') {
+                    return ErrorPopover.show(ids.fieldId, 'Found caches "' + checkRes.firstObj.name + '" and "' + checkRes.secondObj.name + '" with the same data source bean name "' +
+                        checkRes.firstDs.dataSourceBean + '" and different database: "' +
+                        LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.secondDs.dialect) + '" in ' + _objToString(checkRes.secondType, checkRes.secondObj.name) + ' and "' +
+                        LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.firstDs.dialect) + '" in ' + _objToString(checkRes.firstType, checkRes.firstObj.name),
+                        $scope.ui, ids.section, 10000);
                 }
 
-                return ErrorPopover.show('cachesInput',
-                    'Found caches "' + checkRes.firstObj.name + '" and "' + checkRes.secondObj.name + '" ' +
-                    'with the same data source bean name "' + checkRes.firstObj.cacheStoreFactory[checkRes.firstObj.cacheStoreFactory.kind].dataSourceBean +
-                    '" and different databases: "' + LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.firstDB) + '" in "' + checkRes.firstObj.name + '" and "' +
-                    LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.secondDB) + '" in "' + checkRes.secondObj.name + '" cache',
-                    $scope.ui, 'general', 10000);
+                return ErrorPopover.show(ids.fieldId, 'Found ' + _objToString(checkRes.firstType, checkRes.firstObj.name) + ' with the same data source bean name "' +
+                    checkRes.firstDs.dataSourceBean + '" and different database: "' +
+                    LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.secondDs.dialect) + '" in ' + _objToString(checkRes.secondType, checkRes.secondObj.name, 'current') + ' and "' +
+                    LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.firstDs.dialect) + '" in ' + _objToString(checkRes.firstType, checkRes.firstObj.name),
+                    $scope.ui, ids.section, 10000);
             }
 
             return true;
@@ -434,6 +515,38 @@ export default ['clustersController', [
             return true;
         }
 
+        function checkCheckpointSpis(item) {
+            const cfgs = item.checkpointSpi;
+
+            if (_.isEmpty(cfgs))
+                return true;
+
+            return _.isNil(_.find(cfgs, (cfg, ix) => {
+                if (_.isNil(cfg.kind)) {
+                    ErrorPopover.show('checkpointKind' + ix, 'Choose checkpoint implementation variant', $scope.ui, 'checkpoint');
+
+                    return true;
+                }
+
+                switch (cfg.kind) {
+                    case 'Cache':
+                        const cache = _.get(cfg, 'Cache.cache');
+
+                        if (_.isNil(cache) || !_.find($scope.backupItem.caches, (selCache) => cache === selCache)) {
+                            ErrorPopover.show('checkpointCacheCache' + ix, 'Choose cache from configured cluster caches', $scope.ui, 'checkpoint');
+
+                            return true;
+                        }
+
+                        break;
+
+                    default: break;
+                }
+
+                return false;
+            }));
+        }
+
         function checkCommunicationConfiguration(item) {
             const c = item.communication;
 
@@ -467,6 +580,20 @@ export default ['clustersController', [
             return true;
         }
 
+        function checkLoadBalancingConfiguration(item) {
+            const balancingSpis = item.loadBalancingSpi;
+
+            return _.isNil(_.find(balancingSpis, (curSpi, curIx) => {
+                if (_.find(balancingSpis, (spi, ix) => curIx > ix && curSpi.kind === spi.kind)) {
+                    ErrorPopover.show('loadBalancingKind' + curIx, 'Load balancing SPI of that type is already configured', $scope.ui, 'loadBalancing');
+
+                    return true;
+                }
+
+                return false;
+            }));
+        }
+
         function checkSwapConfiguration(item) {
             const swapKind = item.swapSpaceSpi && item.swapSpaceSpi.kind;
 
@@ -535,12 +662,18 @@ export default ['clustersController', [
             if (!checkCacheKeyConfiguration(item))
                 return false;
 
+            if (!checkCheckpointSpis(item))
+                return false;
+
             if (!checkCommunicationConfiguration(item))
                 return false;
 
             if (!checkDiscoveryConfiguration(item))
                 return false;
 
+            if (!checkLoadBalancingConfiguration(item))
+                return false;
+
             if (!checkSwapConfiguration(item))
                 return false;
 
@@ -564,7 +697,7 @@ export default ['clustersController', [
                     const idx = _.findIndex($scope.clusters, (cluster) => cluster._id === _id);
 
                     if (idx >= 0)
-                        angular.merge($scope.clusters[idx], item);
+                        _.assign($scope.clusters[idx], item);
                     else {
                         item._id = _id;
                         $scope.clusters.push(item);
@@ -595,10 +728,10 @@ export default ['clustersController', [
         $scope.saveItem = function() {
             const item = $scope.backupItem;
 
-            const swapSpi = LegacyUtils.autoClusterSwapSpiConfiguration(item, clusterCaches(item));
+            const swapConfigured = item.swapSpaceSpi && item.swapSpaceSpi.kind;
 
-            if (swapSpi)
-                angular.extend(item, swapSpi);
+            if (!swapConfigured && _.find(clusterCaches(item), (cache) => cache.swapEnabled))
+                _.merge(item, {swapSpaceSpi: {kind: 'FileSwapSpaceSpi'}});
 
             if (validate(item))
                 save(item);

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/controllers/domains-controller.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/controllers/domains-controller.js b/modules/web-console/frontend/controllers/domains-controller.js
index 2d450db..0a79d82 100644
--- a/modules/web-console/frontend/controllers/domains-controller.js
+++ b/modules/web-console/frontend/controllers/domains-controller.js
@@ -17,7 +17,7 @@
 
 // Controller for Domain model screen.
 export default ['domainsController', [
-    '$rootScope', '$scope', '$http', '$state', '$filter', '$timeout', '$modal', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteFocus', 'IgniteConfirm', 'IgniteConfirmBatch', 'IgniteClone', 'IgniteLoading', 'IgniteModelNormalizer', 'IgniteUnsavedChangesGuard', 'IgniteAgentMonitor', 'IgniteLegacyTable', 'igniteConfigurationResource', 'IgniteErrorPopover', 'IgniteFormUtils', 'JavaTypes', 'SqlTypes',
+    '$rootScope', '$scope', '$http', '$state', '$filter', '$timeout', '$modal', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteFocus', 'IgniteConfirm', 'IgniteConfirmBatch', 'IgniteClone', 'IgniteLoading', 'IgniteModelNormalizer', 'IgniteUnsavedChangesGuard', 'IgniteAgentMonitor', 'IgniteLegacyTable', 'IgniteConfigurationResource', 'IgniteErrorPopover', 'IgniteFormUtils', 'JavaTypes', 'SqlTypes',
     function($root, $scope, $http, $state, $filter, $timeout, $modal, LegacyUtils, Messages, Focus, Confirm, ConfirmBatch, Clone, Loading, ModelNormalizer, UnsavedChangesGuard, IgniteAgentMonitor, LegacyTable, Resource, ErrorPopover, FormUtils, JavaTypes, SqlTypes) {
         UnsavedChangesGuard.install($scope);
 
@@ -58,6 +58,7 @@ export default ['domainsController', [
 
         $scope.$on('$destroy', $root.$on('user', _packageNameUpdate));
 
+        $scope.ui.generatePojo = true;
         $scope.ui.builtinKeys = true;
         $scope.ui.usePrimitives = true;
         $scope.ui.generateAliases = true;
@@ -75,7 +76,6 @@ export default ['domainsController', [
             return !item.empty && (!item._id || _.find($scope.displayedRows, {_id: item._id}));
         };
 
-        $scope.getModel = LegacyUtils.getModel;
         $scope.javaBuiltInClasses = LegacyUtils.javaBuiltInClasses;
         $scope.compactJavaName = FormUtils.compactJavaName;
         $scope.widthIsSufficient = FormUtils.widthIsSufficient;
@@ -91,7 +91,7 @@ export default ['domainsController', [
                     case 'fields':
                     case 'aliases':
                         if (LegacyTable.tablePairSaveVisible(field, index))
-                            return LegacyTable.tablePairSave($scope.tablePairValid, $scope.backupItem, field, index, stopEdit);
+                            return LegacyTable.tablePairSave($scope.tablePairValid, $scope.backupItem, field, index, stopEdit) || stopEdit;
 
                         break;
 
@@ -140,11 +140,43 @@ export default ['domainsController', [
         $scope.tableEditing = LegacyTable.tableEditing;
 
         $scope.tableRemove = function(item, field, index) {
-            if ($scope.tableReset(true))
+            if ($scope.tableReset(true)) {
+                // Remove field from indexes.
+                if (field.type === 'fields') {
+                    _.forEach($scope.backupItem.indexes, (modelIndex) => {
+                        modelIndex.fields = _.filter(modelIndex.fields, (indexField) => {
+                            return indexField.name !== $scope.backupItem.fields[index].name;
+                        });
+                    });
+                }
+
                 LegacyTable.tableRemove(item, field, index);
+            }
+        };
+
+        $scope.tablePairSave = (pairValid, item, field, index, stopEdit) => {
+            // On change of field name update that field in index fields.
+            if (index >= 0 && field.type === 'fields') {
+                const newName = LegacyTable.tablePairValue(field, index).key;
+                const oldName = _.get(item, field.model)[index][field.keyName];
+
+                const saved = LegacyTable.tablePairSave(pairValid, item, field, index, stopEdit);
+
+                if (saved && oldName !== newName) {
+                    _.forEach($scope.backupItem.indexes, (idx) => {
+                        _.forEach(idx.fields, (fld) => {
+                            if (fld.name === oldName)
+                                fld.name = newName;
+                        });
+                    });
+                }
+
+                return saved;
+            }
+
+            return LegacyTable.tablePairSave(pairValid, item, field, index, stopEdit);
         };
 
-        $scope.tablePairSave = LegacyTable.tablePairSave;
         $scope.tablePairSaveVisible = LegacyTable.tablePairSaveVisible;
 
         $scope.queryFieldsTbl = {
@@ -169,6 +201,19 @@ export default ['domainsController', [
 
         $scope.queryMetadataVariants = LegacyUtils.mkOptions(['Annotations', 'Configuration']);
 
+        // Create list of fields to show in index fields dropdown.
+        $scope.fields = (prefix, cur) => {
+            const fields = _.map($scope.backupItem.fields, (field) => ({value: field.name, label: field.name}));
+
+            if (prefix === 'new')
+                return fields;
+
+            if (cur && !_.find(fields, {value: cur}))
+                fields.push({value: cur, label: cur + ' (Unknown field)'});
+
+            return fields;
+        };
+
         const INFO_CONNECT_TO_DB = 'Configure connection to database';
         const INFO_SELECT_SCHEMAS = 'Select schemas to load tables from';
         const INFO_SELECT_TABLES = 'Select tables to import as domain model';
@@ -215,6 +260,12 @@ export default ['domainsController', [
                 user: 'root'
             },
             {
+                db: 'MySQL',
+                jdbcDriverClass: 'org.mariadb.jdbc.Driver',
+                jdbcUrl: 'jdbc:mariadb://[host]:[port]/[database]',
+                user: 'root'
+            },
+            {
                 db: 'H2',
                 jdbcDriverClass: 'org.h2.Driver',
                 jdbcUrl: 'jdbc:h2:tcp://[host]/[database]',
@@ -223,7 +274,7 @@ export default ['domainsController', [
         ];
 
         $scope.selectedPreset = {
-            db: 'General',
+            db: 'Generic',
             jdbcDriverJar: '',
             jdbcDriverClass: '',
             jdbcUrl: 'jdbc:[database]',
@@ -266,7 +317,7 @@ export default ['domainsController', [
                 const oldPreset = _.find(_dbPresets, {jdbcDriverClass: preset.jdbcDriverClass});
 
                 if (oldPreset)
-                    angular.extend(oldPreset, preset);
+                    _.assign(oldPreset, preset);
                 else
                     _dbPresets.push(preset);
 
@@ -283,7 +334,7 @@ export default ['domainsController', [
             });
 
             if (!result)
-                result = {db: 'General', jdbcUrl: 'jdbc:[database]', user: 'admin'};
+                result = {db: 'Generic', jdbcUrl: 'jdbc:[database]', user: 'admin'};
 
             result.jdbcDriverJar = selectedJdbcJar.jdbcDriverJar;
             result.jdbcDriverClass = selectedJdbcJar.jdbcDriverClass;
@@ -373,6 +424,7 @@ export default ['domainsController', [
         function prepareNewItem(cacheId) {
             return {
                 space: $scope.spaces[0]._id,
+                generatePojo: true,
                 caches: cacheId && _.find($scope.caches, {value: cacheId}) ? [cacheId] : // eslint-disable-line no-nested-ternary
                     (_.isEmpty($scope.caches) ? [] : [$scope.caches[0].value]),
                 queryMetadata: 'Configuration'
@@ -751,20 +803,12 @@ export default ['domainsController', [
                 importDomainModal.hide();
         }
 
-        function _saveDomainModel() {
-            if (LegacyUtils.isEmptyString($scope.ui.packageName)) {
-                ErrorPopover.show('domainPackageNameInput', 'Package could not be empty');
-
-                Focus.move('domainPackageNameInput');
-
-                return false;
-            }
-
-            if (!LegacyUtils.isValidJavaClass('Package', $scope.ui.packageName, false, 'domainPackageNameInput', true)) {
-                Focus.move('domainPackageNameInput');
+        function _saveDomainModel(optionsForm) {
+            const generatePojo = $scope.ui.generatePojo;
+            const packageName = $scope.ui.packageName;
 
+            if (generatePojo && !LegacyUtils.checkFieldValidators({inputForm: optionsForm}))
                 return false;
-            }
 
             const batch = [];
             const checkedCaches = [];
@@ -803,7 +847,7 @@ export default ['domainsController', [
                         containDup = true;
                     }
 
-                    const valType = _toJavaPackage($scope.ui.packageName) + '.' + typeName;
+                    const valType = generatePojo ? _toJavaPackage(packageName) + '.' + typeName : tableName;
 
                     let _containKey = false;
 
@@ -852,7 +896,8 @@ export default ['domainsController', [
                         confirm: false,
                         skip: false,
                         space: $scope.spaces[0],
-                        caches: []
+                        caches: [],
+                        generatePojo
                     };
 
                     if (LegacyUtils.isDefined(domainFound)) {
@@ -985,7 +1030,7 @@ export default ['domainsController', [
             }
         }
 
-        $scope.importDomainNext = function() {
+        $scope.importDomainNext = function(form) {
             if (!$scope.importDomainNextAvailable())
                 return;
 
@@ -1000,7 +1045,7 @@ export default ['domainsController', [
             else if (act === 'tables')
                 _selectOptions();
             else if (act === 'options')
-                _saveDomainModel();
+                _saveDomainModel(form);
         };
 
         $scope.nextTooltipText = function() {
@@ -1101,11 +1146,14 @@ export default ['domainsController', [
         Resource.read()
             .then(({spaces, clusters, caches, domains}) => {
                 $scope.spaces = spaces;
+
                 $scope.clusters = _.map(clusters, (cluster) => ({
                     label: cluster.name,
                     value: cluster._id
                 }));
+
                 $scope.caches = _mapCaches(caches);
+
                 $scope.domains = _.sortBy(domains, 'valueType');
 
                 _.forEach($scope.clusters, (cluster) => $scope.ui.generatedCachesClusters.push(cluster.value));
@@ -1157,8 +1205,14 @@ export default ['domainsController', [
 
                     if (form.$valid && ModelNormalizer.isEqual(__original_value, val))
                         form.$setPristine();
-                    else
+                    else {
                         form.$setDirty();
+
+                        const general = form.general;
+
+                        FormUtils.markPristineInvalidAsDirty(general.keyType);
+                        FormUtils.markPristineInvalidAsDirty(general.valueType);
+                    }
                 }, true);
 
                 $scope.$watch('ui.activePanels.length', () => {
@@ -1210,7 +1264,7 @@ export default ['domainsController', [
                 else
                     $scope.backupItem = emptyDomain;
 
-                $scope.backupItem = angular.merge({}, blank, $scope.backupItem);
+                $scope.backupItem = _.merge({}, blank, $scope.backupItem);
 
                 if ($scope.ui.inputForm) {
                     $scope.ui.inputForm.$error = {};
@@ -1252,9 +1306,12 @@ export default ['domainsController', [
                 const indexes = item.indexes;
 
                 if (indexes && indexes.length > 0) {
-                    if (_.find(indexes, function(index, i) {
+                    if (_.find(indexes, function(index, idx) {
                         if (_.isEmpty(index.fields))
-                            return !ErrorPopover.show('indexes' + i, 'Index fields are not specified', $scope.ui, 'query');
+                            return !ErrorPopover.show('indexes' + idx, 'Index fields are not specified', $scope.ui, 'query');
+
+                        if (_.find(index.fields, (field) => !_.find(item.fields, (configuredField) => configuredField.name === field.name)))
+                            return !ErrorPopover.show('indexes' + idx, 'Index contains not configured fields', $scope.ui, 'query');
                     }))
                         return false;
                 }
@@ -1332,7 +1389,7 @@ export default ['domainsController', [
                     });
 
                     if (idx >= 0)
-                        angular.extend($scope.domains[idx], savedMeta);
+                        _.assign($scope.domains[idx], savedMeta);
                     else
                         $scope.domains.push(savedMeta);
 
@@ -1503,15 +1560,11 @@ export default ['domainsController', [
                     });
 
                     // Found duplicate by key.
-                    if (idx >= 0 && idx !== index) {
-                        if (stopEdit)
-                            return false;
-
-                        return ErrorPopover.show(LegacyTable.tableFieldId(index, pairField.idPrefix + pairField.id), 'Field with such ' + pairField.dupObjName + ' already exists!', $scope.ui, 'query');
-                    }
+                    if (idx >= 0 && idx !== index)
+                        return !stopEdit && ErrorPopover.show(LegacyTable.tableFieldId(index, pairField.idPrefix + pairField.id), 'Field with such ' + pairField.dupObjName + ' already exists!', $scope.ui, 'query');
                 }
 
-                if (pairField.classValidation && !LegacyUtils.isValidJavaClass(pairField.msg, pairValue.value, true, LegacyTable.tableFieldId(index, 'Value' + pairField.id), false, $scope.ui, 'query')) {
+                if (pairField.classValidation && !LegacyUtils.isValidJavaClass(pairField.msg, pairValue.value, true, LegacyTable.tableFieldId(index, 'Value' + pairField.id), false, $scope.ui, 'query', stopEdit)) {
                     if (stopEdit)
                         return false;
 
@@ -1560,8 +1613,8 @@ export default ['domainsController', [
 
                 let model = item[field.model];
 
-                if (!LegacyUtils.isValidJavaIdentifier(dbFieldTable.msg + ' java name', dbFieldValue.javaFieldName, LegacyTable.tableFieldId(index, 'JavaFieldName' + dbFieldTable.id)))
-                    return false;
+                if (!LegacyUtils.isValidJavaIdentifier(dbFieldTable.msg + ' java name', dbFieldValue.javaFieldName, LegacyTable.tableFieldId(index, 'JavaFieldName' + dbFieldTable.id), $scope.ui, 'store', stopEdit))
+                    return stopEdit;
 
                 if (LegacyUtils.isDefined(model)) {
                     let idx = _.findIndex(model, function(dbMeta) {
@@ -1570,7 +1623,7 @@ export default ['domainsController', [
 
                     // Found duplicate.
                     if (idx >= 0 && index !== idx)
-                        return ErrorPopover.show(LegacyTable.tableFieldId(index, 'DatabaseFieldName' + dbFieldTable.id), 'Field with such database name already exists!', $scope.ui, 'store');
+                        return stopEdit || ErrorPopover.show(LegacyTable.tableFieldId(index, 'DatabaseFieldName' + dbFieldTable.id), 'Field with such database name already exists!', $scope.ui, 'store');
 
                     idx = _.findIndex(model, function(dbMeta) {
                         return dbMeta.javaFieldName === dbFieldValue.javaFieldName;
@@ -1578,7 +1631,7 @@ export default ['domainsController', [
 
                     // Found duplicate.
                     if (idx >= 0 && index !== idx)
-                        return ErrorPopover.show(LegacyTable.tableFieldId(index, 'JavaFieldName' + dbFieldTable.id), 'Field with such java name already exists!', $scope.ui, 'store');
+                        return stopEdit || ErrorPopover.show(LegacyTable.tableFieldId(index, 'JavaFieldName' + dbFieldTable.id), 'Field with such java name already exists!', $scope.ui, 'store');
 
                     if (index < 0)
                         model.push(dbFieldValue);
@@ -1639,7 +1692,7 @@ export default ['domainsController', [
 
                 // Found duplicate.
                 if (idx >= 0 && idx !== curIdx)
-                    return ErrorPopover.show(LegacyTable.tableFieldId(curIdx, 'IndexName'), 'Index with such name already exists!', $scope.ui, 'query');
+                    return !stopEdit && ErrorPopover.show(LegacyTable.tableFieldId(curIdx, 'IndexName'), 'Index with such name already exists!', $scope.ui, 'query');
             }
 
             LegacyTable.tableReset();
@@ -1675,10 +1728,8 @@ export default ['domainsController', [
 
         $scope.tableIndexNewItem = function(field, indexIdx) {
             if ($scope.tableReset(true)) {
-                const index = $scope.backupItem.indexes[indexIdx];
-
                 LegacyTable.tableState(field, -1, 'table-index-fields');
-                LegacyTable.tableFocusInvalidField(-1, 'FieldName' + (index.indexType === 'SORTED' ? 'S' : '') + indexIdx);
+                LegacyTable.tableFocusInvalidField(-1, 'FieldName' + indexIdx);
 
                 field.newFieldName = null;
                 field.newDirection = true;
@@ -1734,7 +1785,7 @@ export default ['domainsController', [
                 field.curDirection = indexItem.direction;
                 field.indexIdx = indexIdx;
 
-                Focus.move('curFieldName' + (index.indexType === 'SORTED' ? 'S' : '') + field.indexIdx + '-' + curIdx);
+                Focus.move('curFieldName' + field.indexIdx + '-' + curIdx);
             }
         };
 
@@ -1753,8 +1804,11 @@ export default ['domainsController', [
                 const idx = _.findIndex(fields, (fld) => fld.name === indexItemValue.name);
 
                 // Found duplicate.
-                if (idx >= 0 && idx !== curIdx)
-                    return ErrorPopover.show(LegacyTable.tableFieldId(curIdx, 'FieldName' + (index.indexType === 'SORTED' ? 'S' : '') + indexIdx + (curIdx >= 0 ? '-' : '')), 'Field with such name already exists in index!', $scope.ui, 'query');
+                if (idx >= 0 && idx !== curIdx) {
+                    return !stopEdit && ErrorPopover.show(LegacyTable.tableFieldId(curIdx,
+                        'FieldName' + indexIdx + (curIdx >= 0 ? '-' : '')),
+                        'Field with such name already exists in index!', $scope.ui, 'query');
+                }
             }
 
             LegacyTable.tableReset();

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/controllers/igfs-controller.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/controllers/igfs-controller.js b/modules/web-console/frontend/controllers/igfs-controller.js
index 7617712..e505f1c 100644
--- a/modules/web-console/frontend/controllers/igfs-controller.js
+++ b/modules/web-console/frontend/controllers/igfs-controller.js
@@ -17,7 +17,7 @@
 
 // Controller for IGFS screen.
 export default ['igfsController', [
-    '$scope', '$http', '$state', '$filter', '$timeout', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteConfirm', 'IgniteClone', 'IgniteLoading', 'IgniteModelNormalizer', 'IgniteUnsavedChangesGuard', 'IgniteLegacyTable', 'igniteConfigurationResource', 'IgniteErrorPopover', 'IgniteFormUtils',
+    '$scope', '$http', '$state', '$filter', '$timeout', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteConfirm', 'IgniteClone', 'IgniteLoading', 'IgniteModelNormalizer', 'IgniteUnsavedChangesGuard', 'IgniteLegacyTable', 'IgniteConfigurationResource', 'IgniteErrorPopover', 'IgniteFormUtils',
     function($scope, $http, $state, $filter, $timeout, LegacyUtils, Messages, Confirm, Clone, Loading, ModelNormalizer, UnsavedChangesGuard, LegacyTable, Resource, ErrorPopover, FormUtils) {
         UnsavedChangesGuard.install($scope);
 
@@ -231,7 +231,7 @@ export default ['igfsController', [
                 else
                     $scope.backupItem = emptyIgfs;
 
-                $scope.backupItem = angular.merge({}, blank, $scope.backupItem);
+                $scope.backupItem = _.merge({}, blank, $scope.backupItem);
 
                 if ($scope.ui.inputForm) {
                     $scope.ui.inputForm.$error = {};
@@ -304,7 +304,7 @@ export default ['igfsController', [
                     });
 
                     if (idx >= 0)
-                        angular.merge($scope.igfss[idx], item);
+                        _.assign($scope.igfss[idx], item);
                     else {
                         item._id = _id;
                         $scope.igfss.push(item);