You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ak...@apache.org on 2016/06/27 03:22:54 UTC

[10/22] ignite git commit: Ignite Web Console beta2.

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/generator/generator-optional.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/generator/generator-optional.js b/modules/web-console/src/main/js/generator/generator-optional.js
index 0e23f59..61de1a2 100644
--- a/modules/web-console/src/main/js/generator/generator-optional.js
+++ b/modules/web-console/src/main/js/generator/generator-optional.js
@@ -18,7 +18,7 @@
 // Optional content generation entry point.
 const $generatorOptional = {};
 
-$generatorOptional.optionalContent = function (zip, cluster) {
+$generatorOptional.optionalContent = function(zip, cluster) { // eslint-disable-line no-unused-vars
     // No-op.
 };
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/generator/generator-properties.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/generator/generator-properties.js b/modules/web-console/src/main/js/generator/generator-properties.js
index 1ecaeb6..4773f22 100644
--- a/modules/web-console/src/main/js/generator/generator-properties.js
+++ b/modules/web-console/src/main/js/generator/generator-properties.js
@@ -32,6 +32,7 @@ $generatorProperties.jdbcUrlTemplate = function(dialect) {
             return 'jdbc:postgresql://[host]:[port]/[database]';
         case 'H2':
             return 'jdbc:h2:tcp://[host]/[database]';
+        default:
     }
 
     return 'jdbc:your_database';
@@ -44,17 +45,17 @@ $generatorProperties.jdbcUrlTemplate = function(dialect) {
  * @param res Resulting output with generated properties.
  * @returns {string} Generated content.
  */
-$generatorProperties.dataSourcesProperties = function (cluster, res) {
-    var datasources = [];
+$generatorProperties.dataSourcesProperties = function(cluster, res) {
+    const datasources = [];
 
     if (cluster.caches && cluster.caches.length > 0) {
-        _.forEach(cluster.caches, function (cache) {
+        _.forEach(cluster.caches, function(cache) {
             if (cache.cacheStoreFactory && cache.cacheStoreFactory.kind) {
-                var storeFactory = cache.cacheStoreFactory[cache.cacheStoreFactory.kind];
+                const storeFactory = cache.cacheStoreFactory[cache.cacheStoreFactory.kind];
 
-                var dialect = storeFactory.connectVia ? (storeFactory.connectVia === 'DataSource' ? storeFactory.dialect : undefined): storeFactory.dialect;
+                const dialect = storeFactory.connectVia ? (storeFactory.connectVia === 'DataSource' ? storeFactory.dialect : null) : storeFactory.dialect;
 
-                var connectViaUrl = cache.cacheStoreFactory.kind === 'CacheJdbcBlobStoreFactory' && storeFactory.connectVia === 'URL';
+                const connectViaUrl = cache.cacheStoreFactory.kind === 'CacheJdbcBlobStoreFactory' && storeFactory.connectVia === 'URL';
 
                 if (!res && (dialect || connectViaUrl)) {
                     res = $generatorCommon.builder();
@@ -63,13 +64,13 @@ $generatorProperties.dataSourcesProperties = function (cluster, res) {
                 }
 
                 if (dialect) {
-                    var beanId = storeFactory.dataSourceBean;
+                    const beanId = storeFactory.dataSourceBean;
 
-                    var dsClsName = $generatorCommon.dataSourceClassName(dialect);
+                    const dsClsName = $generatorCommon.dataSourceClassName(dialect);
 
-                    var varType = res.importClass(dsClsName);
+                    const varType = res.importClass(dsClsName);
 
-                    var beanClassName = $generatorCommon.toJavaName(varType, storeFactory.dataSourceBean);
+                    const beanClassName = $generatorCommon.toJavaName(varType, storeFactory.dataSourceBean);
 
                     if (!_.includes(datasources, beanClassName)) {
                         datasources.push(beanClassName);
@@ -111,7 +112,7 @@ $generatorProperties.dataSourcesProperties = function (cluster, res) {
  * @param res Optional configuration presentation builder object.
  * @returns Configuration presentation builder object
  */
-$generatorProperties.sslProperties = function (cluster, res) {
+$generatorProperties.sslProperties = function(cluster, res) {
     if (cluster.sslEnabled && cluster.sslContextFactory) {
         if (!res) {
             res = $generatorCommon.builder();
@@ -138,7 +139,7 @@ $generatorProperties.sslProperties = function (cluster, res) {
  * @param res Optional configuration presentation builder object.
  * @returns Configuration presentation builder object
  */
-$generatorProperties.generateProperties = function (cluster, res) {
+$generatorProperties.generateProperties = function(cluster, res) {
     res = $generatorProperties.dataSourcesProperties(cluster, res);
 
     res = $generatorProperties.sslProperties(cluster, res);

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/generator/generator-readme.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/generator/generator-readme.js b/modules/web-console/src/main/js/generator/generator-readme.js
index c4e2f81..432f1e6 100644
--- a/modules/web-console/src/main/js/generator/generator-readme.js
+++ b/modules/web-console/src/main/js/generator/generator-readme.js
@@ -18,7 +18,7 @@
 // README.txt generation entry point.
 const $generatorReadme = {};
 
-$generatorReadme.generatedBy = function (res) {
+$generatorReadme.generatedBy = function(res) {
     res.line('Content of this folder was generated by Apache Ignite Web Console');
     res.line('=================================================================');
 
@@ -31,7 +31,7 @@ $generatorReadme.generatedBy = function (res) {
  * @param res Resulting output with generated readme.
  * @returns {string} Generated content.
  */
-$generatorReadme.readme = function (res) {
+$generatorReadme.readme = function(res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -72,7 +72,7 @@ $generatorReadme.readme = function (res) {
  * @param res Resulting output with generated readme.
  * @returns {string} Generated content.
  */
-$generatorReadme.readmeJdbc = function (res) {
+$generatorReadme.readmeJdbc = function(res) {
     if (!res)
         res = $generatorCommon.builder();
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/generator/generator-xml.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/generator/generator-xml.js b/modules/web-console/src/main/js/generator/generator-xml.js
index 658553c..4528f36 100644
--- a/modules/web-console/src/main/js/generator/generator-xml.js
+++ b/modules/web-console/src/main/js/generator/generator-xml.js
@@ -19,16 +19,16 @@
 const $generatorXml = {};
 
 // Do XML escape.
-$generatorXml.escape = function (s) {
-    if (typeof(s) !== 'string')
+$generatorXml.escape = function(s) {
+    if (typeof (s) !== 'string')
         return s;
 
     return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
 };
 
 // Add constructor argument
-$generatorXml.constructorArg = function (res, ix, obj, propName, dflt, opt) {
-    var v = (obj ? obj[propName] : undefined) || dflt;
+$generatorXml.constructorArg = function(res, ix, obj, propName, dflt, opt) {
+    const v = (obj ? obj[propName] : null) || dflt;
 
     if ($generatorCommon.isDefinedAndNotEmpty(v))
         res.line('<constructor-arg ' + (ix >= 0 ? 'index="' + ix + '" ' : '') + 'value="' + v + '"/>');
@@ -40,8 +40,8 @@ $generatorXml.constructorArg = function (res, ix, obj, propName, dflt, opt) {
 };
 
 // Add XML element.
-$generatorXml.element = function (res, tag, attr1, val1, attr2, val2) {
-    var elem = '<' + tag;
+$generatorXml.element = function(res, tag, attr1, val1, attr2, val2) {
+    let elem = '<' + tag;
 
     if (attr1)
         elem += ' ' + attr1 + '="' + val1 + '"';
@@ -56,12 +56,12 @@ $generatorXml.element = function (res, tag, attr1, val1, attr2, val2) {
 };
 
 // Add property.
-$generatorXml.property = function (res, obj, propName, setterName, dflt) {
+$generatorXml.property = function(res, obj, propName, setterName, dflt) {
     if (!_.isNil(obj)) {
-        var val = obj[propName];
+        const val = obj[propName];
 
         if ($generatorCommon.isDefinedAndNotEmpty(val)) {
-            var missDflt = _.isNil(dflt);
+            const missDflt = _.isNil(dflt);
 
             // Add to result if no default provided or value not equals to default.
             if (missDflt || (!missDflt && val !== dflt)) {
@@ -76,16 +76,16 @@ $generatorXml.property = function (res, obj, propName, setterName, dflt) {
 };
 
 // Add property for class name.
-$generatorXml.classNameProperty = function (res, obj, propName) {
-    var val = obj[propName];
+$generatorXml.classNameProperty = function(res, obj, propName) {
+    const val = obj[propName];
 
     if (!_.isNil(val))
         $generatorXml.element(res, 'property', 'name', propName, 'value', $generatorCommon.JavaTypes.fullClassName(val));
 };
 
 // Add list property.
-$generatorXml.listProperty = function (res, obj, propName, listType, rowFactory) {
-    var val = obj[propName];
+$generatorXml.listProperty = function(res, obj, propName, listType, rowFactory) {
+    const val = obj[propName];
 
     if (val && val.length > 0) {
         res.emptyLineIfNeeded();
@@ -94,16 +94,12 @@ $generatorXml.listProperty = function (res, obj, propName, listType, rowFactory)
             listType = 'list';
 
         if (!rowFactory)
-            rowFactory = function (val) {
-                return '<value>' + $generatorXml.escape(val) + '</value>';
-            };
+            rowFactory = (v) => '<value>' + $generatorXml.escape(v) + '</value>';
 
         res.startBlock('<property name="' + propName + '">');
         res.startBlock('<' + listType + '>');
 
-        _.forEach(val, function(v) {
-            res.line(rowFactory(v));
-        });
+        _.forEach(val, (v) => res.line(rowFactory(v)));
 
         res.endBlock('</' + listType + '>');
         res.endBlock('</property>');
@@ -113,23 +109,19 @@ $generatorXml.listProperty = function (res, obj, propName, listType, rowFactory)
 };
 
 // Add array property
-$generatorXml.arrayProperty = function (res, obj, propName, descr, rowFactory) {
-    var val = obj[propName];
+$generatorXml.arrayProperty = function(res, obj, propName, descr, rowFactory) {
+    const val = obj[propName];
 
     if (val && val.length > 0) {
         res.emptyLineIfNeeded();
 
         if (!rowFactory)
-            rowFactory = function (val) {
-                return '<bean class="' + val + '"/>';
-            };
+            rowFactory = (v) => '<bean class="' + v + '"/>';
 
         res.startBlock('<property name="' + propName + '">');
         res.startBlock('<list>');
 
-        _.forEach(val, function (v) {
-            res.append(rowFactory(v));
-        });
+        _.forEach(val, (v) => res.append(rowFactory(v)));
 
         res.endBlock('</list>');
         res.endBlock('</property>');
@@ -145,8 +137,8 @@ $generatorXml.arrayProperty = function (res, obj, propName, descr, rowFactory) {
  * @param desc Bean metadata object.
  * @param createBeanAlthoughNoProps Always generate bean even it has no properties defined.
  */
-$generatorXml.beanProperty = function (res, bean, beanPropName, desc, createBeanAlthoughNoProps) {
-    var props = desc.fields;
+$generatorXml.beanProperty = function(res, bean, beanPropName, desc, createBeanAlthoughNoProps) {
+    const props = desc.fields;
 
     if (bean && $generatorCommon.hasProperty(bean, props)) {
         if (!createBeanAlthoughNoProps)
@@ -160,7 +152,7 @@ $generatorXml.beanProperty = function (res, bean, beanPropName, desc, createBean
 
         res.startBlock('<bean class="' + desc.className + '">');
 
-        var hasData = false;
+        let hasData = false;
 
         _.forIn(props, function(descr, propName) {
             if (props.hasOwnProperty(propName)) {
@@ -177,14 +169,14 @@ $generatorXml.beanProperty = function (res, bean, beanPropName, desc, createBean
                             break;
 
                         case 'propertiesAsList':
-                            var val = bean[propName];
+                            const val = bean[propName];
 
                             if (val && val.length > 0) {
                                 res.startBlock('<property name="' + propName + '">');
                                 res.startBlock('<props>');
 
                                 _.forEach(val, function(nameAndValue) {
-                                    var eqIndex = nameAndValue.indexOf('=');
+                                    const eqIndex = nameAndValue.indexOf('=');
                                     if (eqIndex >= 0) {
                                         res.line('<prop key="' + $generatorXml.escape(nameAndValue.substring(0, eqIndex)) + '">' +
                                             $generatorXml.escape(nameAndValue.substr(eqIndex + 1)) + '</prop>');
@@ -249,9 +241,9 @@ $generatorXml.beanProperty = function (res, bean, beanPropName, desc, createBean
  * @param obj Object to take bean class name.
  * @param propName Property name.
  */
-$generatorXml.simpleBeanProperty = function (res, obj, propName) {
+$generatorXml.simpleBeanProperty = function(res, obj, propName) {
     if (!_.isNil(obj)) {
-        var val = obj[propName];
+        const val = obj[propName];
 
         if ($generatorCommon.isDefinedAndNotEmpty(val)) {
             res.startBlock('<property name="' + propName + '">');
@@ -264,7 +256,7 @@ $generatorXml.simpleBeanProperty = function (res, obj, propName) {
 };
 
 // Generate eviction policy.
-$generatorXml.evictionPolicy = function (res, evtPlc, propName) {
+$generatorXml.evictionPolicy = function(res, evtPlc, propName) {
     if (evtPlc && evtPlc.kind) {
         $generatorXml.beanProperty(res, evtPlc[evtPlc.kind.toUpperCase()], propName,
             $generatorCommon.EVICTION_POLICIES[evtPlc.kind], true);
@@ -272,7 +264,7 @@ $generatorXml.evictionPolicy = function (res, evtPlc, propName) {
 };
 
 // Generate discovery.
-$generatorXml.clusterGeneral = function (cluster, res) {
+$generatorXml.clusterGeneral = function(cluster, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -284,7 +276,7 @@ $generatorXml.clusterGeneral = function (cluster, res) {
         res.startBlock('<bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">');
         res.startBlock('<property name="ipFinder">');
 
-        var d = cluster.discovery;
+        const d = cluster.discovery;
 
         switch (d.kind) {
             case 'Multicast':
@@ -306,9 +298,8 @@ $generatorXml.clusterGeneral = function (cluster, res) {
             case 'Vm':
                 res.startBlock('<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">');
 
-                if (d.Vm) {
+                if (d.Vm)
                     $generatorXml.listProperty(res, d.Vm, 'addresses');
-                }
 
                 res.endBlock('</bean>');
 
@@ -359,9 +350,8 @@ $generatorXml.clusterGeneral = function (cluster, res) {
             case 'Jdbc':
                 res.startBlock('<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.jdbc.TcpDiscoveryJdbcIpFinder">');
 
-                if (d.Jdbc) {
+                if (d.Jdbc)
                     res.line('<property name="initSchema" value="' + (!_.isNil(d.Jdbc.initSchema) && d.Jdbc.initSchema) + '"/>');
-                }
 
                 res.endBlock('</bean>');
 
@@ -370,9 +360,8 @@ $generatorXml.clusterGeneral = function (cluster, res) {
             case 'SharedFs':
                 res.startBlock('<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.sharedfs.TcpDiscoverySharedFsIpFinder">');
 
-                if (d.SharedFs) {
+                if (d.SharedFs)
                     $generatorXml.property(res, d.SharedFs, 'path');
-                }
 
                 res.endBlock('</bean>');
 
@@ -391,9 +380,9 @@ $generatorXml.clusterGeneral = function (cluster, res) {
                     $generatorXml.property(res, d.ZooKeeper, 'zkConnectionString');
 
                     if (d.ZooKeeper.retryPolicy && d.ZooKeeper.retryPolicy.kind) {
-                        var kind = d.ZooKeeper.retryPolicy.kind;
-                        var retryPolicy = d.ZooKeeper.retryPolicy[kind];
-                        var customClassDefined = retryPolicy && $generatorCommon.isDefinedAndNotEmpty(retryPolicy.className);
+                        const kind = d.ZooKeeper.retryPolicy.kind;
+                        const retryPolicy = d.ZooKeeper.retryPolicy[kind];
+                        const customClassDefined = retryPolicy && $generatorCommon.isDefinedAndNotEmpty(retryPolicy.className);
 
                         if (kind !== 'Custom' || customClassDefined)
                             res.startBlock('<property name="retryPolicy">');
@@ -403,7 +392,7 @@ $generatorXml.clusterGeneral = function (cluster, res) {
                                 res.startBlock('<bean class="org.apache.curator.retry.ExponentialBackoffRetry">');
                                 $generatorXml.constructorArg(res, 0, retryPolicy, 'baseSleepTimeMs', 1000);
                                 $generatorXml.constructorArg(res, 1, retryPolicy, 'maxRetries', 10);
-                                $generatorXml.constructorArg(res, 2, retryPolicy, 'maxSleepMs', undefined, true);
+                                $generatorXml.constructorArg(res, 2, retryPolicy, 'maxSleepMs', null, true);
                                 res.endBlock('</bean>');
 
                                 break;
@@ -452,6 +441,8 @@ $generatorXml.clusterGeneral = function (cluster, res) {
                                     res.line('<bean class="' + retryPolicy.className + '"/>');
 
                                 break;
+
+                            default:
                         }
 
                         if (kind !== 'Custom' || customClassDefined)
@@ -485,7 +476,7 @@ $generatorXml.clusterGeneral = function (cluster, res) {
 };
 
 // Generate atomics group.
-$generatorXml.clusterAtomics = function (atomics, res) {
+$generatorXml.clusterAtomics = function(atomics, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -497,9 +488,9 @@ $generatorXml.clusterAtomics = function (atomics, res) {
         res.startBlock('<property name="atomicConfiguration">');
         res.startBlock('<bean class="org.apache.ignite.configuration.AtomicConfiguration">');
 
-        var cacheMode = atomics.cacheMode ? atomics.cacheMode : 'PARTITIONED';
+        const cacheMode = atomics.cacheMode ? atomics.cacheMode : 'PARTITIONED';
 
-        var hasData = cacheMode !== 'PARTITIONED';
+        let hasData = cacheMode !== 'PARTITIONED';
 
         $generatorXml.property(res, atomics, 'cacheMode', null, 'PARTITIONED');
 
@@ -521,7 +512,7 @@ $generatorXml.clusterAtomics = function (atomics, res) {
 };
 
 // Generate binary group.
-$generatorXml.clusterBinary = function (binary, res) {
+$generatorXml.clusterBinary = function(binary, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -537,7 +528,7 @@ $generatorXml.clusterBinary = function (binary, res) {
             res.startBlock('<property name="typeConfigurations">');
             res.startBlock('<list>');
 
-            _.forEach(binary.typeConfigurations, function (type) {
+            _.forEach(binary.typeConfigurations, function(type) {
                 res.startBlock('<bean class="org.apache.ignite.binary.BinaryTypeConfiguration">');
 
                 $generatorXml.property(res, type, 'typeName');
@@ -564,8 +555,90 @@ $generatorXml.clusterBinary = function (binary, res) {
     return res;
 };
 
+// Generate collision group.
+$generatorXml.clusterCollision = function(collision, res) {
+    if (!res)
+        res = $generatorCommon.builder();
+
+    if (collision && collision.kind && collision.kind !== 'Noop') {
+        const spi = collision[collision.kind];
+
+        if (collision.kind !== 'Custom' || (spi && $generatorCommon.isDefinedAndNotEmpty(spi.class))) {
+            res.startBlock('<property name="collisionSpi">');
+
+            switch (collision.kind) {
+                case 'JobStealing':
+                    res.startBlock('<bean class="org.apache.ignite.spi.collision.jobstealing.JobStealingCollisionSpi">');
+                    $generatorXml.property(res, spi, 'activeJobsThreshold', null, 95);
+                    $generatorXml.property(res, spi, 'waitJobsThreshold', null, 0);
+                    $generatorXml.property(res, spi, 'messageExpireTime', null, 1000);
+                    $generatorXml.property(res, spi, 'maximumStealingAttempts', null, 5);
+                    $generatorXml.property(res, spi, 'stealingEnabled', null, true);
+
+                    if ($generatorCommon.isDefinedAndNotEmpty(spi.externalCollisionListener)) {
+                        res.needEmptyLine = true;
+
+                        res.startBlock('<property name="externalCollisionListener">');
+                        res.line('<bean class="' + spi.externalCollisionListener + ' "/>');
+                        res.endBlock('</property>');
+                    }
+
+                    if ($generatorCommon.isDefinedAndNotEmpty(spi.stealingAttributes)) {
+                        res.needEmptyLine = true;
+
+                        res.startBlock('<property name="stealingAttributes">');
+                        res.startBlock('<map>');
+
+                        _.forEach(spi.stealingAttributes, function(attr) {
+                            $generatorXml.element(res, 'entry', 'key', attr.name, 'value', attr.value);
+                        });
+
+                        res.endBlock('</map>');
+                        res.endBlock('</property>');
+                    }
+
+                    res.endBlock('</bean>');
+
+                    break;
+
+                case 'FifoQueue':
+                    res.startBlock('<bean class="org.apache.ignite.spi.collision.fifoqueue.FifoQueueCollisionSpi">');
+                    $generatorXml.property(res, spi, 'parallelJobsNumber');
+                    $generatorXml.property(res, spi, 'waitingJobsNumber');
+                    res.endBlock('</bean>');
+
+                    break;
+
+                case 'PriorityQueue':
+                    res.startBlock('<bean class="org.apache.ignite.spi.collision.priorityqueue.PriorityQueueCollisionSpi">');
+                    $generatorXml.property(res, spi, 'parallelJobsNumber');
+                    $generatorXml.property(res, spi, 'waitingJobsNumber');
+                    $generatorXml.property(res, spi, 'priorityAttributeKey', null, 'grid.task.priority');
+                    $generatorXml.property(res, spi, 'jobPriorityAttributeKey', null, 'grid.job.priority');
+                    $generatorXml.property(res, spi, 'defaultPriority', null, 0);
+                    $generatorXml.property(res, spi, 'starvationIncrement', null, 1);
+                    $generatorXml.property(res, spi, 'starvationPreventionEnabled', null, true);
+                    res.endBlock('</bean>');
+
+                    break;
+
+                case 'Custom':
+                    res.line('<bean class="' + spi.class + '"/>');
+
+                    break;
+
+                default:
+            }
+
+            res.endBlock('</property>');
+        }
+    }
+
+    return res;
+};
+
 // Generate communication group.
-$generatorXml.clusterCommunication = function (cluster, res) {
+$generatorXml.clusterCommunication = function(cluster, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -595,7 +668,7 @@ $generatorXml.clusterConnector = function(connector, res) {
         res = $generatorCommon.builder();
 
     if (!_.isNil(connector) && connector.enabled) {
-        var cfg = _.cloneDeep($generatorCommon.CONNECTOR_CONFIGURATION);
+        const cfg = _.cloneDeep($generatorCommon.CONNECTOR_CONFIGURATION);
 
         if (connector.sslEnabled) {
             cfg.fields.sslClientAuth = {dflt: false};
@@ -611,14 +684,14 @@ $generatorXml.clusterConnector = function(connector, res) {
 };
 
 // Generate deployment group.
-$generatorXml.clusterDeployment = function (cluster, res) {
+$generatorXml.clusterDeployment = function(cluster, res) {
     if (!res)
         res = $generatorCommon.builder();
 
     if ($generatorXml.property(res, cluster, 'deploymentMode', null, 'SHARED'))
         res.needEmptyLine = true;
 
-    var p2pEnabled = cluster.peerClassLoadingEnabled;
+    const p2pEnabled = cluster.peerClassLoadingEnabled;
 
     if (!_.isNil(p2pEnabled)) {
         $generatorXml.property(res, cluster, 'peerClassLoadingEnabled', null, false);
@@ -636,7 +709,7 @@ $generatorXml.clusterDeployment = function (cluster, res) {
 };
 
 // Generate discovery group.
-$generatorXml.clusterDiscovery = function (disco, res) {
+$generatorXml.clusterDiscovery = function(disco, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -677,7 +750,7 @@ $generatorXml.clusterDiscovery = function (disco, res) {
 };
 
 // Generate events group.
-$generatorXml.clusterEvents = function (cluster, res) {
+$generatorXml.clusterEvents = function(cluster, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -720,12 +793,120 @@ $generatorXml.clusterEvents = function (cluster, res) {
     return res;
 };
 
+// Generate failover group.
+$generatorXml.clusterFailover = function(cluster, res) {
+    if (!res)
+        res = $generatorCommon.builder();
+
+    if ($generatorCommon.isDefinedAndNotEmpty(cluster.failoverSpi) && _.findIndex(cluster.failoverSpi, function(spi) {
+        return $generatorCommon.isDefinedAndNotEmpty(spi.kind) && (spi.kind !== 'Custom' || $generatorCommon.isDefinedAndNotEmpty(_.get(spi, spi.kind + '.class')));
+    }) >= 0) {
+        res.startBlock('<property name="failoverSpi">');
+        res.startBlock('<list>');
+
+        _.forEach(cluster.failoverSpi, function(spi) {
+            if (spi.kind && (spi.kind !== 'Custom' || $generatorCommon.isDefinedAndNotEmpty(_.get(spi, spi.kind + '.class')))) {
+                const maxAttempts = _.get(spi, spi.kind + '.maximumFailoverAttempts');
+
+                if ((spi.kind === 'JobStealing' || spi.kind === 'Always') && $generatorCommon.isDefinedAndNotEmpty(maxAttempts) && maxAttempts !== 5) {
+                    res.startBlock('<bean class="' + $generatorCommon.failoverSpiClass(spi) + '">');
+
+                    $generatorXml.property(res, spi[spi.kind], 'maximumFailoverAttempts', null, 5);
+
+                    res.endBlock('</bean>');
+                }
+                else
+                    res.line('<bean class="' + $generatorCommon.failoverSpiClass(spi) + '"/>');
+
+                res.needEmptyLine = true;
+            }
+        });
+
+        res.needEmptyLine = true;
+
+        res.endBlock('</list>');
+        res.endBlock('</property>');
+    }
+
+    return res;
+};
+
+// Generate marshaller group.
+$generatorXml.clusterLogger = function(logger, res) {
+    if (!res)
+        res = $generatorCommon.builder();
+
+    if ($generatorCommon.loggerConfigured(logger)) {
+        res.startBlock('<property name="gridLogger">');
+
+        const log = logger[logger.kind];
+
+        switch (logger.kind) {
+            case 'Log4j2':
+                res.startBlock('<bean class="org.apache.ignite.logger.log4j2.Log4J2Logger">');
+                res.line('<constructor-arg value="' + $generatorXml.escape(log.path) + '"/>');
+                $generatorXml.property(res, log, 'level');
+                res.endBlock('</bean>');
+
+                break;
+
+            case 'Null':
+                res.line('<bean class="org.apache.ignite.logger.NullLogger"/>');
+
+                break;
+
+            case 'Java':
+                res.line('<bean class="org.apache.ignite.logger.java.JavaLogger"/>');
+
+                break;
+
+            case 'JCL':
+                res.line('<bean class="org.apache.ignite.logger.jcl.JclLogger"/>');
+
+                break;
+
+            case 'SLF4J':
+                res.line('<bean class="org.apache.ignite.logger.slf4j.Slf4jLogger"/>');
+
+                break;
+
+            case 'Log4j':
+                if (log.mode === 'Default' && !$generatorCommon.isDefinedAndNotEmpty(log.level))
+                    res.line('<bean class="org.apache.ignite.logger.log4j.Log4JLogger"/>');
+                else {
+                    res.startBlock('<bean class="org.apache.ignite.logger.log4j.Log4JLogger">');
+
+                    if (log.mode === 'Path')
+                        res.line('<constructor-arg value="' + $generatorXml.escape(log.path) + '"/>');
+
+                    $generatorXml.property(res, log, 'level');
+                    res.endBlock('</bean>');
+                }
+
+                break;
+
+            case 'Custom':
+                res.line('<bean class="' + log.class + '"/>');
+
+                break;
+
+            default:
+        }
+
+        res.endBlock('</property>');
+
+        res.needEmptyLine = true;
+    }
+
+    return res;
+};
+
 // Generate marshaller group.
-$generatorXml.clusterMarshaller = function (cluster, res) {
+$generatorXml.clusterMarshaller = function(cluster, res) {
     if (!res)
         res = $generatorCommon.builder();
 
-    var marshaller = cluster.marshaller;
+    const marshaller = cluster.marshaller;
 
     if (marshaller && marshaller.kind)
         $generatorXml.beanProperty(res, marshaller[marshaller.kind], 'marshaller', $generatorCommon.MARSHALLERS[marshaller.kind], true);
@@ -742,7 +923,7 @@ $generatorXml.clusterMarshaller = function (cluster, res) {
 };
 
 // Generate metrics group.
-$generatorXml.clusterMetrics = function (cluster, res) {
+$generatorXml.clusterMetrics = function(cluster, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -757,7 +938,7 @@ $generatorXml.clusterMetrics = function (cluster, res) {
 };
 
 // Generate swap group.
-$generatorXml.clusterSwap = function (cluster, res) {
+$generatorXml.clusterSwap = function(cluster, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -772,7 +953,7 @@ $generatorXml.clusterSwap = function (cluster, res) {
 };
 
 // Generate time group.
-$generatorXml.clusterTime = function (cluster, res) {
+$generatorXml.clusterTime = function(cluster, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -787,7 +968,7 @@ $generatorXml.clusterTime = function (cluster, res) {
 };
 
 // Generate thread pools group.
-$generatorXml.clusterPools = function (cluster, res) {
+$generatorXml.clusterPools = function(cluster, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -803,7 +984,7 @@ $generatorXml.clusterPools = function (cluster, res) {
 };
 
 // Generate transactions group.
-$generatorXml.clusterTransactions = function (transactionConfiguration, res) {
+$generatorXml.clusterTransactions = function(transactionConfiguration, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -814,6 +995,28 @@ $generatorXml.clusterTransactions = function (transactionConfiguration, res) {
     return res;
 };
 
+// Generate user attributes group.
+$generatorXml.clusterUserAttributes = function(cluster, res) {
+    if (!res)
+        res = $generatorCommon.builder();
+
+    if ($generatorCommon.isDefinedAndNotEmpty(cluster.attributes)) {
+        res.startBlock('<property name="userAttributes">');
+        res.startBlock('<map>');
+
+        _.forEach(cluster.attributes, function(attr) {
+            $generatorXml.element(res, 'entry', 'key', attr.name, 'value', attr.value);
+        });
+
+        res.endBlock('</map>');
+        res.endBlock('</property>');
+    }
+
+    res.needEmptyLine = true;
+
+    return res;
+};
+
 /**
  * XML generator for cluster's SSL configuration.
  *
@@ -904,9 +1107,7 @@ $generatorXml.cacheQuery = function(cache, res) {
     $generatorXml.property(res, cache, 'sqlOnheapRowCacheSize', null, 10240);
     $generatorXml.property(res, cache, 'longQueryWarningTimeout', null, 3000);
 
-    var indexedTypes = _.filter(cache.domains, function (domain) {
-        return domain.queryMetadata === 'Annotations'
-    });
+    const indexedTypes = _.filter(cache.domains, (domain) => domain.queryMetadata === 'Annotations');
 
     if (indexedTypes.length > 0) {
         res.startBlock('<property name="indexedTypes">');
@@ -941,9 +1142,9 @@ $generatorXml.cacheStore = function(cache, domains, res) {
         res = $generatorCommon.builder();
 
     if (cache.cacheStoreFactory && cache.cacheStoreFactory.kind) {
-        var factoryKind = cache.cacheStoreFactory.kind;
+        const factoryKind = cache.cacheStoreFactory.kind;
 
-        var storeFactory = cache.cacheStoreFactory[factoryKind];
+        const storeFactory = cache.cacheStoreFactory[factoryKind];
 
         if (storeFactory) {
             if (factoryKind === 'CacheJdbcPojoStoreFactory') {
@@ -956,7 +1157,7 @@ $generatorXml.cacheStore = function(cache, domains, res) {
                 res.line('<bean class="' + $generatorCommon.jdbcDialectClassName(storeFactory.dialect) + '"/>');
                 res.endBlock('</property>');
 
-                var domainConfigs = _.filter(domains, function (domain) {
+                const domainConfigs = _.filter(domains, function(domain) {
                     return $generatorCommon.isDefinedAndNotEmpty(domain.databaseTable);
                 });
 
@@ -964,7 +1165,7 @@ $generatorXml.cacheStore = function(cache, domains, res) {
                     res.startBlock('<property name="types">');
                     res.startBlock('<list>');
 
-                    _.forEach(domainConfigs, function (domain) {
+                    _.forEach(domainConfigs, function(domain) {
                         res.startBlock('<bean class="org.apache.ignite.cache.store.jdbc.JdbcType">');
 
                         $generatorXml.property(res, cache, 'name', 'cacheName');
@@ -982,7 +1183,7 @@ $generatorXml.cacheStore = function(cache, domains, res) {
                 }
 
                 res.endBlock('</bean>');
-                res.endBlock("</property>");
+                res.endBlock('</property>');
             }
             else if (factoryKind === 'CacheJdbcBlobStoreFactory') {
                 res.startBlock('<property name="cacheStoreFactory">');
@@ -1004,15 +1205,13 @@ $generatorXml.cacheStore = function(cache, domains, res) {
                 $generatorXml.property(res, storeFactory, 'deleteQuery');
 
                 res.endBlock('</bean>');
-                res.endBlock("</property>");
+                res.endBlock('</property>');
             }
             else
                 $generatorXml.beanProperty(res, storeFactory, 'cacheStoreFactory', $generatorCommon.STORE_FACTORIES[factoryKind], true);
 
-            if (storeFactory.dataSourceBean && (storeFactory.connectVia ? (storeFactory.connectVia === 'DataSource' ? storeFactory.dialect : undefined) : storeFactory.dialect)) {
-                if (_.findIndex(res.datasources, function (ds) {
-                        return ds.dataSourceBean === storeFactory.dataSourceBean;
-                    }) < 0) {
+            if (storeFactory.dataSourceBean && (storeFactory.connectVia ? (storeFactory.connectVia === 'DataSource' ? storeFactory.dialect : null) : storeFactory.dialect)) {
+                if (_.findIndex(res.datasources, (ds) => ds.dataSourceBean === storeFactory.dataSourceBean) < 0) {
                     res.datasources.push({
                         dataSourceBean: storeFactory.dataSourceBean,
                         className: $generatorCommon.DATA_SOURCES[storeFactory.dialect],
@@ -1053,7 +1252,7 @@ $generatorXml.cacheConcurrency = function(cache, res) {
     $generatorXml.property(res, cache, 'maxConcurrentAsyncOperations', null, 500);
     $generatorXml.property(res, cache, 'defaultLockTimeout', null, 0);
     $generatorXml.property(res, cache, 'atomicWriteOrderMode');
-    $generatorXml.property(res, cache, 'writeSynchronizationMode', null, "PRIMARY_SYNC");
+    $generatorXml.property(res, cache, 'writeSynchronizationMode', null, 'PRIMARY_SYNC');
 
     res.needEmptyLine = true;
 
@@ -1130,8 +1329,8 @@ $generatorXml.cacheStatistics = function(cache, res) {
 };
 
 // Generate domain model query fields.
-$generatorXml.domainModelQueryFields = function (res, domain) {
-    var fields = domain.fields;
+$generatorXml.domainModelQueryFields = function(res, domain) {
+    const fields = domain.fields;
 
     if (fields && fields.length > 0) {
         res.emptyLineIfNeeded();
@@ -1139,7 +1338,7 @@ $generatorXml.domainModelQueryFields = function (res, domain) {
         res.startBlock('<property name="fields">');
         res.startBlock('<map>');
 
-        _.forEach(fields, function (field) {
+        _.forEach(fields, function(field) {
             $generatorXml.element(res, 'entry', 'key', field.name, 'value', $generatorCommon.JavaTypes.fullClassName(field.className));
         });
 
@@ -1151,8 +1350,8 @@ $generatorXml.domainModelQueryFields = function (res, domain) {
 };
 
 // Generate domain model query fields.
-$generatorXml.domainModelQueryAliases = function (res, domain) {
-    var aliases = domain.aliases;
+$generatorXml.domainModelQueryAliases = function(res, domain) {
+    const aliases = domain.aliases;
 
     if (aliases && aliases.length > 0) {
         res.emptyLineIfNeeded();
@@ -1160,7 +1359,7 @@ $generatorXml.domainModelQueryAliases = function (res, domain) {
         res.startBlock('<property name="aliases">');
         res.startBlock('<map>');
 
-        _.forEach(aliases, function (alias) {
+        _.forEach(aliases, function(alias) {
             $generatorXml.element(res, 'entry', 'key', alias.field, 'value', alias.alias);
         });
 
@@ -1172,8 +1371,8 @@ $generatorXml.domainModelQueryAliases = function (res, domain) {
 };
 
 // Generate domain model indexes.
-$generatorXml.domainModelQueryIndexes = function (res, domain) {
-    var indexes = domain.indexes;
+$generatorXml.domainModelQueryIndexes = function(res, domain) {
+    const indexes = domain.indexes;
 
     if (indexes && indexes.length > 0) {
         res.emptyLineIfNeeded();
@@ -1181,19 +1380,19 @@ $generatorXml.domainModelQueryIndexes = function (res, domain) {
         res.startBlock('<property name="indexes">');
         res.startBlock('<list>');
 
-        _.forEach(indexes, function (index) {
+        _.forEach(indexes, function(index) {
             res.startBlock('<bean class="org.apache.ignite.cache.QueryIndex">');
 
             $generatorXml.property(res, index, 'name');
             $generatorXml.property(res, index, 'indexType');
 
-            var fields = index.fields;
+            const fields = index.fields;
 
             if (fields && fields.length > 0) {
                 res.startBlock('<property name="fields">');
                 res.startBlock('<map>');
 
-                _.forEach(fields, function (field) {
+                _.forEach(fields, function(field) {
                     $generatorXml.element(res, 'entry', 'key', field.name, 'value', field.direction);
                 });
 
@@ -1212,8 +1411,8 @@ $generatorXml.domainModelQueryIndexes = function (res, domain) {
 };
 
 // Generate domain model db fields.
-$generatorXml.domainModelDatabaseFields = function (res, domain, fieldProp) {
-    var fields = domain[fieldProp];
+$generatorXml.domainModelDatabaseFields = function(res, domain, fieldProp) {
+    const fields = domain[fieldProp];
 
     if (fields && fields.length > 0) {
         res.emptyLineIfNeeded();
@@ -1222,7 +1421,7 @@ $generatorXml.domainModelDatabaseFields = function (res, domain, fieldProp) {
 
         res.startBlock('<list>');
 
-        _.forEach(fields, function (field) {
+        _.forEach(fields, function(field) {
             res.startBlock('<bean class="org.apache.ignite.cache.store.jdbc.JdbcTypeField">');
 
             $generatorXml.property(res, field, 'databaseFieldName');
@@ -1277,6 +1476,8 @@ $generatorXml.domainModelGeneral = function(domain, res) {
             $generatorXml.property(res, domain, 'valueType');
 
             break;
+
+        default:
     }
 
     res.needEmptyLine = true;
@@ -1341,7 +1542,7 @@ $generatorXml.cacheDomains = function(domains, res) {
     if (!res)
         res = $generatorCommon.builder();
 
-    var domainConfigs = _.filter(domains, function (domain) {
+    const domainConfigs = _.filter(domains, function(domain) {
         return $generatorCommon.domainQueryMetadata(domain) === 'Configuration' &&
             $generatorCommon.isDefinedAndNotEmpty(domain.fields);
     });
@@ -1352,7 +1553,7 @@ $generatorXml.cacheDomains = function(domains, res) {
         res.startBlock('<property name="queryEntities">');
         res.startBlock('<list>');
 
-        _.forEach(domainConfigs, function (domain) {
+        _.forEach(domainConfigs, function(domain) {
             $generatorXml.cacheQueryMetadata(domain, res);
         });
 
@@ -1412,8 +1613,8 @@ $generatorXml.clusterCaches = function(caches, igfss, isSrvCfg, res) {
             res.needEmptyLine = true;
         });
 
-        if (isSrvCfg)
-            _.forEach(igfss, function(igfs) {
+        if (isSrvCfg) {
+            _.forEach(igfss, (igfs) => {
                 $generatorXml.cache($generatorCommon.igfsDataCache(igfs), res);
 
                 res.needEmptyLine = true;
@@ -1422,6 +1623,7 @@ $generatorXml.clusterCaches = function(caches, igfss, isSrvCfg, res) {
 
                 res.needEmptyLine = true;
             });
+        }
 
         res.endBlock('</list>');
         res.endBlock('</property>');
@@ -1524,21 +1726,21 @@ $generatorXml.igfsSecondFS = function(igfs, res) {
         res = $generatorCommon.builder();
 
     if (igfs.secondaryFileSystemEnabled) {
-        var secondFs = igfs.secondaryFileSystem || {};
+        const secondFs = igfs.secondaryFileSystem || {};
 
         res.startBlock('<property name="secondaryFileSystem">');
 
         res.startBlock('<bean class="org.apache.ignite.hadoop.fs.IgniteHadoopIgfsSecondaryFileSystem">');
 
-        var nameDefined = $generatorCommon.isDefinedAndNotEmpty(secondFs.userName);
-        var cfgDefined = $generatorCommon.isDefinedAndNotEmpty(secondFs.cfgPath);
+        const nameDefined = $generatorCommon.isDefinedAndNotEmpty(secondFs.userName);
+        const cfgDefined = $generatorCommon.isDefinedAndNotEmpty(secondFs.cfgPath);
 
         $generatorXml.constructorArg(res, 0, secondFs, 'uri');
 
         if (cfgDefined || nameDefined)
             $generatorXml.constructorArg(res, 1, secondFs, 'cfgPath');
 
-        $generatorXml.constructorArg(res, 2, secondFs, 'userName', undefined, true);
+        $generatorXml.constructorArg(res, 2, secondFs, 'userName', null, true);
 
         res.endBlock('</bean>');
         res.endBlock('</property>');
@@ -1561,7 +1763,7 @@ $generatorXml.igfsGeneral = function(igfs, res) {
         $generatorXml.property(res, igfs, 'name');
         $generatorXml.property(res, igfs, 'dataCacheName');
         $generatorXml.property(res, igfs, 'metaCacheName');
-        $generatorXml.property(res, igfs, 'defaultMode', null, "DUAL_ASYNC");
+        $generatorXml.property(res, igfs, 'defaultMode', null, 'DUAL_ASYNC');
 
         res.needEmptyLine = true;
     }
@@ -1605,15 +1807,15 @@ $generatorXml.igfsMisc = function(igfs, res) {
 };
 
 // Generate DataSource beans.
-$generatorXml.generateDataSources = function (datasources, res) {
+$generatorXml.generateDataSources = function(datasources, res) {
     if (!res)
         res = $generatorCommon.builder();
 
     if (datasources.length > 0) {
         res.line('<!-- Data source beans will be initialized from external properties file. -->');
 
-        _.forEach(datasources, function (item) {
-            var beanId = item.dataSourceBean;
+        _.forEach(datasources, function(item) {
+            const beanId = item.dataSourceBean;
 
             res.startBlock('<bean id="' + beanId + '" class="' + item.className + '">');
 
@@ -1658,8 +1860,8 @@ $generatorXml.generateDataSources = function (datasources, res) {
     return res;
 };
 
-$generatorXml.clusterConfiguration = function (cluster, clientNearCfg, res) {
-    var isSrvCfg = _.isNil(clientNearCfg);
+$generatorXml.clusterConfiguration = function(cluster, clientNearCfg, res) {
+    const isSrvCfg = _.isNil(clientNearCfg);
 
     if (!isSrvCfg) {
         res.line('<property name="clientMode" value="true"/>');
@@ -1673,6 +1875,8 @@ $generatorXml.clusterConfiguration = function (cluster, clientNearCfg, res) {
 
     $generatorXml.clusterBinary(cluster.binaryConfiguration, res);
 
+    $generatorXml.clusterCollision(cluster.collision, res);
+
     $generatorXml.clusterCommunication(cluster, res);
 
     $generatorXml.clusterConnector(cluster.connector, res);
@@ -1681,6 +1885,10 @@ $generatorXml.clusterConfiguration = function (cluster, clientNearCfg, res) {
 
     $generatorXml.clusterEvents(cluster, res);
 
+    $generatorXml.clusterFailover(cluster, res);
+
+    $generatorXml.clusterLogger(cluster.logger, res);
+
     $generatorXml.clusterMarshaller(cluster, res);
 
     $generatorXml.clusterMetrics(cluster, res);
@@ -1700,12 +1908,14 @@ $generatorXml.clusterConfiguration = function (cluster, clientNearCfg, res) {
     if (isSrvCfg)
         $generatorXml.igfss(cluster.igfss, res);
 
+    $generatorXml.clusterUserAttributes(cluster, res);
+
     return res;
 };
 
-$generatorXml.cluster = function (cluster, clientNearCfg) {
+$generatorXml.cluster = function(cluster, clientNearCfg) {
     if (cluster) {
-        var res = $generatorCommon.builder(1);
+        const res = $generatorCommon.builder(1);
 
         if (clientNearCfg) {
             res.startBlock('<bean id="nearCacheBean" class="org.apache.ignite.configuration.NearCacheConfiguration">');
@@ -1732,7 +1942,7 @@ $generatorXml.cluster = function (cluster, clientNearCfg) {
 
         // Build final XML:
         // 1. Add header.
-        var xml = '<?xml version="1.0" encoding="UTF-8"?>\n\n';
+        let xml = '<?xml version="1.0" encoding="UTF-8"?>\n\n';
 
         xml += '<!-- ' + $generatorCommon.mainComment() + ' -->\n\n';
         xml += '<beans xmlns="http://www.springframework.org/schema/beans"\n';

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/gulpfile.babel.js/tasks/eslint.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/gulpfile.babel.js/tasks/eslint.js b/modules/web-console/src/main/js/gulpfile.babel.js/tasks/eslint.js
index cb49c64..2d60037 100644
--- a/modules/web-console/src/main/js/gulpfile.babel.js/tasks/eslint.js
+++ b/modules/web-console/src/main/js/gulpfile.babel.js/tasks/eslint.js
@@ -22,6 +22,9 @@ import sequence from 'gulp-sequence';
 
 const paths = [
     './app/**/*.js',
+    './controllers/**/*.js',
+    './generator/**/*.js',
+    './ignite_modules_temp/**/*.js',
     './gulpfile.babel.js/**/*.js',
     './gulpfile.babel.js/*.js'
 ];

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/public/images/cluster.png
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/public/images/cluster.png b/modules/web-console/src/main/js/public/images/cluster.png
index 1add34d..2d8b860 100644
Binary files a/modules/web-console/src/main/js/public/images/cluster.png and b/modules/web-console/src/main/js/public/images/cluster.png differ

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/public/images/query-table.png
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/public/images/query-table.png b/modules/web-console/src/main/js/public/images/query-table.png
index d055125..53becda 100644
Binary files a/modules/web-console/src/main/js/public/images/query-table.png and b/modules/web-console/src/main/js/public/images/query-table.png differ

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/public/stylesheets/style.scss
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/public/stylesheets/style.scss b/modules/web-console/src/main/js/public/stylesheets/style.scss
index 589028c..2c047ac 100644
--- a/modules/web-console/src/main/js/public/stylesheets/style.scss
+++ b/modules/web-console/src/main/js/public/stylesheets/style.scss
@@ -1036,6 +1036,11 @@ button.form-control {
     border: thin dotted $ignite-border-color;
 }
 
+.panel-details-noborder {
+    margin-top: 5px;
+    padding: 10px 5px;
+}
+
 .group {
     border-radius: 5px;
     border: thin dotted $ignite-border-color;
@@ -1346,6 +1351,7 @@ label.required:after {
     position: fixed;
     z-index: 1050;
     margin: 20px;
+    max-width: 700px;
 
     &.top-right {
         top: 60px;
@@ -2126,3 +2132,25 @@ html,body,.splash-screen {
 .nvd3 .nv-axis .nv-axisMaxMin text {
     font-weight: normal; /* Here the text can be modified*/
 }
+
+[ng-hide].ng-hide-add.ng-hide-animate {
+    display: none;
+}
+
+[ng-show].ng-hide-add.ng-hide-animate {
+    display: none;
+}
+
+@media only screen and (max-width: 767px) {
+    .container{
+        padding: 0 $padding-small-horizontal;
+    }
+}
+
+.domains-import-dialog {
+    .modal-body {
+        height: 325px;
+        margin: 0;
+        padding: 0;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/serve/agent.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/serve/agent.js b/modules/web-console/src/main/js/serve/agent.js
index 78dd66f..77da925 100644
--- a/modules/web-console/src/main/js/serve/agent.js
+++ b/modules/web-console/src/main/js/serve/agent.js
@@ -152,7 +152,7 @@ module.exports.factory = function(_, ws, fs, path, JSZip, socketio, settings, mo
                     const code = res.code;
 
                     if (code === 401)
-                        return reject(new Error('Agent is failed to authenticate in grid. Please check agent\'s login and password or node port.'));
+                        return reject(new Error('Agent failed to authenticate in grid. Please check agent\'s login and password or node port.'));
 
                     if (code !== 200)
                         return reject(new Error(error || 'Failed connect to node and execute REST command.'));
@@ -164,7 +164,7 @@ module.exports.factory = function(_, ws, fs, path, JSZip, socketio, settings, mo
                             return resolve(msg.response);
 
                         if (msg.successStatus === 2)
-                            return reject(new Error('Agent is failed to authenticate in grid. Please check agent\'s login and password or node port.'));
+                            return reject(new Error('Agent failed to authenticate in grid. Please check agent\'s login and password or node port.'));
 
                         reject(new Error(msg.error));
                     }
@@ -331,6 +331,27 @@ module.exports.factory = function(_, ws, fs, path, JSZip, socketio, settings, mo
 
         /**
          * @param {Boolean} demo Is need run command on demo node.
+         * @param {Array.<String>} nids Node ids.
+         * @param {Boolean} near true if near cache should be started.
+         * @param {String} cacheName Name for near cache.
+         * @param {String} cfg Cache XML configuration.
+         * @returns {Promise}
+         */
+        cacheStart(demo, nids, near, cacheName, cfg) {
+            const cmd = new Command(demo, 'exe')
+                .addParam('name', 'org.apache.ignite.internal.visor.compute.VisorGatewayTask')
+                .addParam('p1', nids)
+                .addParam('p2', 'org.apache.ignite.internal.visor.cache.VisorCacheStartTask')
+                .addParam('p3', 'org.apache.ignite.internal.visor.cache.VisorCacheStartTask$VisorCacheStartArg')
+                .addParam('p4', near)
+                .addParam('p5', cacheName)
+                .addParam('p6', cfg);
+
+            return this.executeRest(cmd);
+        }
+
+        /**
+         * @param {Boolean} demo Is need run command on demo node.
          * @param {String} nid Node id.
          * @param {String} cacheName Cache name.
          * @returns {Promise}
@@ -349,18 +370,84 @@ module.exports.factory = function(_, ws, fs, path, JSZip, socketio, settings, mo
         /**
          * @param {Boolean} demo Is need run command on demo node.
          * @param {String} nid Node id.
+         * @param {String} cacheName Cache name.
+         * @returns {Promise}
+         */
+        cacheResetMetrics(demo, nid, cacheName) {
+            const cmd = new Command(demo, 'exe')
+                .addParam('name', 'org.apache.ignite.internal.visor.compute.VisorGatewayTask')
+                .addParam('p1', nid)
+                .addParam('p2', 'org.apache.ignite.internal.visor.cache.VisorCacheResetMetricsTask')
+                .addParam('p3', 'java.lang.String')
+                .addParam('p4', cacheName);
+
+            return this.executeRest(cmd);
+        }
+
+        /**
+         * @param {Boolean} demo Is need run command on demo node.
+         * @param {String} nid Node id.
+         * @param {String} cacheNames Cache names separated by comma.
          * @returns {Promise}
          */
-        ping(demo, nid) {
+        cacheSwapBackups(demo, nid, cacheNames) {
             const cmd = new Command(demo, 'exe')
                 .addParam('name', 'org.apache.ignite.internal.visor.compute.VisorGatewayTask')
-                .addParam('p1', 'null')
+                .addParam('p1', nid)
+                .addParam('p2', 'org.apache.ignite.internal.visor.cache.VisorCacheSwapBackupsTask')
+                .addParam('p3', 'java.util.Set')
+                .addParam('p4', 'java.lang.String')
+                .addParam('p5', cacheNames);
+
+            return this.executeRest(cmd);
+        }
+
+        /**
+         * @param {Boolean} demo Is need run command on demo node.
+         * @param {String} nids Node ids.
+         * @returns {Promise}
+         */
+        gc(demo, nids) {
+            const cmd = new Command(demo, 'exe')
+                .addParam('name', 'org.apache.ignite.internal.visor.compute.VisorGatewayTask')
+                .addParam('p1', nids)
+                .addParam('p2', 'org.apache.ignite.internal.visor.node.VisorNodeGcTask')
+                .addParam('p3', 'java.lang.Void');
+
+            return this.executeRest(cmd);
+        }
+
+        /**
+         * @param {Boolean} demo Is need run command on demo node.
+         * @param {String} taskNid node that is not node we want to ping.
+         * @param {String} nid Id of the node to ping.
+         * @returns {Promise}
+         */
+        ping(demo, taskNid, nid) {
+            const cmd = new Command(demo, 'exe')
+                .addParam('name', 'org.apache.ignite.internal.visor.compute.VisorGatewayTask')
+                .addParam('p1', taskNid)
                 .addParam('p2', 'org.apache.ignite.internal.visor.node.VisorNodePingTask')
                 .addParam('p3', 'java.util.UUID')
                 .addParam('p4', nid);
 
             return this.executeRest(cmd);
         }
+
+        /**
+         * @param {Boolean} demo Is need run command on demo node.
+         * @param {String} nid Id of the node to get thread dump.
+         * @returns {Promise}
+         */
+        threadDump(demo, nid) {
+            const cmd = new Command(demo, 'exe')
+                .addParam('name', 'org.apache.ignite.internal.visor.compute.VisorGatewayTask')
+                .addParam('p1', nid)
+                .addParam('p2', 'org.apache.ignite.internal.visor.debug.VisorThreadDumpTask')
+                .addParam('p3', 'java.lang.Void');
+
+            return this.executeRest(cmd);
+        }
     }
 
     /**
@@ -482,66 +569,74 @@ module.exports.factory = function(_, ws, fs, path, JSZip, socketio, settings, mo
                             return cb('You are using an older version of the agent. Please reload agent archive');
                     }
 
-                    mongo.Account.findOne({token: data.token}, (err, account) => {
-                        // TODO IGNITE-1379 send error to web master.
-                        if (err)
-                            cb('Failed to authorize user');
-                        else if (!account)
-                            cb('Invalid token, user not found');
-                        else {
+                    const tokens = data.tokens;
+
+                    mongo.Account.find({token: {$in: tokens}}, '_id token').lean().exec()
+                        .then((accounts) => {
+                            if (!accounts.length)
+                                return cb('Agent is failed to authenticate. Please check agent\'s token(s)');
+
                             const agent = new Agent(socket);
 
-                            socket.on('disconnect', () => {
-                                this._removeAgent(account._id, agent);
-                            });
+                            const accountIds = _.map(accounts, (account) => account._id);
+
+                            socket.on('disconnect', () => this._agentDisconnected(accountIds, agent));
+
+                            this._agentConnected(accountIds, agent);
 
-                            this._addAgent(account._id, agent);
+                            const missedTokens = _.difference(tokens, _.map(accounts, (account) => account.token));
+
+                            if (missedTokens.length) {
+                                agent._emit('agent:warning',
+                                    `Failed to authenticate with token(s): ${missedTokens.join(', ')}.`);
+                            }
 
                             cb();
-                        }
-                    });
+                        })
+                        // TODO IGNITE-1379 send error to web master.
+                        .catch((err) => cb('Agent is failed to authenticate. Please check agent\'s tokens'));
                 });
             });
         }
 
         /**
-         * @param {ObjectId} userId
-         * @param {Socket} user
-         * @returns {int} connected agent count.
+         * @param {ObjectId} accountId
+         * @param {Socket} socket
+         * @returns {int} Connected agent count.
          */
-        addAgentListener(userId, user) {
-            let users = this._browsers[userId];
+        addAgentListener(accountId, socket) {
+            let sockets = this._browsers[accountId];
 
-            if (!users)
-                this._browsers[userId] = users = [];
+            if (!sockets)
+                this._browsers[accountId] = sockets = [];
 
-            users.push(user);
+            sockets.push(socket);
 
-            const agents = this._agents[userId];
+            const agents = this._agents[accountId];
 
             return agents ? agents.length : 0;
         }
 
         /**
-         * @param {ObjectId} userId
-         * @param {Socket} user
+         * @param {ObjectId} accountId.
+         * @param {Socket} socket.
          * @returns {int} connected agent count.
          */
-        removeAgentListener(userId, user) {
-            const users = this._browsers[userId];
+        removeAgentListener(accountId, socket) {
+            const sockets = this._browsers[accountId];
 
-            _.remove(users, (_user) => _user === user);
+            _.pull(sockets, socket);
         }
 
         /**
-         * @param {ObjectId} userId
+         * @param {ObjectId} accountId
          * @returns {Promise.<Agent>}
          */
-        findAgent(userId) {
+        findAgent(accountId) {
             if (!this._server)
                 return Promise.reject(new Error('Agent server not started yet!'));
 
-            const agents = this._agents[userId];
+            const agents = this._agents[accountId];
 
             if (!agents || agents.length === 0)
                 return Promise.reject(new Error('Failed to connect to agent'));
@@ -551,49 +646,66 @@ module.exports.factory = function(_, ws, fs, path, JSZip, socketio, settings, mo
 
         /**
          * Close connections for all user agents.
-         * @param {ObjectId} userId
+         * @param {ObjectId} accountId
+         * @param {String} oldToken
          */
-        close(userId) {
+        close(accountId, oldToken) {
             if (!this._server)
                 return;
 
-            const agents = this._agents[userId];
+            const agentsForClose = this._agents[accountId];
+
+            const agentsForWarning = _.clone(agentsForClose);
 
-            this._agents[userId] = [];
+            this._agents[accountId] = [];
 
-            for (const agent of agents)
-                agent._emit('agent:close', 'Security token was changed for user');
+            _.forEach(this._agents, (sockets) => _.pullAll(agentsForClose, sockets));
+
+            _.pullAll(agentsForWarning, agentsForClose);
+
+            const msg = `Security token has been reset: ${oldToken}`;
+
+            _.forEach(agentsForWarning, (socket) => socket._emit('agent:warning', msg));
+
+            _.forEach(agentsForClose, (socket) => socket._emit('agent:close', msg));
+
+            _.forEach(this._browsers[accountId], (socket) => socket.emit('agent:count', {count: 0}));
         }
 
         /**
-         * @param userId
+         * @param {ObjectId} accountIds
          * @param {Agent} agent
          */
-        _removeAgent(userId, agent) {
-            const agents = this._agents[userId];
+        _agentConnected(accountIds, agent) {
+            _.forEach(accountIds, (accountId) => {
+                let agents = this._agents[accountId];
 
-            _.remove(agents, (_agent) => _agent === agent);
+                if (!agents)
+                    this._agents[accountId] = agents = [];
 
-            const users = this._browsers[userId];
+                agents.push(agent);
 
-            _.forEach(users, (user) => user.emit('agent:count', {count: agents.length}));
+                const sockets = this._browsers[accountId];
+
+                _.forEach(sockets, (socket) => socket.emit('agent:count', {count: agents.length}));
+            });
         }
 
         /**
-         * @param {ObjectId} userId
+         * @param {ObjectId} accountIds
          * @param {Agent} agent
          */
-        _addAgent(userId, agent) {
-            let agents = this._agents[userId];
+        _agentDisconnected(accountIds, agent) {
+            _.forEach(accountIds, (accountId) => {
+                const agents = this._agents[accountId];
 
-            if (!agents)
-                this._agents[userId] = agents = [];
+                if (agents && agents.length)
+                    _.pull(agents, agent);
 
-            agents.push(agent);
+                const sockets = this._browsers[accountId];
 
-            const users = this._browsers[userId];
-
-            _.forEach(users, (user) => user.emit('agent:count', {count: agents.length}));
+                _.forEach(sockets, (socket) => socket.emit('agent:count', {count: agents.length}));
+            });
         }
     }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/serve/browser.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/serve/browser.js b/modules/web-console/src/main/js/serve/browser.js
index 837450d..8a6d33e 100644
--- a/modules/web-console/src/main/js/serve/browser.js
+++ b/modules/web-console/src/main/js/serve/browser.js
@@ -42,13 +42,15 @@ module.exports.factory = (_, socketio, agentMgr, configure) => {
             configure.socketio(io);
 
             io.sockets.on('connection', (socket) => {
-                const user = socket.client.request.user;
+                const user = socket.request.user;
 
-                const demo = socket.client.request._query.IgniteDemoMode === 'true';
+                const demo = socket.request._query.IgniteDemoMode === 'true';
+
+                const accountId = () => user._id;
 
                 // Return available drivers to browser.
                 socket.on('schemaImport:drivers', (cb) => {
-                    agentMgr.findAgent(user._id)
+                    agentMgr.findAgent(accountId())
                         .then((agent) => agent.availableDrivers())
                         .then((drivers) => cb(null, drivers))
                         .catch((err) => cb(_errorToJson(err)));
@@ -56,7 +58,7 @@ module.exports.factory = (_, socketio, agentMgr, configure) => {
 
                 // Return schemas from database to browser.
                 socket.on('schemaImport:schemas', (preset, cb) => {
-                    agentMgr.findAgent(user._id)
+                    agentMgr.findAgent(accountId())
                         .then((agent) => {
                             const jdbcInfo = {user: preset.user, password: preset.password};
 
@@ -68,7 +70,7 @@ module.exports.factory = (_, socketio, agentMgr, configure) => {
 
                 // Return tables from database to browser.
                 socket.on('schemaImport:tables', (preset, cb) => {
-                    agentMgr.findAgent(user._id)
+                    agentMgr.findAgent(accountId())
                         .then((agent) => {
                             const jdbcInfo = {user: preset.user, password: preset.password};
 
@@ -81,7 +83,7 @@ module.exports.factory = (_, socketio, agentMgr, configure) => {
 
                 // Return topology command result from grid to browser.
                 socket.on('node:topology', (attr, mtr, cb) => {
-                    agentMgr.findAgent(user._id)
+                    agentMgr.findAgent(accountId())
                         .then((agent) => agent.topology(demo, attr, mtr))
                         .then((clusters) => cb(null, clusters))
                         .catch((err) => cb(_errorToJson(err)));
@@ -89,7 +91,7 @@ module.exports.factory = (_, socketio, agentMgr, configure) => {
 
                 // Close query on node.
                 socket.on('node:query:close', (queryId, cb) => {
-                    agentMgr.findAgent(user._id)
+                    agentMgr.findAgent(accountId())
                         .then((agent) => agent.queryClose(demo, queryId))
                         .then(() => cb())
                         .catch((err) => cb(_errorToJson(err)));
@@ -97,7 +99,7 @@ module.exports.factory = (_, socketio, agentMgr, configure) => {
 
                 // Execute query on node and return first page to browser.
                 socket.on('node:query', (cacheName, pageSize, query, cb) => {
-                    agentMgr.findAgent(user._id)
+                    agentMgr.findAgent(accountId())
                         .then((agent) => {
                             if (query === null)
                                 return agent.scan(demo, cacheName, pageSize);
@@ -110,7 +112,7 @@ module.exports.factory = (_, socketio, agentMgr, configure) => {
 
                 // Fetch next page for query and return result to browser.
                 socket.on('node:query:fetch', (queryId, pageSize, cb) => {
-                    agentMgr.findAgent(user._id)
+                    agentMgr.findAgent(accountId())
                         .then((agent) => agent.queryFetch(demo, queryId, pageSize))
                         .then((res) => cb(null, res))
                         .catch((err) => cb(_errorToJson(err)));
@@ -121,7 +123,7 @@ module.exports.factory = (_, socketio, agentMgr, configure) => {
                     // Set page size for query.
                     const pageSize = 1024;
 
-                    agentMgr.findAgent(user._id)
+                    agentMgr.findAgent(accountId())
                         .then((agent) => {
                             const firstPage = query === null ? agent.scan(demo, cacheName, pageSize)
                                 : agent.fieldsQuery(demo, cacheName, query, pageSize);
@@ -132,7 +134,7 @@ module.exports.factory = (_, socketio, agentMgr, configure) => {
 
                                 return agent.queryFetch(demo, acc.queryId, pageSize)
                                     .then((res) => {
-                                        acc.rows = acc.rows.concat(res.rows);
+                                        acc.items = acc.items.concat(res.items);
 
                                         acc.last = res.last;
 
@@ -149,7 +151,7 @@ module.exports.factory = (_, socketio, agentMgr, configure) => {
 
                 // Return cache metadata from all nodes in grid.
                 socket.on('node:cache:metadata', (cacheName, cb) => {
-                    agentMgr.findAgent(user._id)
+                    agentMgr.findAgent(accountId())
                         .then((agent) => agent.metadata(demo, cacheName))
                         .then((caches) => {
                             let types = [];
@@ -159,6 +161,8 @@ module.exports.factory = (_, socketio, agentMgr, configure) => {
                             };
 
                             const _typeMapper = (meta, typeName) => {
+                                const maskedName = _.isEmpty(meta.cacheName) ? '<default>' : meta.cacheName;
+
                                 let fields = meta.fields[typeName];
 
                                 let columns = [];
@@ -173,7 +177,8 @@ module.exports.factory = (_, socketio, agentMgr, configure) => {
                                             clazz: fieldClass,
                                             system: fieldName === '_KEY' || fieldName === '_VAL',
                                             cacheName: meta.cacheName,
-                                            typeName
+                                            typeName,
+                                            maskedName
                                         });
                                     }
                                 }
@@ -190,7 +195,8 @@ module.exports.factory = (_, socketio, agentMgr, configure) => {
                                             order: index.descendings.indexOf(field) < 0,
                                             unique: index.unique,
                                             cacheName: meta.cacheName,
-                                            typeName
+                                            typeName,
+                                            maskedName
                                         });
                                     }
 
@@ -200,7 +206,8 @@ module.exports.factory = (_, socketio, agentMgr, configure) => {
                                             name: index.name,
                                             children: fields,
                                             cacheName: meta.cacheName,
-                                            typeName
+                                            typeName,
+                                            maskedName
                                         });
                                     }
                                 }
@@ -213,6 +220,7 @@ module.exports.factory = (_, socketio, agentMgr, configure) => {
                                         name: 'Indexes',
                                         cacheName: meta.cacheName,
                                         typeName,
+                                        maskedName,
                                         children: indexes
                                     });
                                 }
@@ -221,6 +229,7 @@ module.exports.factory = (_, socketio, agentMgr, configure) => {
                                     type: 'type',
                                     cacheName: meta.cacheName || '',
                                     typeName,
+                                    maskedName,
                                     children: columns
                                 };
                             };
@@ -239,7 +248,7 @@ module.exports.factory = (_, socketio, agentMgr, configure) => {
 
                 // Fetch next page for query and return result to browser.
                 socket.on('node:visor:collect', (evtOrderKey, evtThrottleCntrKey, cb) => {
-                    agentMgr.findAgent(user._id)
+                    agentMgr.findAgent(accountId())
                         .then((agent) => agent.collect(demo, evtOrderKey, evtThrottleCntrKey))
                         .then((data) => {
                             if (data.finished)
@@ -250,9 +259,35 @@ module.exports.factory = (_, socketio, agentMgr, configure) => {
                         .catch((err) => cb(_errorToJson(err)));
                 });
 
+                // Swap backups specified caches on specified node and return result to browser.
+                socket.on('node:cache:swap:backups', (nid, cacheNames, cb) => {
+                    agentMgr.findAgent(accountId())
+                        .then((agent) => agent.cacheSwapBackups(demo, nid, cacheNames))
+                        .then((data) => {
+                            if (data.finished)
+                                return cb(null, data.result);
+
+                            cb(_errorToJson(data.error));
+                        })
+                        .catch((err) => cb(_errorToJson(err)));
+                });
+
+                // Reset metrics specified cache on specified node and return result to browser.
+                socket.on('node:cache:reset:metrics', (nid, cacheName, cb) => {
+                    agentMgr.findAgent(accountId())
+                        .then((agent) => agent.cacheResetMetrics(demo, nid, cacheName))
+                        .then((data) => {
+                            if (data.finished)
+                                return cb(null, data.result);
+
+                            cb(_errorToJson(data.error));
+                        })
+                        .catch((err) => cb(_errorToJson(err)));
+                });
+
                 // Clear specified cache on specified node and return result to browser.
                 socket.on('node:cache:clear', (nid, cacheName, cb) => {
-                    agentMgr.findAgent(user._id)
+                    agentMgr.findAgent(accountId())
                         .then((agent) => agent.cacheClear(demo, nid, cacheName))
                         .then((data) => {
                             if (data.finished)
@@ -263,10 +298,23 @@ module.exports.factory = (_, socketio, agentMgr, configure) => {
                         .catch((err) => cb(_errorToJson(err)));
                 });
 
+                // Start specified cache and return result to browser.
+                socket.on('node:cache:start', (nids, near, cacheName, cfg, cb) => {
+                    agentMgr.findAgent(accountId())
+                        .then((agent) => agent.cacheStart(demo, nids, near, cacheName, cfg))
+                        .then((data) => {
+                            if (data.finished)
+                                return cb(null, data.result);
+
+                            cb(_errorToJson(data.error));
+                        })
+                        .catch((err) => cb(_errorToJson(err)));
+                });
+
                 // Stop specified cache on specified node and return result to browser.
-                socket.on('node:cache:stop', (nids, cacheName, cb) => {
-                    agentMgr.findAgent(user._id)
-                        .then((agent) => agent.cacheStop(demo, nids, cacheName))
+                socket.on('node:cache:stop', (nid, cacheName, cb) => {
+                    agentMgr.findAgent(accountId())
+                        .then((agent) => agent.cacheStop(demo, nid, cacheName))
                         .then((data) => {
                             if (data.finished)
                                 return cb(null, data.result);
@@ -278,9 +326,35 @@ module.exports.factory = (_, socketio, agentMgr, configure) => {
 
 
                 // Ping node and return result to browser.
-                socket.on('node:ping', (nid, cb) => {
-                    agentMgr.findAgent(user._id)
-                        .then((agent) => agent.ping(demo, nid))
+                socket.on('node:ping', (taskNid, nid, cb) => {
+                    agentMgr.findAgent(accountId())
+                        .then((agent) => agent.ping(demo, taskNid, nid))
+                        .then((data) => {
+                            if (data.finished)
+                                return cb(null, data.result);
+
+                            cb(_errorToJson(data.error));
+                        })
+                        .catch((err) => cb(_errorToJson(err)));
+                });
+
+                // GC node and return result to browser.
+                socket.on('node:gc', (nids, cb) => {
+                    agentMgr.findAgent(accountId())
+                        .then((agent) => agent.gc(demo, nids))
+                        .then((data) => {
+                            if (data.finished)
+                                return cb(null, data.result);
+
+                            cb(_errorToJson(data.error));
+                        })
+                        .catch((err) => cb(_errorToJson(err)));
+                });
+
+                // GC node and return result to browser.
+                socket.on('node:thread:dump', (nid, cb) => {
+                    agentMgr.findAgent(accountId())
+                        .then((agent) => agent.threadDump(demo, nid))
                         .then((data) => {
                             if (data.finished)
                                 return cb(null, data.result);

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/serve/configure.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/serve/configure.js b/modules/web-console/src/main/js/serve/configure.js
index 71f7c8a..9671d66 100644
--- a/modules/web-console/src/main/js/serve/configure.js
+++ b/modules/web-console/src/main/js/serve/configure.js
@@ -46,6 +46,7 @@ module.exports.factory = function(logger, cookieParser, bodyParser, session, con
                 secret: settings.sessionSecret,
                 resave: false,
                 saveUninitialized: true,
+                unset: 'destroy',
                 cookie: {
                     expires: new Date(Date.now() + settings.cookieTTL),
                     maxAge: settings.cookieTTL

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/serve/mongo.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/serve/mongo.js b/modules/web-console/src/main/js/serve/mongo.js
index 81b4188..8fb0a20 100644
--- a/modules/web-console/src/main/js/serve/mongo.js
+++ b/modules/web-console/src/main/js/serve/mongo.js
@@ -540,7 +540,63 @@ module.exports.factory = function(deepPopulatePlugin, passportMongo, settings, p
             trustStoreType: String,
             trustManagers: [String]
         },
-        rebalanceThreadPoolSize: Number
+        rebalanceThreadPoolSize: Number,
+        attributes: [{name: String, value: String}],
+        collision: {
+            kind: {type: String, enum: ['Noop', 'PriorityQueue', 'FifoQueue', 'JobStealing', 'Custom']},
+            PriorityQueue: {
+                parallelJobsNumber: Number,
+                waitingJobsNumber: Number,
+                priorityAttributeKey: String,
+                jobPriorityAttributeKey: String,
+                defaultPriority: Number,
+                starvationIncrement: Number,
+                starvationPreventionEnabled: Boolean
+            },
+            FifoQueue: {
+                parallelJobsNumber: Number,
+                waitingJobsNumber: Number
+            },
+            JobStealing: {
+                activeJobsThreshold: Number,
+                waitJobsThreshold: Number,
+                messageExpireTime: Number,
+                maximumStealingAttempts: Number,
+                stealingEnabled: Boolean,
+                stealingAttributes: [{name: String, value: String}],
+                externalCollisionListener: String
+            },
+            Custom: {
+                class: String
+            }
+        },
+        failoverSpi: [{
+            kind: {type: String, enum: ['JobStealing', 'Never', 'Always', 'Custom']},
+            JobStealing: {
+                maximumFailoverAttempts: Number
+            },
+            Always: {
+                maximumFailoverAttempts: Number
+            },
+            Custom: {
+                class: String
+            }
+        }],
+        logger: {
+            kind: {type: 'String', enum: ['Log4j2', 'Null', 'Java', 'JCL', 'SLF4J', 'Log4j', 'Custom']},
+            Log4j2: {
+                level: {type: String, enum: ['OFF', 'FATAL', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE', 'ALL']},
+                path: String
+            },
+            Log4j: {
+                mode: {type: String, enum: ['Default', 'Path']},
+                level: {type: String, enum: ['OFF', 'FATAL', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE', 'ALL']},
+                path: String
+            },
+            Custom: {
+                class: String
+            }
+        }
     });
 
     // Install deep populate plugin.

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/serve/routes/agent.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/serve/routes/agent.js b/modules/web-console/src/main/js/serve/routes/agent.js
index 8fd8b75..020b692 100644
--- a/modules/web-console/src/main/js/serve/routes/agent.js
+++ b/modules/web-console/src/main/js/serve/routes/agent.js
@@ -59,7 +59,7 @@ module.exports.factory = function(_, express, fs, JSZip, settings, agentMgr) {
 
                 const host = req.hostname.match(/:/g) ? req.hostname.slice(0, req.hostname.indexOf(':')) : req.hostname;
 
-                prop.push('token=' + req.user.token);
+                prop.push('tokens=' + req.user.token);
                 prop.push('server-uri=' + (settings.agent.SSLOptions ? 'https' : 'http') + '://' + host + ':' + settings.agent.port);
                 prop.push('#Uncomment following options if needed:');
                 prop.push('#node-uri=http://localhost:8080');
@@ -79,4 +79,3 @@ module.exports.factory = function(_, express, fs, JSZip, settings, agentMgr) {
         resolveFactory(router);
     });
 };
-

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/serve/routes/caches.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/serve/routes/caches.js b/modules/web-console/src/main/js/serve/routes/caches.js
index 61a0cfb..ed1f257 100644
--- a/modules/web-console/src/main/js/serve/routes/caches.js
+++ b/modules/web-console/src/main/js/serve/routes/caches.js
@@ -90,8 +90,8 @@ module.exports.factory = function(_, express, mongo) {
 
                     return (new mongo.Cache(params)).save()
                         .then((cache) =>
-                            mongo.Cluster.update({_id: {$in: clusters}}, {$addToSet: {caches: cacheId}}, {multi: true}).exec()
-                                .then(() => mongo.DomainModel.update({_id: {$in: domains}}, {$addToSet: {caches: cacheId}}, {multi: true}).exec())
+                            mongo.Cluster.update({_id: {$in: clusters}}, {$addToSet: {caches: cache._id}}, {multi: true}).exec()
+                                .then(() => mongo.DomainModel.update({_id: {$in: domains}}, {$addToSet: {caches: cache._id}}, {multi: true}).exec())
                                 .then(() => res.send(cache._id))
                         );
                 })

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/serve/routes/clusters.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/serve/routes/clusters.js b/modules/web-console/src/main/js/serve/routes/clusters.js
index bfe89d3..9d13990 100644
--- a/modules/web-console/src/main/js/serve/routes/clusters.js
+++ b/modules/web-console/src/main/js/serve/routes/clusters.js
@@ -103,10 +103,10 @@ module.exports.factory = function(_, express, mongo) {
 
                     return (new mongo.Cluster(params)).save()
                         .then((cluster) =>
-                            mongo.Cache.update({_id: {$in: caches}}, {$addToSet: {clusters: clusterId}}, {multi: true}).exec()
-                                .then(() => mongo.Cache.update({_id: {$nin: caches}}, {$pull: {clusters: clusterId}}, {multi: true}).exec())
-                                .then(() => mongo.Igfs.update({_id: {$in: igfss}}, {$addToSet: {clusters: clusterId}}, {multi: true}).exec())
-                                .then(() => mongo.Igfs.update({_id: {$nin: igfss}}, {$pull: {clusters: clusterId}}, {multi: true}).exec())
+                            mongo.Cache.update({_id: {$in: caches}}, {$addToSet: {clusters: cluster._id}}, {multi: true}).exec()
+                                .then(() => mongo.Cache.update({_id: {$nin: caches}}, {$pull: {clusters: cluster._id}}, {multi: true}).exec())
+                                .then(() => mongo.Igfs.update({_id: {$in: igfss}}, {$addToSet: {clusters: cluster._id}}, {multi: true}).exec())
+                                .then(() => mongo.Igfs.update({_id: {$nin: igfss}}, {$pull: {clusters: cluster._id}}, {multi: true}).exec())
                                 .then(() => res.send(cluster._id))
                         );
                 })

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/serve/routes/igfs.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/serve/routes/igfs.js b/modules/web-console/src/main/js/serve/routes/igfs.js
index 6e5e60c..f590273 100644
--- a/modules/web-console/src/main/js/serve/routes/igfs.js
+++ b/modules/web-console/src/main/js/serve/routes/igfs.js
@@ -82,7 +82,7 @@ module.exports.factory = function(_, express, mongo) {
 
                     return (new mongo.Igfs(params)).save()
                         .then((igfs) =>
-                            mongo.Cluster.update({_id: {$in: clusters}}, {$addToSet: {igfss: igfsId}}, {multi: true}).exec()
+                            mongo.Cluster.update({_id: {$in: clusters}}, {$addToSet: {igfss: igfs._id}}, {multi: true}).exec()
                                 .then(() => res.send(igfs._id))
                         );
                 })

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/serve/routes/profile.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/serve/routes/profile.js b/modules/web-console/src/main/js/serve/routes/profile.js
index 5e4278f..5563a2b 100644
--- a/modules/web-console/src/main/js/serve/routes/profile.js
+++ b/modules/web-console/src/main/js/serve/routes/profile.js
@@ -80,13 +80,20 @@ module.exports.factory = function(_, express, mongo, agentMgr) {
                 })
                 .then((user) => {
                     if (params.token && user.token !== params.token)
-                        agentMgr.close(user._id);
+                        agentMgr.close(user._id, user.token);
 
                     _.extend(user, params);
 
                     return user.save();
                 })
-                .then(() => res.sendStatus(200))
+                .then((user) => {
+                    const becomeUsed = req.session.viewedUser && req.user.admin;
+
+                    if (becomeUsed)
+                        req.session.viewedUser = user;
+
+                    res.sendStatus(200);
+                })
                 .catch((err) => mongo.handleError(res, err));
         });