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:45 UTC

[01/22] ignite git commit: IGNITE-3339 - Fixed entries eviction.

Repository: ignite
Updated Branches:
  refs/heads/master 8ec6db5e8 -> 34fc31e87


IGNITE-3339 - Fixed entries eviction.


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/bb7a6728
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/bb7a6728
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/bb7a6728

Branch: refs/heads/master
Commit: bb7a6728bbd886677a4e7f0c7ad92161dd51122b
Parents: 8ce29a9
Author: Alexey Goncharuk <al...@gmail.com>
Authored: Wed Jun 22 06:34:57 2016 -0700
Committer: Alexey Goncharuk <al...@gmail.com>
Committed: Wed Jun 22 06:42:38 2016 -0700

----------------------------------------------------------------------
 .../distributed/near/GridNearGetFuture.java     |   8 +-
 .../transactions/IgniteTxLocalAdapter.java      |  12 +-
 .../cache/transactions/IgniteTxManager.java     |  16 +-
 .../IgniteCacheTxIteratorSelfTest.java          | 241 +++++++++++++++++++
 .../testsuites/IgniteCacheTestSuite5.java       |   2 +
 5 files changed, 266 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/bb7a6728/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetFuture.java
index 290c08e..b7fcbbd 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetFuture.java
@@ -62,8 +62,6 @@ import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgniteUuid;
 import org.jetbrains.annotations.Nullable;
 
-import static org.apache.ignite.transactions.TransactionIsolation.READ_COMMITTED;
-
 /**
  *
  */
@@ -636,7 +634,7 @@ public final class GridNearGetFuture<K, V> extends CacheDistributedGetFutureAdap
             catch (GridCacheEntryRemovedException ignored) {
                 // Retry.
             }
-            catch (GridDhtInvalidPartitionException e) {
+            catch (GridDhtInvalidPartitionException ignored) {
                 return false;
             }
             catch (IgniteCheckedException e) {
@@ -645,7 +643,9 @@ public final class GridNearGetFuture<K, V> extends CacheDistributedGetFutureAdap
                 return false;
             }
             finally {
-                if (dhtEntry != null && (tx == null || (!tx.implicit() && tx.isolation() == READ_COMMITTED)))
+                if (dhtEntry != null)
+                    // Near cache is enabled, so near entry will be enlisted in the transaction.
+                    // Always touch DHT entry in this case.
                     dht.context().evicts().touch(dhtEntry, topVer);
             }
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/bb7a6728/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalAdapter.java
index d51d873..d9aca4a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalAdapter.java
@@ -1395,11 +1395,15 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
                             log.debug("Got removed entry in transaction getAllAsync(..) (will retry): " + key);
                     }
                     finally {
-                        if (cacheCtx.isNear() && entry != null && readCommitted()) {
-                            if (cacheCtx.affinity().belongs(cacheCtx.localNode(), entry.partition(), topVer)) {
-                                if (entry.markObsolete(xidVer))
-                                    cacheCtx.cache().removeEntry(entry);
+                        if (entry != null && readCommitted()) {
+                            if (cacheCtx.isNear()) {
+                                if (cacheCtx.affinity().belongs(cacheCtx.localNode(), entry.partition(), topVer)) {
+                                    if (entry.markObsolete(xidVer))
+                                        cacheCtx.cache().removeEntry(entry);
+                                }
                             }
+                            else
+                                entry.context().evicts().touch(entry, topVer);
                         }
                     }
                 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/bb7a6728/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxManager.java
index 4ec280f..e8d20b6 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxManager.java
@@ -1236,7 +1236,7 @@ public class IgniteTxManager extends GridCacheSharedManagerAdapter {
      * @param tx Transaction to finish.
      * @param commit {@code True} if transaction is committed, {@code false} if rolled back.
      */
-    public void fastFinishTx(IgniteInternalTx tx, boolean commit) {
+    public void fastFinishTx(GridNearTxLocal tx, boolean commit) {
         assert tx != null;
         assert tx.writeMap().isEmpty();
         assert tx.optimistic() || tx.readMap().isEmpty();
@@ -1247,16 +1247,22 @@ public class IgniteTxManager extends GridCacheSharedManagerAdapter {
             // 1. Notify evictions.
             notifyEvitions(tx);
 
-            // 2. Remove obsolete entries.
+            // 2. Evict near entries.
+            if (!tx.readMap().isEmpty()) {
+                for (IgniteTxEntry entry : tx.readMap().values())
+                    tx.evictNearEntry(entry, false);
+            }
+
+            // 3. Remove obsolete entries.
             removeObsolete(tx);
 
-            // 3. Remove from per-thread storage.
+            // 4. Remove from per-thread storage.
             clearThreadMap(tx);
 
-            // 4. Clear context.
+            // 5. Clear context.
             resetContext();
 
-            // 5. Update metrics.
+            // 6. Update metrics.
             if (!tx.dht() && tx.local()) {
                 if (!tx.system()) {
                     if (commit)

http://git-wip-us.apache.org/repos/asf/ignite/blob/bb7a6728/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheTxIteratorSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheTxIteratorSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheTxIteratorSelfTest.java
new file mode 100644
index 0000000..769a5f6
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheTxIteratorSelfTest.java
@@ -0,0 +1,241 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.distributed;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMemoryMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.CacheWriteSynchronizationMode;
+import org.apache.ignite.cache.eviction.fifo.FifoEvictionPolicy;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.NearCacheConfiguration;
+import org.apache.ignite.configuration.TransactionConfiguration;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.apache.ignite.transactions.Transaction;
+import org.apache.ignite.transactions.TransactionConcurrency;
+import org.apache.ignite.transactions.TransactionIsolation;
+
+import javax.cache.Cache;
+
+/**
+ *
+ */
+public class IgniteCacheTxIteratorSelfTest extends GridCommonAbstractTest {
+    /** */
+    public static final String CACHE_NAME = "testCache";
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        final IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+        final TransactionConfiguration txCfg = new TransactionConfiguration();
+
+        txCfg.setDefaultTxIsolation(TransactionIsolation.READ_COMMITTED);
+
+        cfg.setTransactionConfiguration(txCfg);
+
+        return cfg;
+    }
+
+    /**
+     * @return Cache configuration.
+     */
+    private CacheConfiguration<String, TestClass> cacheConfiguration(
+        CacheMode mode,
+        CacheAtomicityMode atomMode,
+        CacheMemoryMode memMode,
+        boolean nearEnabled,
+        boolean useEvictPlc
+    ) {
+        final CacheConfiguration<String, TestClass> ccfg = new CacheConfiguration<>(CACHE_NAME);
+
+        ccfg.setAtomicityMode(atomMode);
+        ccfg.setCacheMode(mode);
+        ccfg.setMemoryMode(memMode);
+        ccfg.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC);
+
+        if (nearEnabled)
+            ccfg.setNearConfiguration(new NearCacheConfiguration<String, TestClass>());
+
+        if (memMode == CacheMemoryMode.ONHEAP_TIERED && useEvictPlc) {
+            ccfg.setOffHeapMaxMemory(10 * 1024 * 1024);
+            ccfg.setEvictionPolicy(new FifoEvictionPolicy(50));
+        }
+
+        return ccfg;
+    }
+
+    /**
+     * @throws Exception if failed.
+     */
+    public void testModesSingleNode() throws Exception {
+        checkModes(1);
+    }
+
+    /**
+     * @throws Exception if failed.
+     */
+    public void testModesMultiNode() throws Exception {
+        checkModes(3);
+    }
+
+    /**
+     * @throws Exception if failed.
+     */
+    public void checkModes(int gridCnt) throws Exception {
+        startGrids(gridCnt);
+
+        try {
+            for (CacheMode mode : CacheMode.values()) {
+                for (CacheAtomicityMode atomMode : CacheAtomicityMode.values()) {
+                    for (CacheMemoryMode memMode : CacheMemoryMode.values()) {
+                        if (mode == CacheMode.PARTITIONED) {
+                            // Near cache makes sense only for partitioned cache.
+                            checkTxCache(CacheMode.PARTITIONED, atomMode, memMode, true, false);
+                        }
+
+                        if (memMode == CacheMemoryMode.ONHEAP_TIERED)
+                            checkTxCache(mode, atomMode, CacheMemoryMode.ONHEAP_TIERED, false, true);
+
+                        checkTxCache(mode, atomMode, memMode, false, false);
+                    }
+                }
+            }
+        }
+        finally {
+            stopAllGrids();
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    private void checkTxCache(
+        CacheMode mode,
+        CacheAtomicityMode atomMode,
+        CacheMemoryMode memMode,
+        boolean nearEnabled,
+        boolean useEvicPlc
+    ) throws Exception {
+        final Ignite ignite = grid(0);
+
+        final CacheConfiguration<String, TestClass> ccfg = cacheConfiguration(
+            mode,
+            atomMode,
+            memMode,
+            nearEnabled,
+            useEvicPlc);
+
+        final IgniteCache<String, TestClass> cache = ignite.createCache(ccfg);
+
+        info("Checking cache [mode=" + mode + ", atomMode=" + atomMode + ", memMode=" + memMode +
+            ", near=" + nearEnabled + ']');
+
+        try {
+            for (int i = 0; i < 30; i++) {
+                final TestClass val = new TestClass("data");
+                final String key = "key-" + i;
+
+                cache.put(key, val);
+
+                assertEquals(i + 1, cache.size());
+
+                for (TransactionIsolation iso : TransactionIsolation.values()) {
+                    for (TransactionConcurrency con : TransactionConcurrency.values()) {
+                        try (Transaction transaction = ignite.transactions().txStart(con, iso)) {
+                            assertEquals(val, cache.get(key));
+
+                            transaction.commit();
+                        }
+
+                        int cnt = iterateOverKeys(cache);
+
+                        assertEquals("Failed [con=" + con + ", iso=" + iso + ']', i + 1, cnt);
+
+                        assertEquals("Failed [con=" + con + ", iso=" + iso + ']', i + 1, cache.size());
+                    }
+                }
+            }
+        }
+        finally {
+            grid(0).destroyCache(CACHE_NAME);
+        }
+    }
+
+    /**
+     * @param cache Cache.
+     */
+    @SuppressWarnings("TypeMayBeWeakened")
+    private int iterateOverKeys(final IgniteCache<String, TestClass> cache) {
+        int cnt = 0;
+
+        for (final Cache.Entry<String, TestClass> ignore : cache)
+            cnt++;
+
+        return cnt;
+    }
+
+    /**
+     *
+     */
+    private static class TestClass {
+        /** */
+        private String data;
+
+        /**
+         * @param data Data.
+         */
+        private TestClass(String data) {
+            this.data = data;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public boolean equals(final Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            final TestClass testCls = (TestClass)o;
+
+            return data != null ? data.equals(testCls.data) : testCls.data == null;
+
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public int hashCode() {
+            return data != null ? data.hashCode() : 0;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public String toString() {
+            return S.toString(TestClass.class, this);
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/bb7a6728/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite5.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite5.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite5.java
index 41e0ed1..7582f5c 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite5.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite5.java
@@ -29,6 +29,7 @@ import org.apache.ignite.internal.processors.cache.distributed.CacheLateAffinity
 import org.apache.ignite.internal.processors.cache.distributed.CacheLateAffinityAssignmentNodeJoinValidationTest;
 import org.apache.ignite.internal.processors.cache.distributed.CacheLateAffinityAssignmentTest;
 import org.apache.ignite.internal.processors.cache.distributed.replicated.IgniteCacheSyncRebalanceModeSelfTest;
+import org.apache.ignite.internal.processors.cache.distributed.IgniteCacheTxIteratorSelfTest;
 import org.apache.ignite.internal.processors.cache.store.IgniteCacheWriteBehindNoUpdateSelfTest;
 
 /**
@@ -56,6 +57,7 @@ public class IgniteCacheTestSuite5 extends TestSuite {
         suite.addTestSuite(IgniteCacheSyncRebalanceModeSelfTest.class);
 
         suite.addTest(IgniteCacheReadThroughEvictionsVariationsSuite.suite());
+        suite.addTestSuite(IgniteCacheTxIteratorSelfTest.class);
 
         return suite;
     }


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

Posted by ak...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/generator/generator-java.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/generator/generator-java.js b/modules/web-console/src/main/js/generator/generator-java.js
index 7bb6f10..2b6ac2f 100644
--- a/modules/web-console/src/main/js/generator/generator-java.js
+++ b/modules/web-console/src/main/js/generator/generator-java.js
@@ -25,7 +25,7 @@ const $generatorJava = {};
  * @param type Value type.
  * @returns {*} String with value that will be valid for java.
  */
-$generatorJava.toJavaCode = function (val, type) {
+$generatorJava.toJavaCode = function(val, type) {
     if (val === null)
         return 'null';
 
@@ -44,13 +44,13 @@ $generatorJava.toJavaCode = function (val, type) {
     if (type)
         return type + '.' + val;
 
-    if (typeof(val) === 'string')
+    if (typeof (val) === 'string')
         return '"' + val.replace('"', '\\"') + '"';
 
-    if (typeof(val) === 'number' || typeof(val) === 'boolean')
-        return '' + val;
+    if (typeof (val) === 'number' || typeof (val) === 'boolean')
+        return String(val);
 
-    return 'Unknown type: ' + typeof(val) + ' (' + val + ')';
+    return 'Unknown type: ' + typeof (val) + ' (' + val + ')';
 };
 
 /**
@@ -58,20 +58,20 @@ $generatorJava.toJavaCode = function (val, type) {
  * @param setterName Optional concrete setter name.
  * @returns Property setter with name by java conventions.
  */
-$generatorJava.setterName = function (propName, setterName) {
+$generatorJava.setterName = function(propName, setterName) {
     return setterName ? setterName : $generatorCommon.toJavaName('set', propName);
 };
 
 // Add constructor argument
-$generatorJava.constructorArg = function (obj, propName, dflt, notFirst, opt) {
-    var v = (obj ? obj[propName] : null) || dflt;
+$generatorJava.constructorArg = function(obj, propName, dflt, notFirst, opt) {
+    const v = (obj ? obj[propName] : null) || dflt;
 
     if ($generatorCommon.isDefinedAndNotEmpty(v))
         return (notFirst ? ', ' : '') + $generatorJava.toJavaCode(v);
     else if (!opt)
         return notFirst ? ', null' : 'null';
-    else
-        return '';
+
+    return '';
 };
 
 /**
@@ -85,20 +85,20 @@ $generatorJava.constructorArg = function (obj, propName, dflt, notFirst, opt) {
  * @param varFullGenericType2 Optional full class name of second generic.
  * @param subClass If 'true' then variable will be declared as anonymous subclass.
  */
-$generatorJava.declareVariable = function (res, varName, varFullType, varFullActualType, varFullGenericType1, varFullGenericType2, subClass) {
+$generatorJava.declareVariable = function(res, varName, varFullType, varFullActualType, varFullGenericType1, varFullGenericType2, subClass) {
     res.emptyLineIfNeeded();
 
-    var varType = res.importClass(varFullType);
+    const varType = res.importClass(varFullType);
 
-    var varNew = !res.vars[varName];
+    const varNew = !res.vars[varName];
 
     if (varNew)
         res.vars[varName] = true;
 
     if (varFullActualType && varFullGenericType1) {
-        var varActualType = res.importClass(varFullActualType);
-        var varGenericType1 = res.importClass(varFullGenericType1);
-        var varGenericType2 = null;
+        const varActualType = res.importClass(varFullActualType);
+        const varGenericType1 = res.importClass(varFullGenericType1);
+        let varGenericType2 = null;
 
         if (varFullGenericType2)
             varGenericType2 = res.importClass(varFullGenericType2);
@@ -122,8 +122,8 @@ $generatorJava.declareVariable = function (res, varName, varFullType, varFullAct
  * @param varName Variable name.
  * @param varFullType Variable full class name to be added to imports.
  */
-$generatorJava.declareVariableLocal = function (res, varName, varFullType) {
-    var varType = res.importClass(varFullType);
+$generatorJava.declareVariableLocal = function(res, varName, varFullType) {
+    const varType = res.importClass(varFullType);
 
     res.line(varType + ' ' + varName + ' = new ' + varType + '();');
 
@@ -139,10 +139,10 @@ $generatorJava.declareVariableLocal = function (res, varName, varFullType) {
  * @param varExpr Custom variable creation expression.
  * @param modifier Additional variable modifier.
  */
-$generatorJava.declareVariableCustom = function (res, varName, varFullType, varExpr, modifier) {
-    var varType = res.importClass(varFullType);
+$generatorJava.declareVariableCustom = function(res, varName, varFullType, varExpr, modifier) {
+    const varType = res.importClass(varFullType);
 
-    var varNew = !res.vars[varName];
+    const varNew = !res.vars[varName];
 
     if (varNew)
         res.vars[varName] = true;
@@ -160,10 +160,10 @@ $generatorJava.declareVariableCustom = function (res, varName, varFullType, varE
  * @param varFullType Variable full class name to be added to imports.
  * @param length Array length.
  */
-$generatorJava.declareVariableArray = function (res, varName, varFullType, length) {
-    var varType = res.importClass(varFullType);
+$generatorJava.declareVariableArray = function(res, varName, varFullType, length) {
+    const varType = res.importClass(varFullType);
 
-    var varNew = !res.vars[varName];
+    const varNew = !res.vars[varName];
 
     if (varNew)
         res.vars[varName] = true;
@@ -178,7 +178,7 @@ $generatorJava.declareVariableArray = function (res, varName, varFullType, lengt
  *
  * @param res
  */
-$generatorJava.resetVariables = function (res) {
+$generatorJava.resetVariables = function(res) {
     res.vars = {};
 };
 
@@ -193,18 +193,20 @@ $generatorJava.resetVariables = function (res) {
  * @param setterName Optional special setter name.
  * @param dflt Optional default value.
  */
-$generatorJava.property = function (res, varName, obj, propName, dataType, setterName, dflt) {
-    var val = obj[propName];
+$generatorJava.property = function(res, varName, obj, propName, dataType, setterName, dflt) {
+    if (!_.isNil(obj)) {
+        const val = obj[propName];
 
-    if ($generatorCommon.isDefinedAndNotEmpty(val)) {
-        var missDflt = _.isNil(dflt);
+        if ($generatorCommon.isDefinedAndNotEmpty(val)) {
+            const missDflt = _.isNil(dflt);
 
-        // Add to result if no default provided or value not equals to default.
-        if (missDflt || (!missDflt && val !== dflt)) {
-            res.line(varName + '.' + $generatorJava.setterName(propName, setterName) +
-                '(' + $generatorJava.toJavaCode(val, dataType) + ');');
+            // Add to result if no default provided or value not equals to default.
+            if (missDflt || (!missDflt && val !== dflt)) {
+                res.line(varName + '.' + $generatorJava.setterName(propName, setterName) +
+                    '(' + $generatorJava.toJavaCode(val, dataType) + ');');
 
-            return true;
+                return true;
+            }
         }
     }
 
@@ -222,11 +224,11 @@ $generatorJava.property = function (res, varName, obj, propName, dataType, sette
  * @param setterName Optional special setter name.
  * @param dflt Optional default value.
  */
-$generatorJava.enumProperty = function (res, varName, obj, propName, dataType, setterName, dflt) {
-    var val = obj[propName];
+$generatorJava.enumProperty = function(res, varName, obj, propName, dataType, setterName, dflt) {
+    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)) {
@@ -241,8 +243,8 @@ $generatorJava.enumProperty = function (res, varName, obj, propName, dataType, s
 };
 
 // Add property for class name.
-$generatorJava.classNameProperty = function (res, varName, obj, propName) {
-    var val = obj[propName];
+$generatorJava.classNameProperty = function(res, varName, obj, propName) {
+    const val = obj[propName];
 
     if (!_.isNil(val)) {
         res.line(varName + '.' + $generatorJava.setterName(propName) +
@@ -260,26 +262,48 @@ $generatorJava.classNameProperty = function (res, varName, obj, propName) {
  * @param dataType Optional data type.
  * @param setterName Optional setter name.
  */
-$generatorJava.listProperty = function (res, varName, obj, propName, dataType, setterName) {
-    var val = obj[propName];
+$generatorJava.listProperty = function(res, varName, obj, propName, dataType, setterName) {
+    const val = obj[propName];
 
     if (val && val.length > 0) {
         res.emptyLineIfNeeded();
 
         res.importClass('java.util.Arrays');
 
-        res.line(varName + '.' + $generatorJava.setterName(propName, setterName) +
-            '(Arrays.asList(' +
-                _.map(val, function (v) {
-                    return $generatorJava.toJavaCode(v, dataType);
-                }).join(', ') +
-            '));');
+        $generatorJava.fxVarArgs(res, varName + '.' + $generatorJava.setterName(propName, setterName), false,
+            _.map(val, (v) => $generatorJava.toJavaCode(v, dataType)), '(Arrays.asList(', '))');
 
         res.needEmptyLine = true;
     }
 };
 
 /**
+ * Add function with varargs arguments.
+ *
+ * @param res Resulting output with generated code.
+ * @param fx Function name.
+ * @param quote Whether to quote arguments.
+ * @param args Array with arguments.
+ * @param startBlock Optional start block string.
+ * @param endBlock Optional end block string.
+ */
+$generatorJava.fxVarArgs = function(res, fx, quote, args, startBlock = '(', endBlock = ')') {
+    const quoteArg = (arg) => quote ? '"' + arg + '"' : arg;
+
+    if (args.length === 1)
+        res.append(fx + startBlock + quoteArg(args[0]) + endBlock + ';');
+    else {
+        res.startBlock(fx + startBlock);
+
+        const len = args.length - 1;
+
+        _.forEach(args, (arg, ix) => res.line(quoteArg(arg) + (ix < len ? ', ' : '')));
+
+        res.endBlock(endBlock + ';');
+    }
+};
+
+/**
  * Add array property.
  *
  * @param res Resulting output with generated code.
@@ -288,17 +312,14 @@ $generatorJava.listProperty = function (res, varName, obj, propName, dataType, s
  * @param propName Property name to take from source object.
  * @param setterName Optional setter name.
  */
-$generatorJava.arrayProperty = function (res, varName, obj, propName, setterName) {
-    var val = obj[propName];
+$generatorJava.arrayProperty = function(res, varName, obj, propName, setterName) {
+    const val = obj[propName];
 
     if (val && val.length > 0) {
         res.emptyLineIfNeeded();
 
-        res.line(varName + '.' + $generatorJava.setterName(propName, setterName) + '({ ' +
-            _.map(val, function (v) {
-                return 'new ' + res.importClass(v) + '()';
-            }).join(', ') +
-        ' });');
+        $generatorJava.fxVarArgs(res, varName + '.' + $generatorJava.setterName(propName, setterName), false,
+            _.map(val, (v) => 'new ' + res.importClass(v) + '()'), '({ ', ' });');
 
         res.needEmptyLine = true;
     }
@@ -314,19 +335,12 @@ $generatorJava.arrayProperty = function (res, varName, obj, propName, setterName
  * @param dataType Optional data type.
  * @param setterName Optional setter name.
  */
-$generatorJava.multiparamProperty = function (res, varName, obj, propName, dataType, setterName) {
-    var val = obj[propName];
+$generatorJava.multiparamProperty = function(res, varName, obj, propName, dataType, setterName) {
+    const val = obj[propName];
 
     if (val && val.length > 0) {
-        res.emptyLineIfNeeded();
-
-        res.startBlock(varName + '.' + $generatorJava.setterName(propName, setterName) + '(');
-
-        _.forEach(val, function(v, ix) {
-            res.append($generatorJava.toJavaCode(v, dataType) + (ix < val.length - 1 ? ', ' : ''));
-        });
-
-        res.endBlock(');');
+        $generatorJava.fxVarArgs(res, varName + '.' + $generatorJava.setterName(propName, setterName), false,
+            _.map(val, (v) => $generatorJava.toJavaCode(dataType === 'class' ? res.importClass(v) : v, dataType)));
     }
 };
 
@@ -342,7 +356,7 @@ $generatorJava.multiparamProperty = function (res, varName, obj, propName, dataT
  * @param props
  * @param createBeanAlthoughNoProps If 'true' then create empty bean.
  */
-$generatorJava.beanProperty = function (res, varName, bean, beanPropName, beanVarName, beanClass, props, createBeanAlthoughNoProps) {
+$generatorJava.beanProperty = function(res, varName, bean, beanPropName, beanVarName, beanClass, props, createBeanAlthoughNoProps) {
     if (bean && $generatorCommon.hasProperty(bean, props)) {
         res.emptyLineIfNeeded();
 
@@ -377,13 +391,13 @@ $generatorJava.beanProperty = function (res, varName, bean, beanPropName, beanVa
                             break;
 
                         case 'propertiesAsList':
-                            var val = bean[propName];
+                            const val = bean[propName];
 
                             if (val && val.length > 0) {
                                 $generatorJava.declareVariable(res, descr.propVarName, 'java.util.Properties');
 
                                 _.forEach(val, function(nameAndValue) {
-                                    var eqIndex = nameAndValue.indexOf('=');
+                                    const eqIndex = nameAndValue.indexOf('=');
 
                                     if (eqIndex >= 0) {
                                         res.line(descr.propVarName + '.setProperty(' +
@@ -408,9 +422,8 @@ $generatorJava.beanProperty = function (res, varName, bean, beanPropName, beanVa
                             $generatorJava.property(res, beanVarName, bean, propName, null, descr.setterName, descr.dflt);
                     }
                 }
-                else {
+                else
                     $generatorJava.property(res, beanVarName, bean, propName);
-                }
             }
         });
 
@@ -436,11 +449,11 @@ $generatorJava.beanProperty = function (res, varName, bean, beanPropName, beanVa
  * @param evtPlc Data to add.
  * @param propName Name in source data.
  */
-$generatorJava.evictionPolicy = function (res, varName, evtPlc, propName) {
+$generatorJava.evictionPolicy = function(res, varName, evtPlc, propName) {
     if (evtPlc && evtPlc.kind) {
-        var evictionPolicyDesc = $generatorCommon.EVICTION_POLICIES[evtPlc.kind];
+        const evictionPolicyDesc = $generatorCommon.EVICTION_POLICIES[evtPlc.kind];
 
-        var obj = evtPlc[evtPlc.kind.toUpperCase()];
+        const obj = evtPlc[evtPlc.kind.toUpperCase()];
 
         $generatorJava.beanProperty(res, varName, obj, propName, propName,
             evictionPolicyDesc.className, evictionPolicyDesc.fields, true);
@@ -448,7 +461,7 @@ $generatorJava.evictionPolicy = function (res, varName, evtPlc, propName) {
 };
 
 // Generate cluster general group.
-$generatorJava.clusterGeneral = function (cluster, clientNearCfg, res) {
+$generatorJava.clusterGeneral = function(cluster, clientNearCfg, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -467,7 +480,7 @@ $generatorJava.clusterGeneral = function (cluster, clientNearCfg, res) {
     }
 
     if (cluster.discovery) {
-        var d = cluster.discovery;
+        const d = cluster.discovery;
 
         $generatorJava.declareVariable(res, 'discovery', 'org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi');
 
@@ -538,7 +551,7 @@ $generatorJava.clusterGeneral = function (cluster, clientNearCfg, res) {
                 break;
 
             case 'ZooKeeper':
-                var finderVar = 'ipFinder';
+                const finderVar = 'ipFinder';
 
                 $generatorJava.declareVariable(res, 'ipFinder', 'org.apache.ignite.spi.discovery.tcp.ipfinder.zk.TcpDiscoveryZookeeperIpFinder');
 
@@ -549,8 +562,8 @@ $generatorJava.clusterGeneral = function (cluster, clientNearCfg, res) {
                     $generatorJava.property(res, finderVar, d.ZooKeeper, 'zkConnectionString');
 
                     if (d.ZooKeeper.retryPolicy && d.ZooKeeper.retryPolicy.kind) {
-                        var kind = d.ZooKeeper.retryPolicy.kind;
-                        var retryPolicy = d.ZooKeeper.retryPolicy[kind];
+                        const kind = d.ZooKeeper.retryPolicy.kind;
+                        const retryPolicy = d.ZooKeeper.retryPolicy[kind];
 
                         switch (kind) {
                             case 'ExponentialBackoff':
@@ -600,6 +613,8 @@ $generatorJava.clusterGeneral = function (cluster, clientNearCfg, res) {
                                     res.line(finderVar + '.setRetryPolicy(new ' + res.importClass(retryPolicy.className) + '());');
 
                                 break;
+
+                            default:
                         }
                     }
 
@@ -631,7 +646,7 @@ $generatorJava.clusterGeneral = function (cluster, clientNearCfg, res) {
 };
 
 // Generate atomics group.
-$generatorJava.clusterAtomics = function (atomics, res) {
+$generatorJava.clusterAtomics = function(atomics, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -642,9 +657,9 @@ $generatorJava.clusterAtomics = function (atomics, res) {
 
         $generatorJava.enumProperty(res, 'atomicCfg', atomics, 'cacheMode', 'org.apache.ignite.cache.CacheMode', null, 'PARTITIONED');
 
-        var cacheMode = atomics.cacheMode ? atomics.cacheMode : 'PARTITIONED';
+        const cacheMode = atomics.cacheMode ? atomics.cacheMode : 'PARTITIONED';
 
-        var hasData = cacheMode !== 'PARTITIONED';
+        let hasData = cacheMode !== 'PARTITIONED';
 
         hasData = $generatorJava.property(res, 'atomicCfg', atomics, 'atomicSequenceReserveSize', null, null, 1000) || hasData;
 
@@ -665,12 +680,12 @@ $generatorJava.clusterAtomics = function (atomics, res) {
 };
 
 // Generate binary group.
-$generatorJava.clusterBinary = function (binary, res) {
+$generatorJava.clusterBinary = function(binary, res) {
     if (!res)
         res = $generatorCommon.builder();
 
     if ($generatorCommon.binaryIsDefined(binary)) {
-        var varName = 'binary';
+        const varName = 'binary';
 
         $generatorJava.declareVariable(res, varName, 'org.apache.ignite.configuration.BinaryConfiguration');
 
@@ -686,15 +701,13 @@ $generatorJava.clusterBinary = function (binary, res) {
         res.needEmptyLine = $generatorCommon.isDefinedAndNotEmpty(binary.idMapper) || $generatorCommon.isDefinedAndNotEmpty(binary.serializer);
 
         if ($generatorCommon.isDefinedAndNotEmpty(binary.typeConfigurations)) {
-            var arrVar = 'types';
+            const arrVar = 'types';
 
             $generatorJava.declareVariable(res, arrVar, 'java.util.Collection', 'java.util.ArrayList', 'org.apache.ignite.binary.BinaryTypeConfiguration');
 
-            _.forEach(binary.typeConfigurations, function (type) {
-                if ($generatorCommon.isDefinedAndNotEmpty(type.typeName)) {
-                    // TODO IGNITE-2269 Replace using of separated methods for binary type configurations to extended constructors.
-                    res.line(arrVar + '.add(' + $generatorJava.binaryTypeFunctionName(type.typeName) + '());');
-                }
+            _.forEach(binary.typeConfigurations, function(type) {
+                if ($generatorCommon.isDefinedAndNotEmpty(type.typeName))
+                    res.line(arrVar + '.add(' + $generatorJava.binaryTypeFunctionName(type.typeName) + '());'); // TODO IGNITE-2269 Replace using of separated methods for binary type configurations to extended constructors.
             });
 
             res.needEmptyLine = true;
@@ -704,7 +717,7 @@ $generatorJava.clusterBinary = function (binary, res) {
             res.needEmptyLine = true;
         }
 
-        $generatorJava.property(res, varName, binary, 'compactFooter', undefined, undefined, true);
+        $generatorJava.property(res, varName, binary, 'compactFooter', null, null, true);
 
         res.needEmptyLine = true;
 
@@ -718,18 +731,18 @@ $generatorJava.clusterBinary = function (binary, res) {
 
 // TODO IGNITE-2269 Remove specified methods after implamentation of extended constructors.
 // Construct binary type configuration factory method name.
-$generatorJava.binaryTypeFunctionName = function (typeName) {
-    var dotIdx = typeName.lastIndexOf('.');
+$generatorJava.binaryTypeFunctionName = function(typeName) {
+    const dotIdx = typeName.lastIndexOf('.');
 
-    var shortName = dotIdx > 0 ? typeName.substr(dotIdx + 1) : typeName;
+    const shortName = dotIdx > 0 ? typeName.substr(dotIdx + 1) : typeName;
 
     return $generatorCommon.toJavaName('binaryType', shortName);
 };
 
 // TODO IGNITE-2269 Remove specified methods after implamentation of extended constructors.
 // Generate factory method for specified BinaryTypeConfiguration.
-$generatorJava.binaryTypeConfiguration = function (type, res) {
-    var typeName = type.typeName;
+$generatorJava.binaryTypeConfiguration = function(type, res) {
+    const typeName = type.typeName;
 
     res.line('/**');
     res.line(' * Create binary type configuration for ' + typeName + '.');
@@ -740,7 +753,7 @@ $generatorJava.binaryTypeConfiguration = function (type, res) {
 
     $generatorJava.resetVariables(res);
 
-    var typeVar = 'typeCfg';
+    const typeVar = 'typeCfg';
 
     $generatorJava.declareVariable(res, typeVar, 'org.apache.ignite.binary.BinaryTypeConfiguration');
 
@@ -767,12 +780,12 @@ $generatorJava.binaryTypeConfiguration = function (type, res) {
 
 // TODO IGNITE-2269 Remove specified methods after implamentation of extended constructors.
 // Generates binary type configuration factory methods.
-$generatorJava.binaryTypeConfigurations = function (binary, res) {
+$generatorJava.binaryTypeConfigurations = function(binary, res) {
     if (!res)
         res = $generatorCommon.builder();
 
     if (!_.isNil(binary)) {
-        _.forEach(binary.typeConfigurations, function (type) {
+        _.forEach(binary.typeConfigurations, function(type) {
             $generatorJava.binaryTypeConfiguration(type, res);
         });
     }
@@ -780,12 +793,96 @@ $generatorJava.binaryTypeConfigurations = function (binary, res) {
     return res;
 };
 
+// Generate collision group.
+$generatorJava.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))) {
+            const varName = 'collisionSpi';
+
+            switch (collision.kind) {
+                case 'JobStealing':
+                    $generatorJava.declareVariable(res, varName, 'org.apache.ignite.spi.collision.jobstealing.JobStealingCollisionSpi');
+
+                    $generatorJava.property(res, varName, spi, 'activeJobsThreshold', null, null, 95);
+                    $generatorJava.property(res, varName, spi, 'waitJobsThreshold', null, null, 0);
+                    $generatorJava.property(res, varName, spi, 'messageExpireTime', null, null, 1000);
+                    $generatorJava.property(res, varName, spi, 'maximumStealingAttempts', null, null, 5);
+                    $generatorJava.property(res, varName, spi, 'stealingEnabled', null, null, true);
+
+                    if ($generatorCommon.isDefinedAndNotEmpty(spi.externalCollisionListener)) {
+                        res.line(varName + '.' + $generatorJava.setterName('externalCollisionListener') +
+                            '(new ' + res.importClass(spi.externalCollisionListener) + '());');
+                    }
+
+                    if ($generatorCommon.isDefinedAndNotEmpty(spi.stealingAttributes)) {
+                        const stealingAttrsVar = 'stealingAttrs';
+
+                        res.needEmptyLine = true;
+
+                        $generatorJava.declareVariable(res, stealingAttrsVar, 'java.util.Map', 'java.util.HashMap', 'String', 'java.io.Serializable');
+
+                        _.forEach(spi.stealingAttributes, function(attr) {
+                            res.line(stealingAttrsVar + '.put("' + attr.name + '", "' + attr.value + '");');
+                        });
+
+                        res.needEmptyLine = true;
+
+                        res.line(varName + '.setStealingAttributes(' + stealingAttrsVar + ');');
+                    }
+
+                    break;
+
+                case 'FifoQueue':
+                    $generatorJava.declareVariable(res, varName, 'org.apache.ignite.spi.collision.fifoqueue.FifoQueueCollisionSpi');
+
+                    $generatorJava.property(res, varName, spi, 'parallelJobsNumber');
+                    $generatorJava.property(res, varName, spi, 'waitingJobsNumber');
+
+                    break;
+
+                case 'PriorityQueue':
+                    $generatorJava.declareVariable(res, varName, 'org.apache.ignite.spi.collision.priorityqueue.PriorityQueueCollisionSpi');
+
+                    $generatorJava.property(res, varName, spi, 'parallelJobsNumber');
+                    $generatorJava.property(res, varName, spi, 'waitingJobsNumber');
+                    $generatorJava.property(res, varName, spi, 'priorityAttributeKey', null, null, 'grid.task.priority');
+                    $generatorJava.property(res, varName, spi, 'jobPriorityAttributeKey', null, null, 'grid.job.priority');
+                    $generatorJava.property(res, varName, spi, 'defaultPriority', null, null, 0);
+                    $generatorJava.property(res, varName, spi, 'starvationIncrement', null, null, 1);
+                    $generatorJava.property(res, varName, spi, 'starvationPreventionEnabled', null, null, true);
+
+                    break;
+
+                case 'Custom':
+                    $generatorJava.declareVariable(res, varName, spi.class);
+
+                    break;
+
+                default:
+            }
+
+            res.needEmptyLine = true;
+
+            res.line('cfg.setCollisionSpi(' + varName + ');');
+
+            res.needEmptyLine = true;
+        }
+    }
+
+    return res;
+};
+
 // Generate communication group.
-$generatorJava.clusterCommunication = function (cluster, res) {
+$generatorJava.clusterCommunication = function(cluster, res) {
     if (!res)
         res = $generatorCommon.builder();
 
-    var cfg = $generatorCommon.COMMUNICATION_CONFIGURATION;
+    const cfg = $generatorCommon.COMMUNICATION_CONFIGURATION;
 
     $generatorJava.beanProperty(res, 'cfg', cluster.communication, 'communicationSpi', 'commSpi', cfg.className, cfg.fields);
 
@@ -804,12 +901,12 @@ $generatorJava.clusterCommunication = function (cluster, res) {
 };
 
 // Generate REST access group.
-$generatorJava.clusterConnector = function (connector, res) {
+$generatorJava.clusterConnector = function(connector, res) {
     if (!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};
@@ -826,7 +923,7 @@ $generatorJava.clusterConnector = function (connector, res) {
 };
 
 // Generate deployment group.
-$generatorJava.clusterDeployment = function (cluster, res) {
+$generatorJava.clusterDeployment = function(cluster, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -834,7 +931,7 @@ $generatorJava.clusterDeployment = function (cluster, res) {
 
     res.softEmptyLine();
 
-    var p2pEnabled = cluster.peerClassLoadingEnabled;
+    const p2pEnabled = cluster.peerClassLoadingEnabled;
 
     if (!_.isNil(p2pEnabled)) {
         $generatorJava.property(res, 'cfg', cluster, 'peerClassLoadingEnabled', null, null, false);
@@ -852,7 +949,7 @@ $generatorJava.clusterDeployment = function (cluster, res) {
 };
 
 // Generate discovery group.
-$generatorJava.clusterDiscovery = function (disco, res) {
+$generatorJava.clusterDiscovery = function(disco, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -911,31 +1008,30 @@ $generatorJava.clusterDiscovery = function (disco, res) {
 };
 
 // Generate events group.
-$generatorJava.clusterEvents = function (cluster, res) {
+$generatorJava.clusterEvents = function(cluster, res) {
     if (!res)
         res = $generatorCommon.builder();
 
     if (cluster.includeEventTypes && cluster.includeEventTypes.length > 0) {
         res.emptyLineIfNeeded();
 
-        var evtGrps = angular.element(document.getElementById('app')).injector().get('igniteEventGroups');
-
-        var evtGrpDscr = _.find(evtGrps, {value: cluster.includeEventTypes[0]});
+        const evtGrps = angular.element(document.getElementById('app')).injector().get('igniteEventGroups');
 
-        var evt = res.importStatic(evtGrpDscr.class + '.' + evtGrpDscr.value);
+        if (cluster.includeEventTypes.length === 1) {
+            const evtGrp = _.find(evtGrps, {value: cluster.includeEventTypes[0]});
+            const evts = res.importStatic(evtGrp.class + '.' + evtGrp.value);
 
-        if (cluster.includeEventTypes.length === 1)
-            res.line('cfg.setIncludeEventTypes(' + evt + ');');
+            res.line('cfg.setIncludeEventTypes(' + evts + ');');
+        }
         else {
             _.forEach(cluster.includeEventTypes, function(value, ix) {
-                var evtGrpDscr = _.find(evtGrps, {value: value});
-
-                var evt = res.importStatic(evtGrpDscr.class + '.' + evtGrpDscr.value);
+                const evtGrp = _.find(evtGrps, {value});
+                const evts = res.importStatic(evtGrp.class + '.' + evtGrp.value);
 
                 if (ix === 0)
-                    res.line('int[] events = new int[' + evt + '.length');
+                    res.line('int[] events = new int[' + evts + '.length');
                 else
-                    res.line('    + ' + evt + '.length');
+                    res.line('    + ' + evts + '.length');
             });
 
             res.line('];');
@@ -947,14 +1043,13 @@ $generatorJava.clusterEvents = function (cluster, res) {
             _.forEach(cluster.includeEventTypes, function(value, idx) {
                 res.needEmptyLine = true;
 
-                var evtGrpDscr = _.find(evtGrps, {value: value});
-
-                evt = res.importStatic(evtGrpDscr.class + '.' + value);
+                const evtGrp = _.find(evtGrps, {value});
+                const evts = res.importStatic(evtGrp.class + '.' + value);
 
-                res.line('System.arraycopy(' + evt + ', 0, events, k, ' + evt + '.length);');
+                res.line('System.arraycopy(' + evts + ', 0, events, k, ' + evts + '.length);');
 
                 if (idx < cluster.includeEventTypes.length - 1)
-                    res.line('k += ' + evt + '.length;');
+                    res.line('k += ' + evts + '.length;');
             });
 
             res.needEmptyLine = true;
@@ -970,15 +1065,133 @@ $generatorJava.clusterEvents = function (cluster, res) {
     return res;
 };
 
+// Generate failover group.
+$generatorJava.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) {
+        const arrayVarName = 'failoverSpiList';
+
+        $generatorJava.declareVariable(res, arrayVarName, 'java.util.List', 'java.util.ArrayList', 'org.apache.ignite.spi.failover.FailoverSpi');
+
+        _.forEach(cluster.failoverSpi, function(spi) {
+            if (spi.kind && (spi.kind !== 'Custom' || $generatorCommon.isDefinedAndNotEmpty(_.get(spi, spi.kind + '.class')))) {
+                const varName = 'failoverSpi';
+
+                const maxAttempts = _.get(spi, spi.kind + '.maximumFailoverAttempts');
+
+                if ((spi.kind === 'JobStealing' || spi.kind === 'Always') && $generatorCommon.isDefinedAndNotEmpty(maxAttempts) && maxAttempts !== 5) {
+                    const spiCls = res.importClass($generatorCommon.failoverSpiClass(spi));
+
+                    $generatorJava.declareVariableCustom(res, varName, 'org.apache.ignite.spi.failover.FailoverSpi', 'new ' + spiCls + '()');
+
+                    if ($generatorCommon.isDefinedAndNotEmpty(spi[spi.kind].maximumFailoverAttempts))
+                        res.line('((' + spiCls + ') ' + varName + ').setMaximumFailoverAttempts(' + spi[spi.kind].maximumFailoverAttempts + ');');
+
+                    res.needEmptyLine = true;
+
+                    res.line(arrayVarName + '.add(' + varName + ');');
+                }
+                else
+                    res.line(arrayVarName + '.add(new ' + res.importClass($generatorCommon.failoverSpiClass(spi)) + '());');
+
+                res.needEmptyLine = true;
+            }
+        });
+
+        res.line('cfg.setFailoverSpi(' + arrayVarName + '.toArray(new FailoverSpi[' + arrayVarName + '.size()]));');
+
+        res.needEmptyLine = true;
+    }
+
+    return res;
+};
+
 // Generate marshaller group.
-$generatorJava.clusterMarshaller = function (cluster, res) {
+$generatorJava.clusterLogger = function(logger, res) {
     if (!res)
         res = $generatorCommon.builder();
 
-    var marshaller = cluster.marshaller;
+    if ($generatorCommon.loggerConfigured(logger)) {
+        const varName = 'logger';
+
+        const log = logger[logger.kind];
+
+        switch (logger.kind) {
+            case 'Log4j2':
+                $generatorJava.declareVariableCustom(res, varName, 'org.apache.ignite.logger.log4j2.Log4J2Logger',
+                    'new Log4J2Logger(' + $generatorJava.toJavaCode(log.path, 'path') + ')');
+
+                res.needEmptyLine = true;
+
+                if ($generatorCommon.isDefinedAndNotEmpty(log.level))
+                    res.line(varName + '.setLevel(' + res.importClass('org.apache.logging.log4j.Level') + '.' + log.level + ');');
+
+                break;
+
+            case 'Null':
+                $generatorJava.declareVariable(res, varName, 'org.apache.ignite.logger.NullLogger');
+
+                break;
+
+            case 'Java':
+                $generatorJava.declareVariable(res, varName, 'org.apache.ignite.logger.java.JavaLogger');
+
+                break;
+
+            case 'JCL':
+                $generatorJava.declareVariable(res, varName, 'org.apache.ignite.logger.jcl.JclLogger');
+
+                break;
+
+            case 'SLF4J':
+                $generatorJava.declareVariable(res, varName, 'org.apache.ignite.logger.slf4j.Slf4jLogger');
+
+                break;
+
+            case 'Log4j':
+                if (log.mode === 'Default')
+                    $generatorJava.declareVariable(res, varName, 'org.apache.ignite.logger.log4j.Log4JLogger');
+                else {
+                    $generatorJava.declareVariableCustom(res, varName, 'org.apache.ignite.logger.log4j.Log4JLogger',
+                        'new Log4JLogger(' + $generatorJava.toJavaCode(log.path, 'path') + ')');
+                }
+
+                if ($generatorCommon.isDefinedAndNotEmpty(log.level))
+                    res.line(varName + '.setLevel(' + res.importClass('org.apache.log4j.Level') + '.' + log.level + ');');
+
+                break;
+
+            case 'Custom':
+                $generatorJava.declareVariable(res, varName, log.class);
+
+                break;
+
+            default:
+        }
+
+        res.needEmptyLine = true;
+
+        res.line('cfg.setGridLogger(' + varName + ');');
+
+        res.needEmptyLine = true;
+    }
+
+    return res;
+};
+
+// Generate marshaller group.
+$generatorJava.clusterMarshaller = function(cluster, res) {
+    if (!res)
+        res = $generatorCommon.builder();
+
+    const marshaller = cluster.marshaller;
 
     if (marshaller && marshaller.kind) {
-        var marshallerDesc = $generatorCommon.MARSHALLERS[marshaller.kind];
+        const marshallerDesc = $generatorCommon.MARSHALLERS[marshaller.kind];
 
         $generatorJava.beanProperty(res, 'cfg', marshaller[marshaller.kind], 'marshaller', 'marshaller',
             marshallerDesc.className, marshallerDesc.fields, true);
@@ -996,7 +1209,7 @@ $generatorJava.clusterMarshaller = function (cluster, res) {
 };
 
 // Generate metrics group.
-$generatorJava.clusterMetrics = function (cluster, res) {
+$generatorJava.clusterMetrics = function(cluster, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -1011,7 +1224,7 @@ $generatorJava.clusterMetrics = function (cluster, res) {
 };
 
 // Generate swap group.
-$generatorJava.clusterSwap = function (cluster, res) {
+$generatorJava.clusterSwap = function(cluster, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -1026,7 +1239,7 @@ $generatorJava.clusterSwap = function (cluster, res) {
 };
 
 // Generate time group.
-$generatorJava.clusterTime = function (cluster, res) {
+$generatorJava.clusterTime = function(cluster, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -1041,7 +1254,7 @@ $generatorJava.clusterTime = function (cluster, res) {
 };
 
 // Generate thread pools group.
-$generatorJava.clusterPools = function (cluster, res) {
+$generatorJava.clusterPools = function(cluster, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -1057,7 +1270,7 @@ $generatorJava.clusterPools = function (cluster, res) {
 };
 
 // Generate transactions group.
-$generatorJava.clusterTransactions = function (transactionConfiguration, res) {
+$generatorJava.clusterTransactions = function(transactionConfiguration, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -1068,8 +1281,33 @@ $generatorJava.clusterTransactions = function (transactionConfiguration, res) {
     return res;
 };
 
+// Generate user attributes group.
+$generatorJava.clusterUserAttributes = function(cluster, res) {
+    if (!res)
+        res = $generatorCommon.builder();
+
+    if ($generatorCommon.isDefinedAndNotEmpty(cluster.attributes)) {
+        $generatorJava.declareVariable(res, 'attributes', 'java.util.Map', 'java.util.HashMap', 'java.lang.String', 'java.lang.String');
+
+        _.forEach(cluster.attributes, function(attr) {
+            res.line('attributes.put("' + attr.name + '", "' + attr.value + '");');
+        });
+
+        res.needEmptyLine = true;
+
+        res.line('cfg.setUserAttributes(attributes);');
+
+        res.needEmptyLine = true;
+    }
+
+    res.needEmptyLine = true;
+
+    return res;
+};
+
+
 // Generate cache general group.
-$generatorJava.cacheGeneral = function (cache, varName, res) {
+$generatorJava.cacheGeneral = function(cache, varName, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -1096,7 +1334,7 @@ $generatorJava.cacheGeneral = function (cache, varName, res) {
 };
 
 // Generate cache memory group.
-$generatorJava.cacheMemory = function (cache, varName, res) {
+$generatorJava.cacheMemory = function(cache, varName, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -1119,7 +1357,7 @@ $generatorJava.cacheMemory = function (cache, varName, res) {
 };
 
 // Generate cache query & indexing group.
-$generatorJava.cacheQuery = function (cache, varName, res) {
+$generatorJava.cacheQuery = function(cache, varName, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -1150,15 +1388,15 @@ $generatorJava.cacheQuery = function (cache, varName, res) {
  * @param storeFactory Factory to generate data source for.
  * @param res Resulting output with generated code.
  */
-$generatorJava.cacheStoreDataSource = function (storeFactory, res) {
-    var dialect = storeFactory.connectVia ? (storeFactory.connectVia === 'DataSource' ? storeFactory.dialect : null) : storeFactory.dialect;
+$generatorJava.cacheStoreDataSource = function(storeFactory, res) {
+    const dialect = storeFactory.connectVia ? (storeFactory.connectVia === 'DataSource' ? storeFactory.dialect : null) : storeFactory.dialect;
 
     if (dialect) {
-        var varName = 'dataSource';
+        const varName = 'dataSource';
 
-        var dataSourceBean = storeFactory.dataSourceBean;
+        const dataSourceBean = storeFactory.dataSourceBean;
 
-        var varType = res.importClass($generatorCommon.dataSourceClassName(dialect));
+        const varType = res.importClass($generatorCommon.dataSourceClassName(dialect));
 
         res.line('public static final ' + varType + ' INSTANCE_' + dataSourceBean + ' = create' + dataSourceBean + '();');
 
@@ -1219,21 +1457,21 @@ $generatorJava.cacheStoreDataSource = function (storeFactory, res) {
     return null;
 };
 
-$generatorJava.clusterDataSources = function (caches, res) {
+$generatorJava.clusterDataSources = function(caches, res) {
     if (!res)
         res = $generatorCommon.builder();
 
-    var datasources = [];
+    const datasources = [];
 
-    var storeFound = false;
+    let storeFound = false;
 
-    _.forEach(caches, function (cache) {
-        var factoryKind = cache.cacheStoreFactory.kind;
+    _.forEach(caches, function(cache) {
+        const factoryKind = cache.cacheStoreFactory.kind;
 
-        var storeFactory = cache.cacheStoreFactory[factoryKind];
+        const storeFactory = cache.cacheStoreFactory[factoryKind];
 
         if (storeFactory) {
-            var beanClassName = $generatorJava.dataSourceClassName(res, storeFactory);
+            const beanClassName = $generatorJava.dataSourceClassName(res, storeFactory);
 
             if (beanClassName && !_.includes(datasources, beanClassName)) {
                 datasources.push(beanClassName);
@@ -1252,9 +1490,8 @@ $generatorJava.clusterDataSources = function (caches, res) {
         }
     });
 
-    if (storeFound) {
+    if (storeFound)
         res.endBlock('}');
-    }
 
     return res;
 };
@@ -1268,7 +1505,7 @@ $generatorJava.clusterDataSources = function (caches, res) {
  * @param res Resulting output with generated code.
  * @returns {*} Java code for cache store configuration.
  */
-$generatorJava.cacheStore = function (cache, domains, cacheVarName, res) {
+$generatorJava.cacheStore = function(cache, domains, cacheVarName, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -1276,14 +1513,14 @@ $generatorJava.cacheStore = function (cache, domains, cacheVarName, res) {
         cacheVarName = $generatorJava.nextVariableName('cache', cache);
 
     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) {
-            var storeFactoryDesc = $generatorCommon.STORE_FACTORIES[factoryKind];
+            const storeFactoryDesc = $generatorCommon.STORE_FACTORIES[factoryKind];
 
-            var varName = 'storeFactory' + storeFactoryDesc.suffix;
+            const varName = 'storeFactory' + storeFactoryDesc.suffix;
 
             if (factoryKind === 'CacheJdbcPojoStoreFactory') {
                 // Generate POJO store factory.
@@ -1308,7 +1545,7 @@ $generatorJava.cacheStore = function (cache, domains, cacheVarName, res) {
 
                 res.needEmptyLine = true;
 
-                var domainConfigs = _.filter(domains, function (domain) {
+                const domainConfigs = _.filter(domains, function(domain) {
                     return $generatorCommon.domainQueryMetadata(domain) === 'Configuration' &&
                         $generatorCommon.isDefinedAndNotEmpty(domain.databaseTable);
                 });
@@ -1318,7 +1555,7 @@ $generatorJava.cacheStore = function (cache, domains, cacheVarName, res) {
 
                     res.needEmptyLine = true;
 
-                    _.forEach(domainConfigs, function (domain) {
+                    _.forEach(domainConfigs, function(domain) {
                         if ($generatorCommon.isDefinedAndNotEmpty(domain.databaseTable))
                             res.line('jdbcTypes.add(jdbcType' + $generatorJava.extractType(domain.valueType) + '(' + cacheVarName + '.getName()));');
                     });
@@ -1373,8 +1610,7 @@ $generatorJava.cacheStore = function (cache, domains, cacheVarName, res) {
                 res.line(cacheVarName + '.setCacheStoreFactory(' + varName + ');');
             }
             else
-                $generatorJava.beanProperty(res, cacheVarName, storeFactory, 'cacheStoreFactory', varName,
-                    storeFactoryDesc.className, storeFactoryDesc.fields, true);
+                $generatorJava.beanProperty(res, cacheVarName, storeFactory, 'cacheStoreFactory', varName, storeFactoryDesc.className, storeFactoryDesc.fields, true);
 
             res.needEmptyLine = true;
         }
@@ -1403,7 +1639,7 @@ $generatorJava.cacheStore = function (cache, domains, cacheVarName, res) {
 };
 
 // Generate cache concurrency group.
-$generatorJava.cacheConcurrency = function (cache, varName, res) {
+$generatorJava.cacheConcurrency = function(cache, varName, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -1413,7 +1649,7 @@ $generatorJava.cacheConcurrency = function (cache, varName, res) {
     $generatorJava.property(res, varName, cache, 'maxConcurrentAsyncOperations', null, null, 500);
     $generatorJava.property(res, varName, cache, 'defaultLockTimeout', null, null, 0);
     $generatorJava.enumProperty(res, varName, cache, 'atomicWriteOrderMode', 'org.apache.ignite.cache.CacheAtomicWriteOrderMode');
-    $generatorJava.enumProperty(res, varName, cache, 'writeSynchronizationMode', 'org.apache.ignite.cache.CacheWriteSynchronizationMode', null, null, "PRIMARY_SYNC");
+    $generatorJava.enumProperty(res, varName, cache, 'writeSynchronizationMode', 'org.apache.ignite.cache.CacheWriteSynchronizationMode', null, null, 'PRIMARY_SYNC');
 
     res.needEmptyLine = true;
 
@@ -1421,7 +1657,7 @@ $generatorJava.cacheConcurrency = function (cache, varName, res) {
 };
 
 // Generate cache rebalance group.
-$generatorJava.cacheRebalance = function (cache, varName, res) {
+$generatorJava.cacheRebalance = function(cache, varName, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -1451,7 +1687,7 @@ $generatorJava.cacheRebalance = function (cache, varName, res) {
 };
 
 // Generate cache server near cache group.
-$generatorJava.cacheServerNearCache = function (cache, varName, res) {
+$generatorJava.cacheServerNearCache = function(cache, varName, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -1488,7 +1724,7 @@ $generatorJava.cacheServerNearCache = function (cache, varName, res) {
 };
 
 // Generate cache statistics group.
-$generatorJava.cacheStatistics = function (cache, varName, res) {
+$generatorJava.cacheStatistics = function(cache, varName, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -1504,13 +1740,13 @@ $generatorJava.cacheStatistics = function (cache, varName, res) {
 };
 
 // Generate domain model query fields.
-$generatorJava.domainModelQueryFields = function (res, domain) {
-    var fields = domain.fields;
+$generatorJava.domainModelQueryFields = function(res, domain) {
+    const fields = domain.fields;
 
     if (fields && fields.length > 0) {
         $generatorJava.declareVariable(res, 'fields', 'java.util.LinkedHashMap', 'java.util.LinkedHashMap', 'java.lang.String', 'java.lang.String');
 
-        _.forEach(fields, function (field) {
+        _.forEach(fields, function(field) {
             res.line('fields.put("' + field.name + '", "' + $generatorCommon.JavaTypes.fullClassName(field.className) + '");');
         });
 
@@ -1523,13 +1759,13 @@ $generatorJava.domainModelQueryFields = function (res, domain) {
 };
 
 // Generate domain model query aliases.
-$generatorJava.domainModelQueryAliases = function (res, domain) {
-    var aliases = domain.aliases;
+$generatorJava.domainModelQueryAliases = function(res, domain) {
+    const aliases = domain.aliases;
 
     if (aliases && aliases.length > 0) {
         $generatorJava.declareVariable(res, 'aliases', 'java.util.Map', 'java.util.HashMap', 'java.lang.String', 'java.lang.String');
 
-        _.forEach(aliases, function (alias) {
+        _.forEach(aliases, function(alias) {
             res.line('aliases.put("' + alias.field + '", "' + alias.alias + '");');
         });
 
@@ -1542,20 +1778,20 @@ $generatorJava.domainModelQueryAliases = function (res, domain) {
 };
 
 // Generate domain model indexes.
-$generatorJava.domainModelQueryIndexes = function (res, domain) {
-    var indexes = domain.indexes;
+$generatorJava.domainModelQueryIndexes = function(res, domain) {
+    const indexes = domain.indexes;
 
     if (indexes && indexes.length > 0) {
         res.needEmptyLine = true;
 
         $generatorJava.declareVariable(res, 'indexes', 'java.util.List', 'java.util.ArrayList', 'org.apache.ignite.cache.QueryIndex');
 
-        _.forEach(indexes, function (index) {
-            var fields = index.fields;
+        _.forEach(indexes, function(index) {
+            const fields = index.fields;
 
             // One row generation for 1 field index.
             if (fields && fields.length === 1) {
-                var field = index.fields[0];
+                const field = index.fields[0];
 
                 res.line('indexes.add(new ' + res.importClass('org.apache.ignite.cache.QueryIndex') +
                     '("' + field.name + '", ' +
@@ -1597,8 +1833,8 @@ $generatorJava.domainModelQueryIndexes = function (res, domain) {
 };
 
 // Generate domain model db fields.
-$generatorJava.domainModelDatabaseFields = function (res, domain, fieldProperty) {
-    var dbFields = domain[fieldProperty];
+$generatorJava.domainModelDatabaseFields = function(res, domain, fieldProperty) {
+    const dbFields = domain[fieldProperty];
 
     if (dbFields && dbFields.length > 0) {
         res.needEmptyLine = true;
@@ -1607,14 +1843,14 @@ $generatorJava.domainModelDatabaseFields = function (res, domain, fieldProperty)
 
         res.startBlock('jdbcType.' + $generatorCommon.toJavaName('set', fieldProperty) + '(');
 
-        var lastIx = dbFields.length - 1;
+        const lastIx = dbFields.length - 1;
 
         res.importClass('org.apache.ignite.cache.store.jdbc.JdbcTypeField');
 
-        _.forEach(dbFields, function (field, ix) {
+        _.forEach(dbFields, function(field, ix) {
             res.line('new JdbcTypeField(' +
                 'Types.' + field.databaseFieldType + ', ' + '"' + field.databaseFieldName + '", ' +
-                res.importClass(field.javaFieldType) + '.class, ' + '"' + field.javaFieldName + '"'+ ')' + (ix < lastIx ? ',' : ''));
+                res.importClass(field.javaFieldType) + '.class, ' + '"' + field.javaFieldName + '"' + ')' + (ix < lastIx ? ',' : ''));
         });
 
         res.endBlock(');');
@@ -1624,14 +1860,14 @@ $generatorJava.domainModelDatabaseFields = function (res, domain, fieldProperty)
 };
 
 // Generate domain model general group.
-$generatorJava.domainModelGeneral = function (domain, res) {
+$generatorJava.domainModelGeneral = function(domain, res) {
     if (!res)
         res = $generatorCommon.builder();
 
     switch ($generatorCommon.domainQueryMetadata(domain)) {
         case 'Annotations':
             if ($generatorCommon.isDefinedAndNotEmpty(domain.keyType) || $generatorCommon.isDefinedAndNotEmpty(domain.valueType)) {
-                var types = [];
+                const types = [];
 
                 if ($generatorCommon.isDefinedAndNotEmpty(domain.keyType))
                     types.push($generatorJava.toJavaCode(res.importClass(domain.keyType), 'class'));
@@ -1643,13 +1879,8 @@ $generatorJava.domainModelGeneral = function (domain, res) {
                 else
                     types.push('???');
 
-                if ($generatorCommon.isDefinedAndNotEmpty(types)) {
-                    res.startBlock('cache.setIndexedTypes(');
-
-                    res.line(types.join(', '));
-
-                    res.endBlock(');');
-                }
+                if ($generatorCommon.isDefinedAndNotEmpty(types))
+                    $generatorJava.fxVarArgs(res, 'cache.setIndexedTypes', false, types);
             }
 
             break;
@@ -1666,6 +1897,8 @@ $generatorJava.domainModelGeneral = function (domain, res) {
             }
 
             break;
+
+        default:
     }
 
     res.needEmptyLine = true;
@@ -1674,7 +1907,7 @@ $generatorJava.domainModelGeneral = function (domain, res) {
 };
 
 // Generate domain model for query group.
-$generatorJava.domainModelQuery = function (domain, res) {
+$generatorJava.domainModelQuery = function(domain, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -1690,7 +1923,7 @@ $generatorJava.domainModelQuery = function (domain, res) {
 };
 
 // Generate domain model for store group.
-$generatorJava.domainStore = function (domain, withTypes, res) {
+$generatorJava.domainStore = function(domain, withTypes, res) {
     if (!res)
         res = $generatorCommon.builder();
 
@@ -1711,11 +1944,11 @@ $generatorJava.domainStore = function (domain, withTypes, res) {
 };
 
 // Generate domain model configs.
-$generatorJava.cacheDomains = function (domains, varName, res) {
+$generatorJava.cacheDomains = function(domains, varName, 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);
     });
@@ -1724,7 +1957,7 @@ $generatorJava.cacheDomains = function (domains, varName, res) {
     if ($generatorCommon.isDefinedAndNotEmpty(domainConfigs)) {
         $generatorJava.declareVariable(res, 'queryEntities', 'java.util.Collection', 'java.util.ArrayList', 'org.apache.ignite.cache.QueryEntity');
 
-        _.forEach(domainConfigs, function (domain) {
+        _.forEach(domainConfigs, function(domain) {
             if ($generatorCommon.isDefinedAndNotEmpty(domain.fields))
                 res.line('queryEntities.add(queryEntity' + $generatorJava.extractType(domain.valueType) + '());');
         });
@@ -1756,20 +1989,20 @@ $generatorJava.cache = function(cache, varName, res) {
 };
 
 // Generation of cache domain model in separate methods.
-$generatorJava.clusterDomains = function (caches, res) {
-    var domains = [];
+$generatorJava.clusterDomains = function(caches, res) {
+    const domains = [];
 
-    var typeVarName = 'jdbcType';
-    var metaVarName = 'qryMeta';
+    const typeVarName = 'jdbcType';
+    const metaVarName = 'qryMeta';
 
-    _.forEach(caches, function (cache) {
-        _.forEach(cache.domains, function (domain) {
-            if (_.isNil(_.find(domains, function (m) {
-                    return m === domain.valueType;
-                }))) {
+    _.forEach(caches, function(cache) {
+        _.forEach(cache.domains, function(domain) {
+            if (_.isNil(_.find(domains, function(m) {
+                return m === domain.valueType;
+            }))) {
                 $generatorJava.resetVariables(res);
 
-                var type = $generatorJava.extractType(domain.valueType);
+                const type = $generatorJava.extractType(domain.valueType);
 
                 if ($generatorCommon.isDefinedAndNotEmpty(domain.databaseTable)) {
                     res.line('/**');
@@ -1833,18 +2066,15 @@ $generatorJava.clusterDomains = function (caches, res) {
  * @param obj Object to process.
  * @param names Known names to generate next unique name.
  */
-$generatorJava.nextVariableName = function (prefix, obj, names) {
-    var nextName = $generatorCommon.toJavaName(prefix, obj.name);
+$generatorJava.nextVariableName = function(prefix, obj, names) {
+    let nextName = $generatorCommon.toJavaName(prefix, obj.name);
 
-    var checkNextName = function (name) {
-        return name === nextName + (ix === 0 ? '' : '_' + ix);
-    };
+    let ix = 0;
 
-    var ix = 0;
+    const checkNextName = (name) => name === nextName + (ix === 0 ? '' : '_' + ix);
 
-    while (_.find(names, checkNextName)) {
-        ix ++;
-    }
+    while (_.find(names, (name) => checkNextName(name)))
+        ix++;
 
     if (ix > 0)
         nextName = nextName + '_' + ix;
@@ -1853,15 +2083,15 @@ $generatorJava.nextVariableName = function (prefix, obj, names) {
 };
 
 // Generate cluster caches.
-$generatorJava.clusterCaches = function (caches, igfss, isSrvCfg, res) {
-    function clusterCache(res, cache, names) {
+$generatorJava.clusterCaches = function(caches, igfss, isSrvCfg, res) {
+    function clusterCache(cache, names) {
         res.emptyLineIfNeeded();
 
-        var cacheName = $generatorJava.nextVariableName('cache', cache, names);
+        const cacheName = $generatorJava.nextVariableName('cache', cache, names);
 
         $generatorJava.resetVariables(res);
 
-        var hasDatasource = $generatorCommon.cacheHasDatasource(cache);
+        const hasDatasource = $generatorCommon.cacheHasDatasource(cache);
 
         res.line('/**');
         res.line(' * Create configuration for cache "' + cache.name + '".');
@@ -1889,13 +2119,13 @@ $generatorJava.clusterCaches = function (caches, igfss, isSrvCfg, res) {
     if (!res)
         res = $generatorCommon.builder();
 
-    var names = [];
+    const names = [];
 
     if ($generatorCommon.isDefinedAndNotEmpty(caches)) {
         res.emptyLineIfNeeded();
 
-        _.forEach(caches, function (cache) {
-            clusterCache(res, cache, names);
+        _.forEach(caches, function(cache) {
+            clusterCache(cache, names);
         });
 
         res.needEmptyLine = true;
@@ -1904,9 +2134,9 @@ $generatorJava.clusterCaches = function (caches, igfss, isSrvCfg, res) {
     if (isSrvCfg && $generatorCommon.isDefinedAndNotEmpty(igfss)) {
         res.emptyLineIfNeeded();
 
-        _.forEach(igfss, function (igfs) {
-            clusterCache(res, $generatorCommon.igfsDataCache(igfs), names);
-            clusterCache(res, $generatorCommon.igfsMetaCache(igfs), names);
+        _.forEach(igfss, function(igfs) {
+            clusterCache($generatorCommon.igfsDataCache(igfs), names);
+            clusterCache($generatorCommon.igfsMetaCache(igfs), names);
         });
 
         res.needEmptyLine = true;
@@ -1916,33 +2146,31 @@ $generatorJava.clusterCaches = function (caches, igfss, isSrvCfg, res) {
 };
 
 // Generate cluster caches.
-$generatorJava.clusterCacheUse = function (caches, igfss, res) {
+$generatorJava.clusterCacheUse = function(caches, igfss, res) {
     function clusterCacheInvoke(cache, names) {
-        names.push($generatorJava.nextVariableName('cache', cache, names) + '()');
+        names.push($generatorJava.nextVariableName('cache', cache, names));
     }
 
     if (!res)
         res = $generatorCommon.builder();
 
-    var names = [];
+    const cacheNames = [];
 
-    _.forEach(caches, function (cache) {
-        clusterCacheInvoke(cache, names);
+    _.forEach(caches, function(cache) {
+        clusterCacheInvoke(cache, cacheNames);
     });
 
-    var igfsNames = [];
+    const igfsNames = [];
 
-    _.forEach(igfss, function (igfs) {
+    _.forEach(igfss, function(igfs) {
         clusterCacheInvoke($generatorCommon.igfsDataCache(igfs), igfsNames);
         clusterCacheInvoke($generatorCommon.igfsMetaCache(igfs), igfsNames);
     });
 
-    if (names.length > 0 || igfsNames.length > 0) {
-        _.forEach(igfsNames, function (igfsName) {
-            names.push(igfsName);
-        });
+    const allCacheNames = cacheNames.concat(igfsNames);
 
-        res.line('cfg.setCacheConfiguration(' + names.join(', ') + ');');
+    if (allCacheNames.length) {
+        res.line('cfg.setCacheConfiguration(' + allCacheNames.join('(), ') + '());');
 
         res.needEmptyLine = true;
     }
@@ -1951,7 +2179,7 @@ $generatorJava.clusterCacheUse = function (caches, igfss, res) {
 };
 
 // Get class name from fully specified class path.
-$generatorJava.extractType = function (fullType) {
+$generatorJava.extractType = function(fullType) {
     return fullType.substring(fullType.lastIndexOf('.') + 1);
 };
 
@@ -1965,11 +2193,11 @@ $generatorJava.extractType = function (fullType) {
  * @param includeKeyFields If 'true' then include key fields into value POJO.
  * @param res Resulting output with generated code.
  */
-$generatorJava.javaClassCode = function (domain, key, pkg, useConstructor, includeKeyFields, res) {
+$generatorJava.javaClassCode = function(domain, key, pkg, useConstructor, includeKeyFields, res) {
     if (!res)
         res = $generatorCommon.builder();
 
-    var type = $generatorJava.extractType(key ? domain.keyType : domain.valueType);
+    const type = $generatorJava.extractType(key ? domain.keyType : domain.valueType);
 
     // Class comment.
     res.line('/**');
@@ -1984,19 +2212,18 @@ $generatorJava.javaClassCode = function (domain, key, pkg, useConstructor, inclu
     res.line('private static final long serialVersionUID = 0L;');
     res.needEmptyLine = true;
 
-    var allFields = (key || includeKeyFields) ? domain.keyFields.slice() : [];
+    const allFields = (key || includeKeyFields) ? domain.keyFields.slice() : [];
 
-    if (!key)
-        _.forEach(domain.valueFields, function (valFld) {
-            if (_.findIndex(allFields, function(fld) {
-                return fld.javaFieldName === valFld.javaFieldName;
-            }) < 0)
+    if (!key) {
+        _.forEach(domain.valueFields, (valFld) => {
+            if (_.findIndex(allFields, (fld) => fld.javaFieldName === valFld.javaFieldName) < 0)
                 allFields.push(valFld);
         });
+    }
 
     // Generate allFields declaration.
-    _.forEach(allFields, function (field) {
-        var fldName = field.javaFieldName;
+    _.forEach(allFields, function(field) {
+        const fldName = field.javaFieldName;
 
         res.line('/** Value for ' + fldName + '. */');
 
@@ -2029,9 +2256,7 @@ $generatorJava.javaClassCode = function (domain, key, pkg, useConstructor, inclu
 
         res.startBlock();
 
-        _.forEach(allFields, function (field) {
-            res.line('this.' + field.javaFieldName +' = ' + field.javaFieldName + ';');
-        });
+        _.forEach(allFields, (field) => res.line('this.' + field.javaFieldName + ' = ' + field.javaFieldName + ';'));
 
         res.endBlock('}');
 
@@ -2039,10 +2264,10 @@ $generatorJava.javaClassCode = function (domain, key, pkg, useConstructor, inclu
     }
 
     // Generate getters and setters methods.
-    _.forEach(allFields, function (field) {
-        var fldName = field.javaFieldName;
+    _.forEach(allFields, function(field) {
+        const fldName = field.javaFieldName;
 
-        var fldType = res.importClass(field.javaFieldType);
+        const fldType = res.importClass(field.javaFieldType);
 
         res.line('/**');
         res.line(' * Gets ' + fldName + '.');
@@ -2083,16 +2308,16 @@ $generatorJava.javaClassCode = function (domain, key, pkg, useConstructor, inclu
 
     res.line(type + ' that = (' + type + ')o;');
 
-    _.forEach(allFields, function (field) {
+    _.forEach(allFields, function(field) {
         res.needEmptyLine = true;
 
-        var javaName = field.javaFieldName;
-        var javaType = field.javaFieldType;
+        const javaName = field.javaFieldName;
+        const javaType = field.javaFieldType;
 
         if ($generatorCommon.JavaTypes.isJavaPrimitive(javaType)) {
-            if ('float' === javaType)
+            if (javaType === 'float')
                 res.startBlock('if (Float.compare(' + javaName + ', that.' + javaName + ') != 0)');
-            else if ('double' === javaType)
+            else if (javaType === 'double')
                 res.startBlock('if (Double.compare(' + javaName + ', that.' + javaName + ') != 0)');
             else
                 res.startBlock('if (' + javaName + ' != that.' + javaName + ')');
@@ -2115,34 +2340,35 @@ $generatorJava.javaClassCode = function (domain, key, pkg, useConstructor, inclu
     res.line('/** {@inheritDoc} */');
     res.startBlock('@Override public int hashCode() {');
 
-    var first = true;
-    var tempVar = false;
+    let first = true;
+    let tempVar = false;
 
-    _.forEach(allFields, function (field) {
-        var javaName = field.javaFieldName;
-        var javaType = field.javaFieldType;
+    _.forEach(allFields, function(field) {
+        const javaName = field.javaFieldName;
+        const javaType = field.javaFieldType;
 
         if (!first)
             res.needEmptyLine = true;
 
         if ($generatorCommon.JavaTypes.isJavaPrimitive(javaType)) {
-            if ('boolean' === javaType)
+            if (javaType === 'boolean')
                 res.line(first ? 'int res = ' + javaName + ' ? 1 : 0;' : 'res = 31 * res + (' + javaName + ' ? 1 : 0);');
-            else if ('byte' === javaType || 'short' === javaType)
+            else if (javaType === 'byte' || javaType === 'short')
                 res.line(first ? 'int res = (int)' + javaName + ';' : 'res = 31 * res + (int)' + javaName + ';');
-            else if ('int' === javaType)
+            else if (javaType === 'int')
                 res.line(first ? 'int res = ' + javaName + ';' : 'res = 31 * res + ' + javaName + ';');
-            else if ('long' === javaType)
+            else if (javaType === 'long') {
                 res.line(first
                     ? 'int res = (int)(' + javaName + ' ^ (' + javaName + ' >>> 32));'
                     : 'res = 31 * res + (int)(' + javaName + ' ^ (' + javaName + ' >>> 32));');
-            else if ('float' === javaType)
+            }
+            else if (javaType === 'float') {
                 res.line(first
                     ? 'int res = ' + javaName + ' != +0.0f ? Float.floatToIntBits(' + javaName + ') : 0;'
                     : 'res = 31 * res + (' + javaName + ' != +0.0f ? Float.floatToIntBits(' + javaName + ') : 0);');
-            else if ('double' === javaType) {
-                res.line((tempVar ? 'ig_hash_temp' : 'long ig_hash_temp') +
-                        ' = Double.doubleToLongBits(' + javaName + ');');
+            }
+            else if (javaType === 'double') {
+                res.line((tempVar ? 'ig_hash_temp' : 'long ig_hash_temp') + ' = Double.doubleToLongBits(' + javaName + ');');
 
                 res.needEmptyLine = true;
 
@@ -2150,12 +2376,14 @@ $generatorJava.javaClassCode = function (domain, key, pkg, useConstructor, inclu
                         ? 'int res = (int)(ig_hash_temp ^ (ig_hash_temp >>> 32));'
                         : 'res = 31 * res + (int)(ig_hash_temp ^ (ig_hash_temp >>> 32));');
 
-                    tempVar = true;
+                tempVar = true;
             }
         }
-        else
-            res.line(first ? 'int res = ' + javaName + ' != null ? ' + javaName + '.hashCode() : 0;'
+        else {
+            res.line(first
+                ? 'int res = ' + javaName + ' != null ? ' + javaName + '.hashCode() : 0;'
                 : 'res = 31 * res + (' + javaName + ' != null ? ' + javaName + '.hashCode() : 0);');
+        }
 
         first = false;
     });
@@ -2171,7 +2399,7 @@ $generatorJava.javaClassCode = function (domain, key, pkg, useConstructor, inclu
 
     res.startBlock('return \"' + type + ' [" + ');
 
-    _.forEach(allFields, function (field, idx) {
+    _.forEach(allFields, function(field, idx) {
         res.line('\"' + field.javaFieldName + '=\" + ' + field.javaFieldName + (idx < allFields.length - 1 ? ' + ", " + ' : ' +'));
     });
 
@@ -2190,8 +2418,8 @@ $generatorJava.javaClassCode = function (domain, key, pkg, useConstructor, inclu
  * @param useConstructor If 'true' then generate constructors.
  * @param includeKeyFields If 'true' then include key fields into value POJO.
  */
-$generatorJava.pojos = function (caches, useConstructor, includeKeyFields) {
-    var pojos = [];
+$generatorJava.pojos = function(caches, useConstructor, includeKeyFields) {
+    const pojos = [];
 
     _.forEach(caches, function(cache) {
         _.forEach(cache.domains, function(domain) {
@@ -2199,7 +2427,7 @@ $generatorJava.pojos = function (caches, useConstructor, includeKeyFields) {
             if (!_.find(pojos, {valueType: domain.valueType}) &&
                 // Skip domain models without value fields.
                 $generatorCommon.isDefinedAndNotEmpty(domain.valueFields)) {
-                var pojo = {};
+                const pojo = {};
 
                 // Key class generation only if key is not build in java class.
                 if (!_.isNil(domain.keyFields) && domain.keyFields.length > 0) {
@@ -2225,11 +2453,11 @@ $generatorJava.pojos = function (caches, useConstructor, includeKeyFields) {
  * @returns Field java type name.
  */
 $generatorJava.javaTypeName = function(type) {
-    var ix = $generatorJava.javaBuiltInClasses.indexOf(type);
+    const ix = $generatorJava.javaBuiltInClasses.indexOf(type);
 
-    var resType = ix >= 0 ? $generatorJava.javaBuiltInFullNameClasses[ix] : type;
+    const resType = ix >= 0 ? $generatorJava.javaBuiltInFullNameClasses[ix] : type;
 
-    return resType.indexOf("java.lang.") >= 0 ? resType.substring(10) : resType;
+    return resType.indexOf('java.lang.') >= 0 ? resType.substring(10) : resType;
 };
 
 /**
@@ -2251,7 +2479,7 @@ $generatorJava.clusterSsl = function(cluster, res) {
         cluster.sslContextFactory.trustStorePassword = $generatorCommon.isDefinedAndNotEmpty(cluster.sslContextFactory.trustStoreFilePath) ?
             'props.getProperty("ssl.trust.storage.password").toCharArray()' : null;
 
-        var propsDesc = $generatorCommon.isDefinedAndNotEmpty(cluster.sslContextFactory.trustManagers) ?
+        const propsDesc = $generatorCommon.isDefinedAndNotEmpty(cluster.sslContextFactory.trustManagers) ?
             $generatorCommon.SSL_CONFIGURATION_TRUST_MANAGER_FACTORY.fields :
             $generatorCommon.SSL_CONFIGURATION_TRUST_FILE_FACTORY.fields;
 
@@ -2279,8 +2507,8 @@ $generatorJava.igfss = function(igfss, varName, res) {
     if ($generatorCommon.isDefinedAndNotEmpty(igfss)) {
         res.emptyLineIfNeeded();
 
-        var arrayName = 'fileSystems';
-        var igfsInst = 'igfs';
+        const arrayName = 'fileSystems';
+        const igfsInst = 'igfs';
 
         res.line(res.importClass('org.apache.ignite.configuration.FileSystemConfiguration') + '[] ' + arrayName + ' = new FileSystemConfiguration[' + igfss.length + '];');
 
@@ -2323,7 +2551,7 @@ $generatorJava.igfsIPC = function(igfs, varName, res) {
         varName = $generatorJava.nextVariableName('igfs', igfs);
 
     if (igfs.ipcEndpointEnabled) {
-        var desc = $generatorCommon.IGFS_IPC_CONFIGURATION;
+        const desc = $generatorCommon.IGFS_IPC_CONFIGURATION;
 
         $generatorJava.beanProperty(res, varName, igfs.ipcEndpointConfiguration, 'ipcEndpointConfiguration', 'ipcEndpointCfg',
             desc.className, desc.fields, true);
@@ -2397,10 +2625,10 @@ $generatorJava.igfsSecondFS = function(igfs, varName, res) {
         varName = $generatorJava.nextVariableName('igfs', igfs);
 
     if (igfs.secondaryFileSystemEnabled) {
-        var secondFs = igfs.secondaryFileSystem || {};
+        const secondFs = igfs.secondaryFileSystem || {};
 
-        var nameDefined = $generatorCommon.isDefinedAndNotEmpty(secondFs.userName);
-        var cfgDefined = $generatorCommon.isDefinedAndNotEmpty(secondFs.cfgPath);
+        const nameDefined = $generatorCommon.isDefinedAndNotEmpty(secondFs.userName);
+        const cfgDefined = $generatorCommon.isDefinedAndNotEmpty(secondFs.cfgPath);
 
         res.line(varName + '.setSecondaryFileSystem(new ' +
             res.importClass('org.apache.ignite.hadoop.fs.IgniteHadoopIgfsSecondaryFileSystem') + '(' +
@@ -2437,7 +2665,7 @@ $generatorJava.igfsGeneral = function(igfs, varName, res) {
         $generatorJava.property(res, varName, igfs, 'name');
         $generatorJava.property(res, varName, igfs, 'dataCacheName');
         $generatorJava.property(res, varName, igfs, 'metaCacheName');
-        $generatorJava.enumProperty(res, varName, igfs, 'defaultMode', 'org.apache.ignite.igfs.IgfsMode', null, "DUAL_ASYNC");
+        $generatorJava.enumProperty(res, varName, igfs, 'defaultMode', 'org.apache.ignite.igfs.IgfsMode', null, 'DUAL_ASYNC');
 
         res.needEmptyLine = true;
     }
@@ -2478,8 +2706,8 @@ $generatorJava.igfsMisc = function(igfs, varName, res) {
 
         $generatorJava.declareVariable(res, 'pathModes', 'java.util.Map', 'java.util.HashMap', 'String', 'org.apache.ignite.igfs.IgfsMode');
 
-        _.forEach(igfs.pathModes, function (pair) {
-            res.line('pathModes.put("' + pair.path + '", IgfsMode.' + pair.mode +');');
+        _.forEach(igfs.pathModes, function(pair) {
+            res.line('pathModes.put("' + pair.path + '", IgfsMode.' + pair.mode + ');');
         });
 
         res.needEmptyLine = true;
@@ -2492,13 +2720,15 @@ $generatorJava.igfsMisc = function(igfs, varName, res) {
     return res;
 };
 
-$generatorJava.clusterConfiguration = function (cluster, clientNearCfg, res) {
+$generatorJava.clusterConfiguration = function(cluster, clientNearCfg, res) {
     $generatorJava.clusterGeneral(cluster, clientNearCfg, res);
 
     $generatorJava.clusterAtomics(cluster.atomicConfiguration, res);
 
     $generatorJava.clusterBinary(cluster.binaryConfiguration, res);
 
+    $generatorJava.clusterCollision(cluster.collision, res);
+
     $generatorJava.clusterCommunication(cluster, res);
 
     $generatorJava.clusterConnector(cluster.connector, res);
@@ -2507,6 +2737,10 @@ $generatorJava.clusterConfiguration = function (cluster, clientNearCfg, res) {
 
     $generatorJava.clusterEvents(cluster, res);
 
+    $generatorJava.clusterFailover(cluster, res);
+
+    $generatorJava.clusterLogger(cluster.logger, res);
+
     $generatorJava.clusterMarshaller(cluster, res);
 
     $generatorJava.clusterMetrics(cluster, res);
@@ -2519,7 +2753,7 @@ $generatorJava.clusterConfiguration = function (cluster, clientNearCfg, res) {
 
     $generatorJava.clusterTransactions(cluster.transactionConfiguration, res);
 
-    var isSrvCfg = _.isNil(clientNearCfg);
+    const isSrvCfg = _.isNil(clientNearCfg);
 
     if (isSrvCfg)
         $generatorJava.clusterCacheUse(cluster.caches, cluster.igfss, res);
@@ -2529,11 +2763,13 @@ $generatorJava.clusterConfiguration = function (cluster, clientNearCfg, res) {
     if (isSrvCfg)
         $generatorJava.igfss(cluster.igfss, 'cfg', res);
 
+    $generatorJava.clusterUserAttributes(cluster, res);
+
     return res;
 };
 
 // Generate loading of secret properties file.
-$generatorJava.tryLoadSecretProperties = function (cluster, res) {
+$generatorJava.tryLoadSecretProperties = function(cluster, res) {
     if ($generatorCommon.secretPropertiesNeeded(cluster)) {
         res.importClass('org.apache.ignite.configuration.IgniteConfiguration');
 
@@ -2560,13 +2796,13 @@ $generatorJava.tryLoadSecretProperties = function (cluster, res) {
  * @param javaClass Class name for generate factory class otherwise generate code snippet.
  * @param clientNearCfg Optional near cache configuration for client node.
  */
-$generatorJava.cluster = function (cluster, pkg, javaClass, clientNearCfg) {
-    var res = $generatorCommon.builder();
+$generatorJava.cluster = function(cluster, pkg, javaClass, clientNearCfg) {
+    const res = $generatorCommon.builder();
 
-    var isSrvCfg = _.isNil(clientNearCfg);
+    const isSrvCfg = _.isNil(clientNearCfg);
 
     if (cluster) {
-        var resCfg = $generatorJava.clusterConfiguration(cluster, clientNearCfg, $generatorCommon.builder());
+        const resCfg = $generatorJava.clusterConfiguration(cluster, clientNearCfg, $generatorCommon.builder());
 
         res.mergeProps(resCfg);
 
@@ -2635,7 +2871,7 @@ $generatorJava.cluster = function (cluster, pkg, javaClass, clientNearCfg) {
 
         res.endBlock('}');
 
-        return 'package ' + pkg + ';\n\n' + res.generateImports() + '\n\n' + res.generateStaticImports()  + '\n\n' + res.asString();
+        return 'package ' + pkg + ';\n\n' + res.generateImports() + '\n\n' + res.generateStaticImports() + '\n\n' + res.asString();
     }
 
     return res.asString();
@@ -2647,15 +2883,15 @@ $generatorJava.cluster = function (cluster, pkg, javaClass, clientNearCfg) {
  * @param storeFactory Store factory for data source class name generation.
  * @returns {*} Data source class name.
  */
-$generatorJava.dataSourceClassName = function (res, storeFactory) {
-    var dialect = storeFactory.connectVia ? (storeFactory.connectVia === 'DataSource' ? storeFactory.dialect : null) : storeFactory.dialect;
+$generatorJava.dataSourceClassName = function(res, storeFactory) {
+    const dialect = storeFactory.connectVia ? (storeFactory.connectVia === 'DataSource' ? storeFactory.dialect : null) : storeFactory.dialect;
 
     if (dialect) {
-        var dataSourceBean = storeFactory.dataSourceBean;
+        const dataSourceBean = storeFactory.dataSourceBean;
 
-        var dsClsName = $generatorCommon.dataSourceClassName(dialect);
+        const dsClsName = $generatorCommon.dataSourceClassName(dialect);
 
-        var varType = res.importClass(dsClsName);
+        const varType = res.importClass(dsClsName);
 
         return $generatorCommon.toJavaName(varType, dataSourceBean);
     }
@@ -2675,14 +2911,12 @@ const PREDEFINED_QUERIES = [
         clearQuery: 'DELETE FROM CARS.PARKING',
         insertCntConsts: [{name: 'DEMO_MAX_PARKING_CNT', val: 5, comment: 'How many parkings to generate.'}],
         insertPattern: ['INSERT INTO CARS.PARKING(ID, NAME, CAPACITY) VALUES(?, ?, ?)'],
-        fillInsertParameters: function (res) {
+        fillInsertParameters(res) {
             res.line('stmt.setInt(1, id);');
             res.line('stmt.setString(2, "Parking #" + (id + 1));');
             res.line('stmt.setInt(3, 10 + rnd.nextInt(20));');
         },
-        selectQuery: [
-            "SELECT * FROM PARKING WHERE CAPACITY >= 20"
-        ]
+        selectQuery: ['SELECT * FROM PARKING WHERE CAPACITY >= 20']
     },
     {
         schema: 'CARS',
@@ -2698,14 +2932,12 @@ const PREDEFINED_QUERIES = [
             {name: 'DEMO_MAX_PARKING_CNT', val: 5, comment: 'How many parkings to generate.'}
         ],
         insertPattern: ['INSERT INTO CARS.CAR(ID, PARKING_ID, NAME) VALUES(?, ?, ?)'],
-        fillInsertParameters: function (res) {
+        fillInsertParameters(res) {
             res.line('stmt.setInt(1, id);');
             res.line('stmt.setInt(2, rnd.nextInt(DEMO_MAX_PARKING_CNT));');
             res.line('stmt.setString(3, "Car #" + (id + 1));');
         },
-        selectQuery: [
-            "SELECT * FROM CAR WHERE PARKINGID = 2"
-        ]
+        selectQuery: ['SELECT * FROM CAR WHERE PARKINGID = 2']
     },
     {
         type: 'COUNTRY',
@@ -2716,14 +2948,12 @@ const PREDEFINED_QUERIES = [
         clearQuery: 'DELETE FROM COUNTRY',
         insertCntConsts: [{name: 'DEMO_MAX_COUNTRY_CNT', val: 5, comment: 'How many countries to generate.'}],
         insertPattern: ['INSERT INTO COUNTRY(ID, NAME, POPULATION) VALUES(?, ?, ?)'],
-        fillInsertParameters: function (res) {
+        fillInsertParameters(res) {
             res.line('stmt.setInt(1, id);');
             res.line('stmt.setString(2, "Country #" + (id + 1));');
             res.line('stmt.setInt(3, 10000000 + rnd.nextInt(100000000));');
         },
-        selectQuery: [
-            "SELECT * FROM COUNTRY WHERE POPULATION BETWEEN 15000000 AND 25000000"
-        ]
+        selectQuery: ['SELECT * FROM COUNTRY WHERE POPULATION BETWEEN 15000000 AND 25000000']
     },
     {
         type: 'DEPARTMENT',
@@ -2738,14 +2968,12 @@ const PREDEFINED_QUERIES = [
             {name: 'DEMO_MAX_COUNTRY_CNT', val: 5, comment: 'How many countries to generate.'}
         ],
         insertPattern: ['INSERT INTO DEPARTMENT(ID, COUNTRY_ID, NAME) VALUES(?, ?, ?)'],
-        fillInsertParameters: function (res) {
+        fillInsertParameters(res) {
             res.line('stmt.setInt(1, id);');
             res.line('stmt.setInt(2, rnd.nextInt(DEMO_MAX_COUNTRY_CNT));');
             res.line('stmt.setString(3, "Department #" + (id + 1));');
         },
-        selectQuery: [
-            "SELECT * FROM DEPARTMENT"
-        ]
+        selectQuery: ['SELECT * FROM DEPARTMENT']
     },
     {
         type: 'EMPLOYEE',
@@ -2766,28 +2994,28 @@ const PREDEFINED_QUERIES = [
             {name: 'DEMO_MAX_EMPLOYEE_CNT', val: 10, comment: 'How many employees to generate.'},
             {name: 'DEMO_MAX_DEPARTMENT_CNT', val: 5, comment: 'How many departments to generate.'}
         ],
-        specialGeneration: function (res, conVar) {
-            //$generatorJava.declareVariableCustom(res, 'stmt', 'java.sql.PreparedStatement', conVar +
+        specialGeneration(res, conVar) {
+            // $generatorJava.declareVariableCustom(res, 'stmt', 'java.sql.PreparedStatement', conVar +
             //    '.prepareStatement("INSERT INTO EMPLOYEE(ID, DEPARTMENT_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB, SALARY) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)")');
             //
-            //res.startBlock('for (int id = 0; id < DEMO_MAX_DEPARTMENT_CNT; id ++) {');
-            //res.line('stmt.setInt(1, id);');
-            //res.line('stmt.setInt(2, id);');
-            //res.line('stmt.setString(3, "First name manager #" + (id + 1));');
-            //res.line('stmt.setString(4, "Last name manager#" + (id + 1));');
-            //res.line('stmt.setString(5, "Email manager#" + (id + 1));');
-            //res.line('stmt.setString(6, "Phone number manager#" + (id + 1));');
-            //res.line('stmt.setString(7, "2014-01-01");');
-            //res.line('stmt.setString(8, "Job manager #" + (id + 1));');
-            //res.line('stmt.setDouble(9, 1000.0 + rnd.nextInt(500));');
+            // res.startBlock('for (int id = 0; id < DEMO_MAX_DEPARTMENT_CNT; id ++) {');
+            // res.line('stmt.setInt(1, id);');
+            // res.line('stmt.setInt(2, id);');
+            // res.line('stmt.setString(3, "First name manager #" + (id + 1));');
+            // res.line('stmt.setString(4, "Last name manager#" + (id + 1));');
+            // res.line('stmt.setString(5, "Email manager#" + (id + 1));');
+            // res.line('stmt.setString(6, "Phone number manager#" + (id + 1));');
+            // res.line('stmt.setString(7, "2014-01-01");');
+            // res.line('stmt.setString(8, "Job manager #" + (id + 1));');
+            // res.line('stmt.setDouble(9, 1000.0 + rnd.nextInt(500));');
             //
-            //res.needEmptyLine = true;
+            // res.needEmptyLine = true;
             //
-            //res.line('stmt.executeUpdate();');
+            // res.line('stmt.executeUpdate();');
             //
-            //res.endBlock('}');
+            // res.endBlock('}');
             //
-            //res.needEmptyLine = true;
+            // res.needEmptyLine = true;
 
             $generatorJava.declareVariableCustom(res, 'stmt', 'java.sql.PreparedStatement', conVar +
                 '.prepareStatement("INSERT INTO EMPLOYEE(ID, DEPARTMENT_ID, MANAGER_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB, SALARY) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")');
@@ -2817,23 +3045,22 @@ const PREDEFINED_QUERIES = [
 
             res.needEmptyLine = true;
         },
-        selectQuery: [
-            "SELECT * FROM EMPLOYEE WHERE SALARY > 700"
-        ]
+        selectQuery: ['SELECT * FROM EMPLOYEE WHERE SALARY > 700']
     }
 ];
 
 // Generate creation and execution of prepared statement.
 function _prepareStatement(res, conVar, query, select) {
     if (query) {
-        var lines = query.split('\n');
+        const lines = query.split('\n');
 
-        _.forEach(lines, function (line, ix) {
-            if (ix == 0)
-                if (lines.length == 1)
+        _.forEach(lines, function(line, ix) {
+            if (ix === 0) {
+                if (lines.length === 1)
                     res.line(conVar + '.prepareStatement("' + line + '").execute' + (select ? 'Query' : 'Update') + '();');
                 else
                     res.startBlock(conVar + '.prepareStatement("' + line + '" +');
+            }
             else
                 res.line('"' + line + '"' + (ix === lines.length - 1 ? ').execute' + (select ? 'Query' : 'Update') + '();' : ' +'));
         });
@@ -2849,14 +3076,15 @@ function _prepareStatement(res, conVar, query, select) {
 // Generate creation and execution of cache query.
 function _multilineQuery(res, query, prefix, postfix) {
     if (query) {
-        var lines = query.split('\n');
+        const lines = query.split('\n');
 
-        _.forEach(lines, function (line, ix) {
-            if (ix == 0)
-                if (lines.length == 1)
+        _.forEach(lines, function(line, ix) {
+            if (ix === 0) {
+                if (lines.length === 1)
                     res.line(prefix + '"' + line + '"' + postfix);
                 else
                     res.startBlock(prefix + '"' + line + '" +');
+            }
             else
                 res.line('"' + line + '"' + (ix === lines.length - 1 ? postfix : ' +'));
         });
@@ -2879,7 +3107,7 @@ $generatorJava.isDemoConfigured = function(cluster, demo) {
             (desc) => domain.valueType.toUpperCase().endsWith(desc.type))));
 };
 
-$generatorJava.generateDemo = function (cluster, res, factoryCls) {
+$generatorJava.generateDemo = function(cluster, res, factoryCls) {
     const cachesWithDataSource = _.filter(cluster.caches, (cache) => {
         if (cache.cacheStoreFactory && cache.cacheStoreFactory.kind) {
             const storeFactory = cache.cacheStoreFactory[cache.cacheStoreFactory.kind];
@@ -2892,22 +3120,22 @@ $generatorJava.generateDemo = function (cluster, res, factoryCls) {
     });
 
     // Prepare array of cache and his demo domain model list. Every domain is contained only in first cache.
-    var demoTypes = _.filter(_.map(cachesWithDataSource, (cache, idx) => {
+    const demoTypes = _.filter(_.map(cachesWithDataSource, (cache, idx) => {
         return {
-            cache: cache,
+            cache,
             domains: _.filter(cache.domains, (domain) =>
                 $generatorCommon.isDefinedAndNotEmpty(domain.valueFields) &&
                     !_.find(cachesWithDataSource, (checkCache, checkIx) => checkIx < idx && _.find(checkCache.domains, domain))
             )
-        }
+        };
     }), (cache) => $generatorCommon.isDefinedAndNotEmpty(cache.domains));
 
     if ($generatorCommon.isDefinedAndNotEmpty(demoTypes)) {
-        var typeByDs = {};
+        const typeByDs = {};
 
         // Group domain modes by data source
-        _.forEach(demoTypes, function (type) {
-            var ds = type.cache.cacheStoreFactory[type.cache.cacheStoreFactory.kind].dataSourceBean;
+        _.forEach(demoTypes, function(type) {
+            const ds = type.cache.cacheStoreFactory[type.cache.cacheStoreFactory.kind].dataSourceBean;
 
             if (!typeByDs[ds])
                 typeByDs[ds] = [type];
@@ -2915,16 +3143,14 @@ $generatorJava.generateDemo = function (cluster, res, factoryCls) {
                 typeByDs[ds].push(type);
         });
 
-        var rndDefined = false;
+        let rndDefined = false;
 
-        var generatedConsts = [];
+        const generatedConsts = [];
 
-        _.forEach(typeByDs, function (types) {
-            _.forEach(types, function (type) {
-                _.forEach(type.domains, function (domain) {
-                    var desc = _.find(PREDEFINED_QUERIES, function (desc) {
-                        return domain.valueType.toUpperCase().endsWith(desc.type);
-                    });
+        _.forEach(typeByDs, function(types) {
+            _.forEach(types, function(type) {
+                _.forEach(type.domains, function(domain) {
+                    const desc = _.find(PREDEFINED_QUERIES, (d) => domain.valueType.toUpperCase().endsWith(d.type));
 
                     if (desc) {
                         if (!rndDefined && desc.rndRequired) {
@@ -2934,7 +3160,7 @@ $generatorJava.generateDemo = function (cluster, res, factoryCls) {
                             rndDefined = true;
                         }
 
-                        _.forEach(desc.insertCntConsts, function (cnt) {
+                        _.forEach(desc.insertCntConsts, function(cnt) {
                             if (!_.includes(generatedConsts, cnt.name)) {
                                 res.line('/** ' + cnt.comment + ' */');
                                 res.line('private static final int ' + cnt.name + ' = ' + cnt.val + ';');
@@ -2954,16 +3180,1

<TRUNCATED>

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

Posted by ak...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/controllers/igfs-controller.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/controllers/igfs-controller.js b/modules/web-console/src/main/js/controllers/igfs-controller.js
index 3a0e093..c37d08f 100644
--- a/modules/web-console/src/main/js/controllers/igfs-controller.js
+++ b/modules/web-console/src/main/js/controllers/igfs-controller.js
@@ -20,14 +20,14 @@ import consoleModule from 'controllers/common-module';
 
 consoleModule.controller('igfsController', [
     '$scope', '$http', '$state', '$filter', '$timeout', '$common', '$confirm', '$clone', '$loading', '$cleanup', '$unsavedChangesGuard', '$table',
-    function ($scope, $http, $state, $filter, $timeout, $common, $confirm, $clone, $loading, $cleanup, $unsavedChangesGuard, $table) {
+    function($scope, $http, $state, $filter, $timeout, $common, $confirm, $clone, $loading, $cleanup, $unsavedChangesGuard, $table) {
         $unsavedChangesGuard.install($scope);
 
-        var emptyIgfs = {empty: true};
+        const emptyIgfs = {empty: true};
 
-        var __original_value;
+        let __original_value;
 
-        var blank = {
+        const blank = {
             ipcEndpointConfiguration: {},
             secondaryFileSystem: {}
         };
@@ -43,60 +43,55 @@ consoleModule.controller('igfsController', [
         $scope.widthIsSufficient = $common.widthIsSufficient;
         $scope.saveBtnTipText = $common.saveBtnTipText;
 
-        // TODO LEGACY start
-        $scope.tableSave = function (field, index, stopEdit) {
-            switch (field.type) {
-                case 'pathModes':
-                    if ($table.tablePairSaveVisible(field, index))
-                        return $table.tablePairSave($scope.tablePairValid, $scope.backupItem, field, index, stopEdit);
+        const showPopoverMessage = $common.showPopoverMessage;
 
-                    break;
-            }
+        // TODO LEGACY start
+        $scope.tableSave = function(field, index, stopEdit) {
+            if (field.type === 'pathModes' && $table.tablePairSaveVisible(field, index))
+                return $table.tablePairSave($scope.tablePairValid, $scope.backupItem, field, index, stopEdit);
 
             return true;
         };
 
-        $scope.tableReset = function (save) {
-            var field = $table.tableField();
+        $scope.tableReset = (trySave) => {
+            const field = $table.tableField();
 
-            if (!save || !$common.isDefined(field) || $scope.tableSave(field, $table.tableEditedRowIndex(), true)) {
-                $table.tableReset();
+            if (trySave && $common.isDefined(field) && !$scope.tableSave(field, $table.tableEditedRowIndex(), true))
+                return false;
 
-                return true;
-            }
+            $table.tableReset();
 
-            return false;
+            return true;
         };
 
-        $scope.tableNewItem = function (field) {
+        $scope.tableNewItem = function(field) {
             if ($scope.tableReset(true))
                 $table.tableNewItem(field);
         };
 
         $scope.tableNewItemActive = $table.tableNewItemActive;
 
-        $scope.tableStartEdit = function (item, field, index) {
+        $scope.tableStartEdit = function(item, field, index) {
             if ($scope.tableReset(true))
                 $table.tableStartEdit(item, field, index, $scope.tableSave);
         };
 
         $scope.tableEditing = $table.tableEditing;
-
         $scope.tablePairSave = $table.tablePairSave;
         $scope.tablePairSaveVisible = $table.tablePairSaveVisible;
 
-        $scope.tableRemove = function (item, field, index) {
+        $scope.tableRemove = function(item, field, index) {
             if ($scope.tableReset(true))
                 $table.tableRemove(item, field, index);
         };
 
-        $scope.tablePairValid = function (item, field, index) {
-            var pairValue = $table.tablePairValue(field, index);
+        $scope.tablePairValid = function(item, field, index) {
+            const pairValue = $table.tablePairValue(field, index);
 
-            var model = item[field.model];
+            const model = item[field.model];
 
             if ($common.isDefined(model)) {
-                var idx = _.findIndex(model, function (pair) {
+                const idx = _.findIndex(model, function(pair) {
                     return pair.path === pairValue.key;
                 });
 
@@ -108,18 +103,26 @@ consoleModule.controller('igfsController', [
             return true;
         };
 
+        $scope.tblPathModes = {
+            type: 'pathModes',
+            model: 'pathModes',
+            focusId: 'PathMode',
+            ui: 'table-pair',
+            keyName: 'path',
+            valueName: 'mode',
+            save: $scope.tableSave
+        };
+
         $scope.igfsModes = $common.mkOptions(['PRIMARY', 'PROXY', 'DUAL_SYNC', 'DUAL_ASYNC']);
         // TODO LEGACY start - end
 
-        var showPopoverMessage = $common.showPopoverMessage;
-
-        $scope.contentVisible = function () {
-            var item = $scope.backupItem;
+        $scope.contentVisible = function() {
+            const item = $scope.backupItem;
 
             return !item.empty && (!item._id || _.find($scope.displayedRows, {_id: item._id}));
         };
 
-        $scope.toggleExpanded = function () {
+        $scope.toggleExpanded = function() {
             $scope.ui.expanded = !$scope.ui.expanded;
 
             $common.hidePopover();
@@ -137,34 +140,34 @@ consoleModule.controller('igfsController', [
 
         // When landing on the page, get IGFSs and show them.
         $http.post('/api/v1/configuration/igfs/list')
-            .success(function (data) {
+            .success(function(data) {
                 $scope.spaces = data.spaces;
 
                 $scope.igfss = data.igfss || [];
 
                 // For backward compatibility set colocateMetadata and relaxedConsistency default values.
-                _.forEach($scope.igfss, function (igfs) {
-                   if (_.isUndefined(igfs.colocateMetadata))
-                       igfs.colocateMetadata = true;
+                _.forEach($scope.igfss, (igfs) => {
+                    if (_.isUndefined(igfs.colocateMetadata))
+                        igfs.colocateMetadata = true;
 
-                   if (_.isUndefined(igfs.relaxedConsistency))
-                       igfs.relaxedConsistency = true;
+                    if (_.isUndefined(igfs.relaxedConsistency))
+                        igfs.relaxedConsistency = true;
                 });
 
-                $scope.clusters = _.map(data.clusters  || [], function (cluster) {
+                $scope.clusters = _.map(data.clusters || [], function(cluster) {
                     return {
                         value: cluster._id,
                         label: cluster.name
                     };
                 });
 
-                if ($state.params.id)
-                    $scope.createItem($state.params.id);
+                if ($state.params.linkId)
+                    $scope.createItem($state.params.linkId);
                 else {
-                    var lastSelectedIgfs = angular.fromJson(sessionStorage.lastSelectedIgfs);
+                    const lastSelectedIgfs = angular.fromJson(sessionStorage.lastSelectedIgfs);
 
                     if (lastSelectedIgfs) {
-                        var idx = _.findIndex($scope.igfss, function (igfs) {
+                        const idx = _.findIndex($scope.igfss, function(igfs) {
                             return igfs._id === lastSelectedIgfs;
                         });
 
@@ -181,30 +184,29 @@ consoleModule.controller('igfsController', [
                 }
 
                 $scope.$watch('ui.inputForm.$valid', function(valid) {
-                    if (valid && __original_value === JSON.stringify($cleanup($scope.backupItem))) {
+                    if (valid && _.isEqual(__original_value, $cleanup($scope.backupItem)))
                         $scope.ui.inputForm.$dirty = false;
-                    }
                 });
 
-                $scope.$watch('backupItem', function (val) {
-                    var form = $scope.ui.inputForm;
+                $scope.$watch('backupItem', function(val) {
+                    const form = $scope.ui.inputForm;
 
-                    if (form.$pristine || (form.$valid && __original_value === JSON.stringify($cleanup(val))))
+                    if (form.$pristine || (form.$valid && _.isEqual(__original_value, $cleanup(val))))
                         form.$setPristine();
                     else
                         form.$setDirty();
                 }, true);
             })
-            .catch(function (errMsg) {
+            .catch(function(errMsg) {
                 $common.showError(errMsg);
             })
-            .finally(function () {
+            .finally(function() {
                 $scope.ui.ready = true;
                 $scope.ui.inputForm.$setPristine();
                 $loading.finish('loadingIgfsScreen');
             });
 
-        $scope.selectItem = function (item, backup) {
+        $scope.selectItem = function(item, backup) {
             function selectItem() {
                 $table.tableReset(); // TODO LEGACY
 
@@ -229,7 +231,7 @@ consoleModule.controller('igfsController', [
 
                 $scope.backupItem = angular.merge({}, blank, $scope.backupItem);
 
-                __original_value = JSON.stringify($cleanup($scope.backupItem));
+                __original_value = $cleanup($scope.backupItem);
 
                 if ($common.getQueryVariable('new'))
                     $state.go('base.configuration.igfs');
@@ -238,26 +240,26 @@ consoleModule.controller('igfsController', [
             $common.confirmUnsavedChanges($scope.backupItem && $scope.ui.inputForm.$dirty, selectItem);
         };
 
-        function prepareNewItem(id) {
+        $scope.linkId = () => $scope.backupItem._id ? $scope.backupItem._id : 'create';
+
+        function prepareNewItem(linkId) {
             return {
                 space: $scope.spaces[0]._id,
                 ipcEndpointEnabled: true,
                 fragmentizerEnabled: true,
                 colocateMetadata: true,
                 relaxedConsistency: true,
-                clusters: id && _.find($scope.clusters, {value: id}) ? [id] :
-                    (!_.isEmpty($scope.clusters) ? [$scope.clusters[0].value] : [])
+                clusters: linkId && _.find($scope.clusters, {value: linkId}) ? [linkId] :
+                    (_.isEmpty($scope.clusters) ? [] : [$scope.clusters[0].value])
             };
         }
 
         // Add new IGFS.
-        $scope.createItem = function (id) {
+        $scope.createItem = function(linkId) {
             if ($scope.tableReset(true)) { // TODO LEGACY
-                $timeout(function () {
-                    $common.ensureActivePanel($scope.ui, 'general', 'igfsName');
-                });
+                $timeout(() => $common.ensureActivePanel($scope.ui, 'general', 'igfsName'));
 
-                $scope.selectItem(undefined, prepareNewItem(id));
+                $scope.selectItem(null, prepareNewItem(linkId));
             }
         };
 
@@ -268,46 +270,14 @@ consoleModule.controller('igfsController', [
             if ($common.isEmptyString(item.name))
                 return showPopoverMessage($scope.ui, 'general', 'igfsName', 'IGFS name should not be empty!');
 
-            var form = $scope.ui.inputForm;
-            var errors = form.$error;
-            var errKeys = Object.keys(errors);
-
-            if (errKeys && errKeys.length > 0) {
-                var firstErrorKey = errKeys[0];
-
-                var firstError = errors[firstErrorKey][0];
-                var actualError = firstError.$error[firstErrorKey][0];
-
-                var errNameFull = actualError.$name;
-                var errNameShort = errNameFull;
-
-                if (errNameShort.endsWith('TextInput'))
-                    errNameShort = errNameShort.substring(0, errNameShort.length - 9);
-
-                var extractErrorMessage = function (errName) {
-                    try {
-                        return errors[firstErrorKey][0].$errorMessages[errName][firstErrorKey];
-                    }
-                    catch(ignored) {
-                        try {
-                            msg = form[firstError.$name].$errorMessages[errName][firstErrorKey];
-                        }
-                        catch(ignited) {
-                            return false;
-                        }
-                    }
-                };
-
-                var msg = extractErrorMessage(errNameFull) || extractErrorMessage(errNameShort) || 'Invalid value!';
-
-                return showPopoverMessage($scope.ui, firstError.$name, errNameFull, msg);
-            }
+            if (!$common.checkFieldValidators($scope.ui))
+                return false;
 
             if (!item.secondaryFileSystemEnabled && (item.defaultMode === 'PROXY'))
                 return showPopoverMessage($scope.ui, 'secondaryFileSystem', 'secondaryFileSystem-title', 'Secondary file system should be configured for "PROXY" IGFS mode!');
 
             if (item.pathModes) {
-                for (var pathIx = 0; pathIx < item.pathModes.length; pathIx++) {
+                for (let pathIx = 0; pathIx < item.pathModes.length; pathIx++) {
                     if (!item.secondaryFileSystemEnabled && item.pathModes[pathIx].mode === 'PROXY')
                         return showPopoverMessage($scope.ui, 'secondaryFileSystem', 'secondaryFileSystem-title', 'Secondary file system should be configured for "PROXY" path mode!');
                 }
@@ -319,10 +289,10 @@ consoleModule.controller('igfsController', [
         // Save IGFS in database.
         function save(item) {
             $http.post('/api/v1/configuration/igfs/save', item)
-                .success(function (_id) {
+                .success(function(_id) {
                     $scope.ui.inputForm.$setPristine();
 
-                    var idx = _.findIndex($scope.igfss, function (igfs) {
+                    const idx = _.findIndex($scope.igfss, function(igfs) {
                         return igfs._id === _id;
                     });
 
@@ -337,15 +307,15 @@ consoleModule.controller('igfsController', [
 
                     $common.showInfo('IGFS "' + item.name + '" saved.');
                 })
-                .error(function (errMsg) {
+                .error(function(errMsg) {
                     $common.showError(errMsg);
                 });
         }
 
         // Save IGFS.
-        $scope.saveItem = function () {
+        $scope.saveItem = function() {
             if ($scope.tableReset(true)) { // TODO LEGACY
-                var item = $scope.backupItem;
+                const item = $scope.backupItem;
 
                 if (validate(item))
                     save(item);
@@ -353,16 +323,16 @@ consoleModule.controller('igfsController', [
         };
 
         function _igfsNames() {
-            return _.map($scope.igfss, function (igfs) {
+            return _.map($scope.igfss, function(igfs) {
                 return igfs.name;
             });
         }
 
         // Clone IGFS with new name.
-        $scope.cloneItem = function () {
+        $scope.cloneItem = function() {
             if ($scope.tableReset(true) && validate($scope.backupItem)) { // TODO LEGACY
-                $clone.confirm($scope.backupItem.name, _igfsNames()).then(function (newName) {
-                    var item = angular.copy($scope.backupItem);
+                $clone.confirm($scope.backupItem.name, _igfsNames()).then(function(newName) {
+                    const item = angular.copy($scope.backupItem);
 
                     delete item._id;
 
@@ -374,22 +344,22 @@ consoleModule.controller('igfsController', [
         };
 
         // Remove IGFS from db.
-        $scope.removeItem = function () {
+        $scope.removeItem = function() {
             $table.tableReset(); // TODO LEGACY
 
-            var selectedItem = $scope.selectedItem;
+            const selectedItem = $scope.selectedItem;
 
             $confirm.confirm('Are you sure you want to remove IGFS: "' + selectedItem.name + '"?')
-                .then(function () {
-                    var _id = selectedItem._id;
+                .then(function() {
+                    const _id = selectedItem._id;
 
-                    $http.post('/api/v1/configuration/igfs/remove', {_id: _id})
-                        .success(function () {
+                    $http.post('/api/v1/configuration/igfs/remove', {_id})
+                        .success(function() {
                             $common.showInfo('IGFS has been removed: ' + selectedItem.name);
 
-                            var igfss = $scope.igfss;
+                            const igfss = $scope.igfss;
 
-                            var idx = _.findIndex(igfss, function (igfs) {
+                            const idx = _.findIndex(igfss, function(igfs) {
                                 return igfs._id === _id;
                             });
 
@@ -398,41 +368,43 @@ consoleModule.controller('igfsController', [
 
                                 if (igfss.length > 0)
                                     $scope.selectItem(igfss[0]);
-                                else
+                                else {
                                     $scope.backupItem = emptyIgfs;
+                                    $scope.ui.inputForm.$setPristine();
+                                }
                             }
                         })
-                        .error(function (errMsg) {
+                        .error(function(errMsg) {
                             $common.showError(errMsg);
                         });
                 });
         };
 
         // Remove all IGFS from db.
-        $scope.removeAllItems = function () {
+        $scope.removeAllItems = function() {
             $table.tableReset(); // TODO LEGACY
 
             $confirm.confirm('Are you sure you want to remove all IGFS?')
-                .then(function () {
+                .then(function() {
                     $http.post('/api/v1/configuration/igfs/remove/all')
-                        .success(function () {
+                        .success(function() {
                             $common.showInfo('All IGFS have been removed');
 
                             $scope.igfss = [];
                             $scope.backupItem = emptyIgfs;
                             $scope.ui.inputForm.$setPristine();
                         })
-                        .error(function (errMsg) {
+                        .error(function(errMsg) {
                             $common.showError(errMsg);
                         });
                 });
         };
 
-        $scope.resetAll = function () {
+        $scope.resetAll = function() {
             $table.tableReset(); // TODO LEGACY
 
             $confirm.confirm('Are you sure you want to undo all changes for current IGFS?')
-                .then(function () {
+                .then(function() {
                     $scope.backupItem = $scope.selectedItem ? angular.copy($scope.selectedItem) : prepareNewItem();
                     $scope.ui.inputForm.$setPristine();
                 });

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/controllers/profile-controller.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/controllers/profile-controller.js b/modules/web-console/src/main/js/controllers/profile-controller.js
index ffeffd4..39457af 100644
--- a/modules/web-console/src/main/js/controllers/profile-controller.js
+++ b/modules/web-console/src/main/js/controllers/profile-controller.js
@@ -19,66 +19,74 @@
 import consoleModule from 'controllers/common-module';
 
 consoleModule.controller('profileController', [
-    '$rootScope', '$scope', '$http', '$common', '$focus', '$confirm', 'IgniteCountries',
-    function ($root, $scope, $http, $common, $focus, $confirm, Countries) {
+    '$rootScope', '$scope', '$http', '$common', '$focus', '$confirm', 'IgniteCountries', 'User',
+    function($root, $scope, $http, $common, $focus, $confirm, Countries, User) {
         $scope.user = angular.copy($root.user);
 
         $scope.countries = Countries.getAll();
 
         $scope.generateToken = () => {
             $confirm.confirm('Are you sure you want to change security token?')
-                .then(() => $scope.user.token = $common.randomString(20))
+                .then(() => $scope.user.token = $common.randomString(20));
         };
 
-        const _cleanup = () => {
-            const _user = $scope.user;
-
-            if (!$scope.expandedToken)
-                _user.token = $root.user.token;
-
-            if (!$scope.expandedPassword) {
-                delete _user.password;
+        const _passwordValid = () => {
+            const cur = $scope.user;
 
-                delete _user.confirm;
-            }
+            return !$scope.expandedPassword || (cur.password && cur.confirm && cur.password === cur.confirm);
         };
 
         const _profileChanged = () => {
-            _cleanup();
-
             const old = $root.user;
             const cur = $scope.user;
 
             return !_.isEqual(old, cur);
         };
 
-        $scope.profileCouldBeSaved = () => _profileChanged() && $scope.profileForm && $scope.profileForm.$valid;
+        $scope.toggleToken = () => {
+            $scope.expandedToken = !$scope.expandedToken;
+
+            if (!$scope.expandedToken)
+                $scope.user.token = $root.user.token;
+        };
+
+        $scope.togglePassword = () => {
+            $scope.expandedPassword = !$scope.expandedPassword;
+
+            if (!$scope.expandedPassword) {
+                delete $scope.user.password;
+
+                delete $scope.user.confirm;
+            }
+        };
+
+        $scope.profileCouldBeSaved = () => _profileChanged() && $scope.profileForm && $scope.profileForm.$valid && _passwordValid();
 
         $scope.saveBtnTipText = () => {
             if (!_profileChanged())
                 return 'Nothing to save';
 
+            if (!_passwordValid())
+                return 'Invalid password';
+
             return $scope.profileForm && $scope.profileForm.$valid ? 'Save profile' : 'Invalid profile settings';
         };
 
         $scope.saveUser = () => {
-            _cleanup();
-
             $http.post('/api/v1/profile/save', $scope.user)
-                .success(() => {
-                    $scope.expandedPassword = false;
-
-                    _cleanup();
-
-                    $scope.expandedToken = false;
+                .then(User.read)
+                .then(() => {
+                    if ($scope.expandedPassword)
+                        $scope.togglePassword();
 
-                    $root.user = angular.copy($scope.user);
+                    if ($scope.expandedToken)
+                        $scope.toggleToken();
 
                     $common.showInfo('Profile saved.');
 
                     $focus('profile-username');
                 })
-                .error((err) => $common.showError('Failed to save profile: ' + $common.errorMessage(err)));
+                .catch((err) => $common.showError('Failed to save profile: ' + $common.errorMessage(err)));
         };
     }]
 );


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

Posted by ak...@apache.org.
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));
         });
 


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

Posted by ak...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/controllers/domains-controller.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/controllers/domains-controller.js b/modules/web-console/src/main/js/controllers/domains-controller.js
index 5483560..467dc98 100644
--- a/modules/web-console/src/main/js/controllers/domains-controller.js
+++ b/modules/web-console/src/main/js/controllers/domains-controller.js
@@ -20,14 +20,14 @@ import consoleModule from 'controllers/common-module';
 
 consoleModule.controller('domainsController', [
     '$rootScope', '$scope', '$http', '$state', '$filter', '$timeout', '$modal', '$common', '$focus', '$confirm', '$confirmBatch', '$clone', '$loading', '$cleanup', '$unsavedChangesGuard', 'IgniteAgentMonitor', '$table',
-    function ($root, $scope, $http, $state, $filter, $timeout, $modal, $common, $focus, $confirm, $confirmBatch, $clone, $loading, $cleanup, $unsavedChangesGuard, IgniteAgentMonitor, $table) {
+    function($root, $scope, $http, $state, $filter, $timeout, $modal, $common, $focus, $confirm, $confirmBatch, $clone, $loading, $cleanup, $unsavedChangesGuard, IgniteAgentMonitor, $table) {
         $unsavedChangesGuard.install($scope);
 
-        var emptyDomain = {empty: true};
+        const emptyDomain = {empty: true};
 
-        var __original_value;
+        let __original_value;
 
-        var blank = {};
+        const blank = {};
 
         // We need to initialize backupItem with empty object in order to properly used from angular directives.
         $scope.backupItem = emptyDomain;
@@ -36,8 +36,8 @@ consoleModule.controller('domainsController', [
         $scope.ui.activePanels = [0, 1];
         $scope.ui.topPanels = [0, 1, 2];
 
-        var IMPORT_DM_NEW_CACHE = 1;
-        var IMPORT_DM_ASSOCIATE_CACHE = 2;
+        const IMPORT_DM_NEW_CACHE = 1;
+        const IMPORT_DM_ASSOCIATE_CACHE = 2;
 
         /**
          * Convert some name to valid java package name.
@@ -46,7 +46,7 @@ consoleModule.controller('domainsController', [
          * @returns {string} Valid java package name.
          */
         const _toJavaPackage = (name) => {
-            return name ? name.replace(/[^A-Za-z_0-9/.]+/g, '_') : 'org'
+            return name ? name.replace(/[^A-Za-z_0-9/.]+/g, '_') : 'org';
         };
 
         $scope.ui.packageNameUserInput = $scope.ui.packageName =
@@ -57,17 +57,13 @@ consoleModule.controller('domainsController', [
         $scope.ui.generatedCachesClusters = [];
 
         function _mapCaches(caches) {
-            return _.map(caches, function (cache) {
-                return {
-                    label: cache.name,
-                    value: cache._id,
-                    cache: cache
-                }
+            return _.map(caches, (cache) => {
+                return {label: cache.name, value: cache._id, cache};
             });
         }
 
-        $scope.contentVisible = function () {
-            var item = $scope.backupItem;
+        $scope.contentVisible = function() {
+            const item = $scope.backupItem;
 
             return !item.empty && (!item._id || _.find($scope.displayedRows, {_id: item._id}));
         };
@@ -78,7 +74,7 @@ consoleModule.controller('domainsController', [
         $scope.widthIsSufficient = $common.widthIsSufficient;
         $scope.saveBtnTipText = $common.saveBtnTipText;
 
-        $scope.tableSave = function (field, index, stopEdit) {
+        $scope.tableSave = function(field, index, stopEdit) {
             if ($table.tableEditing({model: 'table-index-fields'}, $table.tableEditedRowIndex())) {
                 if ($scope.tableIndexItemSaveVisible(field, index))
                     return $scope.tableIndexItemSave(field, field.indexIdx, index, stopEdit);
@@ -103,57 +99,77 @@ consoleModule.controller('domainsController', [
                             return $scope.tableDbFieldSave(field, index, stopEdit);
 
                         break;
+
+                    default:
                 }
             }
 
             return true;
         };
 
-        $scope.tableReset = function (save) {
-            var field = $table.tableField();
+        $scope.tableReset = (trySave) => {
+            const field = $table.tableField();
 
-            if (!save || !$common.isDefined(field) || $scope.tableSave(field, $table.tableEditedRowIndex(), true)) {
-                $table.tableReset();
+            if (trySave && $common.isDefined(field) && !$scope.tableSave(field, $table.tableEditedRowIndex(), true))
+                return false;
 
-                return true;
-            }
+            $table.tableReset();
 
-            return false;
+            return true;
         };
 
-        $scope.tableNewItem = function (field) {
+        $scope.tableNewItem = function(field) {
             if ($scope.tableReset(true))
                 $table.tableNewItem(field);
         };
 
         $scope.tableNewItemActive = $table.tableNewItemActive;
 
-        $scope.tableStartEdit = function (item, field, index) {
+        $scope.tableStartEdit = function(item, field, index) {
             if ($scope.tableReset(true))
-                $table.tableStartEdit(item, field, index);
+                $table.tableStartEdit(item, field, index, $scope.tableSave);
         };
 
         $scope.tableEditing = $table.tableEditing;
 
-        $scope.tableRemove = function (item, field, index) {
+        $scope.tableRemove = function(item, field, index) {
             if ($scope.tableReset(true))
                 $table.tableRemove(item, field, index);
         };
 
-        $scope.tablePairStartEdit = $table.tablePairStartEdit;
         $scope.tablePairSave = $table.tablePairSave;
         $scope.tablePairSaveVisible = $table.tablePairSaveVisible;
 
+        $scope.queryFieldsTbl = {
+            type: 'fields',
+            model: 'fields',
+            focusId: 'QryField',
+            ui: 'table-pair',
+            keyName: 'name',
+            valueName: 'className',
+            save: $scope.tableSave
+        };
+
+        $scope.aliasesTbl = {
+            type: 'aliases',
+            model: 'aliases',
+            focusId: 'Alias',
+            ui: 'table-pair',
+            keyName: 'field',
+            valueName: 'alias',
+            save: $scope.tableSave
+        };
+
         $scope.queryMetadataVariants = $common.mkOptions(['Annotations', 'Configuration']);
 
-        var INFO_CONNECT_TO_DB = 'Configure connection to database';
-        var INFO_SELECT_SCHEMAS = 'Select schemas to load tables from';
-        var INFO_SELECT_TABLES = 'Select tables to import as domain model';
-        var INFO_SELECT_OPTIONS = 'Select import domain model options';
-        var LOADING_JDBC_DRIVERS = {text: 'Loading JDBC drivers...'};
-        var LOADING_SCHEMAS = {text: 'Loading schemas...'};
-        var LOADING_TABLES = {text: 'Loading tables...'};
-        var SAVING_DOMAINS = {text: 'Saving domain model...'};
+        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';
+        const INFO_SELECT_OPTIONS = 'Select import domain model options';
+        const LOADING_JDBC_DRIVERS = {text: 'Loading JDBC drivers...'};
+        const LOADING_SCHEMAS = {text: 'Loading schemas...'};
+        const LOADING_TABLES = {text: 'Loading tables...'};
+        const SAVING_DOMAINS = {text: 'Saving domain model...'};
 
         $scope.ui.invalidKeyFieldsTooltip = 'Found key types without configured key fields<br/>' +
             'It may be a result of import tables from database without primary keys<br/>' +
@@ -161,11 +177,11 @@ consoleModule.controller('domainsController', [
 
         $scope.hidePopover = $common.hidePopover;
 
-        var showPopoverMessage = $common.showPopoverMessage;
+        const showPopoverMessage = $common.showPopoverMessage;
 
         $scope.indexType = $common.mkOptions(['SORTED', 'FULLTEXT', 'GEOSPATIAL']);
 
-        var _dbPresets = [
+        const _dbPresets = [
             {
                 db: 'Oracle',
                 jdbcDriverClass: 'oracle.jdbc.OracleDriver',
@@ -224,10 +240,10 @@ consoleModule.controller('domainsController', [
 
         function _loadPresets() {
             try {
-                var restoredPresets = JSON.parse(localStorage.dbPresets);
+                const restoredPresets = JSON.parse(localStorage.dbPresets);
 
-                _.forEach(restoredPresets, function (restoredPreset) {
-                    var preset = _.find(_dbPresets, {jdbcDriverClass: restoredPreset.jdbcDriverClass});
+                _.forEach(restoredPresets, (restoredPreset) => {
+                    const preset = _.find(_dbPresets, {jdbcDriverClass: restoredPreset.jdbcDriverClass});
 
                     if (preset) {
                         preset.jdbcUrl = restoredPreset.jdbcUrl;
@@ -244,7 +260,7 @@ consoleModule.controller('domainsController', [
 
         function _savePreset(preset) {
             try {
-                var oldPreset = _.find(_dbPresets, {jdbcDriverClass: preset.jdbcDriverClass});
+                const oldPreset = _.find(_dbPresets, {jdbcDriverClass: preset.jdbcDriverClass});
 
                 if (oldPreset)
                     angular.extend(oldPreset, preset);
@@ -258,11 +274,25 @@ consoleModule.controller('domainsController', [
             }
         }
 
-        $scope.$watch('ui.selectedJdbcDriverJar', function (val) {
+        function _findPreset(selectedJdbcJar) {
+            let result = _.find(_dbPresets, function(preset) {
+                return preset.jdbcDriverClass === selectedJdbcJar.jdbcDriverClass;
+            });
+
+            if (!result)
+                result = {db: 'General', jdbcUrl: 'jdbc:[database]', user: 'admin'};
+
+            result.jdbcDriverJar = selectedJdbcJar.jdbcDriverJar;
+            result.jdbcDriverClass = selectedJdbcJar.jdbcDriverClass;
+
+            return result;
+        }
+
+        $scope.$watch('ui.selectedJdbcDriverJar', function(val) {
             if (val && !$scope.importDomain.demo) {
-                var foundPreset = _findPreset(val);
+                const foundPreset = _findPreset(val);
 
-                var selectedPreset = $scope.selectedPreset;
+                const selectedPreset = $scope.selectedPreset;
 
                 selectedPreset.db = foundPreset.db;
                 selectedPreset.jdbcDriverJar = foundPreset.jdbcDriverJar;
@@ -274,24 +304,6 @@ consoleModule.controller('domainsController', [
 
         $scope.ui.showValid = true;
 
-        function _findPreset(selectedJdbcJar) {
-            var result = _.find(_dbPresets, function (preset) {
-                return preset.jdbcDriverClass === selectedJdbcJar.jdbcDriverClass;
-            });
-
-            if (!result)
-                result = {
-                    db: 'General',
-                    jdbcUrl: 'jdbc:[database]',
-                    user: 'admin'
-                };
-
-            result.jdbcDriverJar = selectedJdbcJar.jdbcDriverJar;
-            result.jdbcDriverClass = selectedJdbcJar.jdbcDriverClass;
-
-            return result;
-        }
-
         $scope.supportedJdbcTypes = $common.mkOptions($common.SUPPORTED_JDBC_TYPES);
 
         $scope.supportedJavaTypes = $common.mkOptions($common.javaBuiltInTypes);
@@ -303,8 +315,8 @@ consoleModule.controller('domainsController', [
 
         $scope.domains = [];
 
-        $scope.isJavaBuiltInClass = function () {
-            var item = $scope.backupItem;
+        $scope.isJavaBuiltInClass = function() {
+            const item = $scope.backupItem;
 
             if (item && item.keyType)
                 return $common.isJavaBuiltInClass(item.keyType);
@@ -312,32 +324,28 @@ consoleModule.controller('domainsController', [
             return false;
         };
 
-        $scope.selectAllSchemas = function () {
-            var allSelected = $scope.importDomain.allSchemasSelected;
+        $scope.selectAllSchemas = function() {
+            const allSelected = $scope.importDomain.allSchemasSelected;
 
-            _.forEach($scope.importDomain.displayedSchemas, function (schema) {
-                schema.use = allSelected;
-            });
+            _.forEach($scope.importDomain.displayedSchemas, (schema) => schema.use = allSelected);
         };
 
-        $scope.selectSchema = function () {
+        $scope.selectSchema = function() {
             if ($common.isDefined($scope.importDomain) && $common.isDefined($scope.importDomain.displayedSchemas))
-                $scope.importDomain.allSchemasSelected = $scope.importDomain.displayedSchemas.length > 0 &&
-                    _.every($scope.importDomain.displayedSchemas, 'use', true);
+                $scope.importDomain.allSchemasSelected = $scope.importDomain.displayedSchemas.length > 0 && _.every($scope.importDomain.displayedSchemas, 'use', true);
         };
 
-        $scope.selectAllTables = function () {
-            var allSelected = $scope.importDomain.allTablesSelected;
+        $scope.selectAllTables = function() {
+            const allSelected = $scope.importDomain.allTablesSelected;
 
-            _.forEach($scope.importDomain.displayedTables, function (table) {
+            _.forEach($scope.importDomain.displayedTables, function(table) {
                 table.use = allSelected;
             });
         };
 
-        $scope.selectTable = function () {
+        $scope.selectTable = function() {
             if ($common.isDefined($scope.importDomain) && $common.isDefined($scope.importDomain.displayedTables))
-                $scope.importDomain.allTablesSelected = $scope.importDomain.displayedTables.length > 0 &&
-                    _.every($scope.importDomain.displayedTables, 'use', true);
+                $scope.importDomain.allTablesSelected = $scope.importDomain.displayedTables.length > 0 && _.every($scope.importDomain.displayedTables, 'use', true);
         };
 
         $scope.$watch('importDomain.displayedSchemas', $scope.selectSchema);
@@ -345,30 +353,41 @@ consoleModule.controller('domainsController', [
         $scope.$watch('importDomain.displayedTables', $scope.selectTable);
 
         // Pre-fetch modal dialogs.
-        var importDomainModal = $modal({scope: $scope, templateUrl: '/configuration/domains-import.html', show: false});
+        const importDomainModal = $modal({scope: $scope, templateUrl: '/configuration/domains-import.html', show: false});
 
-        var hideImportDomain = importDomainModal.hide;
+        const hideImportDomain = importDomainModal.hide;
 
-        importDomainModal.hide = function () {
+        importDomainModal.hide = function() {
             IgniteAgentMonitor.stopWatch();
 
             hideImportDomain();
         };
 
+        $scope.linkId = () => $scope.backupItem._id ? $scope.backupItem._id : 'create';
+
+        function prepareNewItem(cacheId) {
+            return {
+                space: $scope.spaces[0]._id,
+                caches: cacheId && _.find($scope.caches, {value: cacheId}) ? [cacheId] : // eslint-disable-line no-nested-ternary
+                    (_.isEmpty($scope.caches) ? [] : [$scope.caches[0].value]),
+                queryMetadata: 'Configuration'
+            };
+        }
+
         /**
          * Show import domain models modal.
          */
-        $scope.showImportDomainModal = function () {
+        $scope.showImportDomainModal = function() {
             $table.tableReset();
 
-            $common.confirmUnsavedChanges($scope.ui.inputForm.$dirty, function () {
+            $common.confirmUnsavedChanges($scope.ui.inputForm.$dirty, function() {
                 if ($scope.ui.inputForm.$dirty)
                     $scope.backupItem = $scope.selectedItem ? angular.copy($scope.selectedItem) : prepareNewItem();
 
                 const demo = $root.IgniteDemoMode;
 
                 $scope.importDomain = {
-                    demo: demo,
+                    demo,
                     action: demo ? 'connect' : 'drivers',
                     jdbcDriversNotFound: demo,
                     schemas: [],
@@ -381,10 +400,7 @@ consoleModule.controller('domainsController', [
 
                 $scope.importDomain.loadingOptions = LOADING_JDBC_DRIVERS;
 
-                IgniteAgentMonitor.startWatch({
-                        text: 'Back to Domain models',
-                        goal: 'import domain model from database'
-                    })
+                IgniteAgentMonitor.startWatch({text: 'Back to Domain models', goal: 'import domain model from database'})
                     .then(function() {
                         importDomainModal.$promise.then(importDomainModal.show);
 
@@ -408,7 +424,7 @@ consoleModule.controller('domainsController', [
                                 if (drivers && drivers.length > 0) {
                                     drivers = _.sortBy(drivers, 'jdbcDriverJar');
 
-                                    _.forEach(drivers, function (drv) {
+                                    _.forEach(drivers, function(drv) {
                                         $scope.jdbcDriverJars.push({
                                             label: drv.jdbcDriverJar,
                                             value: {
@@ -420,8 +436,8 @@ consoleModule.controller('domainsController', [
 
                                     $scope.ui.selectedJdbcDriverJar = $scope.jdbcDriverJars[0].value;
 
-                                    $common.confirmUnsavedChanges($scope.ui.inputForm.$dirty, function () {
-                                        importDomainModal.$promise.then(function () {
+                                    $common.confirmUnsavedChanges($scope.ui.inputForm.$dirty, function() {
+                                        importDomainModal.$promise.then(() => {
                                             $scope.importDomain.action = 'connect';
                                             $scope.importDomain.tables = [];
 
@@ -434,12 +450,12 @@ consoleModule.controller('domainsController', [
                                     $scope.importDomain.button = 'Cancel';
                                 }
                             })
-                            .finally(function () {
+                            .finally(function() {
                                 $scope.importDomain.info = INFO_CONNECT_TO_DB;
 
                                 $loading.finish('importDomainFromDb');
                             });
-                    })
+                    });
             });
         };
 
@@ -459,10 +475,10 @@ consoleModule.controller('domainsController', [
 
                     _savePreset(preset);
 
-                    return IgniteAgentMonitor.schemas(preset)
+                    return IgniteAgentMonitor.schemas(preset);
                 })
                 .then(function(schemas) {
-                    $scope.importDomain.schemas = _.map(schemas, function (schema) {
+                    $scope.importDomain.schemas = _.map(schemas, function(schema) {
                         return {use: true, name: schema};
                     });
 
@@ -476,12 +492,12 @@ consoleModule.controller('domainsController', [
                 .catch(function(errMsg) {
                     $common.showError(errMsg);
                 })
-                .finally(function () {
+                .finally(function() {
                     $loading.finish('importDomainFromDb');
                 });
         }
 
-        var DFLT_PARTITIONED_CACHE = {
+        const DFLT_PARTITIONED_CACHE = {
             label: 'PARTITIONED',
             value: -1,
             cache: {
@@ -493,7 +509,7 @@ consoleModule.controller('domainsController', [
             }
         };
 
-        var DFLT_REPLICATED_CACHE = {
+        const DFLT_REPLICATED_CACHE = {
             label: 'REPLICATED',
             value: -2,
             cache: {
@@ -505,8 +521,10 @@ consoleModule.controller('domainsController', [
             }
         };
 
+        let _importCachesOrTemplates = [];
+
         $scope.tableActionView = function(tbl) {
-            var cacheName = _.find(_importCachesOrTemplates, {value: tbl.cacheOrTemplate}).label;
+            const cacheName = _.find(_importCachesOrTemplates, {value: tbl.cacheOrTemplate}).label;
 
             if (tbl.action === IMPORT_DM_NEW_CACHE)
                 return 'Create ' + tbl.generatedCacheName + ' (' + cacheName + ')';
@@ -514,6 +532,61 @@ consoleModule.controller('domainsController', [
             return 'Associate with ' + cacheName;
         };
 
+        function toJavaClassName(name) {
+            const len = name.length;
+
+            let buf = '';
+
+            let capitalizeNext = true;
+
+            for (let i = 0; i < len; i++) {
+                const ch = name.charAt(i);
+
+                if (ch === ' ' || ch === '_')
+                    capitalizeNext = true;
+                else if (capitalizeNext) {
+                    buf += ch.toLocaleUpperCase();
+
+                    capitalizeNext = false;
+                }
+                else
+                    buf += ch.toLocaleLowerCase();
+            }
+
+            return buf;
+        }
+
+        function toJavaName(dbName) {
+            const javaName = toJavaClassName(dbName);
+
+            return javaName.charAt(0).toLocaleLowerCase() + javaName.slice(1);
+        }
+
+        function _fillCommonCachesOrTemplates(item) {
+            return function(action) {
+                if (item.cachesOrTemplates)
+                    item.cachesOrTemplates.length = 0;
+                else
+                    item.cachesOrTemplates = [];
+
+                if (action === IMPORT_DM_NEW_CACHE) {
+                    item.cachesOrTemplates.push(DFLT_PARTITIONED_CACHE);
+                    item.cachesOrTemplates.push(DFLT_REPLICATED_CACHE);
+                }
+
+                if (!_.isEmpty($scope.caches)) {
+                    if (item.cachesOrTemplates.length > 0)
+                        item.cachesOrTemplates.push(null);
+
+                    _.forEach($scope.caches, function(cache) {
+                        item.cachesOrTemplates.push(cache);
+                    });
+                }
+
+                if (!_.find(item.cachesOrTemplates, {value: item.cacheOrTemplate}))
+                    item.cacheOrTemplate = item.cachesOrTemplates[0].value;
+            };
+        }
         /**
          * Load list of database tables.
          */
@@ -525,30 +598,30 @@ consoleModule.controller('domainsController', [
 
                     $scope.importDomain.allTablesSelected = false;
 
-                    var preset = $scope.importDomain.demo ? $scope.demoConnection : $scope.selectedPreset;
+                    const preset = $scope.importDomain.demo ? $scope.demoConnection : $scope.selectedPreset;
 
                     preset.schemas = [];
 
-                    _.forEach($scope.importDomain.schemas, function (schema) {
+                    _.forEach($scope.importDomain.schemas, function(schema) {
                         if (schema.use)
                             preset.schemas.push(schema.name);
                     });
 
                     return IgniteAgentMonitor.tables(preset);
                 })
-                .then(function (tables) {
+                .then(function(tables) {
                     _importCachesOrTemplates = [DFLT_PARTITIONED_CACHE, DFLT_REPLICATED_CACHE].concat($scope.caches);
 
                     _fillCommonCachesOrTemplates($scope.importCommon)($scope.importCommon.action);
 
-                    _.forEach(tables, function (tbl, idx) {
+                    _.forEach(tables, function(tbl, idx) {
                         tbl.id = idx;
                         tbl.action = IMPORT_DM_NEW_CACHE;
                         tbl.generatedCacheName = toJavaClassName(tbl.tbl) + 'Cache';
                         tbl.cacheOrTemplate = DFLT_PARTITIONED_CACHE.value;
                         tbl.label = tbl.schema + '.' + tbl.tbl;
                         tbl.edit = false;
-                        tbl.use = $common.isDefined(_.find(tbl.cols, function (col) {
+                        tbl.use = $common.isDefined(_.find(tbl.cols, function(col) {
                             return col.key;
                         }));
                     });
@@ -560,13 +633,13 @@ consoleModule.controller('domainsController', [
                 .catch(function(errMsg) {
                     $common.showError(errMsg);
                 })
-                .finally(function () {
+                .finally(function() {
                     $loading.finish('importDomainFromDb');
                 });
         }
 
-        $scope.applyDefaults = function () {
-            _.forEach($scope.importDomain.displayedTables, function (table) {
+        $scope.applyDefaults = function() {
+            _.forEach($scope.importDomain.displayedTables, function(table) {
                 table.edit = false;
                 table.action = $scope.importCommon.action;
                 table.cacheOrTemplate = $scope.importCommon.cacheOrTemplate;
@@ -575,7 +648,7 @@ consoleModule.controller('domainsController', [
 
         $scope._curDbTable = null;
 
-        $scope.startEditDbTableCache = function (tbl) {
+        $scope.startEditDbTableCache = function(tbl) {
             if ($scope._curDbTable) {
                 $scope._curDbTable.edit = false;
 
@@ -588,7 +661,7 @@ consoleModule.controller('domainsController', [
 
             $scope._curDbTable = tbl;
 
-            var _fillFn = _fillCommonCachesOrTemplates($scope._curDbTable);
+            const _fillFn = _fillCommonCachesOrTemplates($scope._curDbTable);
 
             _fillFn($scope._curDbTable.action);
 
@@ -604,36 +677,8 @@ consoleModule.controller('domainsController', [
             $scope.importDomain.action = 'options';
             $scope.importDomain.button = 'Save';
             $scope.importDomain.info = INFO_SELECT_OPTIONS;
-        }
-
-        function toJavaClassName(name) {
-            var len = name.length;
-
-            var buf = '';
-
-            var capitalizeNext = true;
 
-            for (var i = 0; i < len; i++) {
-                var ch = name.charAt(i);
-
-                if (ch === ' ' || ch === '_')
-                    capitalizeNext = true;
-                else if (capitalizeNext) {
-                    buf += ch.toLocaleUpperCase();
-
-                    capitalizeNext = false;
-                }
-                else
-                    buf += ch.toLocaleLowerCase();
-            }
-
-            return buf;
-        }
-
-        function toJavaName(dbName) {
-            var javaName = toJavaClassName(dbName);
-
-            return javaName.charAt(0).toLocaleLowerCase() + javaName.slice(1);
+            $focus('domainPackageName');
         }
 
         function _saveBatch(batch) {
@@ -642,16 +687,16 @@ consoleModule.controller('domainsController', [
                 $loading.start('importDomainFromDb');
 
                 $http.post('/api/v1/configuration/domains/save/batch', batch)
-                    .success(function (savedBatch) {
-                        var lastItem;
-                        var newItems = [];
+                    .success(function(savedBatch) {
+                        let lastItem;
+                        const newItems = [];
 
                         _.forEach(_mapCaches(savedBatch.generatedCaches), function(cache) {
                             $scope.caches.push(cache);
                         });
 
-                        _.forEach(savedBatch.savedDomains, function (savedItem) {
-                            var idx = _.findIndex($scope.domains, function (domain) {
+                        _.forEach(savedBatch.savedDomains, function(savedItem) {
+                            const idx = _.findIndex($scope.domains, function(domain) {
                                 return domain._id === savedItem._id;
                             });
 
@@ -663,7 +708,7 @@ consoleModule.controller('domainsController', [
                             lastItem = savedItem;
                         });
 
-                        _.forEach(newItems, function (item) {
+                        _.forEach(newItems, function(item) {
                             $scope.domains.push(item);
                         });
 
@@ -678,10 +723,10 @@ consoleModule.controller('domainsController', [
 
                         $scope.ui.showValid = true;
                     })
-                    .error(function (errMsg) {
+                    .error(function(errMsg) {
                         $common.showError(errMsg);
                     })
-                    .finally(function () {
+                    .finally(function() {
                         $loading.finish('importDomainFromDb');
 
                         importDomainModal.hide();
@@ -693,18 +738,17 @@ consoleModule.controller('domainsController', [
 
         function _saveDomainModel() {
             if ($common.isEmptyString($scope.ui.packageName))
-                return $common.showPopoverMessage(undefined, undefined, 'domainPackageName',
-                    'Package should be not empty');
+                return $common.showPopoverMessage(null, null, 'domainPackageName', 'Package could not be empty');
 
             if (!$common.isValidJavaClass('Package', $scope.ui.packageName, false, 'domainPackageName', true))
                 return false;
 
-            var batch = [];
-            var tables = [];
-            var checkedCaches = [];
-            var dupCnt = 0;
+            const batch = [];
+            const tables = [];
+            const checkedCaches = [];
 
-            var containKey = true;
+            let dupCnt = 0;
+            let containKey = true;
 
             function queryField(name, jdbcType) {
                 return {name: toJavaName(name), className: jdbcType.javaType};
@@ -712,7 +756,7 @@ consoleModule.controller('domainsController', [
 
             function dbField(name, jdbcType, nullable) {
                 return {
-                    jdbcType: jdbcType,
+                    jdbcType,
                     databaseFieldName: name,
                     databaseFieldType: jdbcType.dbName,
                     javaFieldName: toJavaName(name),
@@ -721,34 +765,34 @@ consoleModule.controller('domainsController', [
                 };
             }
 
-            _.forEach($scope.importDomain.tables, function (table) {
+            _.forEach($scope.importDomain.tables, function(table) {
                 if (table.use) {
-                    var qryFields = [];
-                    var indexes = [];
-                    var keyFields = [];
-                    var valFields = [];
-                    var aliases = [];
+                    const qryFields = [];
+                    const indexes = [];
+                    const keyFields = [];
+                    const valFields = [];
+                    const aliases = [];
 
-                    var tableName = table.tbl;
+                    const tableName = table.tbl;
 
-                    var dup = tables.indexOf(tableName) >= 0;
+                    const dup = tables.indexOf(tableName) >= 0;
 
                     if (dup)
                         dupCnt++;
 
-                    var typeName = toJavaClassName(tableName);
-                    var valType = _toJavaPackage($scope.ui.packageName) + '.' + typeName;
+                    const typeName = toJavaClassName(tableName);
+                    const valType = _toJavaPackage($scope.ui.packageName) + '.' + typeName;
 
-                    var _containKey = false;
+                    let _containKey = false;
 
-                    _.forEach(table.cols, function (col) {
-                        var colName = col.name;
-                        var jdbcType = $common.findJdbcType(col.type);
-                        var nullable = col.nullable;
+                    _.forEach(table.cols, function(col) {
+                        const colName = col.name;
+                        const jdbcType = $common.findJdbcType(col.type);
+                        const nullable = col.nullable;
 
                         qryFields.push(queryField(colName, jdbcType));
 
-                        var fld = dbField(colName, jdbcType, nullable);
+                        const fld = dbField(colName, jdbcType, nullable);
 
                         if ($scope.ui.generateAliases && !_.find(aliases, {field: fld.javaFieldName}) &&
                             fld.javaFieldName.toUpperCase() !== fld.databaseFieldName.toUpperCase())
@@ -766,11 +810,11 @@ consoleModule.controller('domainsController', [
                     containKey &= _containKey;
 
                     if (table.idxs) {
-                        _.forEach(table.idxs, function (idx) {
-                            var fields = Object.keys(idx.fields);
+                        _.forEach(table.idxs, function(idx) {
+                            const fields = Object.keys(idx.fields);
 
                             indexes.push({
-                                name: idx.name, indexType: 'SORTED', fields: _.map(fields, function (fieldName) {
+                                name: idx.name, indexType: 'SORTED', fields: _.map(fields, function(fieldName) {
                                     return {
                                         name: toJavaName(fieldName),
                                         direction: idx.fields[fieldName]
@@ -780,11 +824,11 @@ consoleModule.controller('domainsController', [
                         });
                     }
 
-                    var domainFound = _.find($scope.domains, function (domain) {
+                    const domainFound = _.find($scope.domains, function(domain) {
                         return domain.valueType === valType;
                     });
 
-                    var newDomain = {
+                    const newDomain = {
                         confirm: false,
                         skip: false,
                         space: $scope.spaces[0],
@@ -797,7 +841,7 @@ consoleModule.controller('domainsController', [
                         newDomain.confirm = true;
                     }
 
-                    var dupSfx = (dup ? '_' + dupCnt : '');
+                    const dupSfx = (dup ? '_' + dupCnt : '');
 
                     newDomain.keyType = valType + 'Key' + dupSfx;
                     newDomain.valueType = valType + dupSfx;
@@ -816,19 +860,19 @@ consoleModule.controller('domainsController', [
 
                     // Use Java built-in type for key.
                     if ($scope.ui.builtinKeys && newDomain.keyFields.length === 1) {
-                        var keyField = newDomain.keyFields[0];
+                        const keyField = newDomain.keyFields[0];
 
                         newDomain.keyType = keyField.jdbcType.javaType;
 
                         // Exclude key column from query fields and indexes.
-                        newDomain.fields = _.filter(newDomain.fields, function (field) {
-                            return field.name != keyField.javaFieldName;
+                        newDomain.fields = _.filter(newDomain.fields, function(field) {
+                            return field.name !== keyField.javaFieldName;
                         });
 
-                        _.forEach(newDomain.indexes, function (index) {
-                            index.fields = _.filter(index.fields, function (field) {
+                        _.forEach(newDomain.indexes, function(index) {
+                            index.fields = _.filter(index.fields, function(field) {
                                 return field.name !== keyField.javaFieldName;
-                            })
+                            });
                         });
 
                         newDomain.indexes = _.filter(newDomain.indexes, (index) => !_.isEmpty(index.fields));
@@ -836,9 +880,9 @@ consoleModule.controller('domainsController', [
 
                     // Prepare caches for generation.
                     if (table.action === IMPORT_DM_NEW_CACHE) {
-                        var template = _.find(_importCachesOrTemplates, {value: table.cacheOrTemplate});
+                        const template = _.find(_importCachesOrTemplates, {value: table.cacheOrTemplate});
 
-                        var newCache = angular.copy(template.cache);
+                        const newCache = angular.copy(template.cache);
 
                         newDomain.newCache = newCache;
 
@@ -847,17 +891,10 @@ consoleModule.controller('domainsController', [
                         newCache.clusters = $scope.ui.generatedCachesClusters;
 
                         // POJO store factory is not defined in template.
-                        if (!newCache.cacheStoreFactory ||
-                            newCache.cacheStoreFactory.kind !== 'CacheJdbcPojoStoreFactory') {
-                            var dialect = $scope.importDomain.demo ? 'H2' : $scope.selectedPreset.db;
-
-                            newCache.cacheStoreFactory = {
-                                kind: 'CacheJdbcPojoStoreFactory',
-                                CacheJdbcPojoStoreFactory: {
-                                    dataSourceBean: 'ds' + dialect,
-                                    dialect: dialect
-                                }
-                            };
+                        if (!newCache.cacheStoreFactory || newCache.cacheStoreFactory.kind !== 'CacheJdbcPojoStoreFactory') {
+                            const dialect = $scope.importDomain.demo ? 'H2' : $scope.selectedPreset.db;
+
+                            newCache.cacheStoreFactory = {kind: 'CacheJdbcPojoStoreFactory', CacheJdbcPojoStoreFactory: {dataSourceBean: 'ds' + dialect, dialect}};
                         }
 
                         if (!newCache.readThrough && !newCache.writeThrough) {
@@ -866,19 +903,19 @@ consoleModule.controller('domainsController', [
                         }
                     }
                     else {
-                        var cacheId = table.cacheOrTemplate;
+                        const cacheId = table.cacheOrTemplate;
 
                         newDomain.caches = [cacheId];
 
                         if (!_.includes(checkedCaches, cacheId)) {
-                            var cache = _.find($scope.caches, {value: cacheId}).cache;
+                            const cache = _.find($scope.caches, {value: cacheId}).cache;
 
-                            var change = $common.autoCacheStoreConfiguration(cache, [newDomain]);
+                            const change = $common.autoCacheStoreConfiguration(cache, [newDomain]);
 
                             if (change)
-                                newDomain.cacheStoreChanges = [{cacheId: cacheId, change: change}];
+                                newDomain.cacheStoreChanges = [{cacheId, change}];
 
-                            checkedCaches.push(cacheId)
+                            checkedCaches.push(cacheId);
                         }
                     }
 
@@ -900,40 +937,32 @@ consoleModule.controller('domainsController', [
                     '</span>';
             }
 
-            var itemsToConfirm = _.filter(batch, function (item) {
-                return item.confirm;
-            });
+            const itemsToConfirm = _.filter(batch, (item) => item.confirm);
 
             function checkOverwrite() {
-                if (itemsToConfirm.length > 0)
+                if (itemsToConfirm.length > 0) {
                     $confirmBatch.confirm(overwriteMessage, itemsToConfirm)
-                        .then(function () {
-                            _saveBatch(_.filter(batch, function (item) {
-                                return !item.skip;
-                            }));
-                        })
-                        .catch(function () {
-                            $common.showError('Importing of domain models interrupted by user.');
-                        });
+                        .then(() => _saveBatch(_.filter(batch, (item) => !item.skip)))
+                        .catch(() => $common.showError('Importing of domain models interrupted by user.'));
+                }
                 else
                     _saveBatch(batch);
             }
 
             if (containKey)
                 checkOverwrite();
-            else
+            else {
                 $confirm.confirm('Some tables have no primary key.<br/>' +
                         'You will need to configure key type and key fields for such tables after import complete.')
-                    .then(function () {
-                        checkOverwrite();
-                    });
+                    .then(() => checkOverwrite());
+            }
         }
 
-        $scope.importDomainNext = function () {
+        $scope.importDomainNext = function() {
             if (!$scope.importDomainNextAvailable())
                 return;
 
-            var act = $scope.importDomain.action;
+            const act = $scope.importDomain.action;
 
             if (act === 'drivers' && $scope.importDomain.jdbcDriversNotFound)
                 importDomainModal.hide();
@@ -947,10 +976,10 @@ consoleModule.controller('domainsController', [
                 _saveDomainModel();
         };
 
-        $scope.nextTooltipText = function () {
-            var importDomainNextAvailable = $scope.importDomainNextAvailable();
+        $scope.nextTooltipText = function() {
+            const importDomainNextAvailable = $scope.importDomainNextAvailable();
 
-            var act = $scope.importDomain.action;
+            const act = $scope.importDomain.action;
 
             if (act === 'drivers' && $scope.importDomain.jdbcDriversNotFound)
                 return 'Resolve issue with JDBC drivers<br>Close this dialog and try again';
@@ -970,8 +999,8 @@ consoleModule.controller('domainsController', [
             return 'Click to continue';
         };
 
-        $scope.prevTooltipText = function () {
-            var act = $scope.importDomain.action;
+        $scope.prevTooltipText = function() {
+            const act = $scope.importDomain.action;
 
             if (act === 'schemas')
                 return $scope.importDomain.demo ? 'Click to return on demo description step' : 'Click to return on connection configuration step';
@@ -983,8 +1012,8 @@ consoleModule.controller('domainsController', [
                 return 'Click to return on tables selection step';
         };
 
-        $scope.importDomainNextAvailable = function () {
-            var res = true;
+        $scope.importDomainNextAvailable = function() {
+            let res = true;
 
             switch ($scope.importDomain.action) {
                 case 'schemas':
@@ -996,12 +1025,14 @@ consoleModule.controller('domainsController', [
                     res = _.find($scope.importDomain.tables, {use: true});
 
                     break;
+
+                default:
             }
 
             return res;
         };
 
-        $scope.importDomainPrev = function () {
+        $scope.importDomainPrev = function() {
             $scope.importDomain.button = 'Next';
 
             if ($scope.importDomain.action === 'options') {
@@ -1018,7 +1049,7 @@ consoleModule.controller('domainsController', [
             }
         };
 
-        $scope.domainModelTitle = function () {
+        $scope.domainModelTitle = function() {
             return $scope.ui.showValid ? 'Domain model types:' : 'Domain model types without key fields:';
         };
 
@@ -1035,41 +1066,13 @@ consoleModule.controller('domainsController', [
 
         $scope.importCommon = {};
 
-        function _fillCommonCachesOrTemplates(item) {
-            return function (action) {
-                if (item.cachesOrTemplates)
-                    item.cachesOrTemplates.length = 0;
-                else
-                    item.cachesOrTemplates = [];
-
-                if (action == IMPORT_DM_NEW_CACHE) {
-                    item.cachesOrTemplates.push(DFLT_PARTITIONED_CACHE);
-                    item.cachesOrTemplates.push(DFLT_REPLICATED_CACHE);
-                }
-
-                if (!_.isEmpty($scope.caches)) {
-                    if (item.cachesOrTemplates.length > 0)
-                        item.cachesOrTemplates.push(null);
-
-                    _.forEach($scope.caches, function (cache) {
-                        item.cachesOrTemplates.push(cache);
-                    });
-                }
-
-                if (!_.find(item.cachesOrTemplates, {value: item.cacheOrTemplate}))
-                    item.cacheOrTemplate = item.cachesOrTemplates[0].value;
-            }
-        }
-
         // When landing on the page, get domain models and show them.
         $loading.start('loadingDomainModelsScreen');
 
-        var _importCachesOrTemplates = [];
-
         $http.post('/api/v1/configuration/domains/list')
-            .success(function (data) {
+            .success(function(data) {
                 $scope.spaces = data.spaces;
-                $scope.clusters = _.map(data.clusters, function (cluster) {
+                $scope.clusters = _.map(data.clusters, function(cluster) {
                     return {
                         value: cluster._id,
                         label: cluster.name
@@ -1078,28 +1081,29 @@ consoleModule.controller('domainsController', [
                 $scope.caches = _mapCaches(data.caches);
                 $scope.domains = data.domains;
 
-                _.forEach($scope.clusters, function (cluster) {
+                _.forEach($scope.clusters, function(cluster) {
                     $scope.ui.generatedCachesClusters.push(cluster.value);
                 });
 
-                if (!_.isEmpty($scope.caches))
+                if (!_.isEmpty($scope.caches)) {
                     $scope.importActions.push({
                         label: 'Associate with existing cache',
                         shortLabel: 'Associate',
                         value: IMPORT_DM_ASSOCIATE_CACHE
                     });
+                }
 
                 $scope.$watch('importCommon.action', _fillCommonCachesOrTemplates($scope.importCommon), true);
 
                 $scope.importCommon.action = IMPORT_DM_NEW_CACHE;
 
-                if ($state.params.id)
-                    $scope.createItem($state.params.id);
+                if ($state.params.linkId)
+                    $scope.createItem($state.params.linkId);
                 else {
-                    var lastSelectedDomain = angular.fromJson(sessionStorage.lastSelectedDomain);
+                    const lastSelectedDomain = angular.fromJson(sessionStorage.lastSelectedDomain);
 
                     if (lastSelectedDomain) {
-                        var idx = _.findIndex($scope.domains, function (domain) {
+                        const idx = _.findIndex($scope.domains, function(domain) {
                             return domain._id === lastSelectedDomain;
                         });
 
@@ -1116,24 +1120,23 @@ consoleModule.controller('domainsController', [
                 }
 
                 $scope.$watch('ui.inputForm.$valid', function(valid) {
-                    if (valid && __original_value === JSON.stringify($cleanup($scope.backupItem))) {
+                    if (valid && _.isEqual(__original_value, $cleanup($scope.backupItem)))
                         $scope.ui.inputForm.$dirty = false;
-                    }
                 });
 
-                $scope.$watch('backupItem', function (val) {
-                    var form = $scope.ui.inputForm;
+                $scope.$watch('backupItem', function(val) {
+                    const form = $scope.ui.inputForm;
 
-                    if (form.$pristine || (form.$valid && __original_value === JSON.stringify($cleanup(val))))
+                    if (form.$pristine || (form.$valid && _.isEqual(__original_value, $cleanup(val))))
                         form.$setPristine();
                     else
                         form.$setDirty();
                 }, true);
             })
-            .catch(function (errMsg) {
+            .catch(function(errMsg) {
                 $common.showError(errMsg);
             })
-            .finally(function () {
+            .finally(function() {
                 $scope.ui.ready = true;
                 $scope.ui.inputForm.$setPristine();
                 $loading.finish('loadingDomainModelsScreen');
@@ -1143,12 +1146,12 @@ consoleModule.controller('domainsController', [
             ngFormCtrl.$defaults = {};
 
             _.forOwn(ngFormCtrl, (value, key) => {
-                if(value && key !== '$$parentForm' && value.constructor.name === 'FormController') 
-                    clearFormDefaults(value)
+                if (value && key !== '$$parentForm' && value.constructor.name === 'FormController')
+                    clearFormDefaults(value);
             });
         };
 
-        $scope.selectItem = function (item, backup) {
+        $scope.selectItem = function(item, backup) {
             function selectItem() {
                 clearFormDefaults($scope.ui.inputForm);
 
@@ -1175,7 +1178,7 @@ consoleModule.controller('domainsController', [
 
                 $scope.backupItem = angular.merge({}, blank, $scope.backupItem);
 
-                __original_value = JSON.stringify($cleanup($scope.backupItem));
+                __original_value = $cleanup($scope.backupItem);
 
                 if ($common.isDefined($scope.backupItem) && !$common.isDefined($scope.backupItem.queryMetadata))
                     $scope.backupItem.queryMetadata = 'Configuration';
@@ -1190,91 +1193,38 @@ consoleModule.controller('domainsController', [
             $common.confirmUnsavedChanges($scope.ui.inputForm.$dirty, selectItem);
         };
 
-        function prepareNewItem(cacheId) {
-            return {
-                space: $scope.spaces[0]._id,
-                caches: cacheId && _.find($scope.caches, {value: cacheId}) ? [cacheId] :
-                    (!_.isEmpty($scope.caches) ? [$scope.caches[0].value] : []),
-                queryMetadata: 'Configuration'
-            };
-        }
-
         // Add new domain model.
-        $scope.createItem = function (cacheId) {
+        $scope.createItem = function(cacheId) {
             if ($scope.tableReset(true)) {
-                $timeout(function () {
+                $timeout(() => {
                     $common.ensureActivePanel($scope.ui, 'query');
                     $common.ensureActivePanel($scope.ui, 'general', 'keyType');
                 });
 
-                $scope.selectItem(undefined, prepareNewItem(cacheId));
+                $scope.selectItem(null, prepareNewItem(cacheId));
             }
         };
 
-        // Check domain model logical consistency.
-        function validate(item) {
-            var form = $scope.ui.inputForm;
-            var errors = form.$error;
-            var errKeys = Object.keys(errors);
-
-            if (errKeys && errKeys.length > 0) {
-                var firstErrorKey = errKeys[0];
-
-                var firstError = errors[firstErrorKey][0];
-                var actualError = firstError.$error[firstErrorKey][0];
-
-                var errNameFull = actualError.$name;
-                var errNameShort = errNameFull;
-
-                if (errNameShort.endsWith('TextInput'))
-                    errNameShort = errNameShort.substring(0, errNameShort.length - 9);
-
-                var extractErrorMessage = function (errName) {
-                    try {
-                        return errors[firstErrorKey][0].$errorMessages[errName][firstErrorKey];
-                    }
-                    catch(ignored) {
-                        try {
-                            msg = form[firstError.$name].$errorMessages[errName][firstErrorKey];
-                        }
-                        catch(ignited) {
-                            return false;
-                        }
-                    }
-                };
-
-                var msg = extractErrorMessage(errNameFull) || extractErrorMessage(errNameShort) || 'Invalid value!';
-
-                return showPopoverMessage($scope.ui, firstError.$name, errNameFull, msg);
-            }
-
-            if ($common.isEmptyString(item.keyType))
-                return showPopoverMessage($scope.ui, 'general', 'keyType', 'Key type should not be empty');
-            else if (!$common.isValidJavaClass('Key type', item.keyType, true, 'keyType', false, $scope.ui, 'general'))
-                return false;
-
-            if ($common.isEmptyString(item.valueType))
-                return showPopoverMessage($scope.ui, 'general', 'valueType', 'Value type should not be empty');
-            else if (!$common.isValidJavaClass('Value type', item.valueType, false, 'valueType', false, $scope.ui, 'general'))
-                return false;
-
-            var qry = $common.domainForQueryConfigured(item);
-
-            if (item.queryMetadata === 'Configuration' && qry) {
+        function checkQueryConfiguration(item) {
+            if (item.queryMetadata === 'Configuration' && $common.domainForQueryConfigured(item)) {
                 if (_.isEmpty(item.fields))
                     return showPopoverMessage($scope.ui, 'query', 'queryFields', 'Query fields should not be empty');
 
-                var indexes = item.indexes;
+                const indexes = item.indexes;
 
                 if (indexes && indexes.length > 0) {
-                    if (_.find(indexes, function (index, i) {
-                            if (_.isEmpty(index.fields))
-                                return !showPopoverMessage($scope.ui, 'query', 'indexes' + i, 'Index fields are not specified');
-                        }))
+                    if (_.find(indexes, function(index, i) {
+                        if (_.isEmpty(index.fields))
+                            return !showPopoverMessage($scope.ui, 'query', 'indexes' + i, 'Index fields are not specified');
+                    }))
                         return false;
                 }
             }
 
+            return true;
+        }
+
+        function checkStoreConfiguration(item) {
             if ($common.domainForStoreConfigured(item)) {
                 if ($common.isEmptyString(item.databaseSchema))
                     return showPopoverMessage($scope.ui, 'store', 'databaseSchema', 'Database schema should not be empty');
@@ -1291,17 +1241,39 @@ consoleModule.controller('domainsController', [
                 if (_.isEmpty(item.valueFields))
                     return showPopoverMessage($scope.ui, 'store', 'valueFields', 'Value fields are not specified');
             }
-            else if (!qry && item.queryMetadata === 'Configuration') {
+
+            return true;
+        }
+
+        // Check domain model logical consistency.
+        function validate(item) {
+            if (!$common.checkFieldValidators($scope.ui))
+                return false;
+
+            if (!checkQueryConfiguration(item))
+                return false;
+
+            if (!checkStoreConfiguration(item))
+                return false;
+
+            if (!$common.domainForStoreConfigured(item) && !$common.domainForQueryConfigured(item) && item.queryMetadata === 'Configuration')
                 return showPopoverMessage($scope.ui, 'query', 'query-title', 'SQL query domain model should be configured');
-            }
 
             return true;
         }
 
+        function _checkShowValidPresentation() {
+            if (!$scope.ui.showValid) {
+                const validFilter = $filter('domainsValidation');
+
+                $scope.ui.showValid = validFilter($scope.domains, false, true).length === 0;
+            }
+        }
+
         // Save domain models into database.
         function save(item) {
-            var qry = $common.domainForQueryConfigured(item);
-            var str = $common.domainForStoreConfigured(item);
+            const qry = $common.domainForQueryConfigured(item);
+            const str = $common.domainForStoreConfigured(item);
 
             item.kind = 'query';
 
@@ -1311,12 +1283,12 @@ consoleModule.controller('domainsController', [
                 item.kind = 'store';
 
             $http.post('/api/v1/configuration/domains/save', item)
-                .success(function (res) {
+                .success(function(res) {
                     $scope.ui.inputForm.$setPristine();
 
-                    var savedMeta = res.savedDomains[0];
+                    const savedMeta = res.savedDomains[0];
 
-                    var idx = _.findIndex($scope.domains, function (domain) {
+                    const idx = _.findIndex($scope.domains, function(domain) {
                         return domain._id === savedMeta._id;
                     });
 
@@ -1325,31 +1297,38 @@ consoleModule.controller('domainsController', [
                     else
                         $scope.domains.push(savedMeta);
 
+                    _.forEach($scope.caches, (cache) => {
+                        if (_.includes(item.caches, cache.value))
+                            cache.cache.domains = _.union(cache.cache.domains, [savedMeta._id]);
+                        else
+                            _.remove(cache.cache.domains, (id) => id === savedMeta._id);
+                    });
+
                     $scope.selectItem(savedMeta);
 
                     $common.showInfo('Domain model "' + item.valueType + '" saved.');
 
                     _checkShowValidPresentation();
                 })
-                .error(function (errMsg) {
+                .error(function(errMsg) {
                     $common.showError(errMsg);
                 });
         }
 
         // Save domain model.
-        $scope.saveItem = function () {
+        $scope.saveItem = function() {
             if ($scope.tableReset(true)) {
-                var item = $scope.backupItem;
+                const item = $scope.backupItem;
 
                 item.cacheStoreChanges = [];
 
-                _.forEach(item.caches, function (cacheId) {
-                    var cache = _.find($scope.caches, {value: cacheId}).cache;
+                _.forEach(item.caches, function(cacheId) {
+                    const cache = _.find($scope.caches, {value: cacheId}).cache;
 
-                    var change = $common.autoCacheStoreConfiguration(cache, [item]);
+                    const change = $common.autoCacheStoreConfiguration(cache, [item]);
 
                     if (change)
-                        item.cacheStoreChanges.push({cacheId: cacheId, change: change});
+                        item.cacheStoreChanges.push({cacheId, change});
                 });
 
                 if (validate(item))
@@ -1358,7 +1337,7 @@ consoleModule.controller('domainsController', [
         };
 
         function _domainNames() {
-            return _.map($scope.domains, function (domain) {
+            return _.map($scope.domains, function(domain) {
                 return domain.valueType;
             });
         }
@@ -1368,10 +1347,10 @@ consoleModule.controller('domainsController', [
         }
 
         // Save domain model with new name.
-        $scope.cloneItem = function () {
+        $scope.cloneItem = function() {
             if ($scope.tableReset(true) && validate($scope.backupItem)) {
-                $clone.confirm($scope.backupItem.valueType, _domainNames(), _newNameIsValidJavaClass).then(function (newName) {
-                    var item = angular.copy($scope.backupItem);
+                $clone.confirm($scope.backupItem.valueType, _domainNames(), _newNameIsValidJavaClass).then(function(newName) {
+                    const item = angular.copy($scope.backupItem);
 
                     delete item._id;
                     item.valueType = newName;
@@ -1382,22 +1361,22 @@ consoleModule.controller('domainsController', [
         };
 
         // Remove domain model from db.
-        $scope.removeItem = function () {
+        $scope.removeItem = function() {
             $table.tableReset();
 
-            var selectedItem = $scope.selectedItem;
+            const selectedItem = $scope.selectedItem;
 
             $confirm.confirm('Are you sure you want to remove domain model: "' + selectedItem.valueType + '"?')
-                .then(function () {
-                    var _id = selectedItem._id;
+                .then(function() {
+                    const _id = selectedItem._id;
 
-                    $http.post('/api/v1/configuration/domains/remove', {_id: _id})
-                        .success(function () {
+                    $http.post('/api/v1/configuration/domains/remove', {_id})
+                        .success(function() {
                             $common.showInfo('Domain model has been removed: ' + selectedItem.valueType);
 
-                            var domains = $scope.domains;
+                            const domains = $scope.domains;
 
-                            var idx = _.findIndex(domains, function (domain) {
+                            const idx = _.findIndex(domains, function(domain) {
                                 return domain._id === _id;
                             });
 
@@ -1406,56 +1385,55 @@ consoleModule.controller('domainsController', [
 
                                 if (domains.length > 0)
                                     $scope.selectItem(domains[0]);
-                                else
+                                else {
                                     $scope.backupItem = emptyDomain;
+                                    $scope.ui.inputForm.$setPristine();
+                                }
+
+                                _.forEach($scope.caches, (cache) => _.remove(cache.cache.domains, (id) => id === _id));
                             }
 
                             _checkShowValidPresentation();
                         })
-                        .error(function (errMsg) {
+                        .error(function(errMsg) {
                             $common.showError(errMsg);
                         });
                 });
         };
 
-        function _checkShowValidPresentation() {
-            if (!$scope.ui.showValid) {
-                var validFilter = $filter('domainsValidation');
-
-                $scope.ui.showValid = validFilter($scope.domains, false, true).length === 0;
-            }
-        }
-
         // Remove all domain models from db.
-        $scope.removeAllItems = function () {
+        $scope.removeAllItems = function() {
             $table.tableReset();
 
             $confirm.confirm('Are you sure you want to remove all domain models?')
-                .then(function () {
+                .then(function() {
                     $http.post('/api/v1/configuration/domains/remove/all')
-                        .success(function () {
+                        .success(function() {
                             $common.showInfo('All domain models have been removed');
 
                             $scope.domains = [];
+
+                            _.forEach($scope.caches, (cache) => cache.cache.domains = []);
+
                             $scope.ui.inputForm.$setPristine();
                             $scope.backupItem = emptyDomain;
                             $scope.ui.showValid = true;
                         })
-                        .error(function (errMsg) {
+                        .error(function(errMsg) {
                             $common.showError(errMsg);
                         });
                 });
         };
 
-        $scope.toggleValid = function () {
+        $scope.toggleValid = function() {
             $scope.ui.showValid = !$scope.ui.showValid;
 
-            var validFilter = $filter('domainsValidation');
+            const validFilter = $filter('domainsValidation');
 
-            var idx = -1;
+            let idx = -1;
 
             if ($common.isDefined($scope.selectedItem)) {
-                idx = _.findIndex(validFilter($scope.domains, $scope.ui.showValid, true), function (domain) {
+                idx = _.findIndex(validFilter($scope.domains, $scope.ui.showValid, true), function(domain) {
                     return domain._id === $scope.selectedItem._id;
                 });
             }
@@ -1464,7 +1442,7 @@ consoleModule.controller('domainsController', [
                 $scope.backupItem = emptyDomain;
         };
 
-        var pairFields = {
+        const pairFields = {
             fields: {
                 msg: 'Query field class',
                 id: 'QryField',
@@ -1477,16 +1455,16 @@ consoleModule.controller('domainsController', [
             aliases: {id: 'Alias', idPrefix: 'Value', searchCol: 'alias', valueCol: 'value', dupObjName: 'alias'}
         };
 
-        $scope.tablePairValid = function (item, field, index) {
-            var pairField = pairFields[field.model];
+        $scope.tablePairValid = function(item, field, index) {
+            const pairField = pairFields[field.model];
 
-            var pairValue = $table.tablePairValue(field, index);
+            const pairValue = $table.tablePairValue(field, index);
 
             if (pairField) {
-                var model = item[field.model];
+                const model = item[field.model];
 
                 if ($common.isDefined(model)) {
-                    var idx = _.findIndex(model, function (pair) {
+                    const idx = _.findIndex(model, function(pair) {
                         return pair[pairField.searchCol] === pairValue[pairField.valueCol];
                     });
 
@@ -1516,8 +1494,8 @@ consoleModule.controller('domainsController', [
             };
         }
 
-        $scope.tableDbFieldSaveVisible = function (field, index) {
-            var dbFieldValue = tableDbFieldValue(field, index);
+        $scope.tableDbFieldSaveVisible = function(field, index) {
+            const dbFieldValue = tableDbFieldValue(field, index);
 
             return $common.isDefined(dbFieldValue.databaseFieldType) &&
                 $common.isDefined(dbFieldValue.javaFieldType) &&
@@ -1525,26 +1503,26 @@ consoleModule.controller('domainsController', [
                 !$common.isEmptyString(dbFieldValue.javaFieldName);
         };
 
-        var dbFieldTables = {
+        const dbFieldTables = {
             keyFields: {msg: 'Key field', id: 'KeyField'},
             valueFields: {msg: 'Value field', id: 'ValueField'}
         };
 
-        $scope.tableDbFieldSave = function (field, index, stopEdit) {
-            var dbFieldTable = dbFieldTables[field.model];
+        $scope.tableDbFieldSave = function(field, index, stopEdit) {
+            const dbFieldTable = dbFieldTables[field.model];
 
             if (dbFieldTable) {
-                var dbFieldValue = tableDbFieldValue(field, index);
+                const dbFieldValue = tableDbFieldValue(field, index);
 
-                var item = $scope.backupItem;
+                const item = $scope.backupItem;
 
-                var model = item[field.model];
+                let model = item[field.model];
 
                 if (!$common.isValidJavaIdentifier(dbFieldTable.msg + ' java name', dbFieldValue.javaFieldName, $table.tableFieldId(index, 'JavaFieldName' + dbFieldTable.id)))
                     return false;
 
                 if ($common.isDefined(model)) {
-                    var idx = _.findIndex(model, function (dbMeta) {
+                    let idx = _.findIndex(model, function(dbMeta) {
                         return dbMeta.databaseFieldName === dbFieldValue.databaseFieldName;
                     });
 
@@ -1552,7 +1530,7 @@ consoleModule.controller('domainsController', [
                     if (idx >= 0 && index !== idx)
                         return showPopoverMessage($scope.ui, 'store', $table.tableFieldId(index, 'DatabaseFieldName' + dbFieldTable.id), 'Field with such database name already exists!');
 
-                    idx = _.findIndex(model, function (dbMeta) {
+                    idx = _.findIndex(model, function(dbMeta) {
                         return dbMeta.javaFieldName === dbFieldValue.javaFieldName;
                     });
 
@@ -1560,11 +1538,10 @@ consoleModule.controller('domainsController', [
                     if (idx >= 0 && index !== idx)
                         return showPopoverMessage($scope.ui, 'store', $table.tableFieldId(index, 'JavaFieldName' + dbFieldTable.id), 'Field with such java name already exists!');
 
-                    if (index < 0) {
+                    if (index < 0)
                         model.push(dbFieldValue);
-                    }
                     else {
-                        var dbField = model[index];
+                        const dbField = model[index];
 
                         dbField.databaseFieldName = dbFieldValue.databaseFieldName;
                         dbField.databaseFieldType = dbFieldValue.databaseFieldType;
@@ -1601,20 +1578,20 @@ consoleModule.controller('domainsController', [
             return index < 0 ? field.newIndexType : field.curIndexType;
         }
 
-        $scope.tableIndexSaveVisible = function (field, index) {
+        $scope.tableIndexSaveVisible = function(field, index) {
             return !$common.isEmptyString(tableIndexName(field, index)) && $common.isDefined(tableIndexType(field, index));
         };
 
-        $scope.tableIndexSave = function (field, curIdx, stopEdit) {
-            var indexName = tableIndexName(field, curIdx);
-            var indexType = tableIndexType(field, curIdx);
+        $scope.tableIndexSave = function(field, curIdx, stopEdit) {
+            const indexName = tableIndexName(field, curIdx);
+            const indexType = tableIndexType(field, curIdx);
 
-            var item = $scope.backupItem;
+            const item = $scope.backupItem;
 
-            var indexes = item.indexes;
+            const indexes = item.indexes;
 
             if ($common.isDefined(indexes)) {
-                var idx = _.findIndex(indexes, function (index) {
+                const idx = _.findIndex(indexes, function(index) {
                     return index.name === indexName;
                 });
 
@@ -1626,7 +1603,7 @@ consoleModule.controller('domainsController', [
             $table.tableReset();
 
             if (curIdx < 0) {
-                var newIndex = {name: indexName, indexType: indexType};
+                const newIndex = {name: indexName, indexType};
 
                 if (item.indexes)
                     item.indexes.push(newIndex);
@@ -1642,7 +1619,7 @@ consoleModule.controller('domainsController', [
                 if (curIdx < 0)
                     $scope.tableIndexNewItem(field, item.indexes.length - 1);
                 else {
-                    var index = item.indexes[curIdx];
+                    const index = item.indexes[curIdx];
 
                     if (index.fields && index.fields.length > 0)
                         $scope.tableIndexItemStartEdit(field, curIdx, 0);
@@ -1654,9 +1631,9 @@ consoleModule.controller('domainsController', [
             return true;
         };
 
-        $scope.tableIndexNewItem = function (field, indexIdx) {
+        $scope.tableIndexNewItem = function(field, indexIdx) {
             if ($scope.tableReset(true)) {
-                var index = $scope.backupItem.indexes[indexIdx];
+                const index = $scope.backupItem.indexes[indexIdx];
 
                 $table.tableState(field, -1, 'table-index-fields');
                 $table.tableFocusInvalidField(-1, 'FieldName' + (index.indexType === 'SORTED' ? 'S' : '') + indexIdx);
@@ -1667,11 +1644,11 @@ consoleModule.controller('domainsController', [
             }
         };
 
-        $scope.tableIndexNewItemActive = function (field, itemIndex) {
-            var indexes = $scope.backupItem.indexes;
+        $scope.tableIndexNewItemActive = function(field, itemIndex) {
+            const indexes = $scope.backupItem.indexes;
 
             if (indexes) {
-                var index = indexes[itemIndex];
+                const index = indexes[itemIndex];
 
                 if (index)
                     return $table.tableNewItemActive({model: 'table-index-fields'}) && field.indexIdx === itemIndex;
@@ -1680,11 +1657,11 @@ consoleModule.controller('domainsController', [
             return false;
         };
 
-        $scope.tableIndexItemEditing = function (field, itemIndex, curIdx) {
-            var indexes = $scope.backupItem.indexes;
+        $scope.tableIndexItemEditing = function(field, itemIndex, curIdx) {
+            const indexes = $scope.backupItem.indexes;
 
             if (indexes) {
-                var index = indexes[itemIndex];
+                const index = indexes[itemIndex];
 
                 if (index)
                     return $table.tableEditing({model: 'table-index-fields'}, curIdx) && field.indexIdx === itemIndex;
@@ -1703,13 +1680,13 @@ consoleModule.controller('domainsController', [
             };
         }
 
-        $scope.tableIndexItemStartEdit = function (field, indexIdx, curIdx) {
+        $scope.tableIndexItemStartEdit = function(field, indexIdx, curIdx) {
             if ($scope.tableReset(true)) {
-                var index = $scope.backupItem.indexes[indexIdx];
+                const index = $scope.backupItem.indexes[indexIdx];
 
                 $table.tableState(field, curIdx, 'table-index-fields');
 
-                var indexItem = index.fields[curIdx];
+                const indexItem = index.fields[curIdx];
 
                 field.curFieldName = indexItem.name;
                 field.curDirection = indexItem.direction;
@@ -1719,21 +1696,19 @@ consoleModule.controller('domainsController', [
             }
         };
 
-        $scope.tableIndexItemSaveVisible = function (field, index) {
+        $scope.tableIndexItemSaveVisible = function(field, index) {
             return !$common.isEmptyString(tableIndexItemValue(field, index).name);
         };
 
-        $scope.tableIndexItemSave = function (field, indexIdx, curIdx, stopEdit) {
-            var indexItemValue = tableIndexItemValue(field, curIdx);
+        $scope.tableIndexItemSave = function(field, indexIdx, curIdx, stopEdit) {
+            const indexItemValue = tableIndexItemValue(field, curIdx);
 
-            var index = $scope.backupItem.indexes[indexIdx];
+            const index = $scope.backupItem.indexes[indexIdx];
 
-            var fields = index.fields;
+            const fields = index.fields;
 
             if ($common.isDefined(fields)) {
-                var idx = _.findIndex(fields, function (field) {
-                    return field.name === indexItemValue.name;
-                });
+                const idx = _.findIndex(fields, (fld) => fld.name === indexItemValue.name);
 
                 // Found duplicate.
                 if (idx >= 0 && idx !== curIdx)
@@ -1767,17 +1742,17 @@ consoleModule.controller('domainsController', [
             return true;
         };
 
-        $scope.tableRemoveIndexItem = function (index, curIdx) {
+        $scope.tableRemoveIndexItem = function(index, curIdx) {
             $table.tableReset();
 
             index.fields.splice(curIdx, 1);
         };
 
-        $scope.resetAll = function () {
+        $scope.resetAll = function() {
             $table.tableReset();
 
             $confirm.confirm('Are you sure you want to undo all changes for current domain model?')
-                .then(function () {
+                .then(function() {
                     $scope.backupItem = $scope.selectedItem ? angular.copy($scope.selectedItem) : prepareNewItem();
                     $scope.ui.inputForm.$setPristine();
                 });


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

Posted by ak...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/controllers/common-module.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/controllers/common-module.js b/modules/web-console/src/main/js/controllers/common-module.js
index 930fbc8..ba6ec62 100644
--- a/modules/web-console/src/main/js/controllers/common-module.js
+++ b/modules/web-console/src/main/js/controllers/common-module.js
@@ -35,22 +35,23 @@ import alertTemplate from '../views/templates/alert.jade!';
 
 consoleModule.run(['$rootScope', '$http', '$state', '$common', 'Auth', 'User', 'gettingStarted',
     ($root, $http, $state, $common, Auth, User, gettingStarted) => {
-    $root.gettingStarted = gettingStarted;
-
-    $root.revertIdentity = function () {
-        $http
-            .get('/api/v1/admin/revert/identity')
-            .then(User.read)
-            .then(function (user) {
-                $root.$broadcast('user', user);
-
-                $state.go('settings.admin');
-            })
-            .catch(function (errMsg) {
-                $common.showError($common.errorMessage(errMsg));
-            });
-    };
-}]);
+        $root.gettingStarted = gettingStarted;
+
+        $root.revertIdentity = function() {
+            $http
+                .get('/api/v1/admin/revert/identity')
+                .then(User.read)
+                .then(function(user) {
+                    $root.$broadcast('user', user);
+
+                    $state.go('settings.admin');
+                })
+                .catch(function(errMsg) {
+                    $common.showError($common.errorMessage(errMsg));
+                });
+        };
+    }
+]);
 
 // Modal popup configuration.
 consoleModule.config(['$modalProvider', ($modalProvider) => {
@@ -124,7 +125,7 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
         $anchorScroll.yOffset = 55;
 
         function isDefined(v) {
-            return !(v === undefined || v === null);
+            return !_.isNil(v);
         }
 
         function isEmptyString(s) {
@@ -134,7 +135,7 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
             return true;
         }
 
-        var msgModal;
+        let msgModal;
 
         function errorMessage(errMsg) {
             if (errMsg) {
@@ -155,7 +156,7 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
                 title: errorMessage(msg),
                 placement: placement ? placement : 'top-right',
                 container: container ? container : 'body',
-                duration: persistent ? false : 5
+                duration: persistent ? false : 10
             });
 
             msgModal.$scope.icon = icon ? icon : 'fa-exclamation-triangle';
@@ -163,16 +164,16 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
             return false;
         }
 
-        var javaBuiltInClasses = [
+        const javaBuiltInClasses = [
             'BigDecimal', 'Boolean', 'Byte', 'Date', 'Double', 'Float', 'Integer', 'Long', 'Object', 'Short', 'String', 'Time', 'Timestamp', 'UUID'
         ];
 
-        var javaBuiltInTypes = [
+        const javaBuiltInTypes = [
             'BigDecimal', 'boolean', 'Boolean', 'byte', 'Byte', 'Date', 'double', 'Double', 'float', 'Float',
             'int', 'Integer', 'long', 'Long', 'Object', 'short', 'Short', 'String', 'Time', 'Timestamp', 'UUID'
         ];
 
-        var javaBuiltInFullNameClasses = [
+        const javaBuiltInFullNameClasses = [
             'java.math.BigDecimal', 'java.lang.Boolean', 'java.lang.Byte', 'java.sql.Date', 'java.lang.Double',
             'java.lang.Float', 'java.lang.Integer', 'java.lang.Long', 'java.lang.Object', 'java.lang.Short',
             'java.lang.String', 'java.sql.Time', 'java.sql.Timestamp', 'java.util.UUID'
@@ -189,7 +190,7 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
             return _.includes(javaBuiltInClasses, clsName) || _.includes(javaBuiltInFullNameClasses, clsName);
         }
 
-        var SUPPORTED_JDBC_TYPES = [
+        const SUPPORTED_JDBC_TYPES = [
             'BIGINT',
             'BIT',
             'BOOLEAN',
@@ -214,10 +215,10 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
             'VARCHAR'
         ];
 
-        var ALL_JDBC_TYPES = [
+        const ALL_JDBC_TYPES = [
             {dbName: 'BIT', dbType: -7, javaType: 'Boolean', primitiveType: 'boolean'},
             {dbName: 'TINYINT', dbType: -6, javaType: 'Byte', primitiveType: 'byte'},
-            {dbName: 'SMALLINT', dbType:  5, javaType: 'Short', primitiveType: 'short'},
+            {dbName: 'SMALLINT', dbType: 5, javaType: 'Short', primitiveType: 'short'},
             {dbName: 'INTEGER', dbType: 4, javaType: 'Integer', primitiveType: 'int'},
             {dbName: 'BIGINT', dbType: -5, javaType: 'Long', primitiveType: 'long'},
             {dbName: 'FLOAT', dbType: 6, javaType: 'Float', primitiveType: 'float'},
@@ -253,7 +254,8 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
             {dbName: 'SQLXML', dbType: 2009, javaType: 'Object'}
         ];
 
-        var JAVA_KEYWORDS = [
+        /*eslint-disable */
+        const JAVA_KEYWORDS = [
             'abstract',     'assert',        'boolean',      'break',           'byte',
             'case',         'catch',         'char',         'class',           'const',
             'continue',     'default',       'do',           'double',          'else',
@@ -266,8 +268,99 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
             'throw',        'throws',        'transient',    'true',            'try',
             'void',         'volatile',      'while'
         ];
+        /*eslint-enable */
+
+        const VALID_JAVA_IDENTIFIER = new RegExp('^[a-zA-Z_$][a-zA-Z\\d_$]*$');
+
+        let popover = null;
+
+        function isElementInViewport(el) {
+            const rect = el.getBoundingClientRect();
+
+            return (
+                rect.top >= 0 &&
+                rect.left >= 0 &&
+                rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
+                rect.right <= (window.innerWidth || document.documentElement.clientWidth)
+            );
+        }
+
+        const _showPopoverMessage = (id, message, showTime) => {
+            const body = $('body');
+
+            let el = body.find('#' + id);
+
+            if (!el || el.length === 0)
+                el = body.find('[name="' + id + '"]');
+
+            if (el && el.length > 0) {
+                if (!isElementInViewport(el[0])) {
+                    $location.hash(el[0].id);
+
+                    $anchorScroll();
+                }
 
-        var VALID_JAVA_IDENTIFIER = new RegExp('^[a-zA-Z_$][a-zA-Z\\d_$]*$');
+                const newPopover = $popover(el, {content: message});
+
+                popover = newPopover;
+
+                $timeout(() => newPopover.$promise.then(newPopover.show), 400);
+                $timeout(() => newPopover.hide(), showTime || 5000);
+            }
+        };
+
+        function ensureActivePanel(ui, pnl, focusId) {
+            if (ui) {
+                const collapses = $('div.panel-collapse');
+
+                ui.loadPanel(pnl);
+
+                const idx = _.findIndex(collapses, function(collapse) {
+                    return collapse.id === pnl;
+                });
+
+                if (idx >= 0) {
+                    const activePanels = ui.activePanels;
+
+                    if (!_.includes(ui.topPanels, idx)) {
+                        ui.expanded = true;
+
+                        const customExpanded = ui[pnl];
+
+                        if (customExpanded)
+                            ui[customExpanded] = true;
+                    }
+
+                    if (!activePanels || activePanels.length < 1)
+                        ui.activePanels = [idx];
+                    else if (!_.includes(activePanels, idx)) {
+                        const newActivePanels = angular.copy(activePanels);
+
+                        newActivePanels.push(idx);
+
+                        ui.activePanels = newActivePanels;
+                    }
+                }
+
+                if (isDefined(focusId))
+                    $focus(focusId);
+            }
+        }
+
+        function showPopoverMessage(ui, panelId, id, message, showTime) {
+            if (popover)
+                popover.hide();
+
+            if (ui) {
+                ensureActivePanel(ui, panelId);
+
+                $timeout(() => _showPopoverMessage(id, message, showTime), ui.isPanelLoaded(panelId) ? 200 : 500);
+            }
+            else
+                _showPopoverMessage(id, message, showTime);
+
+            return false;
+        }
 
         function isValidJavaIdentifier(msg, ident, elemId, panels, panelId) {
             if (isEmptyString(ident))
@@ -282,7 +375,7 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
             return true;
         }
 
-        var context = null;
+        let context = null;
 
         /**
          * Calculate width of specified text in body's font.
@@ -292,11 +385,11 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
          */
         function measureText(text) {
             if (!context) {
-                var canvas = document.createElement('canvas');
+                const canvas = document.createElement('canvas');
 
                 context = canvas.getContext('2d');
 
-                var style = window.getComputedStyle(document.getElementsByTagName('body')[0]);
+                const style = window.getComputedStyle(document.getElementsByTagName('body')[0]);
 
                 context.font = style.fontSize + ' ' + style.fontFamily;
             }
@@ -312,17 +405,17 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
          * @returns {*} Array of compacted class names.
          */
         function compactByMaxCharts(names, nameLength) {
-            for (var nameIx = 0; nameIx < names.length; nameIx ++) {
-                var s = names[nameIx];
+            for (let nameIx = 0; nameIx < names.length; nameIx++) {
+                const s = names[nameIx];
 
                 if (s.length > nameLength) {
-                    var totalLength = s.length;
+                    let totalLength = s.length;
 
-                    var packages = s.split('.');
+                    const packages = s.split('.');
 
-                    var packageCnt = packages.length - 1;
+                    const packageCnt = packages.length - 1;
 
-                    for (var i = 0; i < packageCnt && totalLength > nameLength; i++) {
+                    for (let i = 0; i < packageCnt && totalLength > nameLength; i++) {
                         if (packages[i].length > 0) {
                             totalLength -= packages[i].length - 1;
 
@@ -331,11 +424,11 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
                     }
 
                     if (totalLength > nameLength) {
-                        var className = packages[packageCnt];
+                        const className = packages[packageCnt];
 
-                        var classNameLen = className.length;
+                        const classNameLen = className.length;
 
-                        var remains = Math.min(nameLength - totalLength + classNameLen, classNameLen);
+                        let remains = Math.min(nameLength - totalLength + classNameLen, classNameLen);
 
                         if (remains < 3)
                             remains = Math.min(3, classNameLen);
@@ -343,9 +436,9 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
                         packages[packageCnt] = className.substring(0, remains) + '...';
                     }
 
-                    var result = packages[0];
+                    let result = packages[0];
 
-                    for (i = 1; i < packages.length; i++)
+                    for (let i = 1; i < packages.length; i++)
                         result += '.' + packages[i];
 
                     names[nameIx] = result;
@@ -367,27 +460,27 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
             if (nameWidth <= 0)
                 return names;
 
-            var fitted = [];
+            const fitted = [];
 
-            var widthByName = [];
+            const widthByName = [];
 
-            var len = names.length;
+            const len = names.length;
 
-            var divideTo = len;
+            let divideTo = len;
 
-            for (var nameIx = 0; nameIx < len; nameIx ++) {
+            for (let nameIx = 0; nameIx < len; nameIx++) {
                 fitted[nameIx] = false;
 
                 widthByName[nameIx] = nameWidth;
             }
 
             // Try to distribute space from short class names to long class names.
-            do {
-                var remains = 0;
+            let remains = 0;
 
-                for (nameIx = 0; nameIx < len; nameIx++) {
+            do {
+                for (let nameIx = 0; nameIx < len; nameIx++) {
                     if (!fitted[nameIx]) {
-                        var curNameWidth = measureText(names[nameIx]);
+                        const curNameWidth = measureText(names[nameIx]);
 
                         if (widthByName[nameIx] > curNameWidth) {
                             fitted[nameIx] = true;
@@ -401,29 +494,28 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
                     }
                 }
 
-                var remainsByName = remains / divideTo;
+                const remainsByName = remains / divideTo;
 
-                for (nameIx = 0; nameIx < len; nameIx++) {
-                    if (!fitted[nameIx]) {
+                for (let nameIx = 0; nameIx < len; nameIx++) {
+                    if (!fitted[nameIx])
                         widthByName[nameIx] += remainsByName;
-                    }
                 }
             }
-            while(remains > 0);
+            while (remains > 0);
 
             // Compact class names to available for each space.
-            for (nameIx = 0; nameIx < len; nameIx ++) {
-                var s = names[nameIx];
+            for (let nameIx = 0; nameIx < len; nameIx++) {
+                const s = names[nameIx];
 
                 if (s.length > (nameLength / 2 | 0)) {
-                    var totalWidth = measureText(s);
+                    let totalWidth = measureText(s);
 
                     if (totalWidth > widthByName[nameIx]) {
-                        var packages = s.split('.');
+                        const packages = s.split('.');
 
-                        var packageCnt = packages.length - 1;
+                        const packageCnt = packages.length - 1;
 
-                        for (var i = 0; i < packageCnt && totalWidth > widthByName[nameIx]; i++) {
+                        for (let i = 0; i < packageCnt && totalWidth > widthByName[nameIx]; i++) {
                             if (packages[i].length > 1) {
                                 totalWidth -= measureText(packages[i].substring(1, packages[i].length));
 
@@ -431,39 +523,31 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
                             }
                         }
 
-                        var shortPackage = '';
+                        let shortPackage = '';
 
-                        for (i = 0; i < packageCnt; i++)
+                        for (let i = 0; i < packageCnt; i++)
                             shortPackage += packages[i] + '.';
 
-                        var className = packages[packageCnt];
+                        const className = packages[packageCnt];
 
-                        var classLen = className.length;
+                        const classLen = className.length;
 
-                        var minLen = Math.min(classLen, 3);
+                        let minLen = Math.min(classLen, 3);
 
                         totalWidth = measureText(shortPackage + className);
 
                         // Compact class name if shorten package path is very long.
                         if (totalWidth > widthByName[nameIx]) {
-                            var maxLen = classLen;
-
-                            var middleLen = (minLen + (maxLen - minLen) / 2 ) | 0;
-
-                            var minLenPx = measureText(shortPackage + className.substr(0, minLen) + '...');
-                            var maxLenPx = totalWidth;
+                            let maxLen = classLen;
+                            let middleLen = (minLen + (maxLen - minLen) / 2 ) | 0;
 
                             while (middleLen !== minLen && middleLen !== maxLen) {
-                                var middleLenPx = measureText(shortPackage + className.substr(0, middleLen) + '...');
+                                const middleLenPx = measureText(shortPackage + className.substr(0, middleLen) + '...');
 
-                                if (middleLenPx > widthByName[nameIx]) {
+                                if (middleLenPx > widthByName[nameIx])
                                     maxLen = middleLen;
-                                    maxLenPx = middleLenPx;
-                                }
-                                else {
+                                else
                                     minLen = middleLen;
-                                    minLenPx = middleLenPx;
-                                }
 
                                 middleLen = (minLen + (maxLen - minLen) / 2 ) | 0;
                             }
@@ -490,29 +574,20 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
             if (nameWidth <= 0)
                 return label;
 
-            var totalWidth = measureText(label);
+            const totalWidth = measureText(label);
 
             if (totalWidth > nameWidth) {
-                var maxLen = label.length;
-
-                var minLen = Math.min(maxLen, 3);
-
-                var middleLen = (minLen + (maxLen - minLen) / 2 ) | 0;
-
-                var minLenPx = measureText(label.substr(0, minLen) + '...');
-                var maxLenPx = totalWidth;
+                let maxLen = label.length;
+                let minLen = Math.min(maxLen, 3);
+                let middleLen = (minLen + (maxLen - minLen) / 2 ) | 0;
 
                 while (middleLen !== minLen && middleLen !== maxLen) {
-                    var middleLenPx = measureText(label.substr(0, middleLen) + '...');
+                    const middleLenPx = measureText(label.substr(0, middleLen) + '...');
 
-                    if (middleLenPx > nameWidth) {
+                    if (middleLenPx > nameWidth)
                         maxLen = middleLen;
-                        maxLenPx = middleLenPx;
-                    }
-                    else {
+                    else
                         minLen = middleLen;
-                        minLenPx = middleLenPx;
-                    }
 
                     middleLen = (minLen + (maxLen - minLen) / 2 ) | 0;
                 }
@@ -531,19 +606,19 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
          * @returns {*[]} First element is length of class for single value, second element is length for pair vlaue.
          */
         function availableWidth(index, id) {
-            var idElem = $('#' + id);
+            const idElem = $('#' + id);
 
-            var width = 0;
+            let width = 0;
 
-            switch (idElem.prop("tagName")) {
+            switch (idElem.prop('tagName')) {
                 // Detection of available width in presentation table row.
                 case 'TABLE':
-                    var cont = $(idElem.find('tr')[index - 1]).find('td')[0];
+                    const cont = $(idElem.find('tr')[index - 1]).find('td')[0];
 
                     width = cont.clientWidth;
 
                     if (width > 0) {
-                        var children = $(cont).children(':not("a")');
+                        const children = $(cont).children(':not("a")');
 
                         _.forEach(children, function(child) {
                             if ('offsetWidth' in child)
@@ -563,104 +638,15 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
                     });
 
                     break;
-            }
-
-            return width | 0;
-        }
-
-        var popover = null;
-
-        function ensureActivePanel(ui, pnl, focusId) {
-            if (ui) {
-                var collapses = $('div.panel-collapse');
-
-                ui.loadPanel(pnl);
-
-                var idx = _.findIndex(collapses, function(collapse) {
-                    return collapse.id === pnl;
-                });
-
-                if (idx >= 0) {
-                    var activePanels = ui.activePanels;
-
-                    if (!_.includes(ui.topPanels, idx)) {
-                        ui.expanded = true;
-
-                        var customExpanded = ui[pnl];
-
-                        if (customExpanded)
-                            ui[customExpanded] = true;
-                    }
-
-                    if (!activePanels || activePanels.length < 1)
-                        ui.activePanels = [idx];
-                    else if (!_.includes(activePanels, idx)) {
-                        var newActivePanels = angular.copy(activePanels);
-
-                        newActivePanels.push(idx);
-
-                        ui.activePanels = newActivePanels;
-                    }
-                }
-
-                if (isDefined(focusId))
-                    $focus(focusId);
-            }
-        }
-
-        function isElementInViewport(el) {
-            var rect = el.getBoundingClientRect();
-
-            return (
-                rect.top >= 0 &&
-                rect.left >= 0 &&
-                rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
-                rect.right <= (window.innerWidth || document.documentElement.clientWidth)
-            );
-        }
-
-        const _showPopoverMessage = (id, message, showTime) => {
-            var body = $('body');
-
-            var el = body.find('#' + id);
-
-            if (!el || el.length === 0)
-                el = body.find('[name="' + id + '"]');
-
-            if (el && el.length > 0) {
-                if (!isElementInViewport(el[0])) {
-                    $location.hash(el[0].id);
-
-                    $anchorScroll();
-                }
-
-                var newPopover = $popover(el, {content: message});
-
-                popover = newPopover;
-
-                $timeout(function () { newPopover.$promise.then(newPopover.show); }, 400);
-
-                $timeout(function () { newPopover.hide(); }, showTime || 5000);
-            }
-        };
-
-        function showPopoverMessage(ui, panelId, id, message, showTime) {
-            if (popover)
-                popover.hide();
 
-            if (ui) {
-                ensureActivePanel(ui, panelId);
-
-                $timeout(() => _showPopoverMessage(id, message, showTime), ui.isPanelLoaded(panelId) ? 200 : 500);
+                default:
             }
-            else
-                _showPopoverMessage(id, message, showTime);
 
-            return false;
+            return width | 0;
         }
 
         function getModel(obj, field) {
-            var path = field.path;
+            let path = field.path;
 
             if (!isDefined(path) || !isDefined(obj))
                 return obj;
@@ -668,11 +654,11 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
             path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
             path = path.replace(/^\./, '');           // strip a leading dot
 
-            var segs = path.split('.');
-            var root = obj;
+            const segs = path.split('.');
+            let root = obj;
 
             while (segs.length > 0) {
-                var pathStep = segs.shift();
+                const pathStep = segs.shift();
 
                 if (typeof root[pathStep] === 'undefined')
                     root[pathStep] = {};
@@ -683,52 +669,9 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
             return root;
         }
 
-        function checkGroupDirty(group, curItem, srcItem) {
-            function _compareField(field) {
-                var curModel = getModel(curItem, field);
-                var srcModel = getModel(srcItem, field);
-
-                if (field.model) {
-                    if (field.model === 'kind' && isDefined(curModel.kind)) {
-                        if (curModel.kind !== srcModel.kind)
-                            return true;
-
-                        if (_compareFields(field.details[curModel.kind].fields))
-                            return true;
-                    }
-
-                    var curValue = curModel[field.model];
-                    var srcValue = srcModel[field.model];
-
-                    if ((_.isArray(curValue) || _.isString(curValue)) && (curValue.length === 0) && (srcValue === undefined))
-                        curValue = undefined;
-
-                    if (_.isBoolean(curValue) && !curValue && srcValue === undefined)
-                        curValue = undefined;
-
-                    var isCur = isDefined(curValue);
-                    var isSrc = isDefined(srcValue);
-
-                    return !!((isCur && !isSrc) || (!isCur && isSrc) || (isCur && isSrc && !angular.equals(curValue, srcValue)));
-                }
-                else if (field.type === 'panel-details' &&  _compareFields(field.details))
-                    return true;
-
-                return false;
-            }
-
-            function _compareFields(fields) {
-                return _.findIndex(fields, _compareField) >= 0;
-            }
-
-            group.dirty = _compareFields(group.fields);
-
-            return group.dirty;
-        }
-
         function extractDataSource(cache) {
             if (cache.cacheStoreFactory && cache.cacheStoreFactory.kind) {
-                var storeFactory = cache.cacheStoreFactory[cache.cacheStoreFactory.kind];
+                const storeFactory = cache.cacheStoreFactory[cache.cacheStoreFactory.kind];
 
                 if (storeFactory.dialect || (storeFactory.connectVia === 'DataSource'))
                     return storeFactory;
@@ -737,7 +680,7 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
             return null;
         }
 
-        var cacheStoreJdbcDialects = [
+        const cacheStoreJdbcDialects = [
             {value: 'Generic', label: 'Generic JDBC'},
             {value: 'Oracle', label: 'Oracle'},
             {value: 'DB2', label: 'IBM DB2'},
@@ -748,7 +691,7 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
         ];
 
         function domainForStoreConfigured(domain) {
-            var isEmpty = !isDefined(domain) || (isEmptyString(domain.databaseSchema) &&
+            const isEmpty = !isDefined(domain) || (isEmptyString(domain.databaseSchema) &&
                 isEmptyString(domain.databaseTable) &&
                 _.isEmpty(domain.keyFields) &&
                 _.isEmpty(domain.valueFields));
@@ -756,46 +699,49 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
             return !isEmpty;
         }
 
-        var DS_CHECK_SUCCESS = { checked: true };
+        const DS_CHECK_SUCCESS = { checked: true };
 
         function compareDataSources(firstCache, secondCache) {
-            var firstDs = extractDataSource(firstCache);
-            var secondDs = extractDataSource(secondCache);
+            const firstDs = extractDataSource(firstCache);
+            const secondDs = extractDataSource(secondCache);
 
             if (firstDs && secondDs) {
-                var firstDB = firstDs.dialect;
-                var secondDB = secondDs.dialect;
+                const firstDB = firstDs.dialect;
+                const secondDB = secondDs.dialect;
 
-                if (firstDs.dataSourceBean === secondDs.dataSourceBean && firstDB !== secondDB) {
-                    return {
-                        checked: false,
-                        firstCache: firstCache,
-                        firstDB: firstDB,
-                        secondCache: secondCache,
-                        secondDB: secondDB
-                    };
-                }
+                if (firstDs.dataSourceBean === secondDs.dataSourceBean && firstDB !== secondDB)
+                    return {checked: false, firstCache, firstDB, secondCache, secondDB};
             }
 
             return DS_CHECK_SUCCESS;
         }
 
+        function compareSQLSchemaNames(firstCache, secondCache) {
+            const firstName = firstCache.sqlSchema;
+            const secondName = secondCache.sqlSchema;
+
+            if (firstName && secondName && (firstName === secondName))
+                return {checked: false, firstCache, secondCache};
+
+            return DS_CHECK_SUCCESS;
+        }
+
         function toJavaName(prefix, name) {
-            var javaName = name ? name.replace(/[^A-Za-z_0-9]+/g, '_') : 'dflt';
+            const javaName = name ? name.replace(/[^A-Za-z_0-9]+/g, '_') : 'dflt';
 
             return prefix + javaName.charAt(0).toLocaleUpperCase() + javaName.slice(1);
         }
 
         return {
-            getModel: getModel,
-            mkOptions: function (options) {
-                return _.map(options, function (option) {
+            getModel,
+            mkOptions(options) {
+                return _.map(options, (option) => {
                     return {value: option, label: isDefined(option) ? option : 'Not set'};
                 });
             },
-            isDefined: isDefined,
-            hasProperty: function (obj, props) {
-                for (var propName in props) {
+            isDefined,
+            hasProperty(obj, props) {
+                for (const propName in props) {
                     if (props.hasOwnProperty(propName)) {
                         if (obj[propName])
                             return true;
@@ -804,14 +750,14 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
 
                 return false;
             },
-            isEmptyString: isEmptyString,
-            errorMessage: errorMessage,
-            hideAlert: function () {
+            isEmptyString,
+            errorMessage,
+            hideAlert() {
                 if (msgModal)
                     msgModal.hide();
             },
-            showError: showError,
-            showInfo: function (msg) {
+            showError,
+            showInfo(msg) {
                 if (msgModal)
                     msgModal.hide();
 
@@ -823,25 +769,25 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
 
                 msgModal.$scope.icon = 'fa-check-circle-o';
             },
-            SUPPORTED_JDBC_TYPES: SUPPORTED_JDBC_TYPES,
-            findJdbcType: function (jdbcType) {
-                var res =  _.find(ALL_JDBC_TYPES, function (item) {
+            SUPPORTED_JDBC_TYPES,
+            findJdbcType(jdbcType) {
+                const res = _.find(ALL_JDBC_TYPES, function(item) {
                     return item.dbType === jdbcType;
                 });
 
                 return res ? res : {dbName: 'Unknown', javaType: 'Unknown'};
             },
-            javaBuiltInClasses: javaBuiltInClasses,
-            javaBuiltInTypes: javaBuiltInTypes,
-            isJavaBuiltInClass: isJavaBuiltInClass,
-            isValidJavaIdentifier: isValidJavaIdentifier,
-            isValidJavaClass: function (msg, ident, allowBuiltInClass, elemId, packageOnly, panels, panelId) {
+            javaBuiltInClasses,
+            javaBuiltInTypes,
+            isJavaBuiltInClass,
+            isValidJavaIdentifier,
+            isValidJavaClass(msg, ident, allowBuiltInClass, elemId, packageOnly, panels, panelId) {
                 if (isEmptyString(ident))
                     return showPopoverMessage(panels, panelId, elemId, msg + ' could not be empty!');
 
-                var parts = ident.split('.');
+                const parts = ident.split('.');
 
-                var len = parts.length;
+                const len = parts.length;
 
                 if (!allowBuiltInClass && isJavaBuiltInClass(ident))
                     return showPopoverMessage(panels, panelId, elemId, msg + ' should not be the Java build-in class!');
@@ -849,8 +795,8 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
                 if (len < 2 && !isJavaBuiltInClass(ident) && !packageOnly)
                     return showPopoverMessage(panels, panelId, elemId, msg + ' does not have package specified!');
 
-                for (var i = 0; i < parts.length; i++) {
-                    var part = parts[i];
+                for (let i = 0; i < parts.length; i++) {
+                    const part = parts[i];
 
                     if (!isValidJavaIdentifier(msg, part, elemId, panels, panelId))
                         return false;
@@ -858,14 +804,14 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
 
                 return true;
             },
-            domainForQueryConfigured: function (domain) {
-                var isEmpty = !isDefined(domain) || (_.isEmpty(domain.fields) &&
+            domainForQueryConfigured(domain) {
+                const isEmpty = !isDefined(domain) || (_.isEmpty(domain.fields) &&
                     _.isEmpty(domain.aliases) &&
                     _.isEmpty(domain.indexes));
 
                 return !isEmpty;
             },
-            domainForStoreConfigured: domainForStoreConfigured,
+            domainForStoreConfigured,
             /**
              * Cut class name by width in pixel or width in symbol count.
              *
@@ -876,17 +822,17 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
              * @param divider String to visualy divide items.
              * @returns {*} Array of compacted class names.
              */
-            compactJavaName: function (id, index, maxLength, names, divider) {
+            compactJavaName(id, index, maxLength, names, divider) {
                 divider = ' ' + divider + ' ';
 
-                var prefix = index + ') ';
+                const prefix = index + ') ';
 
-                var nameCnt = names.length;
+                const nameCnt = names.length;
 
-                var nameLength = ((maxLength - 3 * (nameCnt - 1)) / nameCnt) | 0;
+                const nameLength = ((maxLength - 3 * (nameCnt - 1)) / nameCnt) | 0;
 
                 try {
-                    var nameWidth = (availableWidth(index, id) - measureText(prefix) - (nameCnt - 1) * measureText(divider)) /
+                    const nameWidth = (availableWidth(index, id) - measureText(prefix) - (nameCnt - 1) * measureText(divider)) /
                         nameCnt | 0;
 
                     // HTML5 calculation of showed message width.
@@ -896,9 +842,9 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
                     names = compactByMaxCharts(names, nameLength);
                 }
 
-                var result = prefix + names[0];
+                let result = prefix + names[0];
 
-                for (var nameIx = 1; nameIx < names.length; nameIx ++)
+                for (let nameIx = 1; nameIx < names.length; nameIx++)
                     result += divider + names[nameIx];
 
                 return result;
@@ -912,28 +858,28 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
              * @param label Text to compact.
              * @returns Compacted label text.
              */
-            compactTableLabel: function (id, index, maxLength, label) {
+            compactTableLabel(id, index, maxLength, label) {
                 label = index + ') ' + label;
 
                 try {
-                    var nameWidth = availableWidth(index, id) | 0;
+                    const nameWidth = availableWidth(index, id) | 0;
 
                     // HTML5 calculation of showed message width.
                     label = compactLabelByPixels(label, nameWidth);
                 }
                 catch (err) {
-                    var nameLength = maxLength - 3 | 0;
+                    const nameLength = maxLength - 3 | 0;
 
                     label = label.length > maxLength ? label.substr(0, nameLength) + '...' : label;
                 }
 
                 return label;
             },
-            widthIsSufficient: function(id, index, text) {
+            widthIsSufficient(id, index, text) {
                 try {
-                    var available = availableWidth(index, id);
+                    const available = availableWidth(index, id);
 
-                    var required = measureText(text);
+                    const required = measureText(text);
 
                     return !available || available >= Math.floor(required);
                 }
@@ -941,15 +887,15 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
                     return true;
                 }
             },
-            ensureActivePanel: function (panels, id, focusId) {
+            ensureActivePanel(panels, id, focusId) {
                 ensureActivePanel(panels, id, focusId);
             },
-            showPopoverMessage: showPopoverMessage,
-            hidePopover: function () {
+            showPopoverMessage,
+            hidePopover() {
                 if (popover)
                     popover.hide();
             },
-            confirmUnsavedChanges: function(dirty, selectFunc) {
+            confirmUnsavedChanges(dirty, selectFunc) {
                 if (dirty) {
                     if ($window.confirm('You have unsaved changes.\n\nAre you sure you want to discard them?'))
                         selectFunc();
@@ -957,16 +903,16 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
                 else
                     selectFunc();
             },
-            saveBtnTipText: function (dirty, objectName) {
+            saveBtnTipText(dirty, objectName) {
                 if (dirty)
                     return 'Save ' + objectName;
 
                 return 'Nothing to save';
             },
-            download: function (type, name, data) {
-                var file = document.createElement('a');
+            download(type, name, data) {
+                const file = document.createElement('a');
 
-                file.setAttribute('href', 'data:' + type +';charset=utf-8,' + data);
+                file.setAttribute('href', 'data:' + type + ';charset=utf-8,' + data);
                 file.setAttribute('download', name);
                 file.setAttribute('target', '_self');
 
@@ -978,49 +924,46 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
 
                 document.body.removeChild(file);
             },
-            formUI: function () {
+            formUI() {
                 return {
                     ready: false,
                     expanded: false,
                     loadedPanels: [],
-                    loadPanel: function(pnl) {
+                    loadPanel(pnl) {
                         if (!_.includes(this.loadedPanels, pnl))
                             this.loadedPanels.push(pnl);
                     },
-                    isPanelLoaded: function(pnl) {
+                    isPanelLoaded(pnl) {
                         return _.includes(this.loadedPanels, pnl);
                     }
                 };
             },
-            getQueryVariable: function(name) {
-                var attrs = window.location.search.substring(1).split("&");
-
-                var attr = _.find(attrs, function(attr) {
-                    return attr === name || (attr.indexOf('=') >= 0 && attr.substr(0, attr.indexOf('=')) === name);
-                });
+            getQueryVariable(name) {
+                const attrs = window.location.search.substring(1).split('&');
+                const attr = _.find(attrs, (a) => a === name || (a.indexOf('=') >= 0 && a.substr(0, a.indexOf('=')) === name));
 
                 if (!isDefined(attr))
-                    return undefined;
+                    return null;
 
                 if (attr === name)
                     return true;
 
                 return attr.substr(attr.indexOf('=') + 1);
             },
-            cacheStoreJdbcDialects: cacheStoreJdbcDialects,
-            cacheStoreJdbcDialectsLabel: function (dialect) {
-                var found = _.find(cacheStoreJdbcDialects, function (dialectVal) {
+            cacheStoreJdbcDialects,
+            cacheStoreJdbcDialectsLabel(dialect) {
+                const found = _.find(cacheStoreJdbcDialects, function(dialectVal) {
                     return dialectVal.value === dialect;
                 });
 
-                return found ? found.label : undefined;
+                return found ? found.label : null;
             },
-            checkCachesDataSources: function (caches, checkCacheExt) {
-                var res = DS_CHECK_SUCCESS;
+            checkCachesDataSources(caches, checkCacheExt) {
+                let res = DS_CHECK_SUCCESS;
 
-                _.find(caches, function (curCache, curIx) {
+                _.find(caches, function(curCache, curIx) {
                     if (isDefined(checkCacheExt)) {
-                        if (!isDefined(checkCacheExt._id) || checkCacheExt.id != curCache._id) {
+                        if (checkCacheExt._id !== curCache._id) {
                             res = compareDataSources(checkCacheExt, curCache);
 
                             return !res.checked;
@@ -1028,27 +971,53 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
 
                         return false;
                     }
-                    else {
-                        return _.find(caches, function (checkCache, checkIx) {
-                            if (checkIx < curIx) {
-                                res = compareDataSources(checkCache, curCache);
 
-                                return !res.checked;
-                            }
+                    return _.find(caches, function(checkCache, checkIx) {
+                        if (checkIx < curIx) {
+                            res = compareDataSources(checkCache, curCache);
 
-                            return false;
-                        });
+                            return !res.checked;
+                        }
+
+                        return false;
+                    });
+                });
+
+                return res;
+            },
+            checkCacheSQLSchemas(caches, checkCacheExt) {
+                let res = DS_CHECK_SUCCESS;
+
+                _.find(caches, (curCache, curIx) => {
+                    if (isDefined(checkCacheExt)) {
+                        if (checkCacheExt._id !== curCache._id) {
+                            res = compareSQLSchemaNames(checkCacheExt, curCache);
+
+                            return !res.checked;
+                        }
+
+                        return false;
                     }
+
+                    return _.find(caches, function(checkCache, checkIx) {
+                        if (checkIx < curIx) {
+                            res = compareSQLSchemaNames(checkCache, curCache);
+
+                            return !res.checked;
+                        }
+
+                        return false;
+                    });
                 });
 
                 return res;
             },
-            autoCacheStoreConfiguration: function (cache, domains) {
-                var cacheStoreFactory = isDefined(cache.cacheStoreFactory) &&
+            autoCacheStoreConfiguration(cache, domains) {
+                const cacheStoreFactory = isDefined(cache.cacheStoreFactory) &&
                     isDefined(cache.cacheStoreFactory.kind);
 
                 if (!cacheStoreFactory && _.findIndex(domains, domainForStoreConfigured) >= 0) {
-                    var dflt = !cache.readThrough && !cache.writeThrough;
+                    const dflt = !cache.readThrough && !cache.writeThrough;
 
                     return {
                         cacheStoreFactory: {
@@ -1063,17 +1032,17 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
                     };
                 }
             },
-            autoClusterSwapSpiConfiguration: function (cluster, caches) {
-                var swapConfigured = cluster.swapSpaceSpi && cluster.swapSpaceSpi.kind;
+            autoClusterSwapSpiConfiguration(cluster, caches) {
+                const swapConfigured = cluster.swapSpaceSpi && cluster.swapSpaceSpi.kind;
 
-                if (!swapConfigured && _.find(caches, function (cache) {
+                if (!swapConfigured && _.find(caches, function(cache) {
                     return cache.swapEnabled;
                 }))
                     return {swapSpaceSpi: {kind: 'FileSwapSpaceSpi'}};
 
-                return undefined;
+                return null;
             },
-            randomString: (len) => {
+            randomString(len) {
                 const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
                 const possibleLen = possible.length;
 
@@ -1083,6 +1052,41 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
                     res += possible.charAt(Math.floor(Math.random() * possibleLen));
 
                 return res;
+            },
+            checkFieldValidators(ui) {
+                const form = ui.inputForm;
+                const errors = form.$error;
+                const errKeys = Object.keys(errors);
+
+                if (errKeys && errKeys.length > 0) {
+                    const firstErrorKey = errKeys[0];
+
+                    const firstError = errors[firstErrorKey][0];
+                    const actualError = firstError.$error[firstErrorKey][0];
+
+                    const errNameFull = actualError.$name;
+                    const errNameShort = errNameFull.endsWith('TextInput') ? errNameFull.substring(0, errNameFull.length - 9) : errNameFull;
+
+                    const extractErrorMessage = function(errName) {
+                        try {
+                            return errors[firstErrorKey][0].$errorMessages[errName][firstErrorKey];
+                        }
+                        catch (ignored) {
+                            try {
+                                return form[firstError.$name].$errorMessages[errName][firstErrorKey];
+                            }
+                            catch (ignited) {
+                                return false;
+                            }
+                        }
+                    };
+
+                    const msg = extractErrorMessage(errNameFull) || extractErrorMessage(errNameShort) || 'Invalid value!';
+
+                    return showPopoverMessage(ui, firstError.$name, errNameFull, msg);
+                }
+
+                return true;
             }
         };
     }]);
@@ -1090,25 +1094,24 @@ consoleModule.service('$common', ['$alert', '$popover', '$anchorScroll', '$locat
 // Confirm change location.
 consoleModule.service('$unsavedChangesGuard', ['$rootScope', ($root) => {
     return {
-        install: function ($scope) {
-            $scope.$on("$destroy", function() {
+        install($scope, customDirtyCheck = () => $scope.ui.inputForm.$dirty) {
+            $scope.$on('$destroy', function() {
                 window.onbeforeunload = null;
             });
 
-            var unbind = $root.$on('$stateChangeStart', function(event) {
-                if ($scope.ui && $scope.ui.inputForm && $scope.ui.inputForm.$dirty) {
-                    if (!confirm('You have unsaved changes.\n\nAre you sure you want to discard them?')) {
+            const unbind = $root.$on('$stateChangeStart', function(event) {
+                if ($scope.ui && $scope.ui.inputForm && customDirtyCheck()) {
+                    if (!confirm('You have unsaved changes.\n\nAre you sure you want to discard them?')) // eslint-disable-line no-alert
                         event.preventDefault();
-                    } else {
+                    else
                         unbind();
-                    }
                 }
             });
 
-            window.onbeforeunload = function(){
+            window.onbeforeunload = function() {
                 return $scope.ui && $scope.ui.inputForm && $scope.ui.inputForm.$dirty
                     ? 'You have unsaved changes.\n\nAre you sure you want to discard them?'
-                    : undefined;
+                    : null;
             };
         }
     };
@@ -1116,9 +1119,9 @@ consoleModule.service('$unsavedChangesGuard', ['$rootScope', ($root) => {
 
 // Service for confirm or skip several steps.
 consoleModule.service('$confirmBatch', ['$modal', '$rootScope', '$q', ($modal, $root, $q) => {
-    var scope = $root.$new();
+    const scope = $root.$new();
 
-    scope.confirmModal = $modal({templateUrl: '/templates/batch-confirm.html', scope: scope, placement: 'center', show: false});
+    scope.confirmModal = $modal({templateUrl: '/templates/batch-confirm.html', scope, placement: 'center', show: false});
 
     function _done(cancel) {
         scope.confirmModal.hide();
@@ -1138,43 +1141,43 @@ consoleModule.service('$confirmBatch', ['$modal', '$rootScope', '$q', ($modal, $
             _done();
     }
 
-    scope.cancel = function () {
-            _done(true);
+    scope.cancel = function() {
+        _done(true);
     };
 
-    scope.skip = function (applyToAll) {
+    scope.skip = function(applyToAll) {
         if (applyToAll) {
-            for (var i = scope.curIx; i < scope.items.length; i++)
+            for (let i = scope.curIx; i < scope.items.length; i++)
                 scope.items[i].skip = true;
 
-                _done();
-            }
-            else
-                _nextElement(true);
+            _done();
+        }
+        else
+            _nextElement(true);
     };
 
-    scope.overwrite = function (applyToAll) {
+    scope.overwrite = function(applyToAll) {
         if (applyToAll)
-                _done();
-            else
-                _nextElement(false);
+            _done();
+        else
+            _nextElement(false);
     };
 
     return {
-    /**
-     * Show confirm all dialog.
-     *
-         * @param confirmMessageFn Function to generate a confirm message.
-     * @param itemsToConfirm Array of element to process by confirm.
-     */
-        confirm: function (confirmMessageFn, itemsToConfirm) {
+        /**
+         * Show confirm all dialog.
+         *
+             * @param confirmMessageFn Function to generate a confirm message.
+         * @param itemsToConfirm Array of element to process by confirm.
+         */
+        confirm(confirmMessageFn, itemsToConfirm) {
             scope.deferred = $q.defer();
 
             scope.contentGenerator = confirmMessageFn;
 
             scope.items = itemsToConfirm;
             scope.curIx = 0;
-            scope.content = (scope.items && scope.items.length > 0) ? scope.contentGenerator(scope.items[0]) : undefined;
+            scope.content = (scope.items && scope.items.length > 0) ? scope.contentGenerator(scope.items[0]) : null;
 
             scope.confirmModal.$promise.then(scope.confirmModal.show);
 
@@ -1185,28 +1188,17 @@ consoleModule.service('$confirmBatch', ['$modal', '$rootScope', '$q', ($modal, $
 
 // 'Clone' popup service.
 consoleModule.service('$clone', ['$modal', '$rootScope', '$q', ($modal, $root, $q) => {
-    var scope = $root.$new();
+    const scope = $root.$new();
 
-    var deferred;
-    var _names = [];
-    var _validator;
-
-    scope.ok = function (newName) {
-        if (!_validator || _validator(newName)) {
-            deferred.resolve(_nextAvailableName(newName));
-
-            cloneModal.hide();
-        }
-    };
-
-    var cloneModal = $modal({templateUrl: '/templates/clone.html', scope: scope, placement: 'center', show: false});
+    let _names = [];
+    let deferred;
+    let _validator;
 
     function _nextAvailableName(name) {
-        var num = 1;
-
-        var tmpName = name;
+        let num = 1;
+        let tmpName = name;
 
-        while(_.includes(_names, tmpName)) {
+        while (_.includes(_names, tmpName)) {
             tmpName = name + '_' + num.toString();
 
             num++;
@@ -1215,7 +1207,17 @@ consoleModule.service('$clone', ['$modal', '$rootScope', '$q', ($modal, $root, $
         return tmpName;
     }
 
-    cloneModal.confirm = function (oldName, names, validator) {
+    const cloneModal = $modal({templateUrl: '/templates/clone.html', scope, placement: 'center', show: false});
+
+    scope.ok = function(newName) {
+        if (!_validator || _validator(newName)) {
+            deferred.resolve(_nextAvailableName(newName));
+
+            cloneModal.hide();
+        }
+    };
+
+    cloneModal.confirm = function(oldName, names, validator) {
         _names = names;
 
         scope.newName = _nextAvailableName(oldName);
@@ -1233,15 +1235,15 @@ consoleModule.service('$clone', ['$modal', '$rootScope', '$q', ($modal, $root, $
 }]);
 
 // Tables support service.
-consoleModule.service('$table', ['$common', '$focus', function ($common, $focus) {
+consoleModule.service('$table', ['$common', '$focus', function($common, $focus) {
     function _model(item, field) {
         return $common.getModel(item, field);
     }
 
-    var table = {name: 'none', editIndex: -1};
+    const table = {name: 'none', editIndex: -1};
 
     function _tableReset() {
-        table.field = undefined;
+        delete table.field;
         table.name = 'none';
         table.editIndex = -1;
 
@@ -1249,9 +1251,9 @@ consoleModule.service('$table', ['$common', '$focus', function ($common, $focus)
     }
 
     function _tableSaveAndReset() {
-        var field = table.field;
+        const field = table.field;
 
-        var save = $common.isDefined(field) && $common.isDefined(field.save);
+        const save = $common.isDefined(field) && $common.isDefined(field.save);
 
         if (!save || !$common.isDefined(field) || field.save(field, table.editIndex, true)) {
             _tableReset();
@@ -1269,7 +1271,7 @@ consoleModule.service('$table', ['$common', '$focus', function ($common, $focus)
     }
 
     function _tableUI(field) {
-        var ui = field.ui;
+        const ui = field.ui;
 
         return ui ? ui : field.type;
     }
@@ -1285,9 +1287,9 @@ consoleModule.service('$table', ['$common', '$focus', function ($common, $focus)
     function _tableStartEdit(item, tbl, index, save) {
         _tableState(tbl, index);
 
-        var val = _model(item, tbl)[tbl.model][index];
+        const val = _.get(_model(item, tbl), tbl.model)[index];
 
-        var ui = _tableUI(tbl);
+        const ui = _tableUI(tbl);
 
         tbl.save = save;
 
@@ -1317,7 +1319,7 @@ consoleModule.service('$table', ['$common', '$focus', function ($common, $focus)
     function _tableNewItem(tbl) {
         _tableState(tbl, -1);
 
-        var ui = _tableUI(tbl);
+        const ui = _tableUI(tbl);
 
         if (ui === 'table-pair') {
             tbl.newKey = null;
@@ -1347,53 +1349,55 @@ consoleModule.service('$table', ['$common', '$focus', function ($common, $focus)
         tableReset: _tableReset,
         tableSaveAndReset: _tableSaveAndReset,
         tableNewItem: _tableNewItem,
-        tableNewItemActive: function (tbl) {
+        tableNewItemActive(tbl) {
             return table.name === tbl.model && table.editIndex < 0;
         },
-        tableEditing: function (tbl, index) {
+        tableEditing(tbl, index) {
             return table.name === tbl.model && table.editIndex === index;
         },
-        tableEditedRowIndex: function () {
+        tableEditedRowIndex() {
             return table.editIndex;
         },
-        tableField: function () {
+        tableField() {
             return table.field;
         },
         tableStartEdit: _tableStartEdit,
-        tableRemove: function (item, field, index) {
+        tableRemove(item, field, index) {
             _tableReset();
 
-            _model(item, field)[field.model].splice(index, 1);
+            _.get(_model(item, field), field.model).splice(index, 1);
         },
         tablePairValue: _tablePairValue,
-        tablePairSave: function (pairValid, item, field, index, stopEdit) {
-            var valid = pairValid(item, field, index);
+        tablePairSave(pairValid, item, field, index, stopEdit) {
+            const valid = pairValid(item, field, index);
 
             if (valid) {
-                var pairValue = _tablePairValue(field, index);
+                const pairValue = _tablePairValue(field, index);
+
+                let pairModel = {};
 
-                var pairModel = {};
+                const container = _.get(item, field.model);
 
                 if (index < 0) {
                     pairModel[field.keyName] = pairValue.key;
                     pairModel[field.valueName] = pairValue.value;
 
-                    if (item[field.model])
-                        item[field.model].push(pairModel);
+                    if (container)
+                        container.push(pairModel);
                     else
-                        item[field.model] = [pairModel];
+                        _.set(item, field.model, [pairModel]);
 
                     if (!stopEdit)
                         _tableNewItem(field);
                 }
                 else {
-                    pairModel = item[field.model][index];
+                    pairModel = container[index];
 
                     pairModel[field.keyName] = pairValue.key;
                     pairModel[field.valueName] = pairValue.value;
 
                     if (!stopEdit) {
-                        if (index < item[field.model].length - 1)
+                        if (index < container.length - 1)
                             _tableStartEdit(item, field, index + 1);
                         else
                             _tableNewItem(field);
@@ -1403,33 +1407,33 @@ consoleModule.service('$table', ['$common', '$focus', function ($common, $focus)
 
             return valid;
         },
-        tablePairSaveVisible: function (field, index) {
-            var pairValue = _tablePairValue(field, index);
+        tablePairSaveVisible(field, index) {
+            const pairValue = _tablePairValue(field, index);
 
             return !$common.isEmptyString(pairValue.key) && !$common.isEmptyString(pairValue.value);
         },
-        tableFocusInvalidField: function (index, id) {
+        tableFocusInvalidField(index, id) {
             _tableFocus(id, index);
 
             return false;
         },
-        tableFieldId: function (index, id) {
+        tableFieldId(index, id) {
             return (index < 0 ? 'new' : 'cur') + id + (index >= 0 ? index : '');
         }
     };
 }]);
 
-consoleModule.service('ngCopy', ['$window', '$common', function ($window, $common) {
-    var body = angular.element($window.document.body);
+consoleModule.service('ngCopy', ['$window', '$common', function($window, $common) {
+    const body = angular.element($window.document.body);
 
-    var textArea = angular.element('<textarea/>');
+    const textArea = angular.element('<textarea/>');
 
     textArea.css({
         position: 'fixed',
         opacity: '0'
     });
 
-    return function (toCopy) {
+    return function(toCopy) {
         textArea.val(toCopy);
 
         body.append(textArea);
@@ -1440,18 +1444,18 @@ consoleModule.service('ngCopy', ['$window', '$common', function ($window, $commo
             if (document.execCommand('copy'))
                 $common.showInfo('Value copied to clipboard');
             else
-                window.prompt("Copy to clipboard: Ctrl+C, Enter", toCopy);
+                window.prompt('Copy to clipboard: Ctrl+C, Enter', toCopy);  // eslint-disable-line no-alert
         } catch (err) {
-            window.prompt("Copy to clipboard: Ctrl+C, Enter", toCopy);
+            window.prompt('Copy to clipboard: Ctrl+C, Enter', toCopy);  // eslint-disable-line no-alert
         }
 
         textArea.remove();
     };
-}]).directive('ngClickCopy', ['ngCopy', function (ngCopy) {
+}]).directive('ngClickCopy', ['ngCopy', function(ngCopy) {
     return {
         restrict: 'A',
-        link: function (scope, element, attrs) {
-            element.bind('click', function () {
+        link(scope, element, attrs) {
+            element.bind('click', function() {
                 ngCopy(attrs.ngClickCopy);
             });
 
@@ -1461,36 +1465,16 @@ consoleModule.service('ngCopy', ['$window', '$common', function ($window, $commo
     };
 }]);
 
-consoleModule.filter('tablesSearch', function() {
-    return function(array, query) {
-        if (!angular.isUndefined(array) && !angular.isUndefined(query) && !angular.isUndefined(query.$)) {
-            var filtredArray = [];
-
-            var matchString = query.$.toLowerCase();
-
-            angular.forEach(array, function (row) {
-                var label = (row.schema + '.' + row.tbl).toLowerCase();
-
-                if (label.indexOf(matchString) >= 0)
-                    filtredArray.push(row);
-            });
-
-            return filtredArray;
-        } else
-            return array;
-    };
-});
-
 // Filter domain models with key fields configuration.
-consoleModule.filter('domainsValidation', ['$common', function ($common) {
+consoleModule.filter('domainsValidation', ['$common', function($common) {
     return function(domains, valid, invalid) {
         if (valid && invalid)
             return domains;
 
-        var out = [];
+        const out = [];
 
-        _.forEach(domains, function (domain) {
-            var _valid = !$common.domainForStoreConfigured(domain) || $common.isJavaBuiltInClass(domain.keyType) || !_.isEmpty(domain.keyFields);
+        _.forEach(domains, function(domain) {
+            const _valid = !$common.domainForStoreConfigured(domain) || $common.isJavaBuiltInClass(domain.keyType) || !_.isEmpty(domain.keyFields);
 
             if (valid && _valid || invalid && !_valid)
                 out.push(domain);
@@ -1504,7 +1488,7 @@ consoleModule.filter('domainsValidation', ['$common', function ($common) {
 consoleModule.directive('match', ['$parse', ($parse) => {
     return {
         require: 'ngModel',
-        link: (scope, elem, attrs, ctrl) => {
+        link(scope, elem, attrs, ctrl) {
             scope.$watch(() => $parse(attrs.match)(scope) === ctrl.$modelValue, (currentValue) => ctrl.$setValidity('mismatch', currentValue));
         }
     };
@@ -1512,13 +1496,11 @@ consoleModule.directive('match', ['$parse', ($parse) => {
 
 // Directive to bind ENTER key press with some user action.
 consoleModule.directive('onEnter', ['$timeout', ($timeout) => {
-    return function (scope, elem, attrs) {
-        elem.on('keydown keypress', function (event) {
+    return function(scope, elem, attrs) {
+        elem.on('keydown keypress', function(event) {
             if (event.which === 13) {
-                scope.$apply(function () {
-                    $timeout(function () {
-                        scope.$eval(attrs.onEnter)
-                    });
+                scope.$apply(function() {
+                    $timeout(() => scope.$eval(attrs.onEnter));
                 });
 
                 event.preventDefault();
@@ -1526,7 +1508,7 @@ consoleModule.directive('onEnter', ['$timeout', ($timeout) => {
         });
 
         // Removes bound events in the element itself when the scope is destroyed.
-        scope.$on('$destroy', function () {
+        scope.$on('$destroy', function() {
             elem.off('keydown keypress');
         });
     };
@@ -1534,10 +1516,10 @@ consoleModule.directive('onEnter', ['$timeout', ($timeout) => {
 
 // Directive to bind ESC key press with some user action.
 consoleModule.directive('onEscape', () => {
-    return function (scope, elem, attrs) {
-        elem.on('keydown keypress', function (event) {
+    return function(scope, elem, attrs) {
+        elem.on('keydown keypress', function(event) {
             if (event.which === 27) {
-                scope.$apply(function () {
+                scope.$apply(function() {
                     scope.$eval(attrs.onEscape);
                 });
 
@@ -1546,7 +1528,7 @@ consoleModule.directive('onEscape', () => {
         });
 
         // Removes bound events in the element itself when the scope is destroyed
-        scope.$on('$destroy', function () {
+        scope.$on('$destroy', function() {
             elem.off('keydown keypress');
         });
     };
@@ -1554,35 +1536,35 @@ consoleModule.directive('onEscape', () => {
 
 // Directive to retain selection. To fix angular-strap typeahead bug with setting cursor to the end of text.
 consoleModule.directive('retainSelection', ['$timeout', ($timeout) => {
-    var promise;
+    let promise;
 
-    return function (scope, elem) {
-        elem.on('keydown', function (evt) {
-            var key = evt.which;
-            var ctrlDown = evt.ctrlKey || evt.metaKey;
-            var input = this;
-            var start = input.selectionStart;
+    return function(scope, elem) {
+        elem.on('keydown', function(evt) {
+            const key = evt.which;
+            const ctrlDown = evt.ctrlKey || evt.metaKey;
+            const input = this;
+            let start = input.selectionStart;
 
             if (promise)
                 $timeout.cancel(promise);
 
-            promise = $timeout(function () {
-                var setCursor = false;
+            promise = $timeout(() => {
+                let setCursor = false;
 
                 // Handle Backspace[8].
-                if (key == 8 && start > 0) {
+                if (key === 8 && start > 0) {
                     start -= 1;
 
                     setCursor = true;
                 }
                 // Handle Del[46].
-                else if (key == 46)
+                else if (key === 46)
                     setCursor = true;
                 // Handle: Caps Lock[20], Tab[9], Shift[16], Ctrl[17], Alt[18], Esc[27], Enter[13], Arrows[37..40], Home[36], End[35], Ins[45], PgUp[33], PgDown[34], F1..F12[111..124], Num Lock[], Scroll Lock[145].
-                else if (!(key == 8 || key == 9 || key == 13 || (key > 15 && key < 20) || key == 27 ||
-                    (key > 32 && key < 41) || key == 45 || (key > 111 && key < 124) || key == 144 || key == 145)) {
+                else if (!(key === 8 || key === 9 || key === 13 || (key > 15 && key < 20) || key === 27 ||
+                    (key > 32 && key < 41) || key === 45 || (key > 111 && key < 124) || key === 144 || key === 145)) {
                     // Handle: Ctrl + [A[65], C[67], V[86]].
-                    if (!(ctrlDown && (key = 65 || key == 67 || key == 86))) {
+                    if (!(ctrlDown && (key === 65 || key === 67 || key === 86))) {
                         start += 1;
 
                         setCursor = true;
@@ -1592,12 +1574,12 @@ consoleModule.directive('retainSelection', ['$timeout', ($timeout) => {
                 if (setCursor)
                     input.setSelectionRange(start, start);
 
-                promise = undefined;
+                promise = null;
             });
         });
 
         // Removes bound events in the element itself when the scope is destroyed
-        scope.$on('$destroy', function () {
+        scope.$on('$destroy', function() {
             elem.off('keydown');
         });
     };
@@ -1605,12 +1587,12 @@ consoleModule.directive('retainSelection', ['$timeout', ($timeout) => {
 
 // Factory function to focus element.
 consoleModule.factory('$focus', ['$timeout', ($timeout) => {
-    return function (id) {
+    return function(id) {
         // Timeout makes sure that is invoked after any other event has been triggered.
         // E.g. click events that need to run before the focus or inputs elements that are
         // in a disabled state but are enabled when those events are triggered.
-        $timeout(function () {
-            var elem = $('#' + id);
+        $timeout(() => {
+            const elem = $('#' + id);
 
             if (elem.length > 0)
                 elem[0].focus();
@@ -1622,18 +1604,16 @@ consoleModule.factory('$focus', ['$timeout', ($timeout) => {
 consoleModule.directive('autoFocus', ['$timeout', ($timeout) => {
     return {
         restrict: 'AC',
-        link: function(scope, element) {
-            $timeout(function(){
-                element[0].focus();
-            });
+        link(scope, element) {
+            $timeout(() => element[0].focus());
         }
     };
 }]);
 
 // Directive to focus next element on ENTER key.
 consoleModule.directive('enterFocusNext', ['$focus', ($focus) => {
-    return function (scope, elem, attrs) {
-        elem.on('keydown keypress', function (event) {
+    return function(scope, elem, attrs) {
+        elem.on('keydown keypress', function(event) {
             if (event.which === 13) {
                 event.preventDefault();
 
@@ -1645,13 +1625,13 @@ consoleModule.directive('enterFocusNext', ['$focus', ($focus) => {
 
 // Directive to mark elements to focus.
 consoleModule.directive('onClickFocus', ['$focus', ($focus) => {
-    return function (scope, elem, attr) {
-        elem.on('click', function () {
+    return function(scope, elem, attr) {
+        elem.on('click', function() {
             $focus(attr.onClickFocus);
         });
 
         // Removes bound events in the element itself when the scope is destroyed
-        scope.$on('$destroy', function () {
+        scope.$on('$destroy', function() {
             elem.off('click');
         });
     };
@@ -1660,9 +1640,9 @@ consoleModule.directive('onClickFocus', ['$focus', ($focus) => {
 consoleModule.controller('resetPassword', [
     '$scope', '$modal', '$http', '$common', '$focus', 'Auth', '$state',
     ($scope, $modal, $http, $common, $focus, Auth, $state) => {
-        if ($state.params.token)
+        if ($state.params.token) {
             $http.post('/api/v1/password/validate/token', {token: $state.params.token})
-                .success(function (res) {
+                .success((res) => {
                     $scope.email = res.email;
                     $scope.token = res.token;
                     $scope.error = res.error;
@@ -1670,16 +1650,17 @@ consoleModule.controller('resetPassword', [
                     if ($scope.token && !$scope.error)
                         $focus('user_password');
                 });
+        }
 
         // Try to reset user password for provided token.
-        $scope.resetPassword = function (reset_info) {
+        $scope.resetPassword = function(reset_info) {
             $http.post('/api/v1/password/reset', reset_info)
-                .success(function () {
+                .success(function() {
                     $common.showInfo('Password successfully changed');
 
                     $state.go('base.configuration.clusters');
                 })
-                .error(function (data, state) {
+                .error(function(data, state) {
                     $common.showError(data);
 
                     if (state === 503)
@@ -1693,7 +1674,7 @@ consoleModule.controller('resetPassword', [
 // TODO IGNITE-1936 Refactor this controller.
 consoleModule.controller('auth', ['$scope', '$focus', 'Auth', 'IgniteCountries', ($scope, $focus, Auth, Countries) => {
     $scope.auth = Auth.auth;
-
+    $scope.forgotPassword = Auth.forgotPassword;
     $scope.action = 'signin';
     $scope.countries = Countries.getAll();
 
@@ -1703,57 +1684,58 @@ consoleModule.controller('auth', ['$scope', '$focus', 'Auth', 'IgniteCountries',
 // Navigation bar controller.
 consoleModule.controller('notebooks', ['$rootScope', '$scope', '$modal', '$state', '$http', '$common',
     ($root, $scope, $modal, $state, $http, $common) => {
-    $root.notebooks = [];
+        $root.notebooks = [];
 
-    // Pre-fetch modal dialogs.
-    var _notebookNewModal = $modal({scope: $scope, templateUrl: '/sql/notebook-new.html', show: false});
+        // Pre-fetch modal dialogs.
+        const _notebookNewModal = $modal({scope: $scope, templateUrl: '/sql/notebook-new.html', show: false});
 
-    $root.rebuildDropdown = function() {
-        $scope.notebookDropdown = [
-            {text: 'Create new notebook', click: 'inputNotebookName()'},
-            {divider: true}
-        ];
+        $root.rebuildDropdown = function() {
+            $scope.notebookDropdown = [
+                {text: 'Create new notebook', click: 'inputNotebookName()'},
+                {divider: true}
+            ];
 
-        _.forEach($root.notebooks, function (notebook) {
-            $scope.notebookDropdown.push({
-                text: notebook.name,
-                sref: 'base.sql.notebook({noteId:"' + notebook._id + '"})'
+            _.forEach($root.notebooks, function(notebook) {
+                $scope.notebookDropdown.push({
+                    text: notebook.name,
+                    sref: 'base.sql.notebook({noteId:"' + notebook._id + '"})'
+                });
             });
-        });
-    };
+        };
 
-    $root.reloadNotebooks = function() {
-        // When landing on the page, get clusters and show them.
-        $http.post('/api/v1/notebooks/list')
-            .success(function (data) {
-                $root.notebooks = data;
+        $root.reloadNotebooks = function() {
+            // When landing on the page, get clusters and show them.
+            $http.post('/api/v1/notebooks/list')
+                .success(function(data) {
+                    $root.notebooks = data;
 
-                $root.rebuildDropdown();
-            })
-            .error(function (errMsg) {
-                $common.showError(errMsg);
-            });
-    };
+                    $root.rebuildDropdown();
+                })
+                .error(function(errMsg) {
+                    $common.showError(errMsg);
+                });
+        };
 
-    $root.inputNotebookName = function() {
-        _notebookNewModal.$promise.then(_notebookNewModal.show);
-    };
+        $root.inputNotebookName = function() {
+            _notebookNewModal.$promise.then(_notebookNewModal.show);
+        };
 
-    $root.createNewNotebook = function(name) {
-        $http.post('/api/v1/notebooks/new', {name: name})
-            .success(function (noteId) {
-                _notebookNewModal.hide();
+        $root.createNewNotebook = function(name) {
+            $http.post('/api/v1/notebooks/new', {name})
+                .success(function(noteId) {
+                    _notebookNewModal.hide();
 
-                $root.reloadNotebooks();
+                    $root.reloadNotebooks();
 
-                $state.go('base.sql.notebook', {noteId: noteId});
-            })
-            .error(function (message) {
-                $common.showError(message);
-            });
-    };
+                    $state.go('base.sql.notebook', {noteId});
+                })
+                .error(function(message) {
+                    $common.showError(message);
+                });
+        };
 
-    $root.reloadNotebooks();
-}]);
+        $root.reloadNotebooks();
+    }
+]);
 
 export default consoleModule;


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

Posted by ak...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/controllers/sql-controller.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/controllers/sql-controller.js b/modules/web-console/src/main/js/controllers/sql-controller.js
index 4bc39e2..a8058ff 100644
--- a/modules/web-console/src/main/js/controllers/sql-controller.js
+++ b/modules/web-console/src/main/js/controllers/sql-controller.js
@@ -19,18 +19,26 @@
 import consoleModule from 'controllers/common-module';
 
 consoleModule.controller('sqlController', [
-    '$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', '$animate', '$location', '$anchorScroll', '$state', '$modal', '$popover', '$loading', '$common', '$confirm', 'IgniteAgentMonitor', 'IgniteChartColors', 'QueryNotebooks', 'uiGridExporterConstants',
-    function ($root, $scope, $http, $q, $timeout, $interval, $animate, $location, $anchorScroll, $state, $modal, $popover, $loading, $common, $confirm, agentMonitor, IgniteChartColors, QueryNotebooks, uiGridExporterConstants) {
-        var stopTopology = null;
+    '$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', '$animate', '$location', '$anchorScroll', '$state', '$modal', '$popover', '$loading', '$common', '$confirm', 'IgniteAgentMonitor', 'IgniteChartColors', 'QueryNotebooks', 'uiGridConstants', 'uiGridExporterConstants',
+    function($root, $scope, $http, $q, $timeout, $interval, $animate, $location, $anchorScroll, $state, $modal, $popover, $loading, $common, $confirm, agentMonitor, IgniteChartColors, QueryNotebooks, uiGridConstants, uiGridExporterConstants) {
+        let stopTopology = null;
 
-        $scope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
+        const _tryStopRefresh = function(paragraph) {
+            if (paragraph.rate && paragraph.rate.stopTime) {
+                $interval.cancel(paragraph.rate.stopTime);
+
+                delete paragraph.rate.stopTime;
+            }
+        };
+
+        const _stopTopologyRefresh = () => {
             $interval.cancel(stopTopology);
 
             if ($scope.notebook && $scope.notebook.paragraphs)
-                $scope.notebook.paragraphs.forEach(function (paragraph) {
-                    _tryStopRefresh(paragraph);
-                });
-        });
+                $scope.notebook.paragraphs.forEach((paragraph) => _tryStopRefresh(paragraph));
+        };
+
+        $scope.$on('$stateChangeStart', _stopTopologyRefresh);
 
         $scope.caches = [];
 
@@ -51,14 +59,14 @@ consoleModule.controller('sqlController', [
         ];
 
         $scope.exportDropdown = [
-            { 'text': 'Export all', 'click': 'exportCsvAll(paragraph)' }
-            //{ 'text': 'Export all to CSV', 'click': 'exportCsvAll(paragraph)' },
-            //{ 'text': 'Export all to PDF', 'click': 'exportPdfAll(paragraph)' }
+            { text: 'Export all', click: 'exportCsvAll(paragraph)' }
+            // { 'text': 'Export all to CSV', 'click': 'exportCsvAll(paragraph)' },
+            // { 'text': 'Export all to PDF', 'click': 'exportPdfAll(paragraph)' }
         ];
 
         $scope.metadata = [];
 
-        $scope.metaFilter = "";
+        $scope.metaFilter = '';
 
         $scope.metaOptions = {
             nodeChildren: 'children',
@@ -69,1429 +77,1451 @@ consoleModule.controller('sqlController', [
             }
         };
 
-        $scope.maskCacheName = (cacheName) => _.isEmpty(cacheName) ? "&lt;default&gt;" : cacheName;
+        $scope.maskCacheName = (cacheName) => _.isEmpty(cacheName) ? '<default>' : cacheName;
 
-        var _handleException = function(err) {
+        const _handleException = function(err) {
             $common.showError(err);
         };
 
         // Time line X axis descriptor.
-        var TIME_LINE = {value: -1, type: 'java.sql.Date', label: 'TIME_LINE'};
+        const TIME_LINE = {value: -1, type: 'java.sql.Date', label: 'TIME_LINE'};
 
         // Row index X axis descriptor.
-        var ROW_IDX = {value: -2, type: 'java.lang.Integer', label: 'ROW_IDX'};
+        const ROW_IDX = {value: -2, type: 'java.lang.Integer', label: 'ROW_IDX'};
 
         // We need max 1800 items to hold history for 30 mins in case of refresh every second.
-        var HISTORY_LENGTH = 1800;
+        const HISTORY_LENGTH = 1800;
 
-        var MAX_VAL_COLS = IgniteChartColors.length;
+        const MAX_VAL_COLS = IgniteChartColors.length;
 
         $anchorScroll.yOffset = 55;
 
         $scope.chartColor = function(index) {
-            return {"color": "white", "background-color": IgniteChartColors[index]};
+            return {color: 'white', 'background-color': IgniteChartColors[index]};
         };
 
-        $scope.chartRemoveKeyColumn = function (paragraph, index) {
-            paragraph.chartKeyCols.splice(index, 1);
+        function _chartNumber(arr, idx, dflt) {
+            if (idx >= 0 && arr && arr.length > idx && _.isNumber(arr[idx]))
+                return arr[idx];
 
-            _chartApplySettings(paragraph, true);
-        };
+            return dflt;
+        }
 
-        $scope.chartRemoveValColumn = function (paragraph, index) {
-            paragraph.chartValCols.splice(index, 1);
+        function _min(rows, idx, dflt) {
+            let min = _chartNumber(rows[0], idx, dflt);
 
-            _chartApplySettings(paragraph, true);
-        };
+            _.forEach(rows, (row) => {
+                const v = _chartNumber(row, idx, dflt);
 
-        $scope.chartAcceptKeyColumn = function(paragraph, item) {
-            var accepted = _.findIndex(paragraph.chartKeyCols, item) < 0;
+                if (v < min)
+                    min = v;
+            });
 
-            if (accepted) {
-                paragraph.chartKeyCols = [item];
+            return min;
+        }
 
-                _chartApplySettings(paragraph, true);
-            }
+        function _max(rows, idx, dflt) {
+            let max = _chartNumber(rows[0], idx, dflt);
 
-            return false;
-        };
+            _.forEach(rows, (row) => {
+                const v = _chartNumber(row, idx, dflt);
 
-        $scope.chartAcceptValColumn = function(paragraph, item) {
-            var valCols = paragraph.chartValCols;
+                if (v > max)
+                    max = v;
+            });
 
-            var accepted = _.findIndex(valCols, item) < 0 && item.value >= 0 && _numberType(item.type);
+            return max;
+        }
 
-            if (accepted) {
-                if (valCols.length == MAX_VAL_COLS - 1)
-                    valCols.shift();
+        function _sum(rows, idx) {
+            let sum = 0;
 
-                valCols.push(item);
+            _.forEach(rows, (row) => sum += _chartNumber(row, idx, 0));
 
-                _chartApplySettings(paragraph, true);
-            }
+            return sum;
+        }
 
-            return false;
-        };
+        function _aggregate(rows, aggFx, idx, dflt) {
+            const len = rows.length;
 
-        $scope.scrollParagraphs = [];
+            switch (aggFx) {
+                case 'FIRST':
+                    return _chartNumber(rows[0], idx, dflt);
 
-        $scope.rebuildScrollParagraphs = function () {
-            $scope.scrollParagraphs = $scope.notebook.paragraphs.map(function (paragraph) {
-                return {
-                    "text": paragraph.name,
-                    "click": 'scrollToParagraph("' + paragraph.id + '")'
-                };
-            });
-        };
+                case 'LAST':
+                    return _chartNumber(rows[len - 1], idx, dflt);
 
-        $scope.scrollToParagraph = function (paragraphId) {
-            var idx = _.findIndex($scope.notebook.paragraphs, {id: paragraphId});
+                case 'MIN':
+                    return _min(rows, idx, dflt);
 
-            if (idx >= 0) {
-                if (!_.includes($scope.notebook.expandedParagraphs, idx))
-                    $scope.notebook.expandedParagraphs.push(idx);
+                case 'MAX':
+                    return _max(rows, idx, dflt);
 
-                setTimeout(function () {
-                    $scope.notebook.paragraphs[idx].ace.focus();
-                });
-            }
+                case 'SUM':
+                    return _sum(rows, idx);
 
-            $location.hash(paragraphId);
+                case 'AVG':
+                    return len > 0 ? _sum(rows, idx) / len : 0;
 
-            $anchorScroll();
-        };
+                case 'COUNT':
+                    return len;
 
-        const _hideColumn = (col) => col.fieldName !== '_KEY' && col.fieldName !== '_VAL';
+                default:
+            }
 
-        const _allColumn = () => true;
+            return 0;
+        }
 
-        var paragraphId = 0;
+        function _chartLabel(arr, idx, dflt) {
+            if (arr && arr.length > idx && _.isString(arr[idx]))
+                return arr[idx];
 
-        function enhanceParagraph(paragraph) {
-            paragraph.nonEmpty = function () {
-                return this.rows && this.rows.length > 0;
-            };
+            return dflt;
+        }
 
-            paragraph.chart = function () {
-                return this.result != 'table' && this.result != 'none';
-            };
+        function _chartDatum(paragraph) {
+            let datum = [];
 
-            paragraph.queryExecuted = () =>
-                paragraph.queryArgs && paragraph.queryArgs.query && !paragraph.queryArgs.query.startsWith('EXPLAIN ');
+            if (paragraph.chartColumnsConfigured()) {
+                paragraph.chartValCols.forEach(function(valCol) {
+                    let index = 0;
+                    let values = [];
+                    const colIdx = valCol.value;
 
-            paragraph.table = function () {
-                return this.result == 'table';
-            };
+                    if (paragraph.chartTimeLineEnabled()) {
+                        const aggFx = valCol.aggFx;
+                        const colLbl = valCol.label + ' [' + aggFx + ']';
 
-            paragraph.chartColumnsConfigured = function () {
-                return !_.isEmpty(this.chartKeyCols) && !_.isEmpty(this.chartValCols);
-            };
+                        if (paragraph.charts && paragraph.charts.length === 1)
+                            datum = paragraph.charts[0].data;
 
-            paragraph.chartTimeLineEnabled = function () {
-                return !_.isEmpty(this.chartKeyCols) && angular.equals(this.chartKeyCols[0], TIME_LINE);
-            };
+                        const chartData = _.find(datum, {series: valCol.label});
 
-            paragraph.timeLineSupported = function () {
-                return this.result != 'pie';
-            };
+                        const leftBound = new Date();
+                        leftBound.setMinutes(leftBound.getMinutes() - parseInt(paragraph.timeLineSpan, 10));
 
-            paragraph.refreshExecuting = function () {
-                return paragraph.rate && paragraph.rate.stopTime
-            };
+                        if (chartData) {
+                            const lastItem = _.last(paragraph.chartHistory);
 
-            Object.defineProperty(paragraph, 'gridOptions', { value: {
-                onRegisterApi: function(api) {
-                    $animate.enabled(api.grid.element, false);
+                            values = chartData.values;
 
-                    this.api = api;
-                },
-                enableGridMenu: false,
-                enableColumnMenus: false,
-                setRows: function(rows) {
-                    this.height = Math.min(rows.length, 15) * 30 + 42 + 'px';
+                            values.push({
+                                x: lastItem.tm,
+                                y: _aggregate(lastItem.rows, aggFx, colIdx, index++)
+                            });
 
-                    this.data = rows;
-                }
-            }});
+                            while (values.length > 0 && values[0].x < leftBound)
+                                values.shift();
+                        }
+                        else {
+                            _.forEach(paragraph.chartHistory, (history) => {
+                                if (history.tm >= leftBound) {
+                                    values.push({
+                                        x: history.tm,
+                                        y: _aggregate(history.rows, aggFx, colIdx, index++)
+                                    });
+                                }
+                            });
 
-            Object.defineProperty(paragraph, 'chartHistory', {value: []});
-        }
+                            datum.push({series: valCol.label, key: colLbl, values});
+                        }
+                    }
+                    else {
+                        index = paragraph.total;
 
-        $scope.aceInit = function (paragraph) {
-            return function (editor) {
-                editor.setAutoScrollEditorIntoView(true);
-                editor.$blockScrolling = Infinity;
+                        values = _.map(paragraph.rows, function(row) {
+                            const xCol = paragraph.chartKeyCols[0].value;
 
-                var renderer = editor.renderer;
+                            const v = {
+                                x: _chartNumber(row, xCol, index),
+                                xLbl: _chartLabel(row, xCol, null),
+                                y: _chartNumber(row, colIdx, index)
+                            };
 
-                renderer.setHighlightGutterLine(false);
-                renderer.setShowPrintMargin(false);
-                renderer.setOption('fontFamily', 'monospace');
-                renderer.setOption('fontSize', '14px');
-                renderer.setOption('minLines', '5');
-                renderer.setOption('maxLines', '15');
+                            index++;
 
-                editor.setTheme('ace/theme/chrome');
+                            return v;
+                        });
 
-                Object.defineProperty(paragraph, 'ace', { value: editor });
+                        datum.push({series: valCol.label, key: valCol.label, values});
+                    }
+                });
             }
-        };
 
-        var _setActiveCache = function () {
-            if ($scope.caches.length > 0)
-                _.forEach($scope.notebook.paragraphs, function (paragraph) {
-                    if (!_.find($scope.caches, {name: paragraph.cacheName}))
-                        paragraph.cacheName = $scope.caches[0].name;
-                });
-        };
+            return datum;
+        }
 
-        const _refreshFn = () =>
-            agentMonitor.topology()
-                .then((clusters) => {
-                    agentMonitor.checkModal();
+        function _xX(d) {
+            return d.x;
+        }
 
-                    const caches = _.flattenDeep(clusters.map((cluster) => cluster.caches));
+        function _yY(d) {
+            return d.y;
+        }
 
-                    $scope.caches = _.sortBy(_.uniqBy(_.reject(caches, { mode: 'LOCAL' }), 'name'), 'name');
+        function _xAxisTimeFormat(d) {
+            return d3.time.format('%X')(new Date(d));
+        }
 
-                    _setActiveCache();
-                })
-                .catch((err) => {
-                    if (err.code === 2)
-                        return agentMonitor.showNodeError('Agent is failed to authenticate in grid. Please check agent\'s login and password.');
+        const _intClasses = ['java.lang.Byte', 'java.lang.Integer', 'java.lang.Long', 'java.lang.Short'];
 
-                    agentMonitor.showNodeError(err.message)}
-                );
+        function _intType(cls) {
+            return _.includes(_intClasses, cls);
+        }
 
-        var loadNotebook = function (notebook) {
-            $scope.notebook = notebook;
+        const _xAxisWithLabelFormat = function(paragraph) {
+            return function(d) {
+                const values = paragraph.charts[0].data[0].values;
 
-            $scope.notebook_name = notebook.name;
+                const fmt = _intType(paragraph.chartKeyCols[0].type) ? 'd' : ',.2f';
 
-            if (!$scope.notebook.expandedParagraphs)
-                $scope.notebook.expandedParagraphs = [];
+                const dx = values[d];
 
-            if (!$scope.notebook.paragraphs)
-                $scope.notebook.paragraphs = [];
+                if (!dx)
+                    return d3.format(fmt)(d);
 
-            _.forEach(notebook.paragraphs, function (paragraph) {
-                paragraph.id = 'paragraph-' + paragraphId++;
+                const lbl = dx.xLbl;
 
-                enhanceParagraph(paragraph);
-            });
+                return lbl ? lbl : d3.format(fmt)(d);
+            };
+        };
 
-            if (!notebook.paragraphs || notebook.paragraphs.length == 0)
-                $scope.addParagraph();
-            else
-                $scope.rebuildScrollParagraphs();
+        function _xAxisLabel(paragraph) {
+            return _.isEmpty(paragraph.chartKeyCols) ? 'X' : paragraph.chartKeyCols[0].label;
+        }
 
-            agentMonitor.startWatch({
-                    state: 'base.configuration.clusters',
-                    text: 'Back to Configuration',
-                    goal: 'execute sql statements'
-                })
-                .then(() => {
-                    $loading.start('sqlLoading');
+        const _yAxisFormat = function(d) {
+            const fmt = d < 1000 ? ',.2f' : '.3s';
 
-                    _refreshFn()
-                        .finally(() => {
-                            if ($root.IgniteDemoMode)
-                                _.forEach($scope.notebook.paragraphs, $scope.execute);
+            return d3.format(fmt)(d);
+        };
 
-                            $loading.finish('sqlLoading');
+        function _updateCharts(paragraph) {
+            $timeout(() => _.forEach(paragraph.charts, (chart) => chart.api.update()), 100);
+        }
 
-                            stopTopology = $interval(_refreshFn, 5000, 0, false);
-                        });
-                });
-        };
+        function _updateChartsWithData(paragraph, newDatum) {
+            $timeout(() => {
+                if (!paragraph.chartTimeLineEnabled()) {
+                    const chartDatum = paragraph.charts[0].data;
 
-        QueryNotebooks.read($state.params.noteId)
-            .then(loadNotebook)
-            .catch(function() {
-                $scope.notebookLoadFailed = true;
+                    chartDatum.length = 0;
 
-                $loading.finish('sqlLoading');
+                    _.forEach(newDatum, (series) => chartDatum.push(series));
+                }
+
+                paragraph.charts[0].api.update();
             });
+        }
 
-        $scope.renameNotebook = function (name) {
-            if (!name)
-                return;
+        function _yAxisLabel(paragraph) {
+            const cols = paragraph.chartValCols;
 
-            if ($scope.notebook.name != name) {
-                $scope.notebook.name = name;
+            const tml = paragraph.chartTimeLineEnabled();
 
-                QueryNotebooks.save($scope.notebook)
-                    .then(function() {
-                        var idx = _.findIndex($root.notebooks, function (item) {
-                            return item._id == $scope.notebook._id;
-                        });
+            return _.isEmpty(cols) ? 'Y' : _.map(cols, function(col) {
+                let lbl = col.label;
 
-                        if (idx >= 0) {
-                            $root.notebooks[idx].name = name;
+                if (tml)
+                    lbl += ' [' + col.aggFx + ']';
 
-                            $root.rebuildDropdown();
-                        }
+                return lbl;
+            }).join(', ');
+        }
 
-                        $scope.notebook.edit = false;
-                    })
-                    .catch(_handleException);
-            }
-            else
-                $scope.notebook.edit = false
-        };
+        function _barChart(paragraph) {
+            const datum = _chartDatum(paragraph);
 
-        $scope.removeNotebook = function () {
-            $confirm.confirm('Are you sure you want to remove: "' + $scope.notebook.name + '"?')
-                .then(function () {
-                    return QueryNotebooks.remove($scope.notebook);
-                })
-                .then(function (notebook) {
-                    if (notebook)
-                        $state.go('base.sql.notebook', {noteId: notebook._id});
-                    else
-                        $state.go('base.configuration.clusters');
-                })
-                .catch(_handleException);
-        };
-
-        $scope.renameParagraph = function (paragraph, newName) {
-            if (!newName)
-                return;
+            if (_.isEmpty(paragraph.charts)) {
+                const stacked = paragraph.chartsOptions && paragraph.chartsOptions.barChart
+                    ? paragraph.chartsOptions.barChart.stacked
+                    : true;
 
-            if (paragraph.name != newName) {
-                paragraph.name = newName;
+                const options = {
+                    chart: {
+                        type: 'multiBarChart',
+                        height: 400,
+                        margin: {left: 70},
+                        duration: 0,
+                        x: _xX,
+                        y: _yY,
+                        xAxis: {
+                            axisLabel: _xAxisLabel(paragraph),
+                            tickFormat: paragraph.chartTimeLineEnabled() ? _xAxisTimeFormat : _xAxisWithLabelFormat(paragraph),
+                            showMaxMin: false
+                        },
+                        yAxis: {
+                            axisLabel: _yAxisLabel(paragraph),
+                            tickFormat: _yAxisFormat
+                        },
+                        color: IgniteChartColors,
+                        stacked,
+                        showControls: true,
+                        legend: {
+                            vers: 'furious',
+                            margin: {right: -25}
+                        }
+                    }
+                };
 
-                $scope.rebuildScrollParagraphs();
+                paragraph.charts = [{options, data: datum}];
 
-                QueryNotebooks.save($scope.notebook)
-                    .then(function () { paragraph.edit = false; })
-                    .catch(_handleException);
+                _updateCharts(paragraph);
             }
             else
-                paragraph.edit = false
-        };
+                _updateChartsWithData(paragraph, datum);
+        }
 
-        $scope.addParagraph = function () {
-            var sz = $scope.notebook.paragraphs.length;
+        function _pieChartDatum(paragraph) {
+            const datum = [];
 
-            var paragraph = {
-                id: 'paragraph-' + paragraphId++,
-                name: 'Query' + (sz ==0 ? '' : sz),
-                query: '',
-                pageSize: $scope.pageSizes[0],
-                timeLineSpan: $scope.timeLineSpans[0],
-                result: 'none',
-                rate: {
-                    value: 1,
-                    unit: 60000,
-                    installed: false
-                }
-            };
+            if (paragraph.chartColumnsConfigured() && !paragraph.chartTimeLineEnabled()) {
+                paragraph.chartValCols.forEach(function(valCol) {
+                    let index = paragraph.total;
 
-            enhanceParagraph(paragraph);
+                    const values = _.map(paragraph.rows, (row) => {
+                        const xCol = paragraph.chartKeyCols[0].value;
 
-            if ($scope.caches && $scope.caches.length > 0)
-                paragraph.cacheName = $scope.caches[0].name;
+                        const v = {
+                            x: xCol < 0 ? index : row[xCol],
+                            y: _chartNumber(row, valCol.value, index)
+                        };
 
-            $scope.notebook.paragraphs.push(paragraph);
+                        // Workaround for known problem with zero values on Pie chart.
+                        if (v.y === 0)
+                            v.y = 0.0001;
 
-            $scope.notebook.expandedParagraphs.push(sz);
+                        index++;
 
-            $scope.rebuildScrollParagraphs();
+                        return v;
+                    });
 
-            $location.hash(paragraph.id);
+                    datum.push({series: paragraph.chartKeyCols[0].label, key: valCol.label, values});
+                });
+            }
 
-            $anchorScroll();
+            return datum;
+        }
 
-            setTimeout(function () {
-                paragraph.ace.focus();
+        function _pieChart(paragraph) {
+            let datum = _pieChartDatum(paragraph);
+
+            if (datum.length === 0)
+                datum = [{values: []}];
+
+            paragraph.charts = _.map(datum, function(data) {
+                return {
+                    options: {
+                        chart: {
+                            type: 'pieChart',
+                            height: 400,
+                            duration: 0,
+                            x: _xX,
+                            y: _yY,
+                            showLabels: true,
+                            labelThreshold: 0.05,
+                            labelType: 'percent',
+                            donut: true,
+                            donutRatio: 0.35,
+                            legend: {
+                                vers: 'furious',
+                                margin: {
+                                    right: -25
+                                }
+                            }
+                        },
+                        title: {
+                            enable: true,
+                            text: data.key
+                        }
+                    },
+                    data: data.values
+                };
             });
-        };
 
-        $scope.setResult = function (paragraph, new_result) {
-            if (paragraph.result === new_result)
-                return;
+            _updateCharts(paragraph);
+        }
 
-            _saveChartSettings(paragraph);
+        function _lineChart(paragraph) {
+            const datum = _chartDatum(paragraph);
 
-            paragraph.result = new_result;
+            if (_.isEmpty(paragraph.charts)) {
+                const options = {
+                    chart: {
+                        type: 'lineChart',
+                        height: 400,
+                        margin: { left: 70 },
+                        duration: 0,
+                        x: _xX,
+                        y: _yY,
+                        xAxis: {
+                            axisLabel: _xAxisLabel(paragraph),
+                            tickFormat: paragraph.chartTimeLineEnabled() ? _xAxisTimeFormat : _xAxisWithLabelFormat(paragraph),
+                            showMaxMin: false
+                        },
+                        yAxis: {
+                            axisLabel: _yAxisLabel(paragraph),
+                            tickFormat: _yAxisFormat
+                        },
+                        color: IgniteChartColors,
+                        useInteractiveGuideline: true,
+                        legend: {
+                            vers: 'furious',
+                            margin: {
+                                right: -25
+                            }
+                        }
+                    }
+                };
 
-            if (paragraph.chart())
-                _chartApplySettings(paragraph, true);
-        };
+                paragraph.charts = [{options, data: datum}];
 
-        $scope.resultEq = function(paragraph, result) {
-            return (paragraph.result === result);
-        };
+                _updateCharts(paragraph);
+            }
+            else
+                _updateChartsWithData(paragraph, datum);
+        }
 
-        $scope.removeParagraph = function(paragraph) {
-            $confirm.confirm('Are you sure you want to remove: "' + paragraph.name + '"?')
-                .then(function () {
-                    $scope.stopRefresh(paragraph);
+        function _areaChart(paragraph) {
+            const datum = _chartDatum(paragraph);
 
-                    var paragraph_idx = _.findIndex($scope.notebook.paragraphs, function (item) {
-                        return paragraph == item;
-                    });
+            if (_.isEmpty(paragraph.charts)) {
+                const style = paragraph.chartsOptions && paragraph.chartsOptions.areaChart
+                    ? paragraph.chartsOptions.areaChart.style
+                    : 'stack';
 
-                    var panel_idx = _.findIndex($scope.expandedParagraphs, function (item) {
-                        return paragraph_idx == item;
-                    });
+                const options = {
+                    chart: {
+                        type: 'stackedAreaChart',
+                        height: 400,
+                        margin: {left: 70},
+                        duration: 0,
+                        x: _xX,
+                        y: _yY,
+                        xAxis: {
+                            axisLabel: _xAxisLabel(paragraph),
+                            tickFormat: paragraph.chartTimeLineEnabled() ? _xAxisTimeFormat : _xAxisWithLabelFormat(paragraph),
+                            showMaxMin: false
+                        },
+                        yAxis: {
+                            axisLabel: _yAxisLabel(paragraph),
+                            tickFormat: _yAxisFormat
+                        },
+                        color: IgniteChartColors,
+                        style,
+                        legend: {
+                            vers: 'furious',
+                            margin: {right: -25}
+                        }
+                    }
+                };
 
-                    if (panel_idx >= 0)
-                        $scope.expandedParagraphs.splice(panel_idx, 1);
+                paragraph.charts = [{options, data: datum}];
 
-                    $scope.notebook.paragraphs.splice(paragraph_idx, 1);
+                _updateCharts(paragraph);
+            }
+            else
+                _updateChartsWithData(paragraph, datum);
+        }
 
-                    $scope.rebuildScrollParagraphs();
+        function _chartApplySettings(paragraph, resetCharts) {
+            if (resetCharts)
+                paragraph.charts = [];
 
-                    QueryNotebooks.save($scope.notebook)
-                        .catch(_handleException);
-                });
-        };
+            if (paragraph.chart() && paragraph.nonEmpty()) {
+                switch (paragraph.result) {
+                    case 'bar':
+                        _barChart(paragraph);
+                        break;
 
-        $scope.paragraphExpanded = function(paragraph) {
-            var paragraph_idx = _.findIndex($scope.notebook.paragraphs, function (item) {
-                return paragraph == item;
-            });
+                    case 'pie':
+                        _pieChart(paragraph);
+                        break;
 
-            var panel_idx = _.findIndex($scope.notebook.expandedParagraphs, function (item) {
-                return paragraph_idx == item;
-            });
+                    case 'line':
+                        _lineChart(paragraph);
+                        break;
 
-            return panel_idx >= 0;
+                    case 'area':
+                        _areaChart(paragraph);
+                        break;
+
+                    default:
+                }
+            }
+        }
+
+        $scope.chartRemoveKeyColumn = function(paragraph, index) {
+            paragraph.chartKeyCols.splice(index, 1);
+
+            _chartApplySettings(paragraph, true);
         };
 
-        var _columnFilter = function(paragraph) {
-            return paragraph.disabledSystemColumns || paragraph.systemColumns ? _allColumn : _hideColumn;
+        $scope.chartRemoveValColumn = function(paragraph, index) {
+            paragraph.chartValCols.splice(index, 1);
+
+            _chartApplySettings(paragraph, true);
         };
 
-        var _notObjectType = function(cls) {
-            return $common.isJavaBuiltInClass(cls);
+        $scope.chartAcceptKeyColumn = function(paragraph, item) {
+            const accepted = _.findIndex(paragraph.chartKeyCols, item) < 0;
+
+            if (accepted) {
+                paragraph.chartKeyCols = [item];
+
+                _chartApplySettings(paragraph, true);
+            }
+
+            return false;
         };
 
-        var _numberClasses = ['java.math.BigDecimal', 'java.lang.Byte', 'java.lang.Double',
+        const _numberClasses = ['java.math.BigDecimal', 'java.lang.Byte', 'java.lang.Double',
             'java.lang.Float', 'java.lang.Integer', 'java.lang.Long', 'java.lang.Short'];
 
-        var _numberType = function(cls) {
+        const _numberType = function(cls) {
             return _.includes(_numberClasses, cls);
         };
 
-        var _intClasses = ['java.lang.Byte', 'java.lang.Integer', 'java.lang.Long', 'java.lang.Short'];
+        $scope.chartAcceptValColumn = function(paragraph, item) {
+            const valCols = paragraph.chartValCols;
 
-        function _intType(cls) {
-            return _.includes(_intClasses, cls);
-        }
+            const accepted = _.findIndex(valCols, item) < 0 && item.value >= 0 && _numberType(item.type);
 
-        var _rebuildColumns = function (paragraph) {
-            var columnDefs = [];
+            if (accepted) {
+                if (valCols.length === MAX_VAL_COLS - 1)
+                    valCols.shift();
 
-            _.forEach(_.groupBy(paragraph.meta, 'fieldName'), function (colsByName, fieldName) {
-                var colsByTypes = _.groupBy(colsByName, 'typeName');
+                valCols.push(item);
 
-                var needType = _.keys(colsByTypes).length > 1;
+                _chartApplySettings(paragraph, true);
+            }
 
-                _.forEach(colsByTypes, function(colsByType, typeName) {
-                    _.forEach(colsByType, function (col, ix) {
-                        col.fieldName = (needType && !$common.isEmptyString(typeName) ? typeName + '.' : '') + fieldName + (ix > 0 ? ix : '');
-                    })
-                });
-            });
+            return false;
+        };
 
-            _.forEach(paragraph.meta, function (col, idx) {
-                if (paragraph.columnFilter(col)) {
-                    if (_notObjectType(col.fieldTypeName))
-                        paragraph.chartColumns.push({value: idx, type: col.fieldTypeName, label: col.fieldName, aggFx: $scope.aggregateFxs[0]});
-
-                    columnDefs.push({
-                        displayName: col.fieldName,
-                        headerTooltip: _fullColName(col),
-                        field: paragraph.queryArgs.query ? '' + idx : col.fieldName,
-                        minWidth: 50
-                    });
-                }
+        $scope.scrollParagraphs = [];
+
+        $scope.rebuildScrollParagraphs = function() {
+            $scope.scrollParagraphs = $scope.notebook.paragraphs.map(function(paragraph) {
+                return {
+                    text: paragraph.name,
+                    click: 'scrollToParagraph("' + paragraph.id + '")'
+                };
             });
+        };
 
-            paragraph.gridOptions.columnDefs = columnDefs;
+        $scope.scrollToParagraph = function(paragraphId) {
+            const idx = _.findIndex($scope.notebook.paragraphs, {id: paragraphId});
 
-            if (paragraph.chartColumns.length > 0) {
-                paragraph.chartColumns.push(TIME_LINE);
-                paragraph.chartColumns.push(ROW_IDX);
+            if (idx >= 0) {
+                if (!_.includes($scope.notebook.expandedParagraphs, idx))
+                    $scope.notebook.expandedParagraphs.push(idx);
+
+                setTimeout(function() {
+                    $scope.notebook.paragraphs[idx].ace.focus();
+                });
             }
 
-            // We could accept onl not object columns for X axis.
-            paragraph.chartKeyCols = _retainColumns(paragraph.chartColumns, paragraph.chartKeyCols, _notObjectType, true);
+            $location.hash(paragraphId);
 
-            // We could accept only numeric columns for Y axis.
-            paragraph.chartValCols = _retainColumns(paragraph.chartColumns, paragraph.chartValCols, _numberType, false, paragraph.chartKeyCols);
+            $anchorScroll();
         };
 
-        $scope.toggleSystemColumns = function (paragraph) {
-            if (paragraph.disabledSystemColumns)
-                return;
-
-            paragraph.systemColumns = !paragraph.systemColumns;
-
-            paragraph.columnFilter = _columnFilter(paragraph);
+        const _hideColumn = (col) => col.fieldName !== '_KEY' && col.fieldName !== '_VAL';
 
-            paragraph.chartColumns = [];
+        const _allColumn = () => true;
 
-            _rebuildColumns(paragraph);
-        };
+        let paragraphId = 0;
 
-        function _retainColumns(allCols, curCols, acceptableType, xAxis, unwantedCols) {
-            var retainedCols = [];
+        const _fullColName = function(col) {
+            const res = [];
 
-            var availableCols = xAxis ? allCols : _.filter(allCols, function (col) {
-                return col.value >= 0;
-            });
+            if (col.schemaName)
+                res.push(col.schemaName);
 
-            if (availableCols.length > 0) {
-                curCols.forEach(function (curCol) {
-                    var col = _.find(availableCols, {label: curCol.label});
+            if (col.typeName)
+                res.push(col.typeName);
 
-                    if (col && acceptableType(col.type)) {
-                        col.aggFx = curCol.aggFx;
+            res.push(col.fieldName);
 
-                        retainedCols.push(col);
-                    }
-                });
+            return res.join('.');
+        };
 
-                // If nothing was restored, add first acceptable column.
-                if (_.isEmpty(retainedCols)) {
-                    var col;
+        function enhanceParagraph(paragraph) {
+            paragraph.nonEmpty = function() {
+                return this.rows && this.rows.length > 0;
+            };
 
-                    if (unwantedCols)
-                        col = _.find(availableCols, function (col) {
-                            return !_.find(unwantedCols, {label: col.label}) && acceptableType(col.type);
-                        });
+            paragraph.chart = function() {
+                return this.result !== 'table' && this.result !== 'none';
+            };
 
-                    if (!col)
-                        col = _.find(availableCols, function (col) {
-                            return acceptableType(col.type);
-                        });
+            paragraph.queryExecuted = () =>
+                paragraph.queryArgs && paragraph.queryArgs.query && !paragraph.queryArgs.query.startsWith('EXPLAIN ');
 
-                    if (col)
-                        retainedCols.push(col);
-                }
-            }
+            paragraph.table = function() {
+                return this.result === 'table';
+            };
 
-            return retainedCols;
-        }
+            paragraph.chartColumnsConfigured = function() {
+                return !_.isEmpty(this.chartKeyCols) && !_.isEmpty(this.chartValCols);
+            };
 
-        /**
-         * @param {Object} paragraph Query
-         * @param {{fieldsMetadata: Array, items: Array, queryId: int, last: Boolean}} res Query results.
-         * @private
-         */
-        var _processQueryResult = function (paragraph, res) {
-            var prevKeyCols = paragraph.chartKeyCols;
-            var prevValCols = paragraph.chartValCols;
+            paragraph.chartTimeLineEnabled = function() {
+                return !_.isEmpty(this.chartKeyCols) && angular.equals(this.chartKeyCols[0], TIME_LINE);
+            };
 
-            if (!_.eq(paragraph.meta, res.fieldsMetadata)) {
-                paragraph.meta = [];
+            paragraph.timeLineSupported = function() {
+                return this.result !== 'pie';
+            };
 
-                paragraph.chartColumns = [];
+            paragraph.refreshExecuting = function() {
+                return paragraph.rate && paragraph.rate.stopTime;
+            };
 
-                if (!$common.isDefined(paragraph.chartKeyCols))
-                    paragraph.chartKeyCols = [];
+            Object.defineProperty(paragraph, 'gridOptions', { value: {
+                enableGridMenu: false,
+                enableColumnMenus: false,
+                flatEntityAccess: true,
+                fastWatch: true,
+                updateColumns(cols) {
+                    this.columnDefs = _.map(cols, (col) => {
+                        return {
+                            displayName: col.fieldName,
+                            headerTooltip: _fullColName(col),
+                            field: col.field,
+                            minWidth: 50
+                        };
+                    });
 
-                if (!$common.isDefined(paragraph.chartValCols))
-                    paragraph.chartValCols = [];
+                    $timeout(() => this.api.core.notifyDataChange(uiGridConstants.dataChange.COLUMN));
+                },
+                updateRows(rows) {
+                    const sizeChanged = this.data.length !== rows.length;
 
-                if (res.fieldsMetadata.length <= 2) {
-                    var _key = _.find(res.fieldsMetadata, {fieldName: '_KEY'});
-                    var _val = _.find(res.fieldsMetadata, {fieldName: '_VAL'});
+                    this.data = rows;
 
-                    paragraph.disabledSystemColumns = (res.fieldsMetadata.length == 2 && _key && _val) ||
-                        (res.fieldsMetadata.length == 1 && (_key || _val));
-                }
+                    if (sizeChanged) {
+                        const height = Math.min(rows.length, 15) * 30 + 47;
 
-                paragraph.columnFilter = _columnFilter(paragraph);
+                        // Remove header height.
+                        this.api.grid.element.css('height', height + 'px');
 
-                paragraph.meta = res.fieldsMetadata;
+                        $timeout(() => this.api.core.handleWindowResize());
+                    }
+                },
+                onRegisterApi(api) {
+                    $animate.enabled(api.grid.element, false);
 
-                _rebuildColumns(paragraph);
-            }
+                    this.api = api;
+                }
+            }});
 
-            paragraph.page = 1;
+            Object.defineProperty(paragraph, 'chartHistory', {value: []});
+        }
 
-            paragraph.total = 0;
+        $scope.aceInit = function(paragraph) {
+            return function(editor) {
+                editor.setAutoScrollEditorIntoView(true);
+                editor.$blockScrolling = Infinity;
 
-            paragraph.queryId = res.last ? null : res.queryId;
+                const renderer = editor.renderer;
 
-            delete paragraph.errMsg;
+                renderer.setHighlightGutterLine(false);
+                renderer.setShowPrintMargin(false);
+                renderer.setOption('fontFamily', 'monospace');
+                renderer.setOption('fontSize', '14px');
+                renderer.setOption('minLines', '5');
+                renderer.setOption('maxLines', '15');
 
-            // Prepare explain results for display in table.
-            if (paragraph.queryArgs.query && paragraph.queryArgs.query.startsWith('EXPLAIN') && res.items) {
-                paragraph.rows = [];
+                editor.setTheme('ace/theme/chrome');
 
-                res.items.forEach(function (row, i) {
-                    var line = res.items.length - 1 == i ? row[0] : row[0] + '\n';
+                Object.defineProperty(paragraph, 'ace', { value: editor });
+            };
+        };
 
-                    line.replace(/\"/g, '').split('\n').forEach(function (line) {
-                        paragraph.rows.push([line]);
-                    });
+        const _setActiveCache = function() {
+            if ($scope.caches.length > 0) {
+                _.forEach($scope.notebook.paragraphs, (paragraph) => {
+                    if (!_.find($scope.caches, {name: paragraph.cacheName}))
+                        paragraph.cacheName = $scope.caches[0].name;
                 });
             }
-            else
-                paragraph.rows = res.items;
-
-            paragraph.gridOptions.setRows(paragraph.rows);
+        };
 
-            var chartHistory = paragraph.chartHistory;
+        const _updateTopology = () =>
+            agentMonitor.topology()
+                .then((clusters) => {
+                    agentMonitor.checkModal();
 
-            // Clear history on query change.
-            var queryChanged = paragraph.prevQuery != paragraph.query;
+                    const caches = _.flattenDeep(clusters.map((cluster) => cluster.caches));
 
-            if (queryChanged) {
-                paragraph.prevQuery = paragraph.query;
+                    $scope.caches = _.sortBy(_.map(_.uniqBy(_.reject(caches, {mode: 'LOCAL'}), 'name'), (cache) => {
+                        cache.label = $scope.maskCacheName(cache.name);
 
-                chartHistory.length = 0;
+                        return cache;
+                    }), 'label');
 
-                _.forEach(paragraph.charts, function (chart) {
-                    chart.data.length = 0;
+                    _setActiveCache();
                 })
-            }
-
-            // Add results to history.
-            chartHistory.push({tm: new Date(), rows: paragraph.rows});
-
-            // Keep history size no more than max length.
-            while (chartHistory.length > HISTORY_LENGTH)
-                chartHistory.shift();
+                .catch((err) => {
+                    if (err.code === 2)
+                        return agentMonitor.showNodeError('Agent is failed to authenticate in grid. Please check agent\'s login and password.');
 
-            _showLoading(paragraph, false);
+                    agentMonitor.showNodeError(err);
+                });
 
-            if (paragraph.result === 'none' || !paragraph.queryExecuted())
-                paragraph.result = 'table';
-            else if (paragraph.chart()) {
-                var resetCharts = queryChanged;
+        const _startTopologyRefresh = () => {
+            $loading.start('sqlLoading');
 
-                if (!resetCharts) {
-                    var curKeyCols = paragraph.chartKeyCols;
-                    var curValCols = paragraph.chartValCols;
+            agentMonitor.awaitAgent()
+                .then(_updateTopology)
+                .finally(() => {
+                    if ($root.IgniteDemoMode)
+                        _.forEach($scope.notebook.paragraphs, $scope.execute);
 
-                    resetCharts = !prevKeyCols || !prevValCols ||
-                        prevKeyCols.length != curKeyCols.length ||
-                        prevValCols.length != curValCols.length;
-                }
+                    $loading.finish('sqlLoading');
 
-                _chartApplySettings(paragraph, resetCharts);
-            }
+                    stopTopology = $interval(_updateTopology, 5000, 0, false);
+                });
         };
 
-        const _closeOldQuery = (paragraph) => {
-            const queryId = paragraph.queryArgs && paragraph.queryArgs.queryId;
+        const loadNotebook = function(notebook) {
+            $scope.notebook = notebook;
 
-            return queryId ? agentMonitor.queryClose(queryId) : $q.when();
-        };
+            $scope.notebook_name = notebook.name;
 
-        const _executeRefresh = (paragraph) => {
-            const args = paragraph.queryArgs;
+            if (!$scope.notebook.expandedParagraphs)
+                $scope.notebook.expandedParagraphs = [];
 
-            agentMonitor.awaitAgent()
-                .then(() => _closeOldQuery(paragraph))
-                .then(() => agentMonitor.query(args.cacheName, args.pageSize, args.query))
-                .then(_processQueryResult.bind(this, paragraph))
-                .catch((err) => {
-                    paragraph.errMsg = err.message;
-                });
-        };
+            if (!$scope.notebook.paragraphs)
+                $scope.notebook.paragraphs = [];
 
-        const _showLoading = (paragraph, enable) => paragraph.loading = enable;
+            _.forEach(notebook.paragraphs, (paragraph) => {
+                paragraph.id = 'paragraph-' + paragraphId++;
 
-        $scope.execute = function (paragraph) {
-            QueryNotebooks.save($scope.notebook)
-                .catch(_handleException);
+                enhanceParagraph(paragraph);
+            });
 
-            paragraph.prevQuery = paragraph.queryArgs ? paragraph.queryArgs.query : paragraph.query;
+            if (!notebook.paragraphs || notebook.paragraphs.length === 0)
+                $scope.addParagraph();
+            else
+                $scope.rebuildScrollParagraphs();
 
-            _showLoading(paragraph, true);
+            agentMonitor.startWatch({
+                state: 'base.configuration.clusters',
+                text: 'Back to Configuration',
+                goal: 'execute sql statements',
+                onDisconnect: () => {
+                    _stopTopologyRefresh();
 
-            _closeOldQuery(paragraph)
-                .then(function () {
-                    const args = paragraph.queryArgs = {
-                        cacheName: paragraph.cacheName,
-                        pageSize: paragraph.pageSize,
-                        query: paragraph.query
-                    };
+                    _startTopologyRefresh();
+                }
+            })
+            .then(_startTopologyRefresh);
+        };
 
-                    return agentMonitor.query(args.cacheName, args.pageSize, args.query);
-                })
-                .then(function (res) {
-                    _processQueryResult(paragraph, res);
+        QueryNotebooks.read($state.params.noteId)
+            .then(loadNotebook)
+            .catch(() => {
+                $scope.notebookLoadFailed = true;
 
-                    _tryStartRefresh(paragraph);
-                })
-                .catch((err) => {
-                    paragraph.errMsg = err.message;
+                $loading.finish('sqlLoading');
+            });
 
-                    _showLoading(paragraph, false);
+        $scope.renameNotebook = function(name) {
+            if (!name)
+                return;
 
-                    $scope.stopRefresh(paragraph);
-                })
-                .finally(function () {
-                    paragraph.ace.focus();
-                });
-        };
+            if ($scope.notebook.name !== name) {
+                const prevName = $scope.notebook.name;
 
-        $scope.queryExecuted = function(paragraph) {
-            return $common.isDefined(paragraph.queryArgs);
-        };
+                $scope.notebook.name = name;
 
-        $scope.explain = function (paragraph) {
-            QueryNotebooks.save($scope.notebook)
-                .catch(_handleException);
+                QueryNotebooks.save($scope.notebook)
+                    .then(function() {
+                        const idx = _.findIndex($root.notebooks, function(item) {
+                            return item._id === $scope.notebook._id;
+                        });
 
-            _cancelRefresh(paragraph);
+                        if (idx >= 0) {
+                            $root.notebooks[idx].name = name;
 
-            _showLoading(paragraph, true);
+                            $root.rebuildDropdown();
+                        }
 
-            _closeOldQuery(paragraph)
-                .then(function () {
-                    const args = paragraph.queryArgs = {
-                        cacheName: paragraph.cacheName,
-                        pageSize: paragraph.pageSize,
-                        query: 'EXPLAIN ' + paragraph.query
-                    };
+                        $scope.notebook.edit = false;
+                    })
+                    .catch((err) => {
+                        $scope.notebook.name = prevName;
 
-                    return agentMonitor.query(args.cacheName, args.pageSize, args.query);
-                })
-                .then(_processQueryResult.bind(this, paragraph))
-                .catch((err) => {
-                    paragraph.errMsg = err.message;
+                        _handleException(err);
+                    });
+            }
+            else
+                $scope.notebook.edit = false;
+        };
 
-                    _showLoading(paragraph, false);
+        $scope.removeNotebook = function() {
+            $confirm.confirm('Are you sure you want to remove: "' + $scope.notebook.name + '"?')
+                .then(function() {
+                    return QueryNotebooks.remove($scope.notebook);
+                })
+                .then(function(notebook) {
+                    if (notebook)
+                        $state.go('base.sql.notebook', {noteId: notebook._id});
+                    else
+                        $state.go('base.configuration.clusters');
                 })
-                .finally(function () {
-                    paragraph.ace.focus();
-                });
-        };
-
-        $scope.scan = function (paragraph) {
-            QueryNotebooks.save($scope.notebook)
                 .catch(_handleException);
+        };
 
-            _cancelRefresh(paragraph);
-
-            _showLoading(paragraph, true);
+        $scope.renameParagraph = function(paragraph, newName) {
+            if (!newName)
+                return;
 
-            _closeOldQuery(paragraph)
-                .then(() => {
-                    const args = paragraph.queryArgs = {
-                        cacheName: paragraph.cacheName,
-                        pageSize: paragraph.pageSize
-                    };
+            if (paragraph.name !== newName) {
+                paragraph.name = newName;
 
-                    return agentMonitor.query(args.cacheName, args.pageSize);
-                })
-                .then(_processQueryResult.bind(this, paragraph))
-                .catch((err) => {
-                    paragraph.errMsg = err.message;
+                $scope.rebuildScrollParagraphs();
 
-                    _showLoading(paragraph, false);
-                })
-                .finally(function () {
-                    paragraph.ace.focus();
-                });
+                QueryNotebooks.save($scope.notebook)
+                    .then(function() { paragraph.edit = false; })
+                    .catch(_handleException);
+            }
+            else
+                paragraph.edit = false;
         };
 
-        $scope.nextPage = function(paragraph) {
-            _showLoading(paragraph, true);
+        $scope.addParagraph = function() {
+            const sz = $scope.notebook.paragraphs.length;
 
-            paragraph.queryArgs.pageSize = paragraph.pageSize;
+            const paragraph = {
+                id: 'paragraph-' + paragraphId++,
+                name: 'Query' + (sz === 0 ? '' : sz),
+                query: '',
+                pageSize: $scope.pageSizes[0],
+                timeLineSpan: $scope.timeLineSpans[0],
+                result: 'none',
+                rate: {
+                    value: 1,
+                    unit: 60000,
+                    installed: false
+                }
+            };
 
-            agentMonitor.next(paragraph.queryId, paragraph.pageSize)
-                .then(function (res) {
-                    paragraph.page++;
+            enhanceParagraph(paragraph);
 
-                    paragraph.total += paragraph.rows.length;
+            if ($scope.caches && $scope.caches.length > 0)
+                paragraph.cacheName = $scope.caches[0].name;
 
-                    paragraph.rows = res.items;
+            $scope.notebook.paragraphs.push(paragraph);
 
-                    if (paragraph.chart()) {
-                        if (paragraph.result == 'pie')
-                            _updatePieChartsWithData(paragraph, _pieChartDatum(paragraph));
-                        else
-                            _updateChartsWithData(paragraph, _chartDatum(paragraph));
-                    }
+            $scope.notebook.expandedParagraphs.push(sz);
 
-                    paragraph.gridOptions.setRows(paragraph.rows);
+            $scope.rebuildScrollParagraphs();
 
-                    _showLoading(paragraph, false);
+            $location.hash(paragraph.id);
 
-                    if (res.last)
-                        delete paragraph.queryId;
-                })
-                .catch((err) => {
-                    paragraph.errMsg = err.message;
+            $anchorScroll();
 
-                    _showLoading(paragraph, false);
-                })
-                .finally(function () {
-                    paragraph.ace.focus();
-                });
+            setTimeout(function() {
+                paragraph.ace.focus();
+            });
         };
 
-        var _fullColName = function(col) {
-            var res = [];
-
-            if (col.schemaName)
-                res.push(col.schemaName);
-            if (col.typeName)
-                res.push(col.typeName);
+        function _saveChartSettings(paragraph) {
+            if (!_.isEmpty(paragraph.charts)) {
+                const chart = paragraph.charts[0].api.getScope().chart;
 
-            res.push(col.fieldName);
+                if (!$common.isDefined(paragraph.chartsOptions))
+                    paragraph.chartsOptions = {barChart: {stacked: true}, areaChart: {style: 'stack'}};
 
-            return res.join('.');
-        };
+                switch (paragraph.result) {
+                    case 'bar':
+                        paragraph.chartsOptions.barChart.stacked = chart.stacked();
 
-        const _export = (fileName, columnFilter, meta, rows) => {
-            let csvContent = '';
+                        break;
 
-            const cols = [];
-            const excludedCols = [];
+                    case 'area':
+                        paragraph.chartsOptions.areaChart.style = chart.style();
 
-            if (meta) {
-                _.forEach(meta, (col, idx) => {
-                    if (columnFilter(col))
-                        cols.push(_fullColName(col));
-                    else
-                        excludedCols.push(idx);
-                });
+                        break;
 
-                csvContent += cols.join(';') + '\n';
+                    default:
+                }
             }
+        }
 
-            _.forEach(rows, (row) => {
-                cols.length = 0;
+        $scope.setResult = function(paragraph, new_result) {
+            if (paragraph.result === new_result)
+                return;
 
-                if (Array.isArray(row)) {
-                    _.forEach(row, (elem, idx) => {
-                        if (_.includes(excludedCols, idx))
-                            return;
+            _saveChartSettings(paragraph);
 
-                        cols.push(_.isUndefined(elem) ? '' : JSON.stringify(elem));
+            paragraph.result = new_result;
+
+            if (paragraph.chart())
+                _chartApplySettings(paragraph, true);
+            else
+                $timeout(() => paragraph.gridOptions.api.core.handleWindowResize());
+        };
+
+        $scope.resultEq = function(paragraph, result) {
+            return (paragraph.result === result);
+        };
+
+        $scope.removeParagraph = function(paragraph) {
+            $confirm.confirm('Are you sure you want to remove: "' + paragraph.name + '"?')
+                .then(function() {
+                    $scope.stopRefresh(paragraph);
+
+                    const paragraph_idx = _.findIndex($scope.notebook.paragraphs, function(item) {
+                        return paragraph === item;
                     });
-                }
-                else {
-                    _.forEach(meta, (col) => {
-                        if (columnFilter(col)) {
-                            const elem = row[col.fieldName];
 
-                            cols.push(_.isUndefined(elem) ? '' : JSON.stringify(elem));
-                        }
+                    const panel_idx = _.findIndex($scope.expandedParagraphs, function(item) {
+                        return paragraph_idx === item;
                     });
-                }
 
-                csvContent += cols.join(';') + '\n';
-            });
+                    if (panel_idx >= 0)
+                        $scope.expandedParagraphs.splice(panel_idx, 1);
 
-            $common.download('application/octet-stream;charset=utf-8', fileName, escape(csvContent));
-        };
+                    $scope.notebook.paragraphs.splice(paragraph_idx, 1);
 
-        $scope.exportCsv = function(paragraph) {
-            _export(paragraph.name + '.csv', paragraph.columnFilter, paragraph.meta, paragraph.rows);
+                    $scope.rebuildScrollParagraphs();
 
-            //paragraph.gridOptions.api.exporter.csvExport(uiGridExporterConstants.ALL, uiGridExporterConstants.VISIBLE);
+                    QueryNotebooks.save($scope.notebook)
+                        .catch(_handleException);
+                });
         };
 
-        $scope.exportPdf = function(paragraph) {
-            paragraph.gridOptions.api.exporter.pdfExport(uiGridExporterConstants.ALL, uiGridExporterConstants.VISIBLE);
-        };
+        $scope.paragraphExpanded = function(paragraph) {
+            const paragraph_idx = _.findIndex($scope.notebook.paragraphs, function(item) {
+                return paragraph === item;
+            });
 
-        $scope.exportCsvAll = function (paragraph) {
-            const args = paragraph.queryArgs;
+            const panel_idx = _.findIndex($scope.notebook.expandedParagraphs, function(item) {
+                return paragraph_idx === item;
+            });
 
-            agentMonitor.queryGetAll(args.cacheName, args.query)
-                .then((res) => _export(paragraph.name + '-all.csv', paragraph.columnFilter, res.fieldsMetadata, res.items))
-                .finally(() => paragraph.ace.focus());
+            return panel_idx >= 0;
         };
 
-        $scope.exportPdfAll = function(paragraph) {
-            //$http.post('/api/v1/agent/query/getAll', {query: paragraph.query, cacheName: paragraph.cacheName})
-            //    .success(function (item) {
-            //        _export(paragraph.name + '-all.csv', item.meta, item.rows);
-            //    })
-            //    .error(function (errMsg) {
-            //        $common.showError(errMsg);
-            //    });
+        const _columnFilter = function(paragraph) {
+            return paragraph.disabledSystemColumns || paragraph.systemColumns ? _allColumn : _hideColumn;
         };
 
-        $scope.rateAsString = function (paragraph) {
-            if (paragraph.rate && paragraph.rate.installed) {
-                var idx = _.findIndex($scope.timeUnit, function (unit) {
-                    return unit.value == paragraph.rate.unit;
-                });
+        const _notObjectType = function(cls) {
+            return $common.isJavaBuiltInClass(cls);
+        };
 
-                if (idx >= 0)
-                    return ' ' + paragraph.rate.value + $scope.timeUnit[idx].short;
+        function _retainColumns(allCols, curCols, acceptableType, xAxis, unwantedCols) {
+            const retainedCols = [];
 
-                paragraph.rate.installed = false;
-            }
+            const availableCols = xAxis ? allCols : _.filter(allCols, function(col) {
+                return col.value >= 0;
+            });
 
-            return '';
-        };
+            if (availableCols.length > 0) {
+                curCols.forEach(function(curCol) {
+                    const col = _.find(availableCols, {label: curCol.label});
 
-        var _cancelRefresh = function (paragraph) {
-            if (paragraph.rate && paragraph.rate.stopTime) {
-                delete paragraph.queryArgs;
+                    if (col && acceptableType(col.type)) {
+                        col.aggFx = curCol.aggFx;
 
-                paragraph.rate.installed = false;
+                        retainedCols.push(col);
+                    }
+                });
 
-                $interval.cancel(paragraph.rate.stopTime);
+                // If nothing was restored, add first acceptable column.
+                if (_.isEmpty(retainedCols)) {
+                    let col;
 
-                delete paragraph.rate.stopTime;
-            }
-        };
+                    if (unwantedCols)
+                        col = _.find(availableCols, (avCol) => !_.find(unwantedCols, {label: avCol.label}) && acceptableType(avCol.type));
 
-        var _tryStopRefresh = function (paragraph) {
-            if (paragraph.rate && paragraph.rate.stopTime) {
-                $interval.cancel(paragraph.rate.stopTime);
+                    if (!col)
+                        col = _.find(availableCols, (avCol) => acceptableType(avCol.type));
 
-                delete paragraph.rate.stopTime;
+                    if (col)
+                        retainedCols.push(col);
+                }
             }
-        };
 
-        var _tryStartRefresh = function (paragraph) {
-            _tryStopRefresh(paragraph);
+            return retainedCols;
+        }
 
-            if (paragraph.rate && paragraph.rate.installed && paragraph.queryArgs) {
-                $scope.chartAcceptKeyColumn(paragraph, TIME_LINE);
+        const _rebuildColumns = function(paragraph) {
+            _.forEach(_.groupBy(paragraph.meta, 'fieldName'), function(colsByName, fieldName) {
+                const colsByTypes = _.groupBy(colsByName, 'typeName');
 
-                _executeRefresh(paragraph);
+                const needType = _.keys(colsByTypes).length > 1;
 
-                var delay = paragraph.rate.value * paragraph.rate.unit;
+                _.forEach(colsByTypes, function(colsByType, typeName) {
+                    _.forEach(colsByType, function(col, ix) {
+                        col.fieldName = (needType && !$common.isEmptyString(typeName) ? typeName + '.' : '') + fieldName + (ix > 0 ? ix : '');
+                    });
+                });
+            });
 
-                paragraph.rate.stopTime = $interval(_executeRefresh, delay, 0, false, paragraph);
-            }
-        };
+            const cols = [];
 
-        $scope.startRefresh = function (paragraph, value, unit) {
-            paragraph.rate.value = value;
-            paragraph.rate.unit = unit;
-            paragraph.rate.installed = true;
+            _.forEach(paragraph.meta, (col, idx) => {
+                if (paragraph.columnFilter(col)) {
+                    col.field = paragraph.queryArgs.query ? idx.toString() : col.fieldName;
 
-            if (paragraph.queryExecuted())
-                _tryStartRefresh(paragraph);
-        };
+                    cols.push(col);
+                }
+            });
 
-        $scope.stopRefresh = function (paragraph) {
-            paragraph.rate.installed = false;
+            paragraph.gridOptions.updateColumns(cols);
 
-            _tryStopRefresh(paragraph);
-        };
+            paragraph.chartColumns = _.reduce(cols, (acc, col) => {
+                if (_notObjectType(col.fieldTypeName)) {
+                    acc.push({
+                        label: col.fieldName,
+                        type: col.fieldTypeName,
+                        aggFx: $scope.aggregateFxs[0],
+                        value: col.field
+                    });
+                }
 
-        function _chartNumber(arr, idx, dflt) {
-            if (idx >= 0 && arr && arr.length > idx && _.isNumber(arr[idx]))
-                return arr[idx];
+                return acc;
+            }, []);
 
-            return dflt;
-        }
+            if (paragraph.chartColumns.length > 0) {
+                paragraph.chartColumns.push(TIME_LINE);
+                paragraph.chartColumns.push(ROW_IDX);
+            }
 
-        function _chartLabel(arr, idx, dflt) {
-            if (arr && arr.length > idx && _.isString(arr[idx]))
-                return arr[idx];
+            // We could accept onl not object columns for X axis.
+            paragraph.chartKeyCols = _retainColumns(paragraph.chartColumns, paragraph.chartKeyCols, _notObjectType, true);
+
+            // We could accept only numeric columns for Y axis.
+            paragraph.chartValCols = _retainColumns(paragraph.chartColumns, paragraph.chartValCols, _numberType, false, paragraph.chartKeyCols);
+        };
+
+        $scope.toggleSystemColumns = function(paragraph) {
+            if (paragraph.disabledSystemColumns)
+                return;
+
+            paragraph.systemColumns = !paragraph.systemColumns;
 
-            return dflt;
-        }
+            paragraph.columnFilter = _columnFilter(paragraph);
 
-        function _min(rows, idx, dflt) {
-            var min = _chartNumber(rows[0], idx, dflt);
+            paragraph.chartColumns = [];
 
-            _.forEach(rows, function (row) {
-                var v = _chartNumber(row, idx, dflt);
+            _rebuildColumns(paragraph);
+        };
 
-                if (v < min)
-                    min = v;
-            });
+        const _showLoading = (paragraph, enable) => paragraph.loading = enable;
 
-            return min;
-        }
+        /**
+         * @param {Object} paragraph Query
+         * @param {{fieldsMetadata: Array, items: Array, queryId: int, last: Boolean}} res Query results.
+         * @private
+         */
+        const _processQueryResult = function(paragraph, res) {
+            const prevKeyCols = paragraph.chartKeyCols;
+            const prevValCols = paragraph.chartValCols;
 
-        function _max(rows, idx, dflt) {
-            var max = _chartNumber(rows[0], idx, dflt);
+            if (!_.eq(paragraph.meta, res.fieldsMetadata)) {
+                paragraph.meta = [];
 
-            _.forEach(rows, function (row) {
-                var v = _chartNumber(row, idx, dflt);
+                paragraph.chartColumns = [];
 
-                if (v > max)
-                    max = v;
-            });
+                if (!$common.isDefined(paragraph.chartKeyCols))
+                    paragraph.chartKeyCols = [];
 
-            return max;
-        }
+                if (!$common.isDefined(paragraph.chartValCols))
+                    paragraph.chartValCols = [];
 
-        function _sum(rows, idx) {
-            var sum = 0;
+                if (res.fieldsMetadata.length <= 2) {
+                    const _key = _.find(res.fieldsMetadata, {fieldName: '_KEY'});
+                    const _val = _.find(res.fieldsMetadata, {fieldName: '_VAL'});
 
-            _.forEach(rows, function (row) {
-                sum += _chartNumber(row, idx, 0);
-            });
+                    paragraph.disabledSystemColumns = (res.fieldsMetadata.length === 2 && _key && _val) ||
+                        (res.fieldsMetadata.length === 1 && (_key || _val));
+                }
 
-            return sum;
-        }
+                paragraph.columnFilter = _columnFilter(paragraph);
 
-        function _aggregate(rows, aggFx, idx, dflt) {
-            var len = rows.length;
+                paragraph.meta = res.fieldsMetadata;
 
-            switch (aggFx) {
-                case  'FIRST':
-                    return _chartNumber(rows[0], idx, dflt);
+                _rebuildColumns(paragraph);
+            }
 
-                case 'LAST':
-                    return _chartNumber(rows[len - 1], idx, dflt);
+            paragraph.page = 1;
 
-                case 'MIN':
-                    return _min(rows, idx, dflt);
+            paragraph.total = 0;
 
-                case 'MAX':
-                    return _max(rows, idx, dflt);
+            paragraph.queryId = res.last ? null : res.queryId;
 
-                case 'SUM':
-                    return _sum(rows, idx);
+            delete paragraph.errMsg;
 
-                case 'AVG':
-                    return len > 0 ? _sum(rows, idx) / len : 0;
+            // Prepare explain results for display in table.
+            if (paragraph.queryArgs.query && paragraph.queryArgs.query.startsWith('EXPLAIN') && res.items) {
+                paragraph.rows = [];
 
-                case 'COUNT':
-                    return len;
+                res.items.forEach(function(row, i) {
+                    const line = res.items.length - 1 === i ? row[0] : row[0] + '\n';
+
+                    line.replace(/\"/g, '').split('\n').forEach((ln) => paragraph.rows.push([ln]));
+                });
             }
+            else
+                paragraph.rows = res.items;
 
-            return 0;
-        }
+            paragraph.gridOptions.updateRows(paragraph.rows);
 
-        function _chartDatum(paragraph) {
-            var datum = [];
+            const chartHistory = paragraph.chartHistory;
 
-            if (paragraph.chartColumnsConfigured()) {
-                paragraph.chartValCols.forEach(function (valCol) {
-                    var index = 0;
-                    var values = [];
-                    var colIdx = valCol.value;
+            // Clear history on query change.
+            const queryChanged = paragraph.prevQuery !== paragraph.query;
 
-                    if (paragraph.chartTimeLineEnabled()) {
-                        var aggFx = valCol.aggFx;
-                        var colLbl = valCol.label + ' [' + aggFx + ']';
+            if (queryChanged) {
+                paragraph.prevQuery = paragraph.query;
 
-                        if (paragraph.charts && paragraph.charts.length == 1)
-                            datum = paragraph.charts[0].data;
+                chartHistory.length = 0;
 
-                        var chartData = _.find(datum, {series: valCol.label});
+                _.forEach(paragraph.charts, (chart) => chart.data.length = 0);
+            }
 
-                        var leftBound = new Date();
-                        leftBound.setMinutes(leftBound.getMinutes() - parseInt(paragraph.timeLineSpan));
+            // Add results to history.
+            chartHistory.push({tm: new Date(), rows: paragraph.rows});
 
-                        if (chartData) {
-                            var lastItem = _.last(paragraph.chartHistory);
+            // Keep history size no more than max length.
+            while (chartHistory.length > HISTORY_LENGTH)
+                chartHistory.shift();
 
-                            values = chartData.values;
+            _showLoading(paragraph, false);
 
-                            values.push({
-                                x: lastItem.tm,
-                                y: _aggregate(lastItem.rows, aggFx, colIdx, index++)
-                            });
+            if (paragraph.result === 'none' || !paragraph.queryExecuted())
+                paragraph.result = 'table';
+            else if (paragraph.chart()) {
+                let resetCharts = queryChanged;
 
-                            while (values.length > 0 && values[0].x < leftBound)
-                                values.shift();
-                        }
-                        else {
-                            _.forEach(paragraph.chartHistory, function (history) {
-                                if (history.tm >= leftBound)
-                                    values.push({
-                                        x: history.tm,
-                                        y: _aggregate(history.rows, aggFx, colIdx, index++)
-                                    });
-                            });
+                if (!resetCharts) {
+                    const curKeyCols = paragraph.chartKeyCols;
+                    const curValCols = paragraph.chartValCols;
 
-                            datum.push({series: valCol.label, key: colLbl, values: values});
-                        }
-                    }
-                    else {
-                        index = paragraph.total;
+                    resetCharts = !prevKeyCols || !prevValCols ||
+                        prevKeyCols.length !== curKeyCols.length ||
+                        prevValCols.length !== curValCols.length;
+                }
 
-                        values = _.map(paragraph.rows, function (row) {
-                            var xCol = paragraph.chartKeyCols[0].value;
+                _chartApplySettings(paragraph, resetCharts);
+            }
+        };
 
-                            var v = {
-                                x: _chartNumber(row, xCol, index),
-                                xLbl: _chartLabel(row, xCol, undefined),
-                                y: _chartNumber(row, colIdx, index)
-                            };
+        const _closeOldQuery = (paragraph) => {
+            const queryId = paragraph.queryArgs && paragraph.queryArgs.queryId;
 
-                            index++;
+            return queryId ? agentMonitor.queryClose(queryId) : $q.when();
+        };
 
-                            return v;
-                        });
+        const _executeRefresh = (paragraph) => {
+            const args = paragraph.queryArgs;
 
-                        datum.push({series: valCol.label, key: valCol.label, values: values});
-                    }
-                });
-            }
+            agentMonitor.awaitAgent()
+                .then(() => _closeOldQuery(paragraph))
+                .then(() => agentMonitor.query(args.cacheName, args.pageSize, args.query))
+                .then(_processQueryResult.bind(this, paragraph))
+                .catch((err) => paragraph.errMsg = err.message);
+        };
 
-            return datum;
-        }
+        const _tryStartRefresh = function(paragraph) {
+            _tryStopRefresh(paragraph);
 
-        function _pieChartDatum(paragraph) {
-            var datum = [];
+            if (paragraph.rate && paragraph.rate.installed && paragraph.queryArgs) {
+                $scope.chartAcceptKeyColumn(paragraph, TIME_LINE);
 
-            if (paragraph.chartColumnsConfigured() && !paragraph.chartTimeLineEnabled()) {
-                paragraph.chartValCols.forEach(function (valCol) {
-                    var index = paragraph.total;
+                _executeRefresh(paragraph);
 
-                    var values = _.map(paragraph.rows, function (row) {
-                        var xCol = paragraph.chartKeyCols[0].value;
+                const delay = paragraph.rate.value * paragraph.rate.unit;
 
-                        var v = {
-                            x: xCol < 0 ? index : row[xCol],
-                            y: _chartNumber(row, valCol.value, index)
-                        };
+                paragraph.rate.stopTime = $interval(_executeRefresh, delay, 0, false, paragraph);
+            }
+        };
 
-                        index++;
+        $scope.execute = function(paragraph) {
+            QueryNotebooks.save($scope.notebook)
+                .catch(_handleException);
 
-                        return v;
-                    });
+            paragraph.prevQuery = paragraph.queryArgs ? paragraph.queryArgs.query : paragraph.query;
 
-                    datum.push({series: paragraph.chartKeyCols[0].label, key: valCol.label, values: values});
-                });
-            }
+            _showLoading(paragraph, true);
 
-            return datum;
-        }
+            _closeOldQuery(paragraph)
+                .then(function() {
+                    const args = paragraph.queryArgs = {
+                        cacheName: paragraph.cacheName,
+                        pageSize: paragraph.pageSize,
+                        query: paragraph.query
+                    };
 
-        $scope.paragraphTimeSpanVisible = function (paragraph) {
-            return paragraph.timeLineSupported() && paragraph.chartTimeLineEnabled();
-        };
+                    return agentMonitor.query(args.cacheName, args.pageSize, args.query);
+                })
+                .then(function(res) {
+                    _processQueryResult(paragraph, res);
 
-        $scope.paragraphTimeLineSpan = function (paragraph) {
-          if (paragraph && paragraph.timeLineSpan)
-            return paragraph.timeLineSpan.toString();
+                    _tryStartRefresh(paragraph);
+                })
+                .catch((err) => {
+                    paragraph.errMsg = err.message;
 
-            return '1';
-        };
+                    _showLoading(paragraph, false);
 
-        function _saveChartSettings(paragraph) {
-            if (!_.isEmpty(paragraph.charts)) {
-                var chart = paragraph.charts[0].api.getScope().chart;
+                    $scope.stopRefresh(paragraph);
+                })
+                .finally(() => paragraph.ace.focus());
+        };
 
-                if (!$common.isDefined(paragraph.chartsOptions))
-                    paragraph.chartsOptions = {barChart: {stacked: true}, areaChart: {style: 'stack'}};
+        $scope.queryExecuted = function(paragraph) {
+            return $common.isDefined(paragraph.queryArgs);
+        };
 
-                switch (paragraph.result) {
-                    case 'bar':
-                        paragraph.chartsOptions.barChart.stacked = chart.stacked();
+        const _cancelRefresh = function(paragraph) {
+            if (paragraph.rate && paragraph.rate.stopTime) {
+                delete paragraph.queryArgs;
 
-                        break;
+                paragraph.rate.installed = false;
 
-                    case 'area':
-                        paragraph.chartsOptions.areaChart.style = chart.style();
+                $interval.cancel(paragraph.rate.stopTime);
 
-                        break;
-                }
+                delete paragraph.rate.stopTime;
             }
-        }
+        };
 
-        function _chartApplySettings(paragraph, resetCharts) {
-            if (resetCharts)
-                paragraph.charts = [];
+        $scope.explain = function(paragraph) {
+            QueryNotebooks.save($scope.notebook)
+                .catch(_handleException);
 
-            if (paragraph.chart() && paragraph.nonEmpty()) {
-                switch (paragraph.result) {
-                    case 'bar':
-                        _barChart(paragraph);
-                        break;
+            _cancelRefresh(paragraph);
 
-                    case 'pie':
-                        _pieChart(paragraph);
-                        break;
+            _showLoading(paragraph, true);
 
-                    case 'line':
-                        _lineChart(paragraph);
-                        break;
+            _closeOldQuery(paragraph)
+                .then(function() {
+                    const args = paragraph.queryArgs = {
+                        cacheName: paragraph.cacheName,
+                        pageSize: paragraph.pageSize,
+                        query: 'EXPLAIN ' + paragraph.query
+                    };
 
-                    case 'area':
-                        _areaChart(paragraph);
-                        break;
-                }
-            }
-        }
+                    return agentMonitor.query(args.cacheName, args.pageSize, args.query);
+                })
+                .then(_processQueryResult.bind(this, paragraph))
+                .catch((err) => {
+                    paragraph.errMsg = err.message;
 
-        $scope.applyChartSettings = function (paragraph) {
-            _chartApplySettings(paragraph, true);
+                    _showLoading(paragraph, false);
+                })
+                .finally(() => paragraph.ace.focus());
         };
 
-        function _xAxisLabel(paragraph) {
-            return _.isEmpty(paragraph.chartKeyCols) ? 'X' : paragraph.chartKeyCols[0].label;
-        }
+        $scope.scan = function(paragraph) {
+            QueryNotebooks.save($scope.notebook)
+                .catch(_handleException);
+
+            _cancelRefresh(paragraph);
+
+            _showLoading(paragraph, true);
+
+            _closeOldQuery(paragraph)
+                .then(() => {
+                    const args = paragraph.queryArgs = {
+                        cacheName: paragraph.cacheName,
+                        pageSize: paragraph.pageSize
+                    };
 
-        function _yAxisLabel(paragraph) {
-            var cols = paragraph.chartValCols;
+                    return agentMonitor.query(args.cacheName, args.pageSize);
+                })
+                .then(_processQueryResult.bind(this, paragraph))
+                .catch((err) => {
+                    paragraph.errMsg = err.message;
 
-            var tml = paragraph.chartTimeLineEnabled();
+                    _showLoading(paragraph, false);
+                })
+                .finally(() => paragraph.ace.focus());
+        };
 
-            return _.isEmpty(cols) ? 'Y' : _.map(cols, function (col) {
-                var lbl = col.label;
+        function _updatePieChartsWithData(paragraph, newDatum) {
+            $timeout(() => {
+                _.forEach(paragraph.charts, function(chart) {
+                    const chartDatum = chart.data;
 
-                if (tml)
-                 lbl += ' [' + col.aggFx + ']';
+                    chartDatum.length = 0;
 
-                return lbl;
-            }).join(', ');
-        }
+                    _.forEach(newDatum, function(series) {
+                        if (chart.options.title.text === series.key)
+                            _.forEach(series.values, (v) => chartDatum.push(v));
+                    });
+                });
 
-        function _xX(d) {
-            return d.x;
+                _.forEach(paragraph.charts, (chart) => chart.api.update());
+            });
         }
 
-        function _yY(d) {
-            return d.y;
-        }
+        $scope.nextPage = function(paragraph) {
+            _showLoading(paragraph, true);
 
-        function _xAxisTimeFormat(d) {
-            return d3.time.format('%X')(new Date(d));
-        }
+            paragraph.queryArgs.pageSize = paragraph.pageSize;
 
-        var _xAxisWithLabelFormat = function(paragraph) {
-            return function (d) {
-                var values = paragraph.charts[0].data[0].values;
+            agentMonitor.next(paragraph.queryId, paragraph.pageSize)
+                .then(function(res) {
+                    paragraph.page++;
 
-                var fmt = _intType(paragraph.chartKeyCols[0].type) ? 'd' : ',.2f';
+                    paragraph.total += paragraph.rows.length;
 
-                var dx = values[d];
+                    paragraph.rows = res.items;
 
-                if (!dx)
-                    return d3.format(fmt)(d);
+                    if (paragraph.chart()) {
+                        if (paragraph.result === 'pie')
+                            _updatePieChartsWithData(paragraph, _pieChartDatum(paragraph));
+                        else
+                            _updateChartsWithData(paragraph, _chartDatum(paragraph));
+                    }
 
-                var lbl = dx.xLbl;
+                    paragraph.gridOptions.updateRows(paragraph.rows);
 
-                return lbl ? lbl : d3.format(fmt)(d);
-            }
-        };
+                    _showLoading(paragraph, false);
 
-        var _yAxisFormat = function(d) {
-            var fmt = d < 1000 ? ',.2f' : '.3s';
+                    if (res.last)
+                        delete paragraph.queryId;
+                })
+                .catch((err) => {
+                    paragraph.errMsg = err.message;
 
-            return d3.format(fmt)(d);
+                    _showLoading(paragraph, false);
+                })
+                .finally(() => paragraph.ace.focus());
         };
 
-        function _updateCharts(paragraph) {
-            $timeout(function () {
-                _.forEach(paragraph.charts, function (chart) {
-                    chart.api.update();
-                });
-            }, 100);
-        }
+        const _export = (fileName, columnFilter, meta, rows) =>

<TRUNCATED>

[03/22] ignite git commit: IGNITE-3353: IGFS: Fixed "out-of-space" handling.

Posted by ak...@apache.org.
IGNITE-3353: IGFS: Fixed "out-of-space" handling.


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/212dc068
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/212dc068
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/212dc068

Branch: refs/heads/master
Commit: 212dc068f807a055ae3755a9eebf55fe7722c574
Parents: 8e6473a
Author: vozerov-gridgain <vo...@gridgain.com>
Authored: Wed Jun 22 16:40:49 2016 +0300
Committer: vozerov-gridgain <vo...@gridgain.com>
Committed: Wed Jun 22 16:54:47 2016 +0300

----------------------------------------------------------------------
 .../internal/processors/cache/GridCacheAdapter.java | 16 +++-------------
 .../internal/processors/igfs/IgfsDataManager.java   | 14 ++------------
 .../processors/igfs/IgfsOutputStreamImpl.java       | 10 ++++++++--
 .../processors/igfs/IgfsDataManagerSelfTest.java    |  6 +++---
 4 files changed, 16 insertions(+), 30 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/212dc068/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
index 3dbd0f9..944a6b0 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
@@ -340,19 +340,9 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
 
                         igfsDataSpaceMax = igfsCfg.getMaxSpaceSize();
 
-                        if (igfsDataSpaceMax == 0) {
-                            long maxMem = Runtime.getRuntime().maxMemory();
-
-                            // We leave JVM at least 500M of memory for correct operation.
-                            long jvmFreeSize = (maxMem - 512 * 1024 * 1024);
-
-                            if (jvmFreeSize <= 0)
-                                jvmFreeSize = maxMem / 2;
-
-                            long dfltMaxSize = (long)(0.8f * maxMem);
-
-                            igfsDataSpaceMax = Math.min(dfltMaxSize, jvmFreeSize);
-                        }
+                        // Do we have limits?
+                        if (igfsDataSpaceMax <= 0)
+                            igfsDataSpaceMax = Long.MAX_VALUE;
                     }
 
                     break;

http://git-wip-us.apache.org/repos/asf/ignite/blob/212dc068/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsDataManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsDataManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsDataManager.java
index ca8a3af..1397e4e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsDataManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsDataManager.java
@@ -465,23 +465,13 @@ public class IgfsDataManager extends IgfsManager {
      * Notifies data manager that no further writes will be performed on stream.
      *
      * @param fileId File ID.
-     * @param await Await completion.
      * @throws IgniteCheckedException If failed.
      */
-    public void writeClose(IgniteUuid fileId, boolean await) throws IgniteCheckedException {
+    public void writeClose(IgniteUuid fileId) throws IgniteCheckedException {
         WriteCompletionFuture fut = pendingWrites.get(fileId);
 
-        if (fut != null) {
+        if (fut != null)
             fut.markWaitingLastAck();
-
-            if (await)
-                fut.get();
-        }
-        else {
-            if (log.isDebugEnabled())
-                log.debug("Failed to find write completion future for file in pending write map (most likely it was " +
-                    "failed): " + fileId);
-        }
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/212dc068/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsOutputStreamImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsOutputStreamImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsOutputStreamImpl.java
index 7741a25..f6b1104 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsOutputStreamImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsOutputStreamImpl.java
@@ -23,6 +23,7 @@ import org.apache.ignite.igfs.IgfsException;
 import org.apache.ignite.igfs.IgfsMode;
 import org.apache.ignite.igfs.IgfsOutputStream;
 import org.apache.ignite.igfs.IgfsPath;
+import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.managers.eventstorage.GridEventStorageManager;
 import org.apache.ignite.internal.util.typedef.internal.A;
 import org.apache.ignite.internal.util.typedef.internal.S;
@@ -64,6 +65,9 @@ class IgfsOutputStreamImpl extends IgfsOutputStream {
     /** Mutex for synchronization. */
     private final Object mux = new Object();
 
+    /** Write completion future. */
+    private final IgniteInternalFuture<Boolean> writeFut;
+
     /** Flag for this stream open/closed state. */
     private boolean closed;
 
@@ -120,7 +124,7 @@ class IgfsOutputStreamImpl extends IgfsOutputStream {
 
             streamRange = initialStreamRange(fileInfo);
 
-            igfsCtx.data().writeStart(fileInfo.id());
+            writeFut = igfsCtx.data().writeStart(fileInfo.id());
         }
 
         igfsCtx.igfs().localMetrics().incrementFilesOpenedForWrite();
@@ -300,7 +304,9 @@ class IgfsOutputStreamImpl extends IgfsOutputStream {
 
                 flushRemainder();
 
-                igfsCtx.data().writeClose(fileInfo.id(), true);
+                igfsCtx.data().writeClose(fileInfo.id());
+
+                writeFut.get();
 
                 flushSuccess = true;
             }

http://git-wip-us.apache.org/repos/asf/ignite/blob/212dc068/modules/core/src/test/java/org/apache/ignite/internal/processors/igfs/IgfsDataManagerSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/igfs/IgfsDataManagerSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/igfs/IgfsDataManagerSelfTest.java
index 0d1a66f..70d6b99 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/igfs/IgfsDataManagerSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/igfs/IgfsDataManagerSelfTest.java
@@ -195,7 +195,7 @@ public class IgfsDataManagerSelfTest extends IgfsCommonAbstractTest {
 
             assert remainder == null;
 
-            mgr.writeClose(info.id(), false);
+            mgr.writeClose(info.id());
 
             fut.get(3000);
 
@@ -287,7 +287,7 @@ public class IgfsDataManagerSelfTest extends IgfsCommonAbstractTest {
 
             assert left2 == null;
 
-            mgr.writeClose(info.id(), false);
+            mgr.writeClose(info.id());
 
             fut.get(3000);
 
@@ -369,7 +369,7 @@ public class IgfsDataManagerSelfTest extends IgfsCommonAbstractTest {
                 assert left == null : "No remainder should be returned if flush is true: " + Arrays.toString(left);
             }
 
-            mgr.writeClose(info.id(), false);
+            mgr.writeClose(info.id());
 
             assertTrue(range.regionEqual(new IgfsFileAffinityRange(0, writesCnt * chunkSize - 1, null)));
 


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

Posted by ak...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger.jade
new file mode 100644
index 0000000..3406ff5
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger.jade
@@ -0,0 +1,65 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+include ../../../../../app/helpers/jade/mixins.jade
+
+-var model = 'backupItem.logger'
+-var form = 'logger'
+-var kind = model + '.kind'
+
+form.panel.panel-default(name=form novalidate)
+    .panel-heading(bs-collapse-toggle ng-click='ui.loadPanel("#{form}")')
+        ignite-form-panel-chevron
+        label Logger configuration
+        ignite-form-field-tooltip.tipLabel
+            | Logging functionality used throughout the system
+        ignite-form-revert
+    .panel-collapse(role='tabpanel' bs-collapse-target id=form)
+        .panel-body(ng-if='ui.isPanelLoaded("#{form}")')
+            .col-sm-6
+                .settings-row
+                    +dropdown('Logger:', kind, 'logger', 'true', 'Default',
+                        '[\
+                            {value: "Log4j", label: "Apache Log4j"},\
+                            {value: "Log4j2", label: "Apache Log4j 2"},\
+                            {value: "SLF4J", label: "Simple Logging Facade (SLF4J)"},\
+                            {value: "Java", label: "Java logger (JUL)"},\
+                            {value: "JCL", label: "Jakarta Commons Logging (JCL)"},\
+                            {value: "Null", label: "Null logger"},\
+                            {value: "Custom", label: "Custom"},\
+                            {value: undefined, label: "Default"}\
+                        ]',
+                        'Logger implementations\
+                        <ul>\
+                            <li>Apache Log4j - log4j-based logger</li>\
+                            <li>Apache Log4j 2 - Log4j2-based logger</li>\
+                            <li>Simple Logging Facade (SLF4J) - SLF4j-based logger</li>\
+                            <li>Java logger (JUL) - built in java logger</li>\
+                            <li>Jakarta Commons Logging (JCL) - wraps any JCL (Jakarta Commons Logging) loggers</li>\
+                            <li>Null logger - logger which does not output anything</li>\
+                            <li>Custom - custom logger implementation</li>\
+                            <li>Default - Apache Log4j if awailable on classpath or Java logger otherwise</li>\
+                        </ul>')
+                .settings-row(ng-show='#{kind} && (#{kind} === "Log4j2" || #{kind} === "Log4j" || #{kind} === "Custom")')
+                    .panel-details
+                        ignite-configuration-clusters-logger-log4j2(
+                            ng-show='#{kind} === "Log4j2"')
+                        ignite-configuration-clusters-logger-log4j(
+                            ng-show='#{kind} === "Log4j"')
+                        ignite-configuration-clusters-logger-custom(
+                            ng-show='#{kind} === "Custom"')
+            .col-sm-6
+                +preview-xml-java(model, 'clusterLogger')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/custom.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/custom.directive.js b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/custom.directive.js
new file mode 100644
index 0000000..5bd586b
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/custom.directive.js
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import template from './custom.jade!';
+
+export default ['igniteConfigurationClustersLoggerCustom', [() => {
+    return {
+        scope: true,
+        restrict: 'E',
+        template,
+        replace: true
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/custom.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/custom.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/custom.jade
new file mode 100644
index 0000000..58dba15
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/custom.jade
@@ -0,0 +1,24 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+include ../../../../../../app/helpers/jade/mixins.jade
+
+-var model = 'backupItem.logger.Custom'
+-var required = 'backupItem.logger.kind === "Custom"'
+
+div
+    .details-row
+        +java-class('Class:', model + '.class', 'customLogger', 'true', required, 'Logger implementation class')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/log4j.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/log4j.directive.js b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/log4j.directive.js
new file mode 100644
index 0000000..64e4337
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/log4j.directive.js
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import template from './log4j.jade!';
+
+export default ['igniteConfigurationClustersLoggerLog4j', [() => {
+    return {
+        scope: true,
+        restrict: 'E',
+        template,
+        replace: true
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/log4j.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/log4j.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/log4j.jade
new file mode 100644
index 0000000..97f407d
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/log4j.jade
@@ -0,0 +1,49 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+include ../../../../../../app/helpers/jade/mixins.jade
+
+-var model = 'backupItem.logger.Log4j'
+-var pathRequired = model + '.mode === "Path" && backupItem.logger.kind === "Log4j"'
+
+div
+    .details-row
+        +dropdown('Level:', model + '.level', 'log4jLevel', 'true', 'Default',
+            '[\
+                {value: "OFF", label: "OFF"},\
+                {value: "FATAL", label: "FATAL"},\
+                {value: "ERROR", label: "ERROR"},\
+                {value: "WARN", label: "WARN"},\
+                {value: "INFO", label: "INFO"},\
+                {value: "DEBUG", label: "DEBUG"},\
+                {value: "TRACE", label: "TRACE"},\
+                {value: "ALL", label: "ALL"},\
+                {value: undefined, label: "Default"}\
+            ]',
+            'Level for internal log4j implementation')
+    .details-row
+        +dropdown-required('Logger configuration:', model + '.mode', 'log4jMode', 'true', 'true', 'Choose logger mode',
+            '[\
+                {value: "Default", label: "Default"},\
+                {value: "Path", label: "Path"}\
+            ]',
+            'Choose logger configuration\
+            <ul>\
+                <li>Default - default logger</li>\
+                <li>Path - path or URI to XML configuration</li>\
+            </ul>')
+    .details-row(ng-show=pathRequired)
+        +text('Path:', model + '.path', 'log4jPath', pathRequired, 'Input path', 'Path or URI to XML configuration')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/log4j2.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/log4j2.directive.js b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/log4j2.directive.js
new file mode 100644
index 0000000..ad891f7
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/log4j2.directive.js
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import template from './log4j2.jade!';
+
+export default ['igniteConfigurationClustersLoggerLog4j2', [() => {
+    return {
+        scope: true,
+        restrict: 'E',
+        template,
+        replace: true
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/log4j2.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/log4j2.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/log4j2.jade
new file mode 100644
index 0000000..b4cea90
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger/log4j2.jade
@@ -0,0 +1,38 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+include ../../../../../../app/helpers/jade/mixins.jade
+
+-var model = 'backupItem.logger.Log4j2'
+-var log4j2Required = 'backupItem.logger.kind === "Log4j2"'
+
+div
+    .details-row
+        +dropdown('Level:', model + '.level', 'log4j2Level', 'true', 'Default',
+            '[\
+                {value: "OFF", label: "OFF"},\
+                {value: "FATAL", label: "FATAL"},\
+                {value: "ERROR", label: "ERROR"},\
+                {value: "WARN", label: "WARN"},\
+                {value: "INFO", label: "INFO"},\
+                {value: "DEBUG", label: "DEBUG"},\
+                {value: "TRACE", label: "TRACE"},\
+                {value: "ALL", label: "ALL"},\
+                {value: undefined, label: "Default"}\
+            ]',
+            'Level for internal log4j2 implementation')
+    .details-row
+        +text('Path:', model + '.path', 'log4j2Path', log4j2Required, 'Input path', 'Path or URI to XML configuration')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/domains/query.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/domains/query.jade b/modules/web-console/src/main/js/app/modules/states/configuration/domains/query.jade
index 9974417..f66de92 100644
--- a/modules/web-console/src/main/js/app/modules/states/configuration/domains/query.jade
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/domains/query.jade
@@ -62,7 +62,7 @@ form.panel.panel-default(name='query' novalidate)
                 .content-not-available(ng-if='#{model}.queryMetadata === "Annotations"')
                     label Not available for annotated types
                 div(ng-if='#{model}.queryMetadata === "Configuration"')
-                    .settings-row(ng-init='queryFieldsTbl={type: "fields", model: "fields", focusId: "QryField", ui: "table-pair", keyName: "name", valueName: "className"}')
+                    .settings-row
                         ignite-form-group(ng-model='#{queryFields}' ng-form='#{queryFieldsForm}')
                             ignite-form-field-label(id='queryFields')
                                 | Fields
@@ -85,7 +85,7 @@ form.panel.panel-default(name='query' novalidate)
                                         tr
                                             td.col-sm-12
                                                 +table-pair-edit('queryFieldsTbl', 'new', 'Field name', 'Field full class name', false, true, '{{::queryFieldsTbl.focusId + $index}}', '-1', '/')
-                    .settings-row(ng-init='aliasesTbl={type: "aliases", model: "aliases", focusId: "Alias", ui: "table-pair", keyName: "field", valueName: "alias"}')
+                    .settings-row
                         ignite-form-group(ng-model='#{queryAliases}' ng-form='#{queryAliasesForm}')
                             ignite-form-field-label
                                 | Aliases

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/domains/store.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/domains/store.jade b/modules/web-console/src/main/js/app/modules/states/configuration/domains/store.jade
index c6c3351..d2a1dc6 100644
--- a/modules/web-console/src/main/js/app/modules/states/configuration/domains/store.jade
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/domains/store.jade
@@ -66,7 +66,7 @@ form.panel.panel-default(name=form novalidate)
         ignite-form-panel-chevron
         label Domain model for cache store
         ignite-form-field-tooltip.tipLabel
-            | Domain model properties for fields queries
+            | Domain model properties for binding database with cache via POJO cache store
         ignite-form-revert
     .panel-collapse(role='tabpanel' bs-collapse-target id=form)
         .panel-body(ng-if='ui.isPanelLoaded("#{form}")')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/igfs/misc.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/igfs/misc.jade b/modules/web-console/src/main/js/app/modules/states/configuration/igfs/misc.jade
index 7cc0354..fd42805 100644
--- a/modules/web-console/src/main/js/app/modules/states/configuration/igfs/misc.jade
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/igfs/misc.jade
@@ -79,7 +79,7 @@ form.panel.panel-default(name=form novalidate)
                         'If value of this flag is <b>true</b>, IGFS will skip expensive consistency checks. It is recommended to set\
                         this flag to <b>false</b>b> if your application has conflicting operations, or you do not how exactly users will\
                         use your system.')
-                .settings-row(ng-init='tblPathModes={type: "pathModes", model: "pathModes", focusId: "PathMode", ui: "table-pair", keyName: "path", valueName: "mode"}')
+                .settings-row
                     ignite-form-group(ng-model=pathModes ng-form=pathModesForm)
                         ignite-form-field-label
                             | Path modes

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/user/Auth.service.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/user/Auth.service.js b/modules/web-console/src/main/js/app/modules/user/Auth.service.js
index 8ed9f33..f0aa397 100644
--- a/modules/web-console/src/main/js/app/modules/user/Auth.service.js
+++ b/modules/web-console/src/main/js/app/modules/user/Auth.service.js
@@ -41,11 +41,15 @@ export default ['Auth', ['$http', '$rootScope', '$state', '$window', '$common',
             set authorized(auth) {
                 _authorized(auth);
             },
+            forgotPassword(userInfo) {
+                return $http.post('/api/v1/password/forgot', userInfo)
+                    .success(() => $state.go('password.send'))
+                    .error((err) => $common.showPopoverMessage(null, null, 'forgot_email', $common.errorMessage(err)));
+            },
             auth(action, userInfo) {
-                $http.post('/api/v1/' + action, userInfo)
-                    .then(User.read)
-                    .then((user) => {
-                        if (action !== 'password/forgot') {
+                return $http.post('/api/v1/' + action, userInfo)
+                    .success(() => {
+                        return User.read().then((user) => {
                             _authorized(true);
 
                             $root.$broadcast('user', user);
@@ -55,13 +59,12 @@ export default ['Auth', ['$http', '$rootScope', '$state', '$window', '$common',
                             $root.gettingStarted.tryShow();
 
                             agentMonitor.init();
-                        } else
-                            $state.go('password.send');
+                        });
                     })
-                    .catch((errMsg) => $common.showPopoverMessage(null, null, action === 'signup' ? 'signup_email' : 'signin_email', errMsg.data));
+                    .error((err) => $common.showPopoverMessage(null, null, action + '_email', $common.errorMessage(err)));
             },
             logout() {
-                $http.post('/api/v1/logout')
+                return $http.post('/api/v1/logout')
                     .then(() => {
                         User.clean();
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/services/AgentMonitor.service.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/services/AgentMonitor.service.js b/modules/web-console/src/main/js/app/services/AgentMonitor.service.js
deleted file mode 100644
index 88995d5..0000000
--- a/modules/web-console/src/main/js/app/services/AgentMonitor.service.js
+++ /dev/null
@@ -1,337 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import io from 'socket.io-client'; // eslint-disable-line no-unused-vars
-
-class IgniteAgentMonitor {
-    constructor(socketFactory, $root, $q, $state, $modal, $common) {
-        this._scope = $root.$new();
-
-        $root.$on('$stateChangeStart', () => {
-            this.stopWatch();
-        });
-
-        // Pre-fetch modal dialogs.
-        this._downloadAgentModal = $modal({
-            scope: this._scope,
-            templateUrl: '/templates/agent-download.html',
-            show: false,
-            backdrop: 'static'
-        });
-
-        const _modalHide = this._downloadAgentModal.hide;
-
-        /**
-         * Special dialog hide function.
-         */
-        this._downloadAgentModal.hide = () => {
-            $common.hideAlert();
-
-            _modalHide();
-        };
-
-        /**
-         * Close dialog and go by specified link.
-         */
-        this._scope.back = () => {
-            this.stopWatch();
-
-            if (this._scope.backState)
-                this._scope.$$postDigest(() => $state.go(this._scope.backState));
-        };
-
-        this._scope.downloadAgent = () => {
-            const lnk = document.createElement('a');
-
-            lnk.setAttribute('href', '/api/v1/agent/download/zip');
-            lnk.setAttribute('target', '_self');
-            lnk.setAttribute('download', null);
-            lnk.style.display = 'none';
-
-            document.body.appendChild(lnk);
-
-            lnk.click();
-
-            document.body.removeChild(lnk);
-        };
-
-        this._scope.hasAgents = null;
-        this._scope.showModal = false;
-
-        this._evtOrderKey = $common.randomString(20);
-        this._evtThrottleCntrKey = $common.randomString(20);
-
-        /**
-         * @type {Socket}
-         */
-        this._socket = null;
-
-        this._socketFactory = socketFactory;
-
-        this._$q = $q;
-
-        this._$common = $common;
-    }
-
-    /**
-     * @private
-     */
-    checkModal() {
-        if (this._scope.showModal && !this._scope.hasAgents)
-            this._downloadAgentModal.$promise.then(this._downloadAgentModal.show);
-        else if ((this._scope.hasAgents || !this._scope.showModal) && this._downloadAgentModal.$isShown)
-            this._downloadAgentModal.hide();
-    }
-
-    /**
-     * @returns {Promise}
-     */
-    awaitAgent() {
-        if (this._scope.hasAgents)
-            return this._$q.when();
-
-        if (this._scope.hasAgents !== null)
-            this.checkModal();
-
-        const latch = this._$q.defer();
-
-        const offConnected = this._scope.$on('agent:connected', (event, success) => {
-            offConnected();
-
-            if (success)
-                return latch.resolve();
-
-            latch.reject();
-        });
-
-        return latch.promise;
-    }
-
-    init() {
-        this._socket = this._socketFactory();
-
-        this._socket.on('connect_error', () => {
-            this._scope.hasAgents = false;
-        });
-
-        this._socket.on('agent:count', ({count}) => {
-            this._scope.hasAgents = count > 0;
-
-            this.checkModal();
-
-            if (this._scope.hasAgents)
-                this._scope.$broadcast('agent:connected', true);
-        });
-
-        this._socket.on('disconnect', () => {
-            this._scope.hasAgents = false;
-
-            this.checkModal();
-        });
-    }
-
-    /**
-     * @param {Object} back
-     * @returns {Promise}
-     */
-    startWatch(back) {
-        this._scope.backState = back.state;
-        this._scope.backText = back.text;
-
-        this._scope.agentGoal = back.goal;
-
-        this._scope.showModal = true;
-
-        return this.awaitAgent();
-    }
-
-    /**
-     *
-     * @param {String} event
-     * @param {Object} [args]
-     * @returns {Promise}
-     * @private
-     */
-    _emit(event, ...args) {
-        if (!this._socket)
-            return this._$q.reject('Failed to connect to agent');
-
-        const latch = this._$q.defer();
-
-        const onDisconnect = () => {
-            this._socket.removeListener('disconnect', onDisconnect);
-
-            latch.reject('Connection to server was closed');
-        };
-
-        this._socket.on('disconnect', onDisconnect);
-
-        args.push((err, res) => {
-            this._socket.removeListener('disconnect', onDisconnect);
-
-            if (err)
-                latch.reject(err);
-
-            latch.resolve(res);
-        });
-
-        this._socket.emit(event, ...args);
-
-        return latch.promise;
-    }
-
-    drivers() {
-        return this._emit('schemaImport:drivers');
-    }
-
-    /**
-     *
-     * @param {Object} preset
-     * @returns {Promise}
-     */
-    schemas(preset) {
-        return this._emit('schemaImport:schemas', preset);
-    }
-
-    /**
-     *
-     * @param {Object} preset
-     * @returns {Promise}
-     */
-    tables(preset) {
-        return this._emit('schemaImport:tables', preset);
-    }
-
-    /**
-     * @param {String} errMsg
-     */
-    showNodeError(errMsg) {
-        this._downloadAgentModal.show();
-
-        this._$common.showError(errMsg);
-    }
-
-    /**
-     *
-     * @param {String} event
-     * @param {Object} [args]
-     * @returns {Promise}
-     * @private
-     */
-    _rest(event, ...args) {
-        return this._downloadAgentModal.$promise
-            .then(() => this._emit(event, ...args));
-    }
-
-    /**
-     * @param {Boolean} [attr]
-     * @param {Boolean} [mtr]
-     * @returns {Promise}
-     */
-    topology(attr, mtr) {
-        return this._rest('node:topology', !!attr, !!mtr);
-    }
-
-    /**
-     * @param {int} [queryId]
-     * @returns {Promise}
-     */
-    queryClose(queryId) {
-        return this._rest('node:query:close', queryId);
-    }
-
-    /**
-     * @param {String} cacheName Cache name.
-     * @param {int} pageSize
-     * @param {String} [query] Query if null then scan query.
-     * @returns {Promise}
-     */
-    query(cacheName, pageSize, query) {
-        return this._rest('node:query', _.isEmpty(cacheName) ? null : cacheName, pageSize, query);
-    }
-
-    /**
-     * @param {String} cacheName Cache name.
-     * @param {String} [query] Query if null then scan query.
-     * @returns {Promise}
-     */
-    queryGetAll(cacheName, query) {
-        return this._rest('node:query:getAll', _.isEmpty(cacheName) ? null : cacheName, query);
-    }
-
-    /**
-     * @param {String} [cacheName] Cache name.
-     * @returns {Promise}
-     */
-    metadata(cacheName) {
-        return this._rest('node:cache:metadata', _.isEmpty(cacheName) ? null : cacheName);
-    }
-
-    /**
-     * @param {int} queryId
-     * @param {int} pageSize
-     * @returns {Promise}
-     */
-    next(queryId, pageSize) {
-        return this._rest('node:query:fetch', queryId, pageSize);
-    }
-
-    collect() {
-        return this._rest('node:visor:collect', this._evtOrderKey, this._evtThrottleCntrKey);
-    }
-
-    /**
-     * Clear specified cache on specified node.
-     * @param {String} nid Node id.
-     * @param {String} cacheName Cache name.
-     * @returns {Promise}
-     */
-    cacheClear(nid, cacheName) {
-        return this._rest('node:cache:clear', nid, cacheName);
-    }
-
-    /**
-     * Stop specified cache on specified node.
-     * @param {String} nid Node id.
-     * @param {String} cacheName Cache name.
-     * @returns {Promise}
-     */
-    cacheStop(nid, cacheName) {
-        return this._rest('node:cache:stop', nid, cacheName);
-    }
-
-    /**
-     * Ping node.
-     * @param {String} nid Node id.
-     * @returns {Promise}
-     */
-    ping(nid) {
-        return this._rest('node:ping', nid);
-    }
-
-    stopWatch() {
-        this._scope.showModal = false;
-
-        this.checkModal();
-
-        this._scope.$broadcast('agent:connected', false);
-    }
-}
-
-IgniteAgentMonitor.$inject = ['igniteSocketFactory', '$rootScope', '$q', '$state', '$modal', '$common'];
-
-export default ['IgniteAgentMonitor', IgniteAgentMonitor];

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/services/cleanup.service.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/services/cleanup.service.js b/modules/web-console/src/main/js/app/services/cleanup.service.js
index 380beda..bec9ea3 100644
--- a/modules/web-console/src/main/js/app/services/cleanup.service.js
+++ b/modules/web-console/src/main/js/app/services/cleanup.service.js
@@ -32,7 +32,9 @@ export default ['$cleanup', () => {
                     dist[key] = attr;
                 }
             });
-        } else if ((_.isString(original) && original.length) || _.isNumber(original) || _.isBoolean(original))
+        } else if (_.isBoolean(original) && original === true)
+            dist = original;
+        else if ((_.isString(original) && original.length) || _.isNumber(original))
             dist = original;
         else if (_.isArray(original) && original.length)
             dist = _.map(original, (value) => cleanup(value, {}));

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/build/system.config.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/build/system.config.js b/modules/web-console/src/main/js/build/system.config.js
index edd9e9e..20af865 100644
--- a/modules/web-console/src/main/js/build/system.config.js
+++ b/modules/web-console/src/main/js/build/system.config.js
@@ -18,15 +18,15 @@ System.config({
     "angular": "github:angular/bower-angular@1.5.5",
     "angular-animate": "github:angular/bower-angular-animate@1.5.5",
     "angular-drag-and-drop-lists": "github:marceljuenemann/angular-drag-and-drop-lists@1.4.0",
-    "angular-gridster": "github:ManifestWebDesign/angular-gridster@0.13.5",
+    "angular-gridster": "github:ManifestWebDesign/angular-gridster@0.13.9",
     "angular-motion": "github:mgcrea/angular-motion@0.4.4",
-    "angular-nvd3": "github:krispo/angular-nvd3@1.0.6",
+    "angular-nvd3": "github:krispo/angular-nvd3@1.0.7",
     "angular-retina": "github:jrief/angular-retina@0.3.8",
     "angular-sanitize": "github:angular/bower-angular-sanitize@1.5.5",
     "angular-smart-table": "github:lorenzofox3/Smart-Table@2.1.8",
     "angular-socket-io": "github:btford/angular-socket-io@0.7.0",
     "angular-strap": "github:mgcrea/angular-strap@2.3.8",
-    "angular-tree-control": "github:wix/angular-tree-control@0.2.25",
+    "angular-tree-control": "github:wix/angular-tree-control@0.2.26",
     "angular-ui-grid": "github:angular-ui/bower-ui-grid@3.1.1",
     "angular-ui-router": "github:angular-ui/ui-router@0.2.18",
     "angular-ui-router-metatags": "github:tinusn/ui-router-metatags@1.0.3",
@@ -39,11 +39,11 @@ System.config({
     "css": "github:systemjs/plugin-css@0.1.21",
     "file-saver": "github:eligrey/FileSaver.js@master",
     "font-awesome": "npm:font-awesome@4.5.0",
-    "jade": "github:johnsoftek/plugin-jade@0.6.0",
+    "jade": "github:johnsoftek/plugin-jade@0.6.3",
     "jquery": "github:components/jquery@2.2.1",
-    "json": "github:systemjs/plugin-json@0.1.0",
+    "json": "github:systemjs/plugin-json@0.1.2",
     "jszip": "github:Stuk/jszip@2.6.0",
-    "lodash": "github:lodash/lodash@4.11.1",
+    "lodash": "github:lodash/lodash@4.13.0",
     "pdfmake": "github:bpampuch/pdfmake@0.1.20",
     "query-command-supported": "github:zenorocha/document.queryCommandSupported@1.0.0",
     "socket.io-client": "github:socketio/socket.io-client@1.4.5",
@@ -69,12 +69,11 @@ System.config({
     "github:eligrey/FileSaver.js@master": {
       "blob": "github:eligrey/Blob.js@master"
     },
-    "github:johnsoftek/plugin-jade@0.6.0": {
-      "jade-compiler": "npm:jade@1.11.0",
-      "text": "github:systemjs/plugin-text@0.0.4"
+    "github:johnsoftek/plugin-jade@0.6.3": {
+      "jade-compiler": "npm:jade@1.11.0"
     },
     "github:jspm/nodelibs-assert@0.1.0": {
-      "assert": "npm:assert@1.3.0"
+      "assert": "npm:assert@1.4.0"
     },
     "github:jspm/nodelibs-buffer@0.1.0": {
       "buffer": "npm:buffer@3.6.0"
@@ -100,7 +99,7 @@ System.config({
       "path-browserify": "npm:path-browserify@0.0.0"
     },
     "github:jspm/nodelibs-process@0.1.2": {
-      "process": "npm:process@0.11.2"
+      "process": "npm:process@0.11.3"
     },
     "github:jspm/nodelibs-stream@0.1.0": {
       "stream-browserify": "npm:stream-browserify@1.0.0"
@@ -117,7 +116,7 @@ System.config({
     "github:jspm/nodelibs-vm@0.1.0": {
       "vm-browserify": "npm:vm-browserify@0.0.4"
     },
-    "github:krispo/angular-nvd3@1.0.6": {
+    "github:krispo/angular-nvd3@1.0.7": {
       "d3": "npm:d3@3.5.14",
       "nvd3": "npm:nvd3@1.8.1"
     },
@@ -132,7 +131,7 @@ System.config({
       "angular-sanitize": "github:angular/bower-angular-sanitize@1.5.5"
     },
     "github:twbs/bootstrap@3.3.6": {
-      "jquery": "npm:jquery@2.2.3"
+      "jquery": "npm:jquery@2.2.4"
     },
     "npm:acorn-globals@1.0.9": {
       "acorn": "npm:acorn@2.7.0"
@@ -150,7 +149,7 @@ System.config({
       "stream": "github:jspm/nodelibs-stream@0.1.0"
     },
     "npm:align-text@0.1.4": {
-      "kind-of": "npm:kind-of@3.0.2",
+      "kind-of": "npm:kind-of@3.0.3",
       "longest": "npm:longest@1.0.1",
       "repeat-string": "npm:repeat-string@1.5.4"
     },
@@ -163,7 +162,11 @@ System.config({
     "npm:asap@1.0.0": {
       "process": "github:jspm/nodelibs-process@0.1.2"
     },
-    "npm:assert@1.3.0": {
+    "npm:assert@1.4.0": {
+      "assert": "github:jspm/nodelibs-assert@0.1.0",
+      "buffer": "github:jspm/nodelibs-buffer@0.1.0",
+      "buffer-shims": "npm:buffer-shims@1.0.0",
+      "process": "github:jspm/nodelibs-process@0.1.2",
       "util": "npm:util@0.10.3"
     },
     "npm:async@0.2.10": {
@@ -172,6 +175,9 @@ System.config({
     "npm:babel-runtime@5.8.38": {
       "process": "github:jspm/nodelibs-process@0.1.2"
     },
+    "npm:buffer-shims@1.0.0": {
+      "buffer": "github:jspm/nodelibs-buffer@0.1.0"
+    },
     "npm:buffer@3.6.0": {
       "base64-js": "npm:base64-js@0.0.8",
       "child_process": "github:jspm/nodelibs-child_process@0.1.0",
@@ -182,7 +188,7 @@ System.config({
     },
     "npm:center-align@0.1.3": {
       "align-text": "npm:align-text@0.1.4",
-      "lazy-cache": "npm:lazy-cache@1.0.3"
+      "lazy-cache": "npm:lazy-cache@1.0.4"
     },
     "npm:clean-css@3.4.12": {
       "buffer": "github:jspm/nodelibs-buffer@0.1.0",
@@ -223,7 +229,7 @@ System.config({
       "fs": "github:jspm/nodelibs-fs@0.1.2",
       "path": "github:jspm/nodelibs-path@0.1.0",
       "process": "github:jspm/nodelibs-process@0.1.2",
-      "systemjs-json": "github:systemjs/plugin-json@0.1.0"
+      "systemjs-json": "github:systemjs/plugin-json@0.1.2"
     },
     "npm:core-util-is@1.0.2": {
       "buffer": "github:jspm/nodelibs-buffer@0.1.0"
@@ -250,6 +256,9 @@ System.config({
     "npm:is-buffer@1.1.3": {
       "buffer": "github:jspm/nodelibs-buffer@0.1.0"
     },
+    "npm:isarray@1.0.0": {
+      "systemjs-json": "github:systemjs/plugin-json@0.1.2"
+    },
     "npm:jade@1.11.0": {
       "buffer": "github:jspm/nodelibs-buffer@0.1.0",
       "character-parser": "npm:character-parser@1.2.1",
@@ -260,7 +269,7 @@ System.config({
       "mkdirp": "npm:mkdirp@0.5.1",
       "path": "github:jspm/nodelibs-path@0.1.0",
       "process": "github:jspm/nodelibs-process@0.1.2",
-      "systemjs-json": "github:systemjs/plugin-json@0.1.0",
+      "systemjs-json": "github:systemjs/plugin-json@0.1.2",
       "transformers": "npm:transformers@2.1.0",
       "uglify-js": "npm:uglify-js@2.6.2",
       "void-elements": "npm:void-elements@2.0.1",
@@ -272,11 +281,11 @@ System.config({
       "is-promise": "npm:is-promise@2.1.0",
       "promise": "npm:promise@6.1.0"
     },
-    "npm:kind-of@3.0.2": {
+    "npm:kind-of@3.0.3": {
       "buffer": "github:jspm/nodelibs-buffer@0.1.0",
       "is-buffer": "npm:is-buffer@1.1.3"
     },
-    "npm:lazy-cache@1.0.3": {
+    "npm:lazy-cache@1.0.4": {
       "process": "github:jspm/nodelibs-process@0.1.2"
     },
     "npm:mkdirp@0.5.1": {
@@ -299,7 +308,7 @@ System.config({
     "npm:path-browserify@0.0.0": {
       "process": "github:jspm/nodelibs-process@0.1.2"
     },
-    "npm:process@0.11.2": {
+    "npm:process@0.11.3": {
       "assert": "github:jspm/nodelibs-assert@0.1.0"
     },
     "npm:promise@2.0.0": {
@@ -335,7 +344,7 @@ System.config({
       "amdefine": "npm:amdefine@1.0.0",
       "process": "github:jspm/nodelibs-process@0.1.2"
     },
-    "npm:source-map@0.5.3": {
+    "npm:source-map@0.5.6": {
       "process": "github:jspm/nodelibs-process@0.1.2"
     },
     "npm:stream-browserify@1.0.0": {
@@ -369,7 +378,7 @@ System.config({
       "fs": "github:jspm/nodelibs-fs@0.1.2",
       "path": "github:jspm/nodelibs-path@0.1.0",
       "process": "github:jspm/nodelibs-process@0.1.2",
-      "source-map": "npm:source-map@0.5.3",
+      "source-map": "npm:source-map@0.5.6",
       "uglify-to-browserify": "npm:uglify-to-browserify@1.0.2",
       "yargs": "npm:yargs@3.10.0"
     },

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/controllers/admin-controller.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/controllers/admin-controller.js b/modules/web-console/src/main/js/controllers/admin-controller.js
index 5abee04..f46bab0 100644
--- a/modules/web-console/src/main/js/controllers/admin-controller.js
+++ b/modules/web-console/src/main/js/controllers/admin-controller.js
@@ -32,21 +32,21 @@ consoleModule.controller('adminController', [
                         user.userName = user.firstName + ' ' + user.lastName;
                         user.countryCode = Countries.getByName(user.country).code;
                         user.label = user.userName + ' ' + user.email + ' ' +
-                            (user.company || '') + ' ' + (user.country || '');
-                    })
+                            (user.company || '') + ' ' + (user.countryCode || '');
+                    });
                 })
                 .catch((err) => $common.showError(err));
         };
 
         _reloadUsers();
 
-        $scope.becomeUser = function (user) {
+        $scope.becomeUser = function(user) {
             $http.get('/api/v1/admin/become', { params: {viewedUserId: user._id}})
                 .then(User.read)
-                .then((user) => {
-                    $rootScope.$broadcast('user', user);
+                .then((becomeUser) => {
+                    $rootScope.$broadcast('user', becomeUser);
 
-                    $state.go('base.configuration.clusters')
+                    $state.go('base.configuration.clusters');
                 })
                 .catch((errMsg) => $common.showError($common.errorMessage(errMsg)));
         };
@@ -64,7 +64,7 @@ consoleModule.controller('adminController', [
                             $common.showInfo('User has been removed: "' + user.userName + '"');
                         })
                         .error((errMsg, status) => {
-                            if (status == 503)
+                            if (status === 503)
                                 $common.showInfo(errMsg);
                             else
                                 $common.showError('Failed to remove user: "' + $common.errorMessage(errMsg) + '"');

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/controllers/caches-controller.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/controllers/caches-controller.js b/modules/web-console/src/main/js/controllers/caches-controller.js
index 8f4bedd..ac4d951 100644
--- a/modules/web-console/src/main/js/controllers/caches-controller.js
+++ b/modules/web-console/src/main/js/controllers/caches-controller.js
@@ -20,14 +20,14 @@ import consoleModule from 'controllers/common-module';
 
 consoleModule.controller('cachesController', [
     '$scope', '$http', '$state', '$filter', '$timeout', '$common', '$confirm', '$clone', '$loading', '$cleanup', '$unsavedChangesGuard',
-    function ($scope, $http, $state, $filter, $timeout, $common, $confirm, $clone, $loading, $cleanup, $unsavedChangesGuard) {
+    function($scope, $http, $state, $filter, $timeout, $common, $confirm, $clone, $loading, $cleanup, $unsavedChangesGuard) {
         $unsavedChangesGuard.install($scope);
 
-        var emptyCache = {empty: true};
+        const emptyCache = {empty: true};
 
-        var __original_value;
+        let __original_value;
 
-        var blank = {
+        const blank = {
             evictionPolicy: {},
             cacheStoreFactory: {},
             nearConfiguration: {}
@@ -44,15 +44,15 @@ consoleModule.controller('cachesController', [
         $scope.saveBtnTipText = $common.saveBtnTipText;
         $scope.widthIsSufficient = $common.widthIsSufficient;
 
-        var showPopoverMessage = $common.showPopoverMessage;
+        const showPopoverMessage = $common.showPopoverMessage;
 
-        $scope.contentVisible = function () {
-            var item = $scope.backupItem;
+        $scope.contentVisible = function() {
+            const item = $scope.backupItem;
 
             return !item.empty && (!item._id || _.find($scope.displayedRows, {_id: item._id}));
         };
 
-        $scope.toggleExpanded = function () {
+        $scope.toggleExpanded = function() {
             $scope.ui.expanded = !$scope.ui.expanded;
 
             $common.hidePopover();
@@ -71,10 +71,9 @@ consoleModule.controller('cachesController', [
         }
 
         function cacheDomains(item) {
-            return _.reduce($scope.domains, function (memo, domain) {
-                if (item && _.includes(item.domains, domain.value)) {
+            return _.reduce($scope.domains, function(memo, domain) {
+                if (item && _.includes(item.domains, domain.value))
                     memo.push(domain.meta);
-                }
 
                 return memo;
             }, []);
@@ -84,18 +83,15 @@ consoleModule.controller('cachesController', [
 
         // When landing on the page, get caches and show them.
         $http.post('/api/v1/configuration/caches/list')
-            .success(function (data) {
-                var validFilter = $filter('domainsValidation');
+            .success(function(data) {
+                const validFilter = $filter('domainsValidation');
 
                 $scope.spaces = data.spaces;
-
-                _.forEach(data.caches, function (cache) {
-                    cache.label = _cacheLbl(cache);
-                });
-
                 $scope.caches = data.caches;
 
-                $scope.clusters = _.map(data.clusters, function (cluster) {
+                _.forEach($scope.caches, (cache) => cache.label = _cacheLbl(cache));
+
+                $scope.clusters = _.map(data.clusters, function(cluster) {
                     return {
                         value: cluster._id,
                         label: cluster.name,
@@ -103,7 +99,7 @@ consoleModule.controller('cachesController', [
                     };
                 });
 
-                $scope.domains = _.sortBy(_.map(validFilter(data.domains, true, false), function (domain) {
+                $scope.domains = _.sortBy(_.map(validFilter(data.domains, true, false), function(domain) {
                     return {
                         value: domain._id,
                         label: domain.valueType,
@@ -112,13 +108,13 @@ consoleModule.controller('cachesController', [
                     };
                 }), 'label');
 
-                if ($state.params.id)
-                    $scope.createItem($state.params.id);
+                if ($state.params.linkId)
+                    $scope.createItem($state.params.linkId);
                 else {
-                    var lastSelectedCache = angular.fromJson(sessionStorage.lastSelectedCache);
+                    const lastSelectedCache = angular.fromJson(sessionStorage.lastSelectedCache);
 
                     if (lastSelectedCache) {
-                        var idx = _.findIndex($scope.caches, function (cache) {
+                        const idx = _.findIndex($scope.caches, function(cache) {
                             return cache._id === lastSelectedCache;
                         });
 
@@ -135,30 +131,29 @@ consoleModule.controller('cachesController', [
                 }
 
                 $scope.$watch('ui.inputForm.$valid', function(valid) {
-                    if (valid && __original_value === JSON.stringify($cleanup($scope.backupItem))) {
+                    if (valid && _.isEqual(__original_value, $cleanup($scope.backupItem)))
                         $scope.ui.inputForm.$dirty = false;
-                    }
                 });
 
-                $scope.$watch('backupItem', function (val) {
-                    var form = $scope.ui.inputForm;
+                $scope.$watch('backupItem', function(val) {
+                    const form = $scope.ui.inputForm;
 
-                    if (form.$pristine || (form.$valid && __original_value === JSON.stringify($cleanup(val))))
+                    if (form.$pristine || (form.$valid && _.isEqual(__original_value, $cleanup(val))))
                         form.$setPristine();
                     else
                         form.$setDirty();
                 }, true);
             })
-            .catch(function (errMsg) {
+            .catch(function(errMsg) {
                 $common.showError(errMsg);
             })
-            .finally(function () {
+            .finally(function() {
                 $scope.ui.ready = true;
                 $scope.ui.inputForm.$setPristine();
                 $loading.finish('loadingCachesScreen');
             });
 
-        $scope.selectItem = function (item, backup) {
+        $scope.selectItem = function(item, backup) {
             function selectItem() {
                 $scope.selectedItem = item;
 
@@ -181,7 +176,7 @@ consoleModule.controller('cachesController', [
 
                 $scope.backupItem = angular.merge({}, blank, $scope.backupItem);
 
-                __original_value = JSON.stringify($cleanup($scope.backupItem));
+                __original_value = $cleanup($scope.backupItem);
 
                 if ($common.getQueryVariable('new'))
                     $state.go('base.configuration.caches');
@@ -190,44 +185,49 @@ consoleModule.controller('cachesController', [
             $common.confirmUnsavedChanges($scope.backupItem && $scope.ui.inputForm.$dirty, selectItem);
         };
 
-        function prepareNewItem(id) {
+        $scope.linkId = () => $scope.backupItem._id ? $scope.backupItem._id : 'create';
+
+        function prepareNewItem(linkId) {
             return {
                 space: $scope.spaces[0]._id,
                 cacheMode: 'PARTITIONED',
                 atomicityMode: 'ATOMIC',
                 readFromBackup: true,
                 copyOnRead: true,
-                clusters: id && _.find($scope.clusters, {value: id})
-                    ? [id] : _.map($scope.clusters, function (cluster) { return cluster.value; }),
-                domains: id && _.find($scope.domains, { value: id }) ? [id] : [],
+                clusters: linkId && _.find($scope.clusters, {value: linkId})
+                    ? [linkId] : _.map($scope.clusters, function(cluster) { return cluster.value; }),
+                domains: linkId && _.find($scope.domains, { value: linkId }) ? [linkId] : [],
                 cacheStoreFactory: {CacheJdbcBlobStoreFactory: {connectVia: 'DataSource'}}
             };
         }
 
         // Add new cache.
-        $scope.createItem = function (id) {
-            $timeout(function () {
-                $common.ensureActivePanel($scope.ui, 'general', 'cacheName');
-            });
+        $scope.createItem = function(linkId) {
+            $timeout(() => $common.ensureActivePanel($scope.ui, 'general', 'cacheName'));
 
-            $scope.selectItem(undefined, prepareNewItem(id));
+            $scope.selectItem(null, prepareNewItem(linkId));
         };
 
-        function checkDataSources() {
-            var clusters = _.filter($scope.clusters, function (cluster) {
-                return _.includes($scope.backupItem.clusters, cluster.value);
-            });
+        function cacheClusters() {
+            return _.filter($scope.clusters, (cluster) => _.includes($scope.backupItem.clusters, cluster.value));
+        }
 
-            var checkRes = { checked: true };
+        function clusterCaches(cluster) {
+            const caches = _.filter($scope.caches,
+                (cache) => cache._id !== $scope.backupItem._id && _.includes(cluster.caches, cache._id));
 
-            var failCluster = _.find(clusters, function (cluster) {
-                var caches = _.filter($scope.caches, function (cache) {
-                    return cache._id !== $scope.backupItem._id && _.find(cluster.caches, function (clusterCache) {
-                        return clusterCache === cache._id;
-                    });
-                });
+            caches.push($scope.backupItem);
 
-                caches.push($scope.backupItem);
+            return caches;
+        }
+
+        function checkDataSources() {
+            const clusters = cacheClusters();
+
+            let checkRes = {checked: true};
+
+            const failCluster = _.find(clusters, (cluster) => {
+                const caches = clusterCaches(cluster);
 
                 checkRes = $common.checkCachesDataSources(caches, $scope.backupItem);
 
@@ -245,129 +245,88 @@ consoleModule.controller('cachesController', [
             return true;
         }
 
-        // Check cache logical consistency.
-        function validate(item) {
-            $common.hidePopover();
-
-            if ($common.isEmptyString(item.name))
-                return showPopoverMessage($scope.ui, 'general', 'cacheName', 'Cache name should not be empty!');
-
-            if (item.memoryMode === 'ONHEAP_TIERED' && item.offHeapMaxMemory > 0 && !$common.isDefined(item.evictionPolicy.kind))
-                return showPopoverMessage($scope.ui, 'memory', 'evictionPolicyKind', 'Eviction policy should not be configured!');
+        function checkSQLSchemas() {
+            const clusters = cacheClusters();
 
-            var form = $scope.ui.inputForm;
-            var errors = form.$error;
-            var errKeys = Object.keys(errors);
+            let checkRes = {checked: true};
 
-            if (errKeys && errKeys.length > 0) {
-                var firstErrorKey = errKeys[0];
+            const failCluster = _.find(clusters, (cluster) => {
+                const caches = clusterCaches(cluster);
 
-                var firstError = errors[firstErrorKey][0];
-                var actualError = firstError.$error[firstErrorKey][0];
+                checkRes = $common.checkCacheSQLSchemas(caches, $scope.backupItem);
 
-                var errNameFull = actualError.$name;
-                var errNameShort = errNameFull;
+                return !checkRes.checked;
+            });
 
-                if (errNameShort.endsWith('TextInput'))
-                    errNameShort = errNameShort.substring(0, errNameShort.length - 9);
+            if (!checkRes.checked) {
+                return showPopoverMessage($scope.ui, 'query', 'sqlSchema',
+                    'Found cache "' + checkRes.secondCache.name + '" in cluster "' + failCluster.label + '" ' +
+                    'with the same SQL schema name "' + checkRes.firstCache.sqlSchema + '"', 10000);
+            }
 
-                var extractErrorMessage = function (errName) {
-                    try {
-                        return errors[firstErrorKey][0].$errorMessages[errName][firstErrorKey];
-                    }
-                    catch(ignored) {
-                        try {
-                            msg = form[firstError.$name].$errorMessages[errName][firstErrorKey];
-                        }
-                        catch(ignited) {
-                            return false;
-                        }
-                    }
-                };
+            return true;
+        }
 
-                var msg = extractErrorMessage(errNameFull) || extractErrorMessage(errNameShort) || 'Invalid value!';
+        function checkStoreFactoryBean(storeFactory, beanFieldId) {
+            if (!$common.isValidJavaIdentifier('Data source bean', storeFactory.dataSourceBean, beanFieldId, $scope.ui, 'store'))
+                return false;
 
-                return showPopoverMessage($scope.ui, firstError.$name, errNameFull, msg);
-            }
+            return checkDataSources();
+        }
 
-            if (item.memoryMode === 'OFFHEAP_VALUES' && !_.isEmpty(item.domains))
-                return showPopoverMessage($scope.ui, 'memory', 'memoryMode',
-                    'Query indexing could not be enabled while values are stored off-heap!');
+        function checkStoreFactory(item) {
+            const cacheStoreFactorySelected = item.cacheStoreFactory && item.cacheStoreFactory.kind;
 
-            if (item.memoryMode === 'OFFHEAP_TIERED' && !$common.isDefined(item.offHeapMaxMemory))
-                return showPopoverMessage($scope.ui, 'memory', 'offHeapMaxMemory',
-                    'Off-heap max memory should be specified!');
+            if (cacheStoreFactorySelected) {
+                const storeFactory = item.cacheStoreFactory[item.cacheStoreFactory.kind];
 
-            var cacheStoreFactorySelected = item.cacheStoreFactory && item.cacheStoreFactory.kind;
+                if (item.cacheStoreFactory.kind === 'CacheJdbcPojoStoreFactory' && !checkStoreFactoryBean(storeFactory, 'pojoDataSourceBean'))
+                    return false;
 
-            if (cacheStoreFactorySelected) {
-                var storeFactory = item.cacheStoreFactory[item.cacheStoreFactory.kind];
+                if (item.cacheStoreFactory.kind === 'CacheJdbcBlobStoreFactory' && storeFactory.connectVia !== 'URL'
+                    && !checkStoreFactoryBean(storeFactory, 'blobDataSourceBean'))
+                    return false;
+            }
 
-                if (item.cacheStoreFactory.kind === 'CacheJdbcPojoStoreFactory') {
-                    if ($common.isEmptyString(storeFactory.dataSourceBean))
-                        return showPopoverMessage($scope.ui, 'store', 'dataSourceBean',
-                            'Data source bean name should not be empty!');
+            if ((item.readThrough || item.writeThrough) && !cacheStoreFactorySelected)
+                return showPopoverMessage($scope.ui, 'store', 'cacheStoreFactory', (item.readThrough ? 'Read' : 'Write') + ' through are enabled but store is not configured!');
 
-                    if (!$common.isValidJavaIdentifier('Data source bean', storeFactory.dataSourceBean, 'dataSourceBean', $scope.ui, 'store'))
-                        return false;
+            if (item.writeBehindEnabled && !cacheStoreFactorySelected)
+                return showPopoverMessage($scope.ui, 'store', 'cacheStoreFactory', 'Write behind enabled but store is not configured!');
 
-                    if (!storeFactory.dialect)
-                        return showPopoverMessage($scope.ui, 'store', 'pojoDialect',
-                            'Dialect should not be empty!');
+            if (cacheStoreFactorySelected && !item.readThrough && !item.writeThrough)
+                return showPopoverMessage($scope.ui, 'store', 'readThroughTooltip', 'Store is configured but read/write through are not enabled!');
 
-                    if (!checkDataSources())
-                        return false;
-                }
+            return true;
+        }
 
-                if (item.cacheStoreFactory.kind === 'CacheJdbcBlobStoreFactory') {
-                    if (storeFactory.connectVia === 'URL') {
-                        if ($common.isEmptyString(storeFactory.connectionUrl))
-                            return showPopoverMessage($scope.ui, 'store', 'connectionUrl',
-                                'Connection URL should not be empty!');
+        // Check cache logical consistency.
+        function validate(item) {
+            $common.hidePopover();
 
-                        if ($common.isEmptyString(storeFactory.user))
-                            return showPopoverMessage($scope.ui, 'store', 'user',
-                                'User should not be empty!');
-                    }
-                    else {
-                        if ($common.isEmptyString(storeFactory.dataSourceBean))
-                            return showPopoverMessage($scope.ui, 'store', 'dataSourceBean',
-                                'Data source bean name should not be empty!');
+            if ($common.isEmptyString(item.name))
+                return showPopoverMessage($scope.ui, 'general', 'cacheName', 'Cache name should not be empty!');
 
-                        if (!$common.isValidJavaIdentifier('Data source bean', storeFactory.dataSourceBean, 'dataSourceBean', $scope.ui, 'store'))
-                            return false;
+            if (item.memoryMode === 'ONHEAP_TIERED' && item.offHeapMaxMemory > 0 && !$common.isDefined(item.evictionPolicy.kind))
+                return showPopoverMessage($scope.ui, 'memory', 'evictionPolicyKind', 'Eviction policy should not be configured!');
 
-                        if (!storeFactory.dialect)
-                            return showPopoverMessage($scope.ui, 'store', 'blobDialect',
-                                'Database should not be empty!');
+            if (!$common.checkFieldValidators($scope.ui))
+                return false;
 
-                        if (!checkDataSources())
-                            return false;
-                    }
-                }
-            }
+            if (item.memoryMode === 'OFFHEAP_VALUES' && !_.isEmpty(item.domains))
+                return showPopoverMessage($scope.ui, 'memory', 'memoryMode', 'Query indexing could not be enabled while values are stored off-heap!');
 
-            if ((item.readThrough || item.writeThrough) && !cacheStoreFactorySelected)
-                return showPopoverMessage($scope.ui, 'store', 'cacheStoreFactory',
-                    (item.readThrough ? 'Read' : 'Write') + ' through are enabled but store is not configured!');
+            if (item.memoryMode === 'OFFHEAP_TIERED' && (!$common.isDefined(item.offHeapMaxMemory) || item.offHeapMaxMemory < 0))
+                return showPopoverMessage($scope.ui, 'memory', 'offHeapMaxMemory', 'Off-heap max memory should be specified!');
 
-            if (item.writeBehindEnabled && !cacheStoreFactorySelected)
-                return showPopoverMessage($scope.ui, 'store', 'cacheStoreFactory',
-                    'Write behind enabled but store is not configured!');
+            if (!checkSQLSchemas())
+                return false;
 
-            if (cacheStoreFactorySelected) {
-                if (!item.readThrough && !item.writeThrough)
-                    return showPopoverMessage($scope.ui, 'store', 'readThroughTooltip',
-                        'Store is configured but read/write through are not enabled!');
-            }
+            if (!checkStoreFactory(item))
+                return false;
 
             if (item.writeBehindFlushSize === 0 && item.writeBehindFlushFrequency === 0)
-                return showPopoverMessage($scope.ui, 'store', 'writeBehindFlushSize',
-                    'Both "Flush frequency" and "Flush size" are not allowed as 0!');
-
-            if (item.cacheMode !== 'LOCAL' && item.rebalanceMode !== 'NONE' && item.rebalanceBatchSize === 0)
-                return showPopoverMessage($scope.ui, 'rebalance', 'rebalanceBatchSize',
-                    'Batch size should be more than 0 if rebalance mode is "SYNC" or "ASYNC" !', 10000);
+                return showPopoverMessage($scope.ui, 'store', 'writeBehindFlushSize', 'Both "Flush frequency" and "Flush size" are not allowed as 0!');
 
             return true;
         }
@@ -375,12 +334,12 @@ consoleModule.controller('cachesController', [
         // Save cache in database.
         function save(item) {
             $http.post('/api/v1/configuration/caches/save', item)
-                .success(function (_id) {
+                .success(function(_id) {
                     item.label = _cacheLbl(item);
 
                     $scope.ui.inputForm.$setPristine();
 
-                    var idx = _.findIndex($scope.caches, function (cache) {
+                    const idx = _.findIndex($scope.caches, function(cache) {
                         return cache._id === _id;
                     });
 
@@ -391,18 +350,32 @@ consoleModule.controller('cachesController', [
                         $scope.caches.push(item);
                     }
 
+                    _.forEach($scope.clusters, (cluster) => {
+                        if (_.includes(item.clusters, cluster.value))
+                            cluster.caches = _.union(cluster.caches, [_id]);
+                        else
+                            _.remove(cluster.caches, (id) => id === _id);
+                    });
+
+                    _.forEach($scope.domains, (domain) => {
+                        if (_.includes(item.domains, domain.value))
+                            domain.meta.caches = _.union(domain.meta.caches, [_id]);
+                        else
+                            _.remove(domain.meta.caches, (id) => id === _id);
+                    });
+
                     $scope.selectItem(item);
 
                     $common.showInfo('Cache "' + item.name + '" saved.');
                 })
-                .error(function (errMsg) {
+                .error(function(errMsg) {
                     $common.showError(errMsg);
                 });
         }
 
         // Save cache.
-        $scope.saveItem = function () {
-            var item = $scope.backupItem;
+        $scope.saveItem = function() {
+            const item = $scope.backupItem;
 
             angular.extend(item, $common.autoCacheStoreConfiguration(item, cacheDomains(item)));
 
@@ -411,41 +384,43 @@ consoleModule.controller('cachesController', [
         };
 
         function _cacheNames() {
-            return _.map($scope.caches, function (cache) {
+            return _.map($scope.caches, function(cache) {
                 return cache.name;
             });
         }
 
         // Clone cache with new name.
-        $scope.cloneItem = function () {
+        $scope.cloneItem = function() {
             if (validate($scope.backupItem)) {
-                $clone.confirm($scope.backupItem.name, _cacheNames()).then(function (newName) {
-                    var item = angular.copy($scope.backupItem);
+                $clone.confirm($scope.backupItem.name, _cacheNames()).then(function(newName) {
+                    const item = angular.copy($scope.backupItem);
 
                     delete item._id;
 
                     item.name = newName;
 
+                    delete item.sqlSchema;
+
                     save(item);
                 });
             }
         };
 
         // Remove cache from db.
-        $scope.removeItem = function () {
-            var selectedItem = $scope.selectedItem;
+        $scope.removeItem = function() {
+            const selectedItem = $scope.selectedItem;
 
             $confirm.confirm('Are you sure you want to remove cache: "' + selectedItem.name + '"?')
-                .then(function () {
-                    var _id = selectedItem._id;
+                .then(function() {
+                    const _id = selectedItem._id;
 
-                    $http.post('/api/v1/configuration/caches/remove', {_id: _id})
-                        .success(function () {
+                    $http.post('/api/v1/configuration/caches/remove', {_id})
+                        .success(function() {
                             $common.showInfo('Cache has been removed: ' + selectedItem.name);
 
-                            var caches = $scope.caches;
+                            const caches = $scope.caches;
 
-                            var idx = _.findIndex(caches, function (cache) {
+                            const idx = _.findIndex(caches, function(cache) {
                                 return cache._id === _id;
                             });
 
@@ -454,37 +429,46 @@ consoleModule.controller('cachesController', [
 
                                 if (caches.length > 0)
                                     $scope.selectItem(caches[0]);
-                                else
+                                else {
                                     $scope.backupItem = emptyCache;
+                                    $scope.ui.inputForm.$setPristine();
+                                }
+
+                                _.forEach($scope.clusters, (cluster) => _.remove(cluster.caches, (id) => id === _id));
+                                _.forEach($scope.domains, (domain) => _.remove(domain.meta.caches, (id) => id === _id));
                             }
                         })
-                        .error(function (errMsg) {
+                        .error(function(errMsg) {
                             $common.showError(errMsg);
                         });
                 });
         };
 
         // Remove all caches from db.
-        $scope.removeAllItems = function () {
+        $scope.removeAllItems = function() {
             $confirm.confirm('Are you sure you want to remove all caches?')
-                .then(function () {
+                .then(function() {
                     $http.post('/api/v1/configuration/caches/remove/all')
-                        .success(function () {
+                        .success(function() {
                             $common.showInfo('All caches have been removed');
 
                             $scope.caches = [];
+
+                            _.forEach($scope.clusters, (cluster) => cluster.caches = []);
+                            _.forEach($scope.domains, (domain) => domain.meta.caches = []);
+
                             $scope.backupItem = emptyCache;
                             $scope.ui.inputForm.$setPristine();
                         })
-                        .error(function (errMsg) {
+                        .error(function(errMsg) {
                             $common.showError(errMsg);
                         });
                 });
         };
 
-        $scope.resetAll = function () {
+        $scope.resetAll = function() {
             $confirm.confirm('Are you sure you want to undo all changes for current cache?')
-                .then(function () {
+                .then(function() {
                     $scope.backupItem = $scope.selectedItem ? angular.copy($scope.selectedItem) : prepareNewItem();
                     $scope.ui.inputForm.$setPristine();
                 });


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

Posted by ak...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/generator/generator-common.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/generator/generator-common.js b/modules/web-console/src/main/js/generator/generator-common.js
index 812fa6a..10bd299 100644
--- a/modules/web-console/src/main/js/generator/generator-common.js
+++ b/modules/web-console/src/main/js/generator/generator-common.js
@@ -19,23 +19,22 @@
 const $generatorCommon = {};
 
 // Add leading zero.
-$generatorCommon.addLeadingZero = function (numberStr, minSize) {
+$generatorCommon.addLeadingZero = function(numberStr, minSize) {
     if (typeof (numberStr) !== 'string')
-        numberStr = '' + numberStr;
+        numberStr = String(numberStr);
 
-    while (numberStr.length < minSize) {
+    while (numberStr.length < minSize)
         numberStr = '0' + numberStr;
-    }
 
     return numberStr;
 };
 
 // Format date to string.
-$generatorCommon.formatDate = function (date) {
-    var dd = $generatorCommon.addLeadingZero(date.getDate(), 2);
-    var mm = $generatorCommon.addLeadingZero(date.getMonth() + 1, 2);
+$generatorCommon.formatDate = function(date) {
+    const dd = $generatorCommon.addLeadingZero(date.getDate(), 2);
+    const mm = $generatorCommon.addLeadingZero(date.getMonth() + 1, 2);
 
-    var yyyy = date.getFullYear();
+    const yyyy = date.getFullYear();
 
     return mm + '/' + dd + '/' + yyyy + ' ' + $generatorCommon.addLeadingZero(date.getHours(), 2) + ':' + $generatorCommon.addLeadingZero(date.getMinutes(), 2);
 };
@@ -46,11 +45,11 @@ $generatorCommon.mainComment = function mainComment() {
 };
 
 // Create result holder with service functions and properties for XML and java code generation.
-$generatorCommon.builder = function (deep) {
+$generatorCommon.builder = function(deep) {
     if (_.isNil($generatorCommon.JavaTypes))
         $generatorCommon.JavaTypes = angular.element(document.getElementById('app')).injector().get('JavaTypes');
 
-    var res = [];
+    const res = [];
 
     res.deep = deep || 0;
     res.needEmptyLine = false;
@@ -66,7 +65,7 @@ $generatorCommon.builder = function (deep) {
     res.safeDatasources = [];
     res.safePoint = -1;
 
-    res.mergeProps = function (fromRes) {
+    res.mergeProps = function(fromRes) {
         if ($generatorCommon.isDefinedAndNotEmpty(fromRes)) {
             res.datasources = fromRes.datasources;
 
@@ -76,18 +75,18 @@ $generatorCommon.builder = function (deep) {
         }
     };
 
-    res.mergeLines = function (fromRes) {
+    res.mergeLines = function(fromRes) {
         if ($generatorCommon.isDefinedAndNotEmpty(fromRes)) {
             if (res.needEmptyLine)
                 res.push('');
 
-            _.forEach(fromRes, function (line) {
+            _.forEach(fromRes, function(line) {
                 res.append(line);
             });
         }
     };
 
-    res.startSafeBlock = function () {
+    res.startSafeBlock = function() {
         res.safeDeep = this.deep;
         this.safeNeedEmptyLine = this.needEmptyLine;
         this.safeImports = _.cloneDeep(this.imports);
@@ -96,7 +95,7 @@ $generatorCommon.builder = function (deep) {
         this.safePoint = this.length;
     };
 
-    res.rollbackSafeBlock = function () {
+    res.rollbackSafeBlock = function() {
         if (this.safePoint >= 0) {
             this.splice(this.safePoint, this.length - this.safePoint);
 
@@ -110,16 +109,16 @@ $generatorCommon.builder = function (deep) {
     };
 
     res.asString = function() {
-      return this.join('\n');
+        return this.join('\n');
     };
 
-    res.append = function (s) {
+    res.append = function(s) {
         this.push((this.lineStart ? _.repeat('    ', this.deep) : '') + s);
 
         return this;
     };
 
-    res.line = function (s) {
+    res.line = function(s) {
         if (s) {
             if (res.needEmptyLine)
                 res.push('');
@@ -134,7 +133,7 @@ $generatorCommon.builder = function (deep) {
         return res;
     };
 
-    res.startBlock = function (s) {
+    res.startBlock = function(s) {
         if (s) {
             if (this.needEmptyLine)
                 this.push('');
@@ -151,7 +150,7 @@ $generatorCommon.builder = function (deep) {
         return this;
     };
 
-    res.endBlock = function (s) {
+    res.endBlock = function(s) {
         this.deep--;
 
         if (s)
@@ -162,11 +161,11 @@ $generatorCommon.builder = function (deep) {
         return this;
     };
 
-    res.softEmptyLine = function () {
+    res.softEmptyLine = function() {
         this.needEmptyLine = this.length > 0;
     };
 
-    res.emptyLineIfNeeded = function () {
+    res.emptyLineIfNeeded = function() {
         if (this.needEmptyLine) {
             this.push('');
             this.lineStart = true;
@@ -181,15 +180,15 @@ $generatorCommon.builder = function (deep) {
      * @param clsName Full class name.
      * @returns {String} Short class name or full class name in case of names conflict.
      */
-    res.importClass = function (clsName) {
+    res.importClass = function(clsName) {
         if ($generatorCommon.JavaTypes.isJavaPrimitive(clsName))
             return clsName;
 
-        var fullClassName = $generatorCommon.JavaTypes.fullClassName(clsName);
+        const fullClassName = $generatorCommon.JavaTypes.fullClassName(clsName);
 
-        var dotIdx = fullClassName.lastIndexOf('.');
+        const dotIdx = fullClassName.lastIndexOf('.');
 
-        var shortName = dotIdx > 0 ? fullClassName.substr(dotIdx + 1) : fullClassName;
+        const shortName = dotIdx > 0 ? fullClassName.substr(dotIdx + 1) : fullClassName;
 
         if (this.imports[shortName]) {
             if (this.imports[shortName] !== fullClassName)
@@ -207,10 +206,10 @@ $generatorCommon.builder = function (deep) {
      * @param member Static member.
      * @returns {String} Short class name or full class name in case of names conflict.
      */
-    res.importStatic = function (member) {
-        var dotIdx = member.lastIndexOf('.');
+    res.importStatic = function(member) {
+        const dotIdx = member.lastIndexOf('.');
 
-        var shortName = dotIdx > 0 ? member.substr(dotIdx + 1) : member;
+        const shortName = dotIdx > 0 ? member.substr(dotIdx + 1) : member;
 
         if (this.staticImports[shortName]) {
             if (this.staticImports[shortName] !== member)
@@ -225,33 +224,33 @@ $generatorCommon.builder = function (deep) {
     /**
      * @returns String with "java imports" section.
      */
-    res.generateImports = function () {
-        var res = [];
+    res.generateImports = function() {
+        const genImports = [];
 
-        for (var clsName in this.imports) {
+        for (const clsName in this.imports) {
             if (this.imports.hasOwnProperty(clsName) && this.imports[clsName].lastIndexOf('java.lang.', 0) !== 0)
-                res.push('import ' + this.imports[clsName] + ';');
+                genImports.push('import ' + this.imports[clsName] + ';');
         }
 
-        res.sort();
+        genImports.sort();
 
-        return res.join('\n');
+        return genImports.join('\n');
     };
 
     /**
      * @returns String with "java imports" section.
      */
-    res.generateStaticImports = function () {
-        var res = [];
+    res.generateStaticImports = function() {
+        const statImports = [];
 
-        for (var clsName in this.staticImports) {
+        for (const clsName in this.staticImports) {
             if (this.staticImports.hasOwnProperty(clsName) && this.staticImports[clsName].lastIndexOf('java.lang.', 0) !== 0)
-                res.push('import static ' + this.staticImports[clsName] + ';');
+                statImports.push('import static ' + this.staticImports[clsName] + ';');
         }
 
-        res.sort();
+        statImports.sort();
 
-        return res.join('\n');
+        return statImports.join('\n');
     };
 
     return res;
@@ -298,7 +297,7 @@ $generatorCommon.JDBC_DIALECTS = {
 
 // Return JDBC dialect full class name for specified database.
 $generatorCommon.jdbcDialectClassName = function(db) {
-    var dialectClsName = $generatorCommon.JDBC_DIALECTS[db];
+    const dialectClsName = $generatorCommon.JDBC_DIALECTS[db];
 
     return dialectClsName ? dialectClsName : 'Unknown database: ' + db;
 };
@@ -338,7 +337,7 @@ $generatorCommon.DATA_SOURCES = {
 
 // Return data source full class name for specified database.
 $generatorCommon.dataSourceClassName = function(db) {
-    var dsClsName = $generatorCommon.DATA_SOURCES[db];
+    const dsClsName = $generatorCommon.DATA_SOURCES[db];
 
     return dsClsName ? dsClsName : 'Unknown database: ' + db;
 };
@@ -490,22 +489,22 @@ $generatorCommon.IGFS_IPC_CONFIGURATION = {
 };
 
 // Check that cache has datasource.
-$generatorCommon.cacheHasDatasource = function (cache) {
+$generatorCommon.cacheHasDatasource = function(cache) {
     if (cache.cacheStoreFactory && cache.cacheStoreFactory.kind) {
-        var storeFactory = cache.cacheStoreFactory[cache.cacheStoreFactory.kind];
+        const storeFactory = cache.cacheStoreFactory[cache.cacheStoreFactory.kind];
 
-        return !!(storeFactory && (storeFactory.connectVia ? (storeFactory.connectVia === 'DataSource' ? storeFactory.dialect : false) : storeFactory.dialect));
+        return !!(storeFactory && (storeFactory.connectVia ? (storeFactory.connectVia === 'DataSource' ? storeFactory.dialect : false) : storeFactory.dialect)); // eslint-disable-line no-nested-ternary
     }
 
     return false;
 };
 
-$generatorCommon.secretPropertiesNeeded = function (cluster) {
+$generatorCommon.secretPropertiesNeeded = function(cluster) {
     return !_.isNil(_.find(cluster.caches, $generatorCommon.cacheHasDatasource)) || cluster.sslEnabled;
 };
 
 // Check that binary is configured.
-$generatorCommon.binaryIsDefined = function (binary) {
+$generatorCommon.binaryIsDefined = function(binary) {
     return binary && ($generatorCommon.isDefinedAndNotEmpty(binary.idMapper) || $generatorCommon.isDefinedAndNotEmpty(binary.nameMapper) ||
         $generatorCommon.isDefinedAndNotEmpty(binary.serializer) || $generatorCommon.isDefinedAndNotEmpty(binary.typeConfigurations) ||
         (!_.isNil(binary.compactFooter) && !binary.compactFooter));
@@ -521,7 +520,7 @@ $generatorCommon.domainQueryMetadata = function(domain) {
  * @param {Array<String>} props Array of properties names.
  * @returns {boolean} 'true' if
  */
-$generatorCommon.hasAtLeastOneProperty = function (obj, props) {
+$generatorCommon.hasAtLeastOneProperty = function(obj, props) {
     return obj && props && _.findIndex(props, (prop) => !_.isNil(obj[prop])) >= 0;
 };
 
@@ -532,8 +531,8 @@ $generatorCommon.hasAtLeastOneProperty = function (obj, props) {
  * @param name to convert.
  * @returns {string} Valid java name.
  */
-$generatorCommon.toJavaName = function (prefix, name) {
-    var javaName = name ? name.replace(/[^A-Za-z_0-9]+/g, '_') : 'dflt';
+$generatorCommon.toJavaName = function(prefix, name) {
+    const javaName = name ? name.replace(/[^A-Za-z_0-9]+/g, '_') : 'dflt';
 
     return prefix + javaName.charAt(0).toLocaleUpperCase() + javaName.slice(1);
 };
@@ -542,8 +541,8 @@ $generatorCommon.toJavaName = function (prefix, name) {
  * @param v Value to check.
  * @returns {boolean} 'true' if value defined and not empty string.
  */
-$generatorCommon.isDefinedAndNotEmpty = function (v) {
-    var defined = !_.isNil(v);
+$generatorCommon.isDefinedAndNotEmpty = function(v) {
+    let defined = !_.isNil(v);
 
     if (defined && (_.isString(v) || _.isArray(v)))
         defined = v.length > 0;
@@ -556,8 +555,8 @@ $generatorCommon.isDefinedAndNotEmpty = function (v) {
  * @param {Array<String>} props Properties names.
  * @returns {boolean} 'true' if object contains at least one from specified properties.
  */
-$generatorCommon.hasProperty = function (obj, props) {
-    for (var propName in props) {
+$generatorCommon.hasProperty = function(obj, props) {
+    for (const propName in props) {
         if (props.hasOwnProperty(propName)) {
             if (obj[propName])
                 return true;
@@ -567,4 +566,46 @@ $generatorCommon.hasProperty = function (obj, props) {
     return false;
 };
 
+/**
+ * Get class for selected implementation of Failover SPI.
+ *
+ * @param spi Failover SPI configuration.
+ * @returns {*} Class for selected implementation of Failover SPI.
+ */
+$generatorCommon.failoverSpiClass = function(spi) {
+    switch (spi.kind) {
+        case 'JobStealing': return 'org.apache.ignite.spi.failover.jobstealing.JobStealingFailoverSpi';
+        case 'Never': return 'org.apache.ignite.spi.failover.never.NeverFailoverSpi';
+        case 'Always': return 'org.apache.ignite.spi.failover.always.AlwaysFailoverSpi';
+        case 'Custom': return _.get(spi, 'Custom.class');
+        default: return 'Unknown';
+    }
+};
+
+$generatorCommon.loggerConfigured = function(logger) {
+    if (logger && logger.kind) {
+        const log = logger[logger.kind];
+
+        switch (logger.kind) {
+            case 'Log4j2': return log && $generatorCommon.isDefinedAndNotEmpty(log.path);
+
+            case 'Log4j':
+                if (!log || !log.mode)
+                    return false;
+
+                if (log.mode === 'Path')
+                    return $generatorCommon.isDefinedAndNotEmpty(log.path);
+
+                return true;
+
+            case 'Custom': return log && $generatorCommon.isDefinedAndNotEmpty(log.class);
+
+            default:
+                return true;
+        }
+    }
+
+    return false;
+};
+
 export default $generatorCommon;


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

Posted by ak...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/caches/store.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/caches/store.jade b/modules/web-console/src/main/js/app/modules/states/configuration/caches/store.jade
index d4f698e..cb26d03 100644
--- a/modules/web-console/src/main/js/app/modules/states/configuration/caches/store.jade
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/caches/store.jade
@@ -20,7 +20,7 @@ include ../../../../../app/helpers/jade/mixins.jade
 -var model = 'backupItem'
 
 //- Mixin for DB dialect.
-mixin dialect(lbl, model, name, tipTitle, genericDialectName, placeholder)
+mixin dialect(lbl, model, name, required, tipTitle, genericDialectName, placeholder)
     ignite-form-field
         ignite-form-field-label
             | #{lbl}
@@ -46,7 +46,7 @@ mixin dialect(lbl, model, name, tipTitle, genericDialectName, placeholder)
                 {value: "H2", label: "H2 database"}\
             ]'
             data-ng-model=model
-            data-ng-required='true'
+            data-ng-required=required
             data-placeholder=placeholder
         )
 
@@ -60,7 +60,7 @@ mixin hibernateField(items, field, valid, save, newItem)
     -var onBlur = valid + ' && ( ' + save + '); ' + resetOnBlur + ';'
 
     ignite-form-field-input-text(
-        data-name=field
+        data-name='#{field}{{ $index || "" }}'
         data-ng-model=field
         data-ng-required='true'
         data-placeholder='key=value'
@@ -96,22 +96,23 @@ form.panel.panel-default(name=form novalidate)
                         ]',
                         'Factory for persistent storage for cache data'
                     )
-                    span(ng-if=storeFactoryKind ng-init='__.expanded = true')
+                    span(ng-show=storeFactoryKind ng-init='__.expanded = true')
                         a.customize(ng-show='__.expanded' ng-click='__.expanded = false') Hide settings
                         a.customize(ng-hide='__.expanded' ng-click='__.expanded = true') Show settings
-                        .panel-details(ng-if='__.expanded')
-                            div(ng-if='#{storeFactoryKind} === "CacheJdbcPojoStoreFactory"')
+                        .panel-details(ng-show='__.expanded')
+                            div(ng-show='#{storeFactoryKind} === "CacheJdbcPojoStoreFactory"')
                                 -var pojoStoreFactory = storeFactory + '.CacheJdbcPojoStoreFactory'
+                                -var required = storeFactoryKind + ' === "CacheJdbcPojoStoreFactory"'
 
                                 .details-row
                                     +text('Data source bean name:', pojoStoreFactory + '.dataSourceBean',
-                                        'pojoDataSourceBean', 'true', 'Input bean name',
+                                        'pojoDataSourceBean', required, 'Input bean name',
                                         'Name of the data source bean in Spring context')
                                 .details-row
-                                    +dialect('Dialect:', pojoStoreFactory + '.dialect', 'pojoDialect',
+                                    +dialect('Dialect:', pojoStoreFactory + '.dialect', 'pojoDialect', required,
                                         'Dialect of SQL implemented by a particular RDBMS:', 'Generic JDBC dialect',
                                         'Choose JDBC dialect')
-                            div(ng-if='#{storeFactoryKind} === "CacheJdbcBlobStoreFactory"')
+                            div(ng-show='#{storeFactoryKind} === "CacheJdbcBlobStoreFactory"')
                                 -var blobStoreFactory = storeFactory + '.CacheJdbcBlobStoreFactory'
                                 -var blobStoreFactoryVia = blobStoreFactory + '.connectVia'
 
@@ -126,20 +127,24 @@ form.panel.panel-default(name=form novalidate)
                                             <li>JDBC URL, for example: jdbc:h2:mem:myDatabase</li>\
                                             <li>Configured data source</li>\
                                         </ul>')
-                                div(ng-if='#{blobStoreFactoryVia} === "URL"')
+                                div(ng-show='#{blobStoreFactoryVia} === "URL"')
+                                    -var required = storeFactoryKind + ' === "CacheJdbcBlobStoreFactory" && ' + blobStoreFactoryVia + ' === "URL"'
+
                                     .details-row
-                                        +text('Connection URL:', blobStoreFactory + '.connectionUrl', 'connectionUrl', 'true', 'Input URL',
+                                        +text('Connection URL:', blobStoreFactory + '.connectionUrl', 'connectionUrl', required, 'Input URL',
                                             'URL for database access, for example: jdbc:h2:mem:myDatabase')
                                     .details-row
-                                        +text('User:', blobStoreFactory + '.user', 'user', 'true', 'Input user name', 'User name for database access')
+                                        +text('User:', blobStoreFactory + '.user', 'user', required, 'Input user name', 'User name for database access')
                                     .details-row
                                         label Note, password will be generated as stub
-                                div(ng-if='#{blobStoreFactoryVia} !== "URL"')
+                                div(ng-show='#{blobStoreFactoryVia} !== "URL"')
+                                    -var required = storeFactoryKind + ' === "CacheJdbcBlobStoreFactory" && ' + blobStoreFactoryVia + '!== "URL"'
+
                                     .details-row
-                                        +text('Data source bean name:', blobStoreFactory + '.dataSourceBean', 'blobDataSourceBean', 'true', 'Input bean name',
+                                        +text('Data source bean name:', blobStoreFactory + '.dataSourceBean', 'blobDataSourceBean', required, 'Input bean name',
                                             'Name of the data source bean in Spring context')
                                     .details-row
-                                        +dialect('Database:', blobStoreFactory + '.dialect', 'blobDialect', 'Supported databases:', 'Generic database', 'Choose database')
+                                        +dialect('Database:', blobStoreFactory + '.dialect', 'blobDialect', required, 'Supported databases:', 'Generic database', 'Choose database')
                                 .details-row
                                     +checkbox('Init schema', blobStoreFactory + '.initSchema', 'initSchema',
                                         'Flag indicating whether DB schema should be initialized by Ignite (default behaviour) or was explicitly created by user')
@@ -164,7 +169,7 @@ form.panel.panel-default(name=form novalidate)
                                         'Query for delete entry from underlying database<br/>\
                                         Default value: delete from ENTRIES where key=?')
 
-                            div(ng-if='#{storeFactoryKind} === "CacheHibernateBlobStoreFactory"')
+                            div(ng-show='#{storeFactoryKind} === "CacheHibernateBlobStoreFactory"')
                                 -var hibernateStoreFactory = storeFactory + '.CacheHibernateBlobStoreFactory'
                                 -var hibernateProperties = hibernateStoreFactory + '.hibernateProperties'
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/attributes.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/attributes.directive.js b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/attributes.directive.js
new file mode 100644
index 0000000..cc9d474
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/attributes.directive.js
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import template from './attributes.jade!';
+
+export default ['igniteConfigurationUserAttributes', [() => {
+    return {
+        scope: true,
+        restrict: 'E',
+        template,
+        replace: true
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/attributes.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/attributes.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/attributes.jade
new file mode 100644
index 0000000..e6ffd50
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/attributes.jade
@@ -0,0 +1,58 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+include ../../../../../app/helpers/jade/mixins.jade
+
+-var form = 'attributes'
+-var model = 'backupItem'
+-var types = model + '.typeConfigurations'
+-var userAttributes = model + '.attributes'
+
+form.panel.panel-default(name=form novalidate)
+    .panel-heading(bs-collapse-toggle ng-click='ui.loadPanel("#{form}")')
+        ignite-form-panel-chevron
+        label User attributes
+        ignite-form-field-tooltip.tipLabel
+            | Configuration for Ignite user attributes
+        ignite-form-revert
+    .panel-collapse(role='tabpanel' bs-collapse-target id=form)
+        .panel-body(ng-if='ui.isPanelLoaded("#{form}")')
+            .col-sm-6
+                .settings-row
+                    ignite-form-group(ng-model='#{userAttributes}' ng-form='#{form}')
+                        ignite-form-field-label
+                            | User attributes
+                        ignite-form-group-tooltip
+                            | User-defined attributes to add to node
+                        ignite-form-group-add(ng-click='tableNewItem(attributesTbl)')
+                            | Add user attribute
+                        .group-content-empty(ng-if='!((#{userAttributes} && #{userAttributes}.length > 0) || tableNewItemActive(attributesTbl))')
+                            | Not defined
+                        .group-content(ng-show='(#{userAttributes} && #{userAttributes}.length > 0) || tableNewItemActive(attributesTbl)')
+                            table.links-edit(id='attributes' st-table=userAttributes)
+                                tbody
+                                    tr(ng-repeat='item in #{userAttributes}')
+                                        td.col-sm-12(ng-show='!tableEditing(attributesTbl, $index)')
+                                            a.labelFormField(ng-click='tableStartEdit(backupItem, attributesTbl, $index)') {{item.name}} = {{item.value}}
+                                            +btn-remove('tableRemove(backupItem, attributesTbl, $index)', '"Remove attribute"')
+                                        td.col-sm-12(ng-show='tableEditing(attributesTbl, $index)')
+                                            +table-pair-edit('attributesTbl', 'cur', 'Attribute name', 'Attribute value', false, false, '{{::attributesTbl.focusId + $index}}', '$index', '=')
+                                tfoot(ng-show='tableNewItemActive(attributesTbl)')
+                                    tr
+                                        td.col-sm-12
+                                            +table-pair-edit('attributesTbl', 'new', 'Attribute name', 'Attribute value', false, false, '{{::attributesTbl.focusId + $index}}', '-1', '=')
+            .col-sm-6
+                +preview-xml-java(model, 'clusterUserAttributes')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/binary.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/binary.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/binary.jade
index 37e7559..77caa36 100644
--- a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/binary.jade
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/binary.jade
@@ -79,20 +79,20 @@ form.panel.panel-default(name=form novalidate)
                             | Add new type configuration.
                         .group-content-empty(ng-if='!#{types}.length')
                             | Not defined
-                        .group-content(ng-repeat='type in #{types} track by $index')
+                        .group-content(ng-repeat='model in #{types} track by $index')
                             hr(ng-if='$index !== 0')
                             .settings-row
-                                +binary-types-java-class('Type name:', 'type.typeName', '"typeName" + $index', 'true', 'true', true, 'true', 'Type name')
+                                +binary-types-java-class('Type name:', 'model.typeName', '"typeName" + $index', 'true', 'true', true, 'true', 'Type name')
                             .settings-row
-                                +binary-types-java-class('ID mapper:', 'type.idMapper', '"idMapper" + $index', 'true', 'false', false, 'false',
+                                +binary-types-java-class('ID mapper:', 'model.idMapper', '"idMapper" + $index', 'true', 'false', false, 'false',
                                     'Maps given from BinaryNameMapper type and filed name to ID that will be used by Ignite in internals<br/>\
                                     Ignite never writes full strings for field or type/class names. Instead, for performance reasons, Ignite writes integer hash codes for type/class and field names. It has been tested that hash code conflicts for the type/class names or the field names within the same type are virtually non - existent and, to gain performance, it is safe to work with hash codes. For the cases when hash codes for different types or fields actually do collide #[b BinaryIdMapper] allows to override the automatically generated hash code IDs for the type and field names')
                             .settings-row
-                                +binary-types-java-class('Name mapper:', 'type.nameMapper', '"nameMapper" + $index', 'true', 'false', false, 'false', 'Maps type/class and field names to different names')
+                                +binary-types-java-class('Name mapper:', 'model.nameMapper', '"nameMapper" + $index', 'true', 'false', false, 'false', 'Maps type/class and field names to different names')
                             .settings-row
-                                +binary-types-java-class('Serializer:', 'type.serializer', '"serializer" + $index', 'true', 'false', false, 'false', 'Class with custom serialization logic for binary object')
+                                +binary-types-java-class('Serializer:', 'model.serializer', '"serializer" + $index', 'true', 'false', false, 'false', 'Class with custom serialization logic for binary object')
                             .settings-row
-                                +checkbox('Enum', 'type.enum', 'enum', 'Flag indicating that this type is the enum')
+                                +checkbox('Enum', 'model.enum', 'enum', 'Flag indicating that this type is the enum')
 
                 .settings-row
                     +checkbox('Compact footer', model + '.compactFooter', 'compactFooter', 'When enabled, Ignite will not write fields metadata when serializing objects(this will increase serialization performance), because internally #[b BinaryMarshaller] already distribute metadata inside cluster')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision.directive.js b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision.directive.js
new file mode 100644
index 0000000..624056e
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision.directive.js
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import template from './collision.jade!';
+
+export default ['igniteConfigurationClustersCollision', [() => {
+    return {
+        scope: true,
+        restrict: 'E',
+        template,
+        replace: true
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision.jade
new file mode 100644
index 0000000..10e51f0
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision.jade
@@ -0,0 +1,60 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+include ../../../../../app/helpers/jade/mixins.jade
+
+-var form = 'collision'
+-var model = 'backupItem.collision'
+
+form.panel.panel-default(name=form novalidate)
+    .panel-heading(bs-collapse-toggle ng-click='ui.loadPanel("#{form}")')
+        ignite-form-panel-chevron
+        label Collision configuration
+        ignite-form-field-tooltip.tipLabel
+            | Configuration Collision SPI allows to regulate how grid jobs get executed when they arrive on a destination node for execution
+        ignite-form-revert
+    .panel-collapse(role='tabpanel' bs-collapse-target id=form)
+        .panel-body(ng-if='ui.isPanelLoaded("#{form}")')
+            .col-sm-6
+                .settings-row
+                    +dropdown('CollisionSpi:', model + '.kind', 'collision', 'true', '',
+                        '[\
+                            {value: "JobStealing", label: "Job stealing"},\
+                            {value: "FifoQueue", label: "FIFO queue"},\
+                            {value: "PriorityQueue", label: "Priority queue"},\
+                            {value: "Custom", label: "Custom"},\
+                            {value: "Noop", label: "Default"}\
+                        ]',
+                        'Regulate how grid jobs get executed when they arrive on a destination node for execution\
+                        <ul>\
+                            <li>Job stealing - supports job stealing from over-utilized nodes to under-utilized nodes</li>\
+                            <li>FIFO queue - jobs are ordered as they arrived</li>\
+                            <li>Priority queue - jobs are first ordered by their priority</li>\
+                            <li>Custom - custom CollisionSpi implementation</li>\
+                            <li>Default - jobs are activated immediately on arrival to mapped node</li>\
+                        </ul>')
+                .settings-row(ng-show='#{model}.kind !== "Noop"')
+                    .panel-details
+                        ignite-configuration-clusters-collision-job-stealing(
+                            ng-show='#{model}.kind === "JobStealing"')
+                        ignite-configuration-clusters-collision-fifo-queue(
+                            ng-show='#{model}.kind === "FifoQueue"')
+                        ignite-configuration-clusters-collision-priority-queue(
+                            ng-show='#{model}.kind === "PriorityQueue"')
+                        ignite-configuration-clusters-collision-custom(
+                            ng-show='#{model}.kind === "Custom"')
+            .col-sm-6
+                +preview-xml-java(model, 'clusterCollision')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/custom.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/custom.directive.js b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/custom.directive.js
new file mode 100644
index 0000000..9a8d414
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/custom.directive.js
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import template from './custom.jade!';
+
+export default ['igniteConfigurationClustersCollisionCustom', [() => {
+    return {
+        scope: true,
+        restrict: 'E',
+        template,
+        replace: true
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/custom.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/custom.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/custom.jade
new file mode 100644
index 0000000..b666f54
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/custom.jade
@@ -0,0 +1,24 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+include ../../../../../../app/helpers/jade/mixins.jade
+
+-var model = 'backupItem.collision.Custom'
+-var required = 'backupItem.collision.kind === "Custom"'
+
+div
+    .details-row
+        +java-class('Class:', model + '.class', 'collisionCustom', 'true', required, 'CollisionSpi implementation class')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/fifo-queue.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/fifo-queue.directive.js b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/fifo-queue.directive.js
new file mode 100644
index 0000000..240c98a
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/fifo-queue.directive.js
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import template from './fifo-queue.jade!';
+
+export default ['igniteConfigurationClustersCollisionFifoQueue', [() => {
+    return {
+        scope: true,
+        restrict: 'E',
+        template,
+        replace: true
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/fifo-queue.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/fifo-queue.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/fifo-queue.jade
new file mode 100644
index 0000000..9c9d315
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/fifo-queue.jade
@@ -0,0 +1,28 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+include ../../../../../../app/helpers/jade/mixins.jade
+
+-var model = 'backupItem.collision.FifoQueue'
+-var form = 'collisionFifoQueue'
+
+div
+    .details-row
+        +number('Parallel jobs number:', model + '.parallelJobsNumber', 'fifoParallelJobsNumber', 'true', 'availableProcessors * 2', '1',
+            'Number of jobs that can be executed in parallel')
+    .details-row
+        +number('Wait jobs number:', model + '.waitingJobsNumber', 'fifoWaitingJobsNumber', 'true', 'Integer.MAX_VALUE', '0',
+            'Maximum number of jobs that are allowed to wait in waiting queue')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/job-stealing.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/job-stealing.directive.js b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/job-stealing.directive.js
new file mode 100644
index 0000000..3c187d1
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/job-stealing.directive.js
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import template from './job-stealing.jade!';
+
+export default ['igniteConfigurationClustersCollisionJobStealing', [() => {
+    return {
+        scope: true,
+        restrict: 'E',
+        template,
+        replace: true
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/job-stealing.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/job-stealing.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/job-stealing.jade
new file mode 100644
index 0000000..17392c0
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/job-stealing.jade
@@ -0,0 +1,64 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+include ../../../../../../app/helpers/jade/mixins.jade
+
+-var model = 'backupItem.collision.JobStealing'
+-var form = 'collisionJobStealing'
+-var stealingAttributes = model + '.stealingAttributes'
+
+div
+    .details-row
+        +number('Active jobs threshold:', model + '.activeJobsThreshold', 'jsActiveJobsThreshold', 'true', '95', '0',
+            'Number of jobs that can be executed in parallel')
+    .details-row
+        +number('Wait jobs threshold:', model + '.waitJobsThreshold', 'jsWaitJobsThreshold', 'true', '0', '0',
+            'Job count threshold at which this node will start stealing jobs from other nodes')
+    .details-row
+        +number('Message expire time:', model + '.messageExpireTime', 'jsMessageExpireTime', 'true', '1000', '1',
+            'Message expire time in ms')
+    .details-row
+        +number('Maximum stealing attempts:', model + '.maximumStealingAttempts', 'jsMaximumStealingAttempts', 'true', '5', '1',
+            'Maximum number of attempts to steal job by another node')
+    .details-row
+        +checkbox('Stealing enabled', model + '.stealingEnabled', 'jsStealingEnabled',
+            'Node should attempt to steal jobs from other nodes')
+    .details-row
+        +java-class('External listener:', model + '.externalCollisionListener', 'jsExternalCollisionListener', 'true', 'false',
+            'Listener to be set for notification of external collision events')
+    .details-row
+        ignite-form-group(ng-model='#{stealingAttributes}' ng-form='#{form}')
+            ignite-form-field-label
+                | Stealing attributes
+            ignite-form-group-tooltip
+                | Configuration parameter to enable stealing to/from only nodes that have these attributes set
+            ignite-form-group-add(ng-click='tableNewItem(stealingAttributesTbl)')
+                | Add stealing attribute
+            .group-content-empty(ng-if='!((#{stealingAttributes} && #{stealingAttributes}.length > 0) || tableNewItemActive(stealingAttributesTbl))')
+                | Not defined
+            .group-content(ng-show='(#{stealingAttributes} && #{stealingAttributes}.length > 0) || tableNewItemActive(stealingAttributesTbl)')
+                table.links-edit(id='attributes' st-table=stealingAttributes)
+                    tbody
+                        tr(ng-repeat='item in #{stealingAttributes}')
+                            td.col-sm-12(ng-show='!tableEditing(stealingAttributesTbl, $index)')
+                                a.labelFormField(ng-click='tableStartEdit(backupItem, stealingAttributesTbl, $index)') {{item.name}} = {{item.value}}
+                                +btn-remove('tableRemove(backupItem, stealingAttributesTbl, $index)', '"Remove attribute"')
+                            td.col-sm-12(ng-show='tableEditing(stealingAttributesTbl, $index)')
+                                +table-pair-edit('stealingAttributesTbl', 'cur', 'Attribute name', 'Attribute value', false, false, '{{::stealingAttributesTbl.focusId + $index}}', '$index', '=')
+                    tfoot(ng-show='tableNewItemActive(stealingAttributesTbl)')
+                        tr
+                            td.col-sm-12
+                                +table-pair-edit('stealingAttributesTbl', 'new', 'Attribute name', 'Attribute value', false, false, '{{::stealingAttributesTbl.focusId + $index}}', '-1', '=')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/priority-queue.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/priority-queue.directive.js b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/priority-queue.directive.js
new file mode 100644
index 0000000..e7e8798
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/priority-queue.directive.js
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import template from './priority-queue.jade!';
+
+export default ['igniteConfigurationClustersCollisionPriorityQueue', [() => {
+    return {
+        scope: true,
+        restrict: 'E',
+        template,
+        replace: true
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/priority-queue.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/priority-queue.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/priority-queue.jade
new file mode 100644
index 0000000..208c12b
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/collision/priority-queue.jade
@@ -0,0 +1,43 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+include ../../../../../../app/helpers/jade/mixins.jade
+
+-var model = 'backupItem.collision.PriorityQueue'
+-var form = 'collisionPriorityQueue'
+
+div
+    .details-row
+        +number('Parallel jobs number:', model + '.parallelJobsNumber', 'priorityParallelJobsNumber', 'true', 'availableProcessors * 2', '1',
+            'Number of jobs that can be executed in parallel')
+    .details-row
+        +number('Waiting jobs number:', model + '.waitingJobsNumber', 'priorityWaitingJobsNumber', 'true', 'Integer.MAX_VALUE', '0',
+            'Maximum number of jobs that are allowed to wait in waiting queue')
+    .details-row
+        +text('Priority attribute key:', model + '.priorityAttributeKey', 'priorityPriorityAttributeKey', 'false', 'grid.task.priority',
+            'Task priority attribute key')
+    .details-row
+        +text('Job priority attribute key:', model + '.jobPriorityAttributeKey', 'priorityJobPriorityAttributeKey', 'false', 'grid.job.priority',
+            'Job priority attribute key')
+    .details-row
+        +number('Default priority:', model + '.defaultPriority', 'priorityDefaultPriority', 'true', '0', '0',
+            'Default priority to use if a job does not have priority attribute set')
+    .details-row
+        +number('Starvation increment:', model + '.starvationIncrement', 'priorityStarvationIncrement', 'true', '1', '0',
+            'Value to increment job priority by every time a lower priority job gets behind a higher priority job')
+    .details-row
+        +checkbox('Starvation prevention enabled', model + '.starvationPreventionEnabled', 'priorityStarvationPreventionEnabled',
+            'Job starvation prevention is enabled')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/communication.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/communication.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/communication.jade
index 5d8ed10..7073f27 100644
--- a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/communication.jade
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/communication.jade
@@ -41,7 +41,7 @@ form.panel.panel-default(name=form novalidate)
                 .settings-row
                     +java-class('Communication listener:', communication + '.listener', 'comListener', 'true', 'false', 'Communication listener')
                 .settings-row
-                    +text-ip-address('Local IP address:', communication + '.localAddress', 'comLocalAddress', 'true', '228.1.2.4', 'Local host address for socket binding')
+                    +text-ip-address('Local IP address:', communication + '.localAddress', 'comLocalAddress', 'true', '0.0.0.0', 'Local host address for socket binding')
                 .settings-row
                     +number-min-max('Local port:', communication + '.localPort', 'comLocalPort', 'true', '47100', '1024', '65535', 'Local port for socket binding')
                 .settings-row
@@ -51,12 +51,6 @@ form.panel.panel-default(name=form novalidate)
                         'Local port to accept shared memory connections<br/>\
                         If set to #[b -1] shared memory communication will be disabled')
                 .settings-row
-                    +checkbox('Direct buffer', communication + '.directBuffer', 'directBuffer',
-                        'If value is true, then SPI will use ByteBuffer.allocateDirect(int) call<br/>\
-                        Otherwise, SPI will use ByteBuffer.allocate(int) call.')
-                .settings-row
-                    +checkbox('Direct send buffer', communication + '.directSendBuffer', 'directSendBuffer', 'Flag defining whether direct send buffer should be used')
-                .settings-row
                     +number('Idle connection timeout:', communication + '.idleConnectionTimeout', 'idleConnectionTimeout', 'true', '30000', '1',
                         'Maximum idle connection timeout upon which a connection to client will be closed')
                 .settings-row
@@ -71,20 +65,32 @@ form.panel.panel-default(name=form novalidate)
                 .settings-row
                     +number('Socket receive buffer:', communication + '.socketReceiveBuffer', 'socketReceiveBuffer', 'true', '32768', '0', 'Receive buffer size for sockets created or accepted by this SPI')
                 .settings-row
-                    +number('Message queue limit:', communication + '.messageQueueLimit', 'messageQueueLimit', 'true', '1024', '0', 'Message queue limit for incoming and outgoing messages')
-                .settings-row
                     +number('Slow client queue limit:', communication + '.slowClientQueueLimit', 'slowClientQueueLimit', 'true', '0', '0', 'Slow client queue limit')
                 .settings-row
-                    +checkbox('TCP_NODELAY option', communication + '.tcpNoDelay', 'tcpNoDelay', 'Value for TCP_NODELAY socket option')
-                .settings-row
                     +number('Ack send threshold:', communication + '.ackSendThreshold', 'ackSendThreshold', 'true', '16', '1', 'Number of received messages per connection to node after which acknowledgment message is sent')
                 .settings-row
-                    +number('Unacknowledged messages:', communication + '.unacknowledgedMessagesBufferSize', 'unacknowledgedMessagesBufferSize', 'true', '0', '0', 'Maximum number of stored unacknowledged messages per connection to node')
+                    +number('Message queue limit:', communication + '.messageQueueLimit', 'messageQueueLimit', 'true', '1024', '0', 'Message queue limit for incoming and outgoing messages')
+                .settings-row
+                    +number('Unacknowledged messages:', communication + '.unacknowledgedMessagesBufferSize', 'unacknowledgedMessagesBufferSize', 'true', '0', '0',
+                        'Maximum number of stored unacknowledged messages per connection to node<br/>\
+                        If specified non zero value it should be\
+                        <ul>\
+                            <li>At least ack send threshold * 5</li>\
+                            <li>At least message queue limit * 5</li>\
+                        </ul>')
                 .settings-row
                     +number('Socket write timeout:', communication + '.socketWriteTimeout', 'socketWriteTimeout', 'true', '2000', '0', 'Socket write timeout')
                 .settings-row
                     +number('Selectors count:', communication + '.selectorsCount', 'selectorsCount', 'true', 'min(4, availableProcessors)', '1', 'Count of selectors te be used in TCP server')
                 .settings-row
                     +java-class('Address resolver:', communication + '.addressResolver', 'comAddressResolver', 'true', 'false', 'Address resolver')
+                .settings-row
+                    +checkbox('Direct buffer', communication + '.directBuffer', 'directBuffer',
+                    'If value is true, then SPI will use ByteBuffer.allocateDirect(int) call<br/>\
+                    Otherwise, SPI will use ByteBuffer.allocate(int) call.')
+                .settings-row
+                    +checkbox('Direct send buffer', communication + '.directSendBuffer', 'directSendBuffer', 'Flag defining whether direct send buffer should be used')
+                .settings-row
+                    +checkbox('TCP_NODELAY option', communication + '.tcpNoDelay', 'tcpNoDelay', 'Value for TCP_NODELAY socket option')
             .col-sm-6
                 +preview-xml-java(model, 'clusterCommunication')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/deployment.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/deployment.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/deployment.jade
index b2f1fb9..98e7f61 100644
--- a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/deployment.jade
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/deployment.jade
@@ -77,18 +77,25 @@ form.panel.panel-default(name='deployment' novalidate)
                             -var javaKeywords = 'form[ngModelName].$error.javaKeywords'
                             -var save = exclude + '[$index] = ' + field
 
-                            ignite-form-field(ng-repeat='model in #{exclude} track by $index' type='internal' name='Package name')
-                                .indexField
-                                    | {{ $index+1 }})
-                                +table-remove-button(exclude, 'Remove package name')
-                                span(ng-hide='field.edit')
-                                    a.labelFormField(ng-click='#{enabled} && (field.edit = true)') {{ model }}
-                                span(ng-if='field.edit' ng-init='#{field} = model')
-                                    +table-java-package-field(field, exclude, valid, save, false)
-                                        +table-save-button(valid, save, false)
-                                        +unique-feedback(unique, uniqueTip)
-                                        +error-feedback(javaPackageName, 'javaPackageName', tipJavaPackageName)
-                                        +error-feedback(javaKeywords, 'javaKeywords', tipJavaKeyWord)
+                            div(ng-show=enabled)
+                                ignite-form-field(ng-repeat='model in #{exclude} track by $index' type='internal' name='Package name')
+                                    .indexField
+                                        | {{ $index+1 }})
+                                    +table-remove-button(exclude, 'Remove package name')
+                                    span(ng-hide='field.edit')
+                                        a.labelFormField(ng-click='#{enabled} && (field.edit = true)') {{ model }}
+                                    span(ng-if='field.edit' ng-init='#{field} = model')
+                                        +table-java-package-field(field, exclude, valid, save, false)
+                                            +table-save-button(valid, save, false)
+                                            +unique-feedback(unique, uniqueTip)
+                                            +error-feedback(javaPackageName, 'javaPackageName', tipJavaPackageName)
+                                            +error-feedback(javaKeywords, 'javaKeywords', tipJavaKeyWord)
+                            div(ng-hide=enabled)
+                                ignite-form-field(ng-repeat='model in #{exclude} track by $index' type='internal' name='Package name')
+                                    .labelFormField.labelField
+                                        | {{ $index+1 }})
+                                    span.labelFormField
+                                        | {{ model }}
 
                         .group-content(ng-repeat='field in group.add')
                             -var field = 'new'

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/discovery.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/discovery.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/discovery.jade
index d1ee763..15b7065 100644
--- a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/discovery.jade
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/discovery.jade
@@ -47,7 +47,9 @@ form.panel.panel-default(name=form novalidate)
                 .settings-row
                     +number('Network timeout:', model + '.networkTimeout', 'discoNetworkTimeout', 'true', '5000', '1', 'Network timeout')
                 .settings-row
-                    +number('Join timeout:', model + '.joinTimeout', 'joinTimeout', 'true', '0', '0', 'Join timeout')
+                    +number('Join timeout:', model + '.joinTimeout', 'joinTimeout', 'true', '0', '0',
+                        'Join timeout<br/>' +
+                        '0 means wait forever')
                 .settings-row
                     +number('Thread priority:', model + '.threadPriority', 'threadPriority', 'true', '10', '1', 'Thread priority for all threads started by SPI')
                 .settings-row

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/failover.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/failover.directive.js b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/failover.directive.js
new file mode 100644
index 0000000..98335a7
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/failover.directive.js
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import template from './failover.jade!';
+
+export default ['igniteConfigurationClustersFailover', [() => {
+    return {
+        scope: true,
+        restrict: 'E',
+        template,
+        replace: true
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/failover.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/failover.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/failover.jade
new file mode 100644
index 0000000..a973aeb
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/failover.jade
@@ -0,0 +1,82 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+include ../../../../../app/helpers/jade/mixins.jade
+
+-var model = 'backupItem'
+-var form = 'failoverSpi'
+-var failoverSpi = model + '.failoverSpi'
+-var failoverCustom = 'failover.kind === "Custom"'
+
+form.panel.panel-default(name=form novalidate)
+    .panel-heading(bs-collapse-toggle ng-click='ui.loadPanel("#{form}")')
+        ignite-form-panel-chevron
+        label Failover configuration
+        ignite-form-field-tooltip.tipLabel
+            | Failover SPI provides ability to supply custom logic for handling failed execution of a grid job
+        ignite-form-revert
+    .panel-collapse(role='tabpanel' bs-collapse-target id=form)
+        .panel-body(ng-if='ui.isPanelLoaded("#{form}")')
+            .col-sm-6
+                .settings-row(ng-init='failoverSpiTbl={type: "failoverSpi", model: "failoverSpi", focusId: "kind", ui: "failover-table"}')
+                    ignite-form-group(ng-model='#{failoverSpi}' ng-form=form)
+                        ignite-form-field-label
+                            | Failover SPI configurations
+                        ignite-form-group-tooltip
+                            | Failover SPI configurations
+                        ignite-form-group-add(ng-click='tableNewItem(failoverSpiTbl)')
+                            | Add failover SPI
+                        .group-content-empty(ng-if='!(#{failoverSpi} && #{failoverSpi}.length > 0)')
+                            | Not defined
+                        .group-content(ng-show='#{failoverSpi} && #{failoverSpi}.length > 0' ng-repeat='failover in #{failoverSpi} track by $index')
+                            hr(ng-if='$index != 0')
+                            .settings-row
+                                ignite-form-field
+                                    ignite-form-field-label
+                                        | Failover SPI
+                                    i.tipField.fa.fa-remove(bs-tooltip='"Remove Failover SPI"' ng-click='removeFailoverConfiguration($index)')
+                                    ignite-form-field-tooltip
+                                        | Provides ability to supply custom logic for handling failed execution of a grid job
+                                        ul
+                                            li Job stealing - Supports job stealing from over-utilized nodes to under-utilized nodes
+                                            li Never - Jobs are ordered as they arrived
+                                            li Always - Jobs are first ordered by their priority
+                                            li Custom - Jobs are activated immediately on arrival to mapped node
+                                            li Default - Default FailoverSpi implementation
+                                    ignite-form-field-dropdown(
+                                        data-id='failoverKind{{$index}}'
+                                        data-name='failoverKind{{$index}}'
+                                        data-options='[\
+                                            {value: "JobStealing", label: "Job stealing"},\
+                                            {value: "Never", label: "Never"},\
+                                            {value: "Always", label: "Always"},\
+                                            {value: "Custom", label: "Custom"}\
+                                        ]'
+                                        data-ng-model='failover.kind'
+                                        data-ng-required='true'
+                                        data-placeholder='Choose Failover SPI'
+                                    )
+                            .settings-row(ng-show='failover.kind === "JobStealing"')
+                                +number('Maximum failover attempts:', 'failover.JobStealing.maximumFailoverAttempts', 'jsMaximumFailoverAttempts{{$index}}', 'true', '5', '0',
+                                    'Maximum number of attempts to execute a failed job on another node')
+                            .settings-row(ng-show='failover.kind === "Always"')
+                                +number('Maximum failover attempts:', 'failover.Always.maximumFailoverAttempts', 'alwaysMaximumFailoverAttempts{{$index}}', 'true', '5', '0',
+                                    'Maximum number of attempts to execute a failed job on another node')
+                            .settings-row(ng-show=failoverCustom)
+                                +java-class('SPI implementation', 'failover.Custom.class', 'failoverSpiClass{{$index}}', 'true', failoverCustom,
+                                    'Custom FailoverSpi implementation class name.')
+            .col-sm-6
+                +preview-xml-java(model, 'clusterFailover')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general.jade
index aea42d6..2ed0db0 100644
--- a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general.jade
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general.jade
@@ -30,9 +30,7 @@ form.panel.panel-default(name=form novalidate)
                 .settings-row
                     +text('Name:', model + '.name', 'clusterName', 'true', 'Input name', 'Grid name')
                 .settings-row
-                    +dropdown-multiple('<span>Caches:</span><a ui-sref="base.configuration.caches({id: ' + model + '._id})"> (add)</a>',
-                        model + '.caches', 'caches', 'true', 'Choose caches', 'No caches configured', 'caches',
-                        'Select caches to start in cluster or add a new cache')
+                    +caches(model, 'Select caches to start in cluster or add a new cache')
                 .settings-row
                     +text-ip-address('Local host:', model + '.localHost', 'localHost', 'true', '0.0.0.0', 'System-wide local address or host for all Ignite components to bind to')
                 .settings-row
@@ -51,20 +49,20 @@ form.panel.panel-default(name=form novalidate)
                 .settings-row
                     .panel-details
                         ignite-configuration-clusters-general-discovery-cloud(
-                            ng-if='#{model}.discovery.kind === "Cloud"')
+                            ng-show='#{model}.discovery.kind === "Cloud"')
                         ignite-configuration-clusters-general-discovery-google(
-                            ng-if='#{model}.discovery.kind === "GoogleStorage"')
+                            ng-show='#{model}.discovery.kind === "GoogleStorage"')
                         ignite-configuration-clusters-general-discovery-jdbc(
-                            ng-if='#{model}.discovery.kind === "Jdbc"')
+                            ng-show='#{model}.discovery.kind === "Jdbc"')
                         ignite-configuration-clusters-general-discovery-multicast(
-                            ng-if='#{model}.discovery.kind === "Multicast"')
+                            ng-show='#{model}.discovery.kind === "Multicast"')
                         ignite-configuration-clusters-general-discovery-s3(
-                            ng-if='#{model}.discovery.kind === "S3"')
+                            ng-show='#{model}.discovery.kind === "S3"')
                         ignite-configuration-clusters-general-discovery-shared(
-                            ng-if='#{model}.discovery.kind === "SharedFs"')
+                            ng-show='#{model}.discovery.kind === "SharedFs"')
                         ignite-configuration-clusters-general-discovery-vm(
-                            ng-if='#{model}.discovery.kind === "Vm"')
+                            ng-show='#{model}.discovery.kind === "Vm"')
                         ignite-configuration-clusters-general-discovery-zookeeper(
-                            ng-if='#{model}.discovery.kind === "ZooKeeper"')
+                            ng-show='#{model}.discovery.kind === "ZooKeeper"')
             .col-sm-6
                 +preview-xml-java(model, 'clusterCaches', 'caches')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/cloud.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/cloud.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/cloud.jade
index 1f5ef16..3a6565d 100644
--- a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/cloud.jade
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/cloud.jade
@@ -16,6 +16,8 @@
 
 include ../../../../../../../app/helpers/jade/mixins.jade
 
+-var discoveryKind = 'Cloud'
+-var required = 'backupItem.discovery.kind == "' + discoveryKind + '"'
 -var model = 'backupItem.discovery.Cloud'
 -var regions = model + '.regions'
 -var zones = model + '.zones'
@@ -32,11 +34,11 @@ div
             'Path to a credential that is used during authentication on the cloud<br/>\
             Access key or private key should be stored in a plain or PEM file without a passphrase')
     .details-row
-        +text('Identity:', model + '.identity', 'identity', 'true', 'Input identity',
+        +text('Identity:', model + '.identity', discoveryKind + 'Identity', required, 'Input identity',
             'Identity that is used as a user name during a connection to the cloud<br/>\
             Depending on a cloud platform it can be an email address, user name, etc')
     .details-row
-        +text('Provider:', model + '.provider', 'provider', 'true', 'Input provider', 'Cloud provider to use')
+        +text('Provider:', model + '.provider', discoveryKind + 'Provider', required, 'Input provider', 'Cloud provider to use')
     .details-row
         ignite-form-group(ng-model=regions ng-form=formRegions)
             -var uniqueTip = 'Such region already exists!'

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/google.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/google.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/google.jade
index 54e4bb5..2a651df 100644
--- a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/google.jade
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/google.jade
@@ -16,20 +16,23 @@
 
 include ../../../../../../../app/helpers/jade/mixins.jade
 
+
+-var discoveryKind = 'GoogleStorage'
+-var required = 'backupItem.discovery.kind == "' + discoveryKind + '"'
 -var model = 'backupItem.discovery.GoogleStorage'
 
 div
     .details-row
-        +text('Project name:', model + '.projectName', 'projectName', 'true', 'Input project name', '' +
+        +text('Project name:', model + '.projectName', discoveryKind + 'ProjectName', required, 'Input project name', '' +
             'Google Cloud Platforms project name<br/>\
             Usually this is an auto generated project number(ex. 208709979073) that can be found in "Overview" section of Google Developer Console')
     .details-row
-        +text('Bucket name:', model + '.bucketName', 'bucketName', 'true', 'Input bucket name',
+        +text('Bucket name:', model + '.bucketName', discoveryKind + 'BucketName', required, 'Input bucket name',
             'Google Cloud Storage bucket name<br/>\
             If the bucket does not exist Ignite will automatically create it<br/>\
             However the name must be unique across whole Google Cloud Storage and Service Account Id must be authorized to perform this operation')
     .details-row
-        +text('Private key path:', model + '.serviceAccountP12FilePath', 'serviceAccountP12FilePath', 'true', 'Input private key path',
+        +text('Private key path:', model + '.serviceAccountP12FilePath', discoveryKind + 'ServiceAccountP12FilePath', required, 'Input private key path',
             'Full path to the private key in PKCS12 format of the Service Account')
     .details-row
-        +text('Account id:', model + '.serviceAccountId', 'serviceAccountId', 'true', 'Input account id', 'Service account ID (typically an e-mail address)')
+        +text('Account id:', model + '.serviceAccountId', discoveryKind + 'ServiceAccountId', required, 'Input account id', 'Service account ID (typically an e-mail address)')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/s3.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/s3.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/s3.jade
index d693709..c2e29be 100644
--- a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/s3.jade
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/s3.jade
@@ -16,10 +16,12 @@
 
 include ../../../../../../../app/helpers/jade/mixins.jade
 
+-var discoveryKind = 'S3'
+-var required = 'backupItem.discovery.kind == "' + discoveryKind + '"'
 -var model = 'backupItem.discovery.S3'
 
 div
     .details-row
-        +text('Bucket name:', model + '.bucketName', 'bucketName', 'true', 'Input bucket name', 'Bucket name for IP finder')
+        +text('Bucket name:', model + '.bucketName', discoveryKind + 'BucketName', required, 'Input bucket name', 'Bucket name for IP finder')
     .details-row
         label Note, AWS credentials will be generated as stub

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper.jade
index ad3dbea..72f0678 100644
--- a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper.jade
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper.jade
@@ -16,6 +16,8 @@
 
 include ../../../../../../../app/helpers/jade/mixins.jade
 
+-var discoveryKind = 'ZooKeeper'
+-var required = 'backupItem.discovery.kind == "' + discoveryKind + '"'
 -var model = 'backupItem.discovery.ZooKeeper'
 
 div
@@ -25,7 +27,7 @@ div
             By default generates curator of org.apache.curator. framework.imps.CuratorFrameworkImpl\
             class with configured connect string, retry policy, and default session and connection timeouts')
     .details-row
-        +text('Connect string:', model + '.zkConnectionString', 'zkConnectionString', 'true', 'host:port[chroot][,host:port[chroot]]',
+        +text('Connect string:', model + '.zkConnectionString', ZooKeeper + 'ConnectionString', required, 'host:port[chroot][,host:port[chroot]]',
             'When "IGNITE_ZK_CONNECTION_STRING" system property is not configured this property will be used')
     .details-row
         +dropdown('Retry policy:', model + '.retryPolicy.kind', 'retryPolicy', 'true', 'Default',
@@ -50,16 +52,15 @@ div
                 <li>Custom - custom retry policy implementation</li>\
                 <li>Default - exponential backoff retry policy with configured base sleep time equal to 1000ms and max retry count equal to 10</li>\
             </ul>')
-    .details-row(ng-if='#{model}.retryPolicy.kind')
+    .details-row(ng-show='#{model}.retryPolicy.kind')
         .panel-details
-            div(ng-switch='#{model}.retryPolicy.kind')
-                ignite-configuration-clusters-general-discovery-zookeeper-exponential(ng-switch-when='ExponentialBackoff')
-                ignite-configuration-clusters-general-discovery-zookeeper-bounded-exponential(ng-switch-when='BoundedExponentialBackoff')
-                ignite-configuration-clusters-general-discovery-zookeeper-until-elapsed(ng-switch-when='UntilElapsed')
-                ignite-configuration-clusters-general-discovery-zookeeper-n-times(ng-switch-when='NTimes')
-                ignite-configuration-clusters-general-discovery-zookeeper-one-time(ng-switch-when='OneTime')
-                ignite-configuration-clusters-general-discovery-zookeeper-forever(ng-switch-when='Forever')
-                ignite-configuration-clusters-general-discovery-zookeeper-custom(ng-switch-when='Custom')
+            ignite-configuration-clusters-general-discovery-zookeeper-exponential(ng-show='#{model}.retryPolicy.kind === "ExponentialBackoff"')
+            ignite-configuration-clusters-general-discovery-zookeeper-bounded-exponential(ng-show='#{model}.retryPolicy.kind === "BoundedExponentialBackoff"')
+            ignite-configuration-clusters-general-discovery-zookeeper-until-elapsed(ng-show='#{model}.retryPolicy.kind === "UntilElapsed"')
+            ignite-configuration-clusters-general-discovery-zookeeper-n-times(ng-show='#{model}.retryPolicy.kind === "NTimes"')
+            ignite-configuration-clusters-general-discovery-zookeeper-one-time(ng-show='#{model}.retryPolicy.kind === "OneTime"')
+            ignite-configuration-clusters-general-discovery-zookeeper-forever(ng-show='#{model}.retryPolicy.kind === "Forever"')
+            ignite-configuration-clusters-general-discovery-zookeeper-custom(ng-show='#{model}.retryPolicy.kind === "Custom"')
     .details-row
         +text('Base path:', model + '.basePath', 'basePath', 'false', '/services', 'Base path for service registration')
     .details-row

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/bounded-exponential-backoff.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/bounded-exponential-backoff.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/bounded-exponential-backoff.jade
index f8f78f8..6f6e035 100644
--- a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/bounded-exponential-backoff.jade
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/bounded-exponential-backoff.jade
@@ -20,8 +20,8 @@ include ../../../../../../../../../app/helpers/jade/mixins.jade
 
 div
     .details-row
-        +number('Base interval:', model + '.baseSleepTimeMs', 'baseSleepTimeMs', 'true', '1000', '0', 'Initial amount of time in ms to wait between retries')
+        +number('Base interval:', model + '.baseSleepTimeMs', 'beBaseSleepTimeMs', 'true', '1000', '0', 'Initial amount of time in ms to wait between retries')
     .details-row
-        +number('Max interval:', model + '.maxSleepTimeMs', 'maxSleepTimeMs', 'true', 'Integer.MAX_VALUE', '0', 'Max time in ms to sleep on each retry')
+        +number('Max interval:', model + '.maxSleepTimeMs', 'beMaxSleepTimeMs', 'true', 'Integer.MAX_VALUE', '0', 'Max time in ms to sleep on each retry')
     .details-row
-        +number-min-max('Max retries:', model + '.maxRetries', 'maxRetries', 'true', '10', '0', '29', 'Max number of times to retry')
+        +number-min-max('Max retries:', model + '.maxRetries', 'beMaxRetries', 'true', '10', '0', '29', 'Max number of times to retry')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/custom.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/custom.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/custom.jade
index 8c55ad8..6e631ab 100644
--- a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/custom.jade
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/custom.jade
@@ -16,7 +16,9 @@
 
 include ../../../../../../../../../app/helpers/jade/mixins.jade
 
--var model = 'backupItem.discovery.ZooKeeper.retryPolicy.Custom'
+-var model = 'backupItem.discovery.ZooKeeper.retryPolicy'
+-var retry = model + '.Custom'
+-var required = 'backupItem.discovery.kind === "ZooKeeper" && backupItem.discovery.ZooKeeper.retryPolicy.kind === "Custom"'
 
 .details-row
-    +java-class('Class name:', model + '.className', 'className', 'true', 'true', 'Custom retry policy implementation class name')
+    +java-class('Class name:', retry + '.className', 'customClassName', 'true', required, 'Custom retry policy implementation class name')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/exponential-backoff.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/exponential-backoff.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/exponential-backoff.jade
index b039335..9be2a71 100644
--- a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/exponential-backoff.jade
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/exponential-backoff.jade
@@ -20,8 +20,8 @@ include ../../../../../../../../../app/helpers/jade/mixins.jade
 
 div
     .details-row
-        +number('Base interval:', model + '.baseSleepTimeMs', 'baseSleepTimeMs', 'true', '1000', '0', 'Initial amount of time in ms to wait between retries')
+        +number('Base interval:', model + '.baseSleepTimeMs', 'expBaseSleepTimeMs', 'true', '1000', '0', 'Initial amount of time in ms to wait between retries')
     .details-row
-        +number-min-max('Max retries:', model + '.maxRetries', 'maxRetries', 'true', '10', '0', '29', 'Max number of times to retry')
+        +number-min-max('Max retries:', model + '.maxRetries', 'expMaxRetries', 'true', '10', '0', '29', 'Max number of times to retry')
     .details-row
-        +number('Max interval:', model + '.maxSleepMs', 'maxSleepMs', 'true', 'Integer.MAX_VALUE', '0', 'Max time in ms to sleep on each retry')
+        +number('Max interval:', model + '.maxSleepMs', 'expMaxSleepMs', 'true', 'Integer.MAX_VALUE', '0', 'Max time in ms to sleep on each retry')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/forever.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/forever.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/forever.jade
index 1d09caa..f4045eb 100644
--- a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/forever.jade
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/forever.jade
@@ -19,4 +19,4 @@ include ../../../../../../../../../app/helpers/jade/mixins.jade
 -var model = 'backupItem.discovery.ZooKeeper.retryPolicy.Forever'
 
 .details-row
-    +number('Interval:', model + '.retryIntervalMs', 'retryIntervalMs', 'true', '1000', '0', 'Time in ms between retry attempts')
+    +number('Interval:', model + '.retryIntervalMs', 'feRetryIntervalMs', 'true', '1000', '0', 'Time in ms between retry attempts')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/n-times.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/n-times.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/n-times.jade
index 0b70f59..a4083b7 100644
--- a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/n-times.jade
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/n-times.jade
@@ -22,4 +22,4 @@ div
     .details-row
         +number('Retries:', model + '.n', 'n', 'true', '10', '0', 'Number of times to retry')
     .details-row
-        +number('Interval:', model + '.sleepMsBetweenRetries', 'sleepMsBetweenRetries', 'true', '1000', '0', 'Time in ms between retry attempts')
+        +number('Interval:', model + '.sleepMsBetweenRetries', 'ntSleepMsBetweenRetries', 'true', '1000', '0', 'Time in ms between retry attempts')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/one-time.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/one-time.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/one-time.jade
index 8b9fa18..f259630 100644
--- a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/one-time.jade
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/one-time.jade
@@ -20,4 +20,4 @@ include ../../../../../../../../../app/helpers/jade/mixins.jade
 
 div
     .details-row
-        +number('Interval:', model + '.sleepMsBetweenRetry', 'sleepMsBetweenRetry', 'true', '1000', '0', 'Time in ms to retry attempt')
+        +number('Interval:', model + '.sleepMsBetweenRetry', 'oneSleepMsBetweenRetry', 'true', '1000', '0', 'Time in ms to retry attempt')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/until-elapsed.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/until-elapsed.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/until-elapsed.jade
index ad9f3c1..24a884e 100644
--- a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/until-elapsed.jade
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/general/discovery/zookeeper/retrypolicy/until-elapsed.jade
@@ -20,6 +20,6 @@ include ../../../../../../../../../app/helpers/jade/mixins.jade
 
 div
     .details-row
-        +number('Total time:', model + '.maxElapsedTimeMs', 'maxElapsedTimeMs', 'true', '60000', '0', 'Total time in ms for execution of retry attempt')
+        +number('Total time:', model + '.maxElapsedTimeMs', 'ueMaxElapsedTimeMs', 'true', '60000', '0', 'Total time in ms for execution of retry attempt')
     .details-row
-        +number('Interval:', model + '.sleepMsBetweenRetries', 'sleepMsBetweenRetries', 'true', '1000', '0', 'Time in ms between retry attempts')
+        +number('Interval:', model + '.sleepMsBetweenRetries', 'ueSleepMsBetweenRetries', 'true', '1000', '0', 'Time in ms between retry attempts')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/igfs.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/igfs.jade b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/igfs.jade
index c76c66d..de20dfe 100644
--- a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/igfs.jade
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/igfs.jade
@@ -30,8 +30,8 @@ form.panel.panel-default(name=form novalidate)
         .panel-body(ng-if='ui.isPanelLoaded("#{form}")')
             .col-sm-6
                 .settings-row
-                    +dropdown-multiple('<span>IGFS:</span><a ui-sref="base.configuration.igfs({id: ' + model + '._id})"> (add)</a>',
+                    +dropdown-multiple('<span>IGFS:</span><a ui-sref="base.configuration.igfs({linkId: linkId()})"> (add)</a>',
                         model + '.igfss', 'igfss', 'true', 'Choose IGFS', 'No IGFS configured', 'igfss',
                         'Select IGFS to start in cluster or add a new IGFS')
             .col-sm-6
-                +preview-xml-java(model, 'igfss')
+                +preview-xml-java(model, 'igfss', 'igfss')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger.directive.js b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger.directive.js
new file mode 100644
index 0000000..3df231f
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/clusters/logger.directive.js
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import template from './logger.jade!';
+
+export default ['igniteConfigurationClustersLogger', [() => {
+    return {
+        scope: true,
+        restrict: 'E',
+        template,
+        replace: true
+    };
+}]];


[04/22] ignite git commit: IGNITE-3354: IGFS: Fixed (removed) max space validation logic.

Posted by ak...@apache.org.
IGNITE-3354: IGFS: Fixed (removed) max space validation logic.


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/5e915944
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/5e915944
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/5e915944

Branch: refs/heads/master
Commit: 5e9159449b7b7704cfec7c82c846badf7689df43
Parents: 212dc06
Author: vozerov-gridgain <vo...@gridgain.com>
Authored: Wed Jun 22 17:27:13 2016 +0300
Committer: vozerov-gridgain <vo...@gridgain.com>
Committed: Wed Jun 22 17:29:56 2016 +0300

----------------------------------------------------------------------
 .../internal/processors/igfs/IgfsProcessor.java | 24 ------------------
 .../igfs/IgfsProcessorValidationSelfTest.java   | 26 --------------------
 2 files changed, 50 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/5e915944/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsProcessor.java
index 6df9986..85dcb1c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsProcessor.java
@@ -38,7 +38,6 @@ import org.apache.ignite.internal.util.ipc.IpcServerEndpoint;
 import org.apache.ignite.internal.util.typedef.C1;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.X;
-import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgniteClosure;
 import org.jetbrains.annotations.Nullable;
 import org.jsr166.ConcurrentHashMap8;
@@ -57,7 +56,6 @@ import java.util.concurrent.ConcurrentMap;
 import static org.apache.ignite.IgniteSystemProperties.IGNITE_SKIP_CONFIGURATION_CONSISTENCY_CHECK;
 import static org.apache.ignite.IgniteSystemProperties.getBoolean;
 import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
-import static org.apache.ignite.cache.CacheMemoryMode.OFFHEAP_VALUES;
 import static org.apache.ignite.igfs.IgfsMode.PROXY;
 import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_IGFS;
 
@@ -346,28 +344,6 @@ public class IgfsProcessor extends IgfsProcessorAdapter {
                         ipcCfg.getThreadCount());
             }
 
-            long maxSpaceSize = cfg.getMaxSpaceSize();
-
-            if (maxSpaceSize > 0) {
-                // Max space validation.
-                long maxHeapSize = Runtime.getRuntime().maxMemory();
-                long offHeapSize = dataCacheCfg.getOffHeapMaxMemory();
-
-                if (offHeapSize < 0 && maxSpaceSize > maxHeapSize)
-                    // Offheap is disabled.
-                    throw new IgniteCheckedException("Maximum IGFS space size cannot be greater that size of available heap " +
-                        "memory [maxHeapSize=" + maxHeapSize + ", maxIgfsSpaceSize=" + maxSpaceSize + ']');
-                else if (offHeapSize > 0 && maxSpaceSize > maxHeapSize + offHeapSize)
-                    // Offheap is enabled, but limited.
-                    throw new IgniteCheckedException("Maximum IGFS space size cannot be greater than size of available heap " +
-                        "memory and offheap storage [maxHeapSize=" + maxHeapSize + ", offHeapSize=" + offHeapSize +
-                        ", maxIgfsSpaceSize=" + maxSpaceSize + ']');
-            }
-
-            if (cfg.getMaxSpaceSize() == 0 && dataCacheCfg.getMemoryMode() == OFFHEAP_VALUES)
-                U.warn(log, "IGFS max space size is not specified but data cache values are stored off-heap (max " +
-                    "space will be limited to 80% of max JVM heap size): " + cfg.getName());
-
             boolean secondary = cfg.getDefaultMode() == PROXY;
 
             if (cfg.getPathModes() != null) {

http://git-wip-us.apache.org/repos/asf/ignite/blob/5e915944/modules/core/src/test/java/org/apache/ignite/internal/processors/igfs/IgfsProcessorValidationSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/igfs/IgfsProcessorValidationSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/igfs/IgfsProcessorValidationSelfTest.java
index 29bb2cd..97334da 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/igfs/IgfsProcessorValidationSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/igfs/IgfsProcessorValidationSelfTest.java
@@ -228,32 +228,6 @@ public class IgfsProcessorValidationSelfTest extends IgfsCommonAbstractTest {
     /**
      * @throws Exception If failed.
      */
-    public void testLocalIfOffheapIsDisabledAndMaxSpaceSizeIsGreater() throws Exception {
-        g1Cfg.setCacheConfiguration(concat(dataCaches(1024), metaCaches(), CacheConfiguration.class));
-
-        g1IgfsCfg2.setMaxSpaceSize(999999999999999999L);
-
-        checkGridStartFails(g1Cfg, "Maximum IGFS space size cannot be greater that size of available heap", true);
-    }
-
-    /**
-     * @throws Exception If failed.
-     */
-    public void testLocalIfOffheapIsEnabledAndMaxSpaceSizeIsGreater() throws Exception {
-        g1Cfg.setCacheConfiguration(concat(dataCaches(1024), metaCaches(), CacheConfiguration.class));
-
-        for (CacheConfiguration cc : g1Cfg.getCacheConfiguration())
-            cc.setOffHeapMaxMemory(1000000);
-
-        g1IgfsCfg2.setMaxSpaceSize(999999999999999999L);
-
-        checkGridStartFails(g1Cfg,
-            "Maximum IGFS space size cannot be greater than size of available heap memory and offheap storage", true);
-    }
-
-    /**
-     * @throws Exception If failed.
-     */
     public void testLocalIfNonPrimaryModeAndHadoopFileSystemUriIsNull() throws Exception {
         g1Cfg.setCacheConfiguration(concat(dataCaches(1024), metaCaches(), CacheConfiguration.class));
 


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

Posted by ak...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/serve/routes/public.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/serve/routes/public.js b/modules/web-console/src/main/js/serve/routes/public.js
index 0009e6a..207289a 100644
--- a/modules/web-console/src/main/js/serve/routes/public.js
+++ b/modules/web-console/src/main/js/serve/routes/public.js
@@ -112,12 +112,16 @@ module.exports.factory = function(express, passport, nodemailer, settings, mail,
                     account.resetPasswordToken = _randomString();
 
                     return account.save()
-                        .then(() => mail.send(account, `Thanks for signing up for ${settings.smtp.username}.`,
-                            `Hello ${account.firstName} ${account.lastName}!<br><br>` +
-                            `You are receiving this email because you have signed up to use <a href="http://${req.headers.host}">${settings.smtp.username}</a>.<br><br>` +
-                            'If you have not done the sign up and do not know what this email is about, please ignore it.<br>' +
-                            'You may reset the password by clicking on the following link, or paste this into your browser:<br><br>' +
-                            `http://${req.headers.host}/password/reset?token=${account.resetPasswordToken}`));
+                        .then(() => {
+                            const resetLink = `http://${req.headers.host}/password/reset?token=${account.resetPasswordToken}`;
+
+                            mail.send(account, `Thanks for signing up for ${settings.smtp.username}.`,
+                                `Hello ${account.firstName} ${account.lastName}!<br><br>` +
+                                `You are receiving this email because you have signed up to use <a href="http://${req.headers.host}">${settings.smtp.username}</a>.<br><br>` +
+                                'If you have not done the sign up and do not know what this email is about, please ignore it.<br>' +
+                                'You may reset the password by clicking on the following link, or paste this into your browser:<br><br>' +
+                                `<a href="${resetLink}">${resetLink}</a>`);
+                        });
                 })
                 .catch((err) => {
                     res.status(401).send(err.message);
@@ -166,14 +170,17 @@ module.exports.factory = function(express, passport, nodemailer, settings, mail,
 
                     return user.save();
                 })
-                .then((user) => mail.send(user, 'Password Reset',
-                    `Hello ${user.firstName} ${user.lastName}!<br><br>` +
-                    'You are receiving this because you (or someone else) have requested the reset of the password for your account.<br><br>' +
-                    'Please click on the following link, or paste this into your browser to complete the process:<br><br>' +
-                    'http://' + req.headers.host + '/password/reset?token=' + user.resetPasswordToken + '<br><br>' +
-                    'If you did not request this, please ignore this email and your password will remain unchanged.',
-                    'Failed to send email with reset link!')
-                )
+                .then((user) => {
+                    const resetLink = `http://${req.headers.host}/password/reset?token=${user.resetPasswordToken}`;
+
+                    mail.send(user, 'Password Reset',
+                        `Hello ${user.firstName} ${user.lastName}!<br><br>` +
+                        'You are receiving this because you (or someone else) have requested the reset of the password for your account.<br><br>' +
+                        'Please click on the following link, or paste this into your browser to complete the process:<br><br>' +
+                        `<a href="${resetLink}">${resetLink}</a><br><br>` +
+                        'If you did not request this, please ignore this email and your password will remain unchanged.',
+                        'Failed to send email with reset link!');
+                })
                 .then(() => res.status(200).send('An email has been sent with further instructions.'))
                 .catch((err) => {
                     // TODO IGNITE-843 Send email to admin

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/views/configuration/clusters.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/configuration/clusters.jade b/modules/web-console/src/main/js/views/configuration/clusters.jade
index 6e29d86..6450163 100644
--- a/modules/web-console/src/main/js/views/configuration/clusters.jade
+++ b/modules/web-console/src/main/js/views/configuration/clusters.jade
@@ -43,12 +43,15 @@ include ../../app/helpers/jade/mixins.jade
                         div(ng-show='ui.expanded')
                             ignite-configuration-clusters-atomic
                             ignite-configuration-clusters-binary
+                            ignite-configuration-clusters-collision
                             ignite-configuration-clusters-communication
                             ignite-configuration-clusters-connector
                             ignite-configuration-clusters-deployment
                             ignite-configuration-clusters-discovery
                             ignite-configuration-clusters-events
+                            ignite-configuration-clusters-failover
                             ignite-configuration-clusters-igfs
+                            ignite-configuration-clusters-logger
                             ignite-configuration-clusters-marshaller
                             ignite-configuration-clusters-metrics
                             ignite-configuration-clusters-ssl
@@ -56,5 +59,6 @@ include ../../app/helpers/jade/mixins.jade
                             ignite-configuration-clusters-thread
                             ignite-configuration-clusters-time
                             ignite-configuration-clusters-transactions
+                            ignite-configuration-user-attributes
 
                             +advanced-options-toggle-default

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/views/configuration/domains-import.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/configuration/domains-import.jade b/modules/web-console/src/main/js/views/configuration/domains-import.jade
index 46385f9..23b9434 100644
--- a/modules/web-console/src/main/js/views/configuration/domains-import.jade
+++ b/modules/web-console/src/main/js/views/configuration/domains-import.jade
@@ -14,6 +14,8 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 
+include ../../app/helpers/jade/mixins.jade
+
 mixin chk(mdl, change, tip)
     input(type='checkbox' ng-model=mdl ng-change=change bs-tooltip='' data-title=tip data-trigger='hover' data-placement='top')
 
@@ -22,174 +24,187 @@ mixin td-ellipses-lbl(w, lbl)
         label #{lbl}
 
 .modal.modal-domain-import.center(role='dialog')
-    .modal-dialog
+    .modal-dialog.domains-import-dialog
         .modal-content(ignite-loading='importDomainFromDb' ignite-loading-text='{{importDomain.loadingOptions.text}}')
             #errors-container.modal-header.header
                 button.close(ng-click='$hide()' aria-hidden='true') &times;
                 h4.modal-title(ng-if='!importDomain.demo') Import domain models from database
                 h4.modal-title(ng-if='importDomain.demo') Import domain models from demo database
-            .import-domain-model-wizard-page(ng-if='importDomain.action == "drivers" && !importDomain.jdbcDriversNotFound' style='margin-bottom: 321px')
-            .import-domain-model-wizard-page(ng-if='importDomain.action == "drivers" && importDomain.jdbcDriversNotFound' style='margin-bottom: 220px')
-                | Domain model could not be imported
-                ul
-                    li Agent failed to find JDBC drivers
-                    li Copy required JDBC drivers into agent '\jdbc-drivers' folder and try again
-                    li Refer to agent README.txt for more information
-            .import-domain-model-wizard-page(ng-show='importDomain.action == "connect" && importDomain.demo' style='margin-bottom: 211px')
-                div(ng-if='demoConnection.db == "H2"')
-                    label Demo description:
+            .modal-body
+                .import-domain-model-wizard-page(ng-if='importDomain.action == "drivers" && !importDomain.jdbcDriversNotFound')
+                .import-domain-model-wizard-page(ng-if='importDomain.action == "drivers" && importDomain.jdbcDriversNotFound')
+                    | Domain model could not be imported
                     ul
-                        li In-memory H2 database server will be started inside agent
-                        li Database will be populated with sample tables
-                        li You could test domain model generation with this demo database
-                        li Click "Next" to continue
-                div(ng-if='demoConnection.db != "H2"')
-                    label Demo could not be started
+                        li Agent failed to find JDBC drivers
+                        li Copy required JDBC drivers into agent 'jdbc-drivers' folder and try again
+                        li Refer to agent README.txt for more information
+                .import-domain-model-wizard-page(ng-show='importDomain.action == "connect" && importDomain.demo')
+                    div(ng-if='demoConnection.db == "H2"')
+                        label Demo description:
                         ul
-                            li Agent failed to resolve H2 database jar
-                            li Copy h2-x.x.x.jar into agent '\jdbc-drivers' folder and try again
-                            li Refer to agent README.txt for more information
-            .import-domain-model-wizard-page(ng-show='importDomain.action == "connect" && !importDomain.demo' style='margin-bottom: 90px')
-                form.form-horizontal(name='connectForm' novalidate)
-                    .settings-row
-                        label.col-xs-4.col-sm-2.col-md-2 Driver JAR:
-                        .col-xs-8.col-sm-10.col-md-10
-                            i.tipField.fa.fa-question-circle(bs-tooltip='' data-title='Select appropriate JAR with JDBC driver<br> To add another driver you need to place it into "/jdbc-drivers" folder of Ignite Web Agent<br> Refer to Ignite Web Agent README.txt for for more information')
-                            .input-tip
-                                button.select-toggle.form-control(id='jdbcDriverJar' bs-select data-container='.modal-domain-import' ng-model='ui.selectedJdbcDriverJar' ng-class='{placeholder: !(jdbcDriverJars && jdbcDriverJars.length > 0)}' placeholder='Choose JDBC driver' bs-options='item.value as item.label for item in jdbcDriverJars')
-                    .settings-row
-                        label.col-xs-4.col-sm-2.col-md-2 JDBC Driver:
-                        .col-xs-8.col-sm-10.col-md-10
-                            i.tipField.fa.fa-question-circle(bs-tooltip='' data-title='Fully qualified class name of JDBC driver that will be used to connect to database')
-                            .input-tip
-                                input.form-control(id='jdbcDriverClass' type='text' ng-model='selectedPreset.jdbcDriverClass' placeholder='JDBC driver fully qualified class name' required=true)
-                    .settings-row
-                        label.col-xs-4.col-sm-2.col-md-2 JDBC URL:
-                        .col-xs-8.col-sm-10.col-md-10
-                            i.tipField.fa.fa-question-circle(bs-tooltip='' data-title='JDBC URL for connecting to database<br>Refer to your database documentation for details')
-                            .input-tip
-                                input.form-control(id='jdbcUrl' type='text' ng-model='selectedPreset.jdbcUrl' placeholder='JDBC URL' required=true)
-                    .settings-row
-                        label.col-xs-4.col-sm-2.col-md-2 User:
-                        .col-xs-8.col-sm-10.col-md-10
-                            i.tipField.fa.fa-question-circle(bs-tooltip='' data-title='User name for connecting to database')
-                            .input-tip
-                                input.form-control(id='user' type='text' ng-model='selectedPreset.user')
-                    .settings-row
-                        label.col-xs-4.col-sm-2.col-md-2 Password:
-                        .col-xs-8.col-sm-10.col-md-10
-                            i.tipField.fa.fa-question-circle(bs-tooltip='' data-title='Password for connecting to database<br>Note, password would not be saved in preferences for security reasons')
-                            .input-tip
-                                input.form-control(id='password' type='password' ng-model='selectedPreset.password' on-enter='importDomainNext()')
-                    .settings-row
-                        .checkbox
-                            label
-                                input(id='tablesOnly' type='checkbox' ng-model='selectedPreset.tablesOnly')
-                                | Tables only
-                            i.tipLabel.fa.fa-question-circle(bs-tooltip='' data-title='If selected, then only tables metadata will be parsed<br>Otherwise table and view metadata will be parsed')
-            .import-domain-model-wizard-page(ng-show='importDomain.action == "schemas"')
-                table.table.metadata(st-table='importDomain.displayedSchemas' st-safe-src='importDomain.schemas')
-                    thead
-                        tr
-                            th.header(colspan='2')
-                                .col-sm-4.pull-right(style='margin-bottom: 5px')
-                                    input.form-control(type='text' st-search='name' placeholder='Filter schemas...' ng-model='importDomain.displayedSchemasFilter' ng-change='selectSchema()')
-                        tr
-                            th(width='30px')
-                                +chk('importDomain.allSchemasSelected',  'selectAllSchemas()', 'Select all schemas')
-                            th
-                                label Schema
+                            li In-memory H2 database server will be started inside agent
+                            li Database will be populated with sample tables
+                            li You could test domain model generation with this demo database
+                            li Click "Next" to continue
+                    div(ng-if='demoConnection.db != "H2"')
+                        label Demo could not be started
+                            ul
+                                li Agent failed to resolve H2 database jar
+                                li Copy h2-x.x.x.jar into agent 'jdbc-drivers' folder and try again
+                                li Refer to agent README.txt for more information
+                .import-domain-model-wizard-page(ng-show='importDomain.action == "connect" && !importDomain.demo')
+                    form.form-horizontal(name='connectForm' novalidate)
+                        .settings-row
+                            label.col-xs-4.col-sm-2.col-md-2 Driver JAR:
+                            .col-xs-8.col-sm-10.col-md-10
+                                i.tipField.fa.fa-question-circle(bs-tooltip='' data-title='Select appropriate JAR with JDBC driver<br> To add another driver you need to place it into "/jdbc-drivers" folder of Ignite Web Agent<br> Refer to Ignite Web Agent README.txt for for more information')
+                                .input-tip
+                                    button.select-toggle.form-control(id='jdbcDriverJar' bs-select data-container='.modal-domain-import' ng-model='ui.selectedJdbcDriverJar' ng-class='{placeholder: !(jdbcDriverJars && jdbcDriverJars.length > 0)}' placeholder='Choose JDBC driver' bs-options='item.value as item.label for item in jdbcDriverJars')
+                        .settings-row
+                            label.col-xs-4.col-sm-2.col-md-2 JDBC Driver:
+                            .col-xs-8.col-sm-10.col-md-10
+                                i.tipField.fa.fa-question-circle(bs-tooltip='' data-title='Fully qualified class name of JDBC driver that will be used to connect to database')
+                                .input-tip
+                                    input.form-control(id='jdbcDriverClass' type='text' ng-model='selectedPreset.jdbcDriverClass' placeholder='JDBC driver fully qualified class name' required=true)
+                        .settings-row
+                            label.col-xs-4.col-sm-2.col-md-2 JDBC URL:
+                            .col-xs-8.col-sm-10.col-md-10
+                                i.tipField.fa.fa-question-circle(bs-tooltip='' data-title='JDBC URL for connecting to database<br>Refer to your database documentation for details')
+                                .input-tip
+                                    input.form-control(id='jdbcUrl' type='text' ng-model='selectedPreset.jdbcUrl' placeholder='JDBC URL' required=true)
+                        .settings-row
+                            label.col-xs-4.col-sm-2.col-md-2 User:
+                            .col-xs-8.col-sm-10.col-md-10
+                                i.tipField.fa.fa-question-circle(bs-tooltip='' data-title='User name for connecting to database')
+                                .input-tip
+                                    input.form-control(id='user' type='text' ng-model='selectedPreset.user')
+                        .settings-row
+                            label.col-xs-4.col-sm-2.col-md-2 Password:
+                            .col-xs-8.col-sm-10.col-md-10
+                                i.tipField.fa.fa-question-circle(bs-tooltip='' data-title='Password for connecting to database<br>Note, password would not be saved in preferences for security reasons')
+                                .input-tip
+                                    input.form-control(id='password' type='password' ng-model='selectedPreset.password' on-enter='importDomainNext()')
+                        .settings-row
+                            .checkbox
+                                label
+                                    input(id='tablesOnly' type='checkbox' ng-model='selectedPreset.tablesOnly')
+                                    | Tables only
+                                i.tipLabel.fa.fa-question-circle(bs-tooltip='' data-title='If selected, then only tables metadata will be parsed<br>Otherwise table and view metadata will be parsed')
+                .import-domain-model-wizard-page(ng-show='importDomain.action == "schemas"')
+                    table.table.metadata(st-table='importDomain.displayedSchemas' st-safe-src='importDomain.schemas')
+                        thead
+                            tr
+                                th.header(colspan='2')
+                                    .col-sm-4.pull-right(style='margin-bottom: 5px')
+                                        input.form-control(type='text' st-search='name' placeholder='Filter schemas...' ng-model='importDomain.displayedSchemasFilter' ng-change='selectSchema()')
+                            tr
+                                th(width='30px')
+                                    +chk('importDomain.allSchemasSelected',  'selectAllSchemas()', 'Select all schemas')
+                                th
+                                    label Schema
+                            tbody
+                                tr
+                                    td(colspan='2')
+                                        .scrollable-y(style='height: 213px')
+                                            table.table-modal-striped(id='importSchemasData')
+                                                tbody
+                                                    tr(ng-repeat='schema in importDomain.displayedSchemas')
+                                                        td(width='30px')
+                                                            input(type='checkbox' ng-model='schema.use' ng-change='selectSchema()')
+                                                        td
+                                                            label {{schema.name}}
+                .import-domain-model-wizard-page(ng-show='importDomain.action == "tables"')
+                    table.table.metadata(st-table='importDomain.displayedTables' st-safe-src='importDomain.tables')
+                        thead
+                            tr
+                                th.header(colspan='6')
+                                    .col-sm-4.pull-right(style='margin-bottom: 8px')
+                                        input.form-control(type='text' st-search='label' placeholder='Filter tables...' ng-model='importDomain.displayedTablesFilter' ng-change='selectTable()')
+                            tr
+                                th(width='30px')
+                                    +chk('importDomain.allTablesSelected',  'selectAllTables()', 'Select all tables')
+                                th(width='130px')
+                                    label Schema
+                                th(width='160px')
+                                    label Table name
+                                th(colspan=2 width='288px')
+                                    label Cache
+                                th
                         tbody
                             tr
-                                td(colspan='2')
-                                    .scrollable-y(style='height: 213px')
-                                        table.table-modal-striped(id='importSchemasData')
+                                td(colspan='6')
+                                    .scrollable-y(style='height: 143px')
+                                        table.table-modal-striped(id='importTableData')
                                             tbody
-                                                tr(ng-repeat='schema in importDomain.displayedSchemas')
-                                                    td(width='30px')
-                                                        input(type='checkbox' ng-model='schema.use' ng-change='selectSchema()')
+                                                tr(ng-repeat='table in importDomain.displayedTables track by $index')
+                                                    td(width='30px' style='min-width: 30px; max-width: 30px')
+                                                        input(type='checkbox' ng-model='table.use' ng-change='selectTable()')
+                                                    +td-ellipses-lbl('130px', '{{table.schema}}')
+                                                    +td-ellipses-lbl('160px', '{{table.tbl}}')
+                                                    td(colspan='2' width='288px' style='min-width: 160px; max-width: 160px')
+                                                        div.td-ellipsis
+                                                            a(ng-if='!table.edit' ng-click='startEditDbTableCache(table)') {{tableActionView(table)}}
+                                                            div(style='display: flex' ng-if='table.edit')
+                                                                button.select-toggle.form-control(style='width: 35%; margin-right: 5px' bs-select ng-model='table.action' data-container='.modal-domain-import' bs-options='item.value as item.shortLabel for item in importActions')
+                                                                button.select-toggle.form-control(style='width: 65%; margin-right: 0' bs-select ng-model='table.cacheOrTemplate' data-container='.modal-domain-import' bs-options='item.value as item.label for item in table.cachesOrTemplates')
                                                     td
-                                                        label {{schema.name}}
-            .import-domain-model-wizard-page(ng-show='importDomain.action == "tables"')
-                table.table.metadata(st-table='importDomain.displayedTables' st-safe-src='importDomain.tables')
-                    thead
-                        tr
-                            th.header(colspan='6')
-                                .col-sm-4.pull-right(style='margin-bottom: 8px')
-                                    input.form-control(type='text' st-search='label' placeholder='Filter tables...' ng-model='importDomain.displayedTablesFilter' ng-change='selectTable()')
-                        tr
-                            th(width='30px')
-                                +chk('importDomain.allTablesSelected',  'selectAllTables()', 'Select all tables')
-                            th(width='130px')
-                                label Schema
-                            th(width='160px')
-                                label Table name
-                            th(colspan=2 width='288px')
-                                label Cache
-                            th
-                    tbody
-                        tr
-                            td(colspan='6')
-                                .scrollable-y(style='height: 143px')
-                                    table.table-modal-striped(id='importTableData')
-                                        tbody
-                                            tr(ng-repeat='table in importDomain.displayedTables track by $index')
-                                                td(width='30px' style='min-width: 30px; max-width: 30px')
-                                                    input(type='checkbox' ng-model='table.use' ng-change='selectTable()')
-                                                +td-ellipses-lbl('130px', '{{table.schema}}')
-                                                +td-ellipses-lbl('160px', '{{table.tbl}}')
-                                                td(colspan='2' width='288px' style='min-width: 160px; max-width: 160px')
-                                                    div.td-ellipsis
-                                                        a(ng-if='!table.edit' ng-click='startEditDbTableCache(table)') {{tableActionView(table)}}
-                                                        div(style='display: flex' ng-if='table.edit')
-                                                            button.select-toggle.form-control(style='width: 35%; margin-right: 5px' bs-select ng-model='table.action' data-container='.modal-domain-import' bs-options='item.value as item.shortLabel for item in importActions')
-                                                            button.select-toggle.form-control(style='width: 65%; margin-right: 0' bs-select ng-model='table.cacheOrTemplate' data-container='.modal-domain-import' bs-options='item.value as item.label for item in table.cachesOrTemplates')
-                                                td
-                .settings-row
-                    label Defaults to be applied for filtered tables
-                    i.tipLabel.fa.fa-question-circle(bs-tooltip='' data-title='Select and apply options for caches generation')
-                .settings-row
-                    .col-sm-11
-                        .col-sm-6(style='padding-right: 5px')
-                            button.select-toggle.form-control(bs-select ng-model='importCommon.action' data-container='.modal-domain-import' bs-options='item.value as item.label for item in importActions')
-                        .col-sm-6(style='padding-left: 5px; padding-right: 5px')
-                            button.select-toggle.form-control(bs-select ng-model='importCommon.cacheOrTemplate' data-container='.modal-domain-import' bs-options='item.value as item.label for item in importCommon.cachesOrTemplates')
-                    .col-sm-1(style='padding-left: 5px')
-                        button.btn.btn-primary(ng-click='applyDefaults()') Apply
-            .import-domain-model-wizard-page(ng-show='importDomain.action == "options"' style='margin-bottom: 176px')
-                form.form-horizontal(name='optionsForm' novalidate)
-                    .settings-row
-                        .col-xs-3.col-sm-2.col-md-2.required
-                            label.required Package:
-                        .col-xs-9.col-sm-10.col-md-10
-                            i.tipField.fa.fa-question-circle(bs-tooltip='' data-title='Package that will be used for POJOs generation')
-                            .input-tip
-                                input.form-control(id='domainPackageName' type='text' ng-model='ui.packageName' placeholder='Package for POJOs generation')
-                    .settings-row
-                        .checkbox
-                            label
-                                input(id='domainBuiltinKeys' type='checkbox' ng-model='ui.builtinKeys')
-                                | Use Java built-in types for keys
-                                i.tipLabel.fa.fa-question-circle(bs-tooltip='' data-title='Use Java built-in types like "Integer", "Long", "String" instead of POJO generation in case when table primary key contains only one field')
-                    .settings-row
-                        .checkbox
-                            label
-                                input(id='domainUsePrimitives' type='checkbox' ng-model='ui.usePrimitives')
-                                | Use primitive types for NOT NULL table columns
-                                i.tipLabel.fa.fa-question-circle(bs-tooltip='' data-title='Use primitive types like "int", "long", "double" for POJOs fields generation in case of NOT NULL columns')
                     .settings-row
-                        .checkbox
-                            label
-                                input(id='domainGenerateAliases' type='checkbox' ng-model='ui.generateAliases')
-                                | Generate aliases for query fields
-                                i.tipLabel.fa.fa-question-circle(bs-tooltip='' data-title='Generate aliases for query fields with names from database fields')
+                        label Defaults to be applied for filtered tables
+                        i.tipLabel.fa.fa-question-circle(bs-tooltip='' data-title='Select and apply options for caches generation')
                     .settings-row
-                        .col-xs-3.col-sm-2.col-md-2.required
-                            label Clusters:
-                        .col-xs-9.col-sm-10.col-md-10
-                            i.tipField.fa.fa-question-circle(bs-tooltip='' data-title='Choose clusters that will be associated with generated caches')
-                            .input-tip
-                                button.select-toggle.form-control(id='generatedCachesClusters' bs-select ng-model='ui.generatedCachesClusters' ng-class='{placeholder: !(ui.generatedCachesClusters && ui.generatedCachesClusters.length > 0)}' data-container='.modal-domain-import' data-multiple='1' placeholder='Choose clusters for generated caches' bs-options='item.value as item.label for item in clusters')
+                        .col-sm-11
+                            .col-sm-6(style='padding-right: 5px')
+                                button.select-toggle.form-control(bs-select ng-model='importCommon.action' data-container='.modal-domain-import' bs-options='item.value as item.label for item in importActions')
+                            .col-sm-6(style='padding-left: 5px; padding-right: 5px')
+                                button.select-toggle.form-control(bs-select ng-model='importCommon.cacheOrTemplate' data-container='.modal-domain-import' bs-options='item.value as item.label for item in importCommon.cachesOrTemplates')
+                        .col-sm-1(style='padding-left: 5px')
+                            button.btn.btn-primary(ng-click='applyDefaults()') Apply
+                .import-domain-model-wizard-page(ng-show='importDomain.action == "options"')
+                    form.form-horizontal(name='optionsForm' novalidate)
+                        .settings-row
+                            .col-xs-3.col-sm-2.col-md-2.required
+                                label.required Package:
+                            .col-xs-9.col-sm-10.col-md-10
+                                i.tipField.fa.fa-question-circle(bs-tooltip='' data-title='Package that will be used for POJOs generation')
+                                .input-tip
+                                    ignite-form-field-input-text(
+                                        data-id='domainPackageName'
+                                        data-name='domainPackageName'
+                                        data-ng-model='ui.packageName'
+                                        data-ignite-label-name='Package'
+                                        data-ng-required='true'
+                                        data-placeholder='Enter package name'
+                                        data-java-keywords='true'
+                                        data-java-package-name='package-only'
+                                        ng-model-options='{allowInvalid: true}'
+                                    )
+                                        +error-feedback('optionsForm.$error.javaPackageName', 'javaPackageName', 'Package name is invalid')
+                                        +error-feedback('optionsForm.$error.javaKeywords', 'javaKeywords', 'Package name could not contains reserved java keyword')
+                        .settings-row
+                            .checkbox
+                                label
+                                    input(id='domainBuiltinKeys' type='checkbox' ng-model='ui.builtinKeys')
+                                    | Use Java built-in types for keys
+                                    i.tipLabel.fa.fa-question-circle(bs-tooltip='' data-title='Use Java built-in types like "Integer", "Long", "String" instead of POJO generation in case when table primary key contains only one field')
+                        .settings-row
+                            .checkbox
+                                label
+                                    input(id='domainUsePrimitives' type='checkbox' ng-model='ui.usePrimitives')
+                                    | Use primitive types for NOT NULL table columns
+                                    i.tipLabel.fa.fa-question-circle(bs-tooltip='' data-title='Use primitive types like "int", "long", "double" for POJOs fields generation in case of NOT NULL columns')
+                        .settings-row
+                            .checkbox
+                                label
+                                    input(id='domainGenerateAliases' type='checkbox' ng-model='ui.generateAliases')
+                                    | Generate aliases for query fields
+                                    i.tipLabel.fa.fa-question-circle(bs-tooltip='' data-title='Generate aliases for query fields with names from database fields')
+                        .settings-row
+                            .col-xs-3.col-sm-2.col-md-2.required
+                                label Clusters:
+                            .col-xs-9.col-sm-10.col-md-10
+                                i.tipField.fa.fa-question-circle(bs-tooltip='' data-title='Choose clusters that will be associated with generated caches')
+                                .input-tip
+                                    button.select-toggle.form-control(id='generatedCachesClusters' bs-select ng-model='ui.generatedCachesClusters' ng-class='{placeholder: !(ui.generatedCachesClusters && ui.generatedCachesClusters.length > 0)}' data-container='.modal-domain-import' data-multiple='1' placeholder='Choose clusters for generated caches' bs-options='item.value as item.label for item in clusters')
             .modal-footer
                 label(ng-hide='importDomain.action == "drivers" || (importDomain.action == "connect" && importDomain.demo)').labelField {{importDomain.info}}
                 a.btn.btn-primary(ng-hide='importDomain.action == "drivers" || importDomain.action == "connect"' ng-click='importDomainPrev()' bs-tooltip='' data-title='{{prevTooltipText()}}' data-placement='bottom') Prev

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/views/settings/profile.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/settings/profile.jade b/modules/web-console/src/main/js/views/settings/profile.jade
index 355d232..53991b3 100644
--- a/modules/web-console/src/main/js/views/settings/profile.jade
+++ b/modules/web-console/src/main/js/views/settings/profile.jade
@@ -48,29 +48,29 @@ mixin lbl(txt)
                     .details-row
                         .advanced-options
                             i.fa(
-                            ng-click='expandedToken = !expandedToken'
+                            ng-click='toggleToken()'
                             ng-class='expandedToken ? "fa-chevron-circle-down" : "fa-chevron-circle-right"')
-                            a(ng-click='expandedToken = !expandedToken') {{expandedToken ? 'Cancel security token changing...' : 'Show security token...'}}
+                            a(ng-click='toggleToken()') {{expandedToken ? 'Cancel security token changing...' : 'Show security token...'}}
                         div(ng-if='expandedToken')
                             +lbl('Security token:')
-                            label(ng-init='user.token = $root.user.token') {{user.token || 'No security token. Regenerate please.'}}
+                            label {{user.token || 'No security token. Regenerate please.'}}
                             i.tipLabel.fa.fa-refresh(ng-click='generateToken()' bs-tooltip='' data-title='Generate random security token')
                             i.tipLabel.fa.fa-clipboard(ng-click-copy='{{user.token}}' bs-tooltip='' data-title='Copy security token to clipboard')
                             i.tipLabel.fa.fa-question-circle(ng-if=lines bs-tooltip='' data-title='The security token is used for authorization of web agent')
                     .details-row
                         .advanced-options
                             i.fa(
-                            ng-click='expandedPassword = !expandedPassword'
+                            ng-click='togglePassword()'
                             ng-class='expandedPassword ? "fa-chevron-circle-down" : "fa-chevron-circle-right"')
-                            a(ng-click='expandedPassword = !expandedPassword') {{expandedPassword ? 'Cancel password changing...' : 'Change password...'}}
+                            a(ng-click='togglePassword()') {{expandedPassword ? 'Cancel password changing...' : 'Change password...'}}
                         div(ng-if='expandedPassword')
                             .details-row
                                 +lbl('New password:')
                                 .col-xs-5.col-sm-4
-                                    input.form-control(ng-init='user.password=null' type='password' ng-model='user.password' placeholder='New password')
+                                    input.form-control(type='password' ng-model='user.password' placeholder='New password')
                             .details-row
                                 +lbl('Confirm:')
                                 .col-xs-5.col-sm-4
-                                    input.form-control(ng-init='user.confirm=null' type='password' ng-model='user.confirm' match='user.password' placeholder='Confirm new password')
+                                    input.form-control(type='password' ng-model='user.confirm' match='user.password' placeholder='Confirm new password')
                 .col-xs-12.col-sm-12.details-row
                     a.btn.btn-primary(ng-disabled='!profileCouldBeSaved()' ng-click='profileCouldBeSaved() && saveUser()' bs-tooltip='' data-title='{{saveBtnTipText()}}' data-placement='bottom' data-trigger='hover') Save

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/views/signin.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/signin.jade b/modules/web-console/src/main/js/views/signin.jade
index 7c25a5a..73a07e7 100644
--- a/modules/web-console/src/main/js/views/signin.jade
+++ b/modules/web-console/src/main/js/views/signin.jade
@@ -48,11 +48,15 @@ header#header.header
                             .settings-row(ng-show='action == "signup"')
                                 +lblRequired('Last Name:')
                                 .col-xs-9.col-md-8
-                                    input#last_name.form-control(enter-focus-next='email' type='text' ng-model='ui.lastName' placeholder='Input last name' ng-required='action=="signup"')
-                            .settings-row(ng-show='action != "signup"')
+                                    input#last_name.form-control(enter-focus-next='signup_email' type='text' ng-model='ui.lastName' placeholder='Input last name' ng-required='action=="signup"')
+                            .settings-row(ng-show='action == "password/forgot"')
                                 +lblRequired('Email:')
                                 .col-xs-9.col-md-8
-                                    input#signin_email.form-control(enter-focus-next='company' type='email' ng-model='ui.email' placeholder='Input email' required)
+                                    input#forgot_email.form-control(on-enter='form.$valid && forgotPassword(ui)' type='email' ng-model='ui.email' placeholder='Input email' required)
+                            .settings-row(ng-show='action == "signin"')
+                                +lblRequired('Email:')
+                                .col-xs-9.col-md-8
+                                    input#signin_email.form-control(enter-focus-next='user_password' type='email' ng-model='ui.email' placeholder='Input email' required)
                             .settings-row(ng-show='action == "signup"')
                                 +lblRequired('Email:')
                                 .col-xs-9.col-md-8
@@ -80,17 +84,17 @@ header#header.header
                                         | I agree to the #[a(ui-sref='{{::terms.termsState}}' target='_blank') terms and conditions]
                         .col-xs-12.col-md-11
                             .login-footer(ng-show='action == "signup"')
-                                a.labelField(ng-click='action = "password/forgot"' on-click-focus='email') Forgot password?
-                                a.labelLogin(ng-click='action = "signin"' on-click-focus='email') Sign In
+                                a.labelField(ng-click='action = "password/forgot"' on-click-focus='signin_email') Forgot password?
+                                a.labelLogin(ng-click='action = "signin"' on-click-focus='signin_email') Sign In
                                 button#signup.btn.btn-primary(ng-click='auth(action, ui)' ng-disabled='form.$invalid') Sign Up
                         .col-xs-12.col-md-11
                             .login-footer(ng-show='action == "password/forgot"')
-                                a.labelField(ng-click='action = "signin"' on-click-focus='email') Sign In
-                                button#forgot.btn.btn-primary(ng-click='auth(action, ui)' ng-disabled='form.$invalid') Send it to me
+                                a.labelField(ng-click='action = "signin"' on-click-focus='signin_email') Sign In
+                                button#forgot.btn.btn-primary(ng-click='forgotPassword(ui)' ng-disabled='form.$invalid') Send it to me
                         .col-xs-12.col-md-11
                             .login-footer(ng-show='action == "signin"')
-                                a.labelField(ng-click='action = "password/forgot"' on-click-focus='email') Forgot password?
-                                a.labelLogin(ng-click='action = "signup"' on-click-focus='user_name') Sign Up
+                                a.labelField(ng-click='action = "password/forgot"' on-click-focus='signin_email') Forgot password?
+                                a.labelLogin(ng-click='action = "signup"' on-click-focus='first_name') Sign Up
                                 button#login.btn.btn-primary(ng-click='auth(action, ui)' ng-disabled='form.$invalid') Sign In
 
                     .col-xs-12.col-md-11.home-panel
@@ -144,10 +148,10 @@ header#header.header
                                     h3 Query chart
                                     p View data in tabular form and as charts
                         // Controls
-                        a.left.carousel-control(href='#carousel', role='button', data-slide='prev')
+                        a.left.carousel-control(href='#carousel', ng-click='$event.preventDefault()', role='button', data-slide='prev')
                             span.fa.fa-chevron-left(aria-hidden='true')
                             span.sr-only Previous
-                        a.right.carousel-control(href='#carousel', role='button', data-slide='next')
+                        a.right.carousel-control(href='#carousel', ng-click='$event.preventDefault()', role='button', data-slide='next')
                             span.fa.fa-chevron-right(aria-hidden='true')
                             span.sr-only Next
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/views/sql/cache-metadata.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/sql/cache-metadata.jade b/modules/web-console/src/main/js/views/sql/cache-metadata.jade
index 40b7a6b..450c178 100644
--- a/modules/web-console/src/main/js/views/sql/cache-metadata.jade
+++ b/modules/web-console/src/main/js/views/sql/cache-metadata.jade
@@ -24,7 +24,7 @@
             span(ng-switch='' on='node.type')
                 span(ng-switch-when='type' ng-dblclick='dblclickMetadata(paragraph, node)')
                     i.fa.fa-table
-                    label.clickable(ng-bind-html='node.displayMame')
+                    label.clickable(ng-bind='node.displayName')
                 span(ng-switch-when='plain')
                     label {{node.name}}
                 span(ng-switch-when='field' ng-dblclick='dblclickMetadata(paragraph, node)')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/views/sql/sql.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/sql/sql.jade b/modules/web-console/src/main/js/views/sql/sql.jade
index 5eade69..be6d85e 100644
--- a/modules/web-console/src/main/js/views/sql/sql.jade
+++ b/modules/web-console/src/main/js/views/sql/sql.jade
@@ -42,24 +42,105 @@ mixin chart-settings(mdl)
         .col-xs-4
             +result-toolbar
 
+mixin notebook-rename
+    .docs-header.notebook-header
+        h1.col-sm-6(ng-hide='notebook.edit')
+            label(style='max-width: calc(100% - 60px)') {{notebook.name}}
+            .btn-group(ng-if='!demo')
+                +btn-toolbar('fa-pencil', 'notebook.edit = true;notebook.editName = notebook.name', 'Rename notebook')
+                +btn-toolbar('fa-trash', 'removeNotebook()', 'Remove notebook')
+        h1.col-sm-6(ng-show='notebook.edit')
+            i.btn.fa.fa-floppy-o(ng-show='notebook.editName' ng-click='renameNotebook(notebook.editName)' bs-tooltip data-title='Save notebook name' data-trigger='hover')
+            .input-tip
+                input.form-control(ng-model='notebook.editName' required on-enter='renameNotebook(notebook.editName)' on-escape='notebook.edit = false;')
+        h1.pull-right
+            a.dropdown-toggle(data-toggle='dropdown' bs-dropdown='scrollParagraphs' data-placement='bottom-right') Scroll to query
+                span.caret
+            .btn-group(style='margin-top: 2px')
+                +btn-toolbar('fa-plus', 'addParagraph()', 'Add new query')
+
+mixin notebook-error
+    h2 Failed to load notebook
+    label.col-sm-12 Notebook not accessible any more. Go back to configuration or open to another notebook.
+    button.h3.btn.btn-primary(ui-sref='base.configuration.clusters') Back to configuration
+
+mixin paragraph-rename
+    .col-sm-6(ng-hide='paragraph.edit')
+        i.tipLabel.fa(ng-class='paragraphExpanded(paragraph) ? "fa-chevron-circle-down" : "fa-chevron-circle-right"')
+        label {{paragraph.name}}
+
+        .btn-group(ng-hide='notebook.paragraphs.length > 1')
+            +btn-toolbar('fa-pencil', 'paragraph.edit = true; paragraph.editName = paragraph.name; $event.stopPropagation();', 'Rename query', 'paragraph-name-{{paragraph.id}}')
+
+        .btn-group(ng-show='notebook.paragraphs.length > 1' ng-click='$event.stopPropagation();')
+            +btn-toolbar('fa-pencil', 'paragraph.edit = true; paragraph.editName = paragraph.name;', 'Rename query', 'paragraph-name-{{paragraph.id}}')
+            +btn-toolbar('fa-remove', 'removeParagraph(paragraph)', 'Remove query')
+
+    .col-sm-6(ng-show='paragraph.edit')
+        i.tipLabel.fa(style='float: left;' ng-class='paragraphExpanded(paragraph) ? "fa-chevron-circle-down" : "fa-chevron-circle-right"')
+        i.tipLabel.fa.fa-floppy-o(style='float: right;' ng-show='paragraph.editName' ng-click='renameParagraph(paragraph, paragraph.editName); $event.stopPropagation();' bs-tooltip data-title='Save query name' data-trigger='hover')
+        .input-tip
+            input.form-control(id='paragraph-name-{{paragraph.id}}' ng-model='paragraph.editName' required ng-click='$event.stopPropagation();' on-enter='renameParagraph(paragraph, paragraph.editName)' on-escape='paragraph.edit = false')
+
+mixin query-controls
+    .sql-controls
+        a.btn.btn-primary(ng-disabled='!actionAvailable(paragraph, true)' ng-click='actionAvailable(paragraph, true) && execute(paragraph)' data-placement='bottom' bs-tooltip data-title='{{actionTooltip(paragraph, "execute", true)}}') Execute
+        a.btn.btn-primary(ng-disabled='!actionAvailable(paragraph, true)' ng-click='actionAvailable(paragraph, true) && explain(paragraph)' data-placement='bottom' bs-tooltip data-title='{{actionTooltip(paragraph, "explain", true)}}') Explain
+        a.btn.btn-primary(ng-disabled='!actionAvailable(paragraph, false)' ng-click='actionAvailable(paragraph, false) && scan(paragraph)' data-placement='bottom' bs-tooltip data-title='{{actionTooltip(paragraph, "execute scan", false)}}') Scan
+        .pull-right
+            labelHide System columns:
+            a.btn.btn-default.fa.fa-bars.tipLabel(ng-class='{"btn-info": paragraph.systemColumns}' ng-click='toggleSystemColumns(paragraph)' ng-disabled='paragraph.disabledSystemColumns' bs-tooltip data-title='Show "_KEY", "_VAL" columns')
+            label.tipLabel Refresh rate:
+            button.btn.btn-default.fa.fa-clock-o.tipLabel(title='Click to show refresh rate dialog' ng-class='{"btn-info": paragraph.rate && paragraph.rate.installed}' bs-popover data-template-url='/sql/paragraph-rate.html' data-placement='left' data-auto-close='1' data-trigger='click') {{rateAsString(paragraph)}}
+            label.tipLabel Page size:
+            button.select-toggle.fieldButton.btn.btn-default(ng-model='paragraph.pageSize' bs-options='item for item in pageSizes' bs-select bs-tooltip data-placement='bottom-right' data-title='Max number of rows to show in query result as one page')
+
+mixin table-result
+    .sql-table-total.row
+        .col-xs-4
+            label(style='margin-right: 10px;') Page: #[b {{paragraph.page}}]
+            label Results so far: #[b {{paragraph.rows.length + paragraph.total}}]
+        .col-xs-4
+            +result-toolbar
+        .col-xs-4
+            .btn-group.pull-right(ng-disabled='paragraph.loading')
+                button.btn.btn-primary.fieldButton(ng-click='exportCsv(paragraph)' bs-tooltip data-title='{{actionTooltip(paragraph, "export", false)}}') Export
+                button.btn.btn-primary(id='export-item-dropdown' data-toggle='dropdown' data-container='body' bs-dropdown='exportDropdown' data-placement='bottom-right')
+                    span.caret
+    .grid(ui-grid='paragraph.gridOptions' ui-grid-resize-columns ui-grid-exporter)
+
+mixin chart-result
+    div(ng-show='paragraph.queryExecuted()')
+        +chart-settings
+        div(ng-show='paragraph.chartColumns.length > 0 && !paragraph.chartColumnsConfigured()')
+            .sql-empty-result Cannot display chart. Please configure axis using #[b Chart settings]
+        div(ng-show='paragraph.chartColumns.length == 0')
+            .sql-empty-result Cannot display chart. Result set must contain Java build-in type columns. Please change query and execute it again.
+        div(ng-show='paragraph.chartColumnsConfigured()')
+            div(ng-show='paragraph.timeLineSupported() || !paragraph.chartTimeLineEnabled()')
+                div(ng-repeat='chart in paragraph.charts')
+                    nvd3(options='chart.options' data='chart.data' api='chart.api')
+            .sql-empty-result(ng-show='!paragraph.timeLineSupported() && paragraph.chartTimeLineEnabled()') Pie chart does not support 'TIME_LINE' column for X-axis. Please use another column for X-axis or switch to another chart.
+    .sql-empty-result(ng-hide='paragraph.queryExecuted()')
+        .row
+            .col-xs-4.col-xs-offset-4
+                +result-toolbar
+        label.margin-top-dflt Charts do not support #[b Explain] and #[b Scan] query
+
+mixin footer-controls
+    hr(style='margin-top: 0; margin-bottom: 5px')
+    a(style='float: left; margin-left: 10px; margin-bottom: 5px' ng-click='showResultQuery(paragraph)') Show query
+
+    -var nextVisibleCondition = 'paragraph.queryId && (paragraph.table() || paragraph.chart() && (paragraph.timeLineSupported() || !paragraph.chartTimeLineEnabled()))'
+
+    .sql-next(ng-show=nextVisibleCondition)
+        i.fa.fa-chevron-circle-right(ng-class='{disabled: paragraph.loading}' ng-click='!paragraph.loading && nextPage(paragraph)')
+        a(ng-class='{disabled: paragraph.loading}' ng-click='!paragraph.loading && nextPage(paragraph)') Next
+
 .row(ng-controller='sqlController')
     .docs-content
         .row(ng-if='notebook' bs-affix style='margin-bottom: 20px;')
-            .docs-header.notebook-header
-                h1.col-sm-6(ng-hide='notebook.edit')
-                    label(style='max-width: calc(100% - 60px)') {{notebook.name}}
-                    .btn-group(ng-if='!demo')
-                        +btn-toolbar('fa-pencil', 'notebook.edit = true;notebook.editName = notebook.name', 'Rename notebook')
-                        +btn-toolbar('fa-trash', 'removeNotebook()', 'Remove notebook')
-                h1.col-sm-6(ng-show='notebook.edit')
-                    i.btn.fa.fa-floppy-o(ng-show='notebook.editName' ng-click='renameNotebook(notebook.editName)' bs-tooltip data-title='Save notebook name' data-trigger='hover')
-                    .input-tip
-                        input.form-control(ng-model='notebook.editName' required on-enter='renameNotebook(notebook.editName)' on-escape='notebook.edit = false;')
-                h1.pull-right
-                    a.dropdown-toggle(data-toggle='dropdown' bs-dropdown='scrollParagraphs' data-placement='bottom-right') Scroll to query
-                        span.caret
-                    .btn-group(style='margin-top: 2px')
-                        +btn-toolbar('fa-plus', 'addParagraph()', 'Add new query')
+            +notebook-rename
 
         ignite-information(data-title='With SQL notebook you can' style='margin-top: 0; margin-bottom: 30px')
             ul
@@ -69,31 +150,15 @@ mixin chart-settings(mdl)
                 li View data in tabular form and as charts
 
         div(ng-if='notebookLoadFailed' style='text-align: center')
-            h2 Failed to load notebook
-            label.col-sm-12 Notebook not accessible any more. Go back to configuration or open to another notebook.
-            button.h3.btn.btn-primary(ui-sref='base.configuration.clusters') Back to configuration
+            +notebook-error
+
         div(ng-if='notebook' ignite-loading='sqlLoading' ignite-loading-text='{{ loadingText }}' ignite-loading-position='top')
             .docs-body.paragraphs
                 .panel-group(bs-collapse ng-model='notebook.expandedParagraphs' data-allow-multiple='true' data-start-collapsed='false')
                     .panel.panel-default(ng-repeat='paragraph in notebook.paragraphs')
                         .panel-heading(id='{{paragraph.id}}' bs-collapse-toggle)
                             .row
-                                .col-sm-6(ng-hide='paragraph.edit')
-                                    i.tipLabel.fa(ng-class='paragraphExpanded(paragraph) ? "fa-chevron-circle-down" : "fa-chevron-circle-right"')
-                                    label {{paragraph.name}}
-
-                                    .btn-group(ng-hide='notebook.paragraphs.length > 1')
-                                        +btn-toolbar('fa-pencil', 'paragraph.edit = true; paragraph.editName = paragraph.name; $event.stopPropagation();', 'Rename query', 'paragraph-name-{{paragraph.id}}')
-
-                                    .btn-group(ng-show='notebook.paragraphs.length > 1' ng-click='$event.stopPropagation();')
-                                        +btn-toolbar('fa-pencil', 'paragraph.edit = true; paragraph.editName = paragraph.name;', 'Rename query', 'paragraph-name-{{paragraph.id}}')
-                                        +btn-toolbar('fa-remove', 'removeParagraph(paragraph)', 'Remove query')
-
-                                .col-sm-6(ng-show='paragraph.edit')
-                                    i.tipLabel.fa(style='float: left;' ng-class='paragraphExpanded(paragraph) ? "fa-chevron-circle-down" : "fa-chevron-circle-right"')
-                                    i.tipLabel.fa.fa-floppy-o(style='float: right;' ng-show='paragraph.editName' ng-click='renameParagraph(paragraph, paragraph.editName); $event.stopPropagation();' bs-tooltip data-title='Save query name' data-trigger='hover')
-                                    .input-tip
-                                        input.form-control(id='paragraph-name-{{paragraph.id}}' ng-model='paragraph.editName' required ng-click='$event.stopPropagation();' on-enter='renameParagraph(paragraph, paragraph.editName)' on-escape='paragraph.edit = false')
+                                +paragraph-rename
                         .panel-collapse(role='tabpanel' bs-collapse-target)
                             .col-sm-12
                                 .col-xs-8.col-sm-9(style='border-right: 1px solid #eee')
@@ -105,13 +170,13 @@ mixin chart-settings(mdl)
                                         lable.labelField.labelFormField Caches:
                                         i.fa.fa-database.tipField(title='Click to show cache types metadata dialog' bs-popover data-template-url='/sql/cache-metadata.html', data-placement='bottom', data-trigger='click')
                                         .input-tip
-                                            input.form-control(type='text' st-search='name' placeholder='Filter caches...')
+                                            input.form-control(type='text' st-search='label' placeholder='Filter caches...')
                                         table.links
-                                            tbody.scrollable-y(style='max-height: 15em;display:block;' ng-model='paragraph.cacheName' bs-radio-group)
+                                            tbody.scrollable-y(style='max-height: 15em; display: block;')
                                                 tr(ng-repeat='cache in displayedCaches track by cache.name')
                                                     td(style='width: 100%')
-                                                        input.labelField(type='radio' value='{{cache.name}}')
-                                                        label(ng-bind-html='maskCacheName(cache.name)')
+                                                        input.labelField(id='cache{{$index}}' type='radio' value='{{cache.name}}' ng-model='paragraph.cacheName')
+                                                        label(for='cache{{$index}}' ng-bind='cache.label')
                                     .empty-caches(ng-show='displayedCaches.length == 0 && caches.length != 0')
                                         label Wrong caches filter
                                     .empty-caches(ng-show='caches.length == 0')
@@ -119,62 +184,18 @@ mixin chart-settings(mdl)
                             .col-sm-12
                                 hr(style='margin: 0')
                             .col-sm-12
-                                .sql-controls
-                                    a.btn.btn-primary(ng-disabled='!actionAvailable(paragraph, true)' ng-click='actionAvailable(paragraph, true) && execute(paragraph)' data-placement='bottom' bs-tooltip data-title='{{actionTooltip(paragraph, "execute", true)}}') Execute
-                                    a.btn.btn-primary(ng-disabled='!actionAvailable(paragraph, true)' ng-click='actionAvailable(paragraph, true) && explain(paragraph)' data-placement='bottom' bs-tooltip data-title='{{actionTooltip(paragraph, "explain", true)}}') Explain
-                                    a.btn.btn-primary(ng-disabled='!actionAvailable(paragraph, false)' ng-click='actionAvailable(paragraph, false) && scan(paragraph)' data-placement='bottom' bs-tooltip data-title='{{actionTooltip(paragraph, "execute scan", false)}}') Scan
-                                    .pull-right
-                                        labelHide System columns:
-                                        a.btn.btn-default.fa.fa-bars.tipLabel(ng-class='{"btn-info": paragraph.systemColumns}' ng-click='toggleSystemColumns(paragraph)' ng-disabled='paragraph.disabledSystemColumns' bs-tooltip data-title='Show "_KEY", "_VAL" columns')
-                                        label.tipLabel Refresh rate:
-                                        button.btn.btn-default.fa.fa-clock-o.tipLabel(title='Click to show refresh rate dialog' ng-class='{"btn-info": paragraph.rate && paragraph.rate.installed}' bs-popover data-template-url='/sql/paragraph-rate.html' data-placement='left' data-auto-close='1' data-trigger='click') {{rateAsString(paragraph)}}
-                                        label.tipLabel Page size:
-                                        button.select-toggle.fieldButton.btn.btn-default(ng-model='paragraph.pageSize' bs-options='item for item in pageSizes' bs-select bs-tooltip data-title='Max number of rows to show in query result as one page')
+                                +query-controls
                             .col-sm-12.sql-error-result(ng-show='paragraph.errMsg') Error: {{paragraph.errMsg}}
-                            .col-sm-12(ng-show='!paragraph.errMsg && paragraph.result != "none"')
+                            .col-sm-12(ng-show='!paragraph.errMsg && paragraph.queryArgs')
                                 hr(style='margin-top: 0; margin-bottom: 10px')
+
                                 .sql-empty-result(ng-show='!paragraph.nonEmpty()') Result set is empty
+
                                 div(ng-show='paragraph.table() && paragraph.nonEmpty()')
-                                    .sql-table-total.row
-                                        .col-xs-4
-                                            label Page #&nbsp;
-                                            b {{paragraph.page}}&nbsp;&nbsp;&nbsp;
-                                            label Results so far:&nbsp;
-                                            b {{paragraph.rows.length + paragraph.total}}
-                                        .col-xs-4
-                                            +result-toolbar
-                                        .col-xs-4
-                                            .btn-group.pull-right(ng-disabled='paragraph.loading')
-                                                button.btn.btn-primary.fieldButton(ng-click='exportCsv(paragraph)' bs-tooltip data-title='{{actionTooltip(paragraph, "export", false)}}') Export
-                                                button.btn.btn-primary(id='export-item-dropdown' data-toggle='dropdown' data-container='body' bs-dropdown='exportDropdown' data-placement='bottom-right')
-                                                    span.caret
-                                    .grid(ui-grid='paragraph.gridOptions' ui-grid-auto-resize ui-grid-exporter ng-style='{ height: paragraph.gridOptions.height }')
+                                    +table-result
+
                                 div(ng-show='paragraph.chart() && paragraph.nonEmpty()')
-                                    div(ng-show='paragraph.queryExecuted()')
-                                        +chart-settings
-                                        div(ng-show='paragraph.chartColumns.length > 0 && !paragraph.chartColumnsConfigured()')
-                                            .sql-empty-result Cannot display chart. Please configure axis using #[b Chart settings]
-                                        div(ng-show='paragraph.chartColumns.length == 0')
-                                            .sql-empty-result Cannot display chart. Result set must contain Java build-in type columns. Please change query and execute it again.
-                                        div(ng-show='paragraph.chartColumnsConfigured()')
-                                            div(ng-show='paragraph.timeLineSupported() || !paragraph.chartTimeLineEnabled()')
-                                                div(ng-repeat='chart in paragraph.charts')
-                                                    nvd3(options='chart.options' data='chart.data' api='chart.api')
-                                            .sql-empty-result(ng-show='!paragraph.timeLineSupported() && paragraph.chartTimeLineEnabled()') Pie chart does not support 'TIME_LINE' column for X-axis. Please use another column for X-axis or switch to another chart.
-                                    .sql-empty-result(ng-hide='paragraph.queryExecuted()')
-                                        .row
-                                            .col-xs-4.col-xs-offset-4
-                                                +result-toolbar
-                                        label.margin-top-dflt Charts do not support&nbsp
-                                            b Explain
-                                            | &nbsp;and&nbsp;
-                                            b Scan
-                                            | &nbsp;query
-                                div(ng-show='paragraph.queryArgs && !paragraph.refreshExecuting()')
-                                    -var nextVisibleCondition = 'paragraph.queryId && (paragraph.table() || paragraph.chart() && paragraph.queryExecute() && (paragraph.timeLineSupported() || !paragraph.chartTimeLineEnabled()))'
-
-                                    hr(style='margin-top: 0; margin-bottom: 5px')
-                                    a(style='float: left; margin-left: 10px; margin-bottom: 5px' ng-click='showResultQuery(paragraph)') Show query
-                                    .sql-next(ng-show=nextVisibleCondition )
-                                        i.fa.fa-chevron-circle-right(ng-class='{disabled: paragraph.loading}' ng-click='!paragraph.loading && nextPage(paragraph)')
-                                        a(ng-class='{disabled: paragraph.loading}' ng-click='!paragraph.loading && nextPage(paragraph)') Next
+                                    +chart-result
+
+                                div(ng-show='!paragraph.refreshExecuting()')
+                                    +footer-controls


[08/22] ignite git commit: Merge remote-tracking branch 'upstream/gridgain-7.6.1' into gridgain-7.6.1

Posted by ak...@apache.org.
Merge remote-tracking branch 'upstream/gridgain-7.6.1' into gridgain-7.6.1


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/e7ebe0a3
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/e7ebe0a3
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/e7ebe0a3

Branch: refs/heads/master
Commit: e7ebe0a30da8b2f5995d51c1e59e10900b84e2b2
Parents: 9344560 a70ff4c
Author: vozerov-gridgain <vo...@gridgain.com>
Authored: Thu Jun 23 15:49:59 2016 +0300
Committer: vozerov-gridgain <vo...@gridgain.com>
Committed: Thu Jun 23 15:49:59 2016 +0300

----------------------------------------------------------------------
 .../internal/processors/cache/transactions/IgniteTxManager.java  | 4 ++++
 1 file changed, 4 insertions(+)
----------------------------------------------------------------------



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

Posted by ak...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/controllers/clusters-controller.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/controllers/clusters-controller.js b/modules/web-console/src/main/js/controllers/clusters-controller.js
index 8aecc9f..3277397 100644
--- a/modules/web-console/src/main/js/controllers/clusters-controller.js
+++ b/modules/web-console/src/main/js/controllers/clusters-controller.js
@@ -19,15 +19,15 @@
 import consoleModule from 'controllers/common-module';
 
 consoleModule.controller('clustersController', [
-    '$rootScope', '$scope', '$http', '$state', '$timeout', '$common', '$confirm', '$clone', '$loading', '$cleanup', '$unsavedChangesGuard', 'igniteEventGroups', 'DemoInfo',
-    function ($root, $scope, $http, $state, $timeout, $common, $confirm, $clone, $loading, $cleanup, $unsavedChangesGuard, igniteEventGroups, DemoInfo) {
+    '$rootScope', '$scope', '$http', '$state', '$timeout', '$common', '$confirm', '$clone', '$loading', '$cleanup', '$unsavedChangesGuard', 'igniteEventGroups', 'DemoInfo', '$table',
+    function($root, $scope, $http, $state, $timeout, $common, $confirm, $clone, $loading, $cleanup, $unsavedChangesGuard, igniteEventGroups, DemoInfo, $table) {
         $unsavedChangesGuard.install($scope);
 
-        var emptyCluster = {empty: true};
+        const emptyCluster = {empty: true};
 
-        var __original_value;
+        let __original_value;
 
-        var blank = {
+        const blank = {
             atomicConfiguration: {},
             binaryConfiguration: {},
             communication: {},
@@ -36,7 +36,109 @@ consoleModule.controller('clustersController', [
             marshaller: {},
             sslContextFactory: {},
             swapSpaceSpi: {},
-            transactionConfiguration: {}
+            transactionConfiguration: {},
+            collision: {}
+        };
+
+        const pairFields = {
+            attributes: {id: 'Attribute', idPrefix: 'Key', searchCol: 'name', valueCol: 'key', dupObjName: 'name', group: 'attributes'},
+            'collision.JobStealing.stealingAttributes': {id: 'CAttribute', idPrefix: 'Key', searchCol: 'name', valueCol: 'key', dupObjName: 'name', group: 'collision'}
+        };
+
+        const showPopoverMessage = $common.showPopoverMessage;
+
+        $scope.tablePairValid = function(item, field, index) {
+            const pairField = pairFields[field.model];
+
+            const pairValue = $table.tablePairValue(field, index);
+
+            if (pairField) {
+                const model = _.get(item, field.model);
+
+                if ($common.isDefined(model)) {
+                    const idx = _.findIndex(model, (pair) => {
+                        return pair[pairField.searchCol] === pairValue[pairField.valueCol];
+                    });
+
+                    // Found duplicate by key.
+                    if (idx >= 0 && idx !== index)
+                        return showPopoverMessage($scope.ui, pairField.group, $table.tableFieldId(index, pairField.idPrefix + pairField.id), 'Attribute with such ' + pairField.dupObjName + ' already exists!');
+                }
+            }
+
+            return true;
+        };
+
+        $scope.tableSave = function(field, index, stopEdit) {
+            if ($table.tablePairSaveVisible(field, index))
+                return $table.tablePairSave($scope.tablePairValid, $scope.backupItem, field, index, stopEdit);
+
+            return true;
+        };
+
+        $scope.tableReset = (trySave) => {
+            const field = $table.tableField();
+
+            if (trySave && $common.isDefined(field) && !$scope.tableSave(field, $table.tableEditedRowIndex(), true))
+                return false;
+
+            $table.tableReset();
+
+            return true;
+        };
+
+        $scope.tableNewItem = function(field) {
+            if ($scope.tableReset(true)) {
+                if (field.type === 'failoverSpi') {
+                    if ($common.isDefined($scope.backupItem.failoverSpi))
+                        $scope.backupItem.failoverSpi.push({});
+                    else
+                        $scope.backupItem.failoverSpi = {};
+                }
+                else
+                    $table.tableNewItem(field);
+            }
+        };
+
+        $scope.tableNewItemActive = $table.tableNewItemActive;
+
+        $scope.tableStartEdit = function(item, field, index) {
+            if ($scope.tableReset(true))
+                $table.tableStartEdit(item, field, index, $scope.tableSave);
+        };
+
+        $scope.tableEditing = $table.tableEditing;
+
+        $scope.tableRemove = function(item, field, index) {
+            if ($scope.tableReset(true))
+                $table.tableRemove(item, field, index);
+        };
+
+        $scope.tablePairSave = $table.tablePairSave;
+        $scope.tablePairSaveVisible = $table.tablePairSaveVisible;
+
+        $scope.attributesTbl = {
+            type: 'attributes',
+            model: 'attributes',
+            focusId: 'Attribute',
+            ui: 'table-pair',
+            keyName: 'name',
+            valueName: 'value',
+            save: $scope.tableSave
+        };
+
+        $scope.stealingAttributesTbl = {
+            type: 'attributes',
+            model: 'collision.JobStealing.stealingAttributes',
+            focusId: 'CAttribute',
+            ui: 'table-pair',
+            keyName: 'name',
+            valueName: 'value',
+            save: $scope.tableSave
+        };
+
+        $scope.removeFailoverConfiguration = function(idx) {
+            $scope.backupItem.failoverSpi.splice(idx, 1);
         };
 
         // We need to initialize backupItem with empty object in order to properly used from angular directives.
@@ -50,15 +152,13 @@ consoleModule.controller('clustersController', [
         $scope.saveBtnTipText = $common.saveBtnTipText;
         $scope.widthIsSufficient = $common.widthIsSufficient;
 
-        var showPopoverMessage = $common.showPopoverMessage;
-
-        $scope.contentVisible = function () {
-            var item = $scope.backupItem;
+        $scope.contentVisible = function() {
+            const item = $scope.backupItem;
 
             return !item.empty && (!item._id || _.find($scope.displayedRows, {_id: item._id}));
         };
 
-        $scope.toggleExpanded = function () {
+        $scope.toggleExpanded = function() {
             $scope.ui.expanded = !$scope.ui.expanded;
 
             $common.hidePopover();
@@ -77,14 +177,14 @@ consoleModule.controller('clustersController', [
 
         $scope.swapSpaceSpis = [
             {value: 'FileSwapSpaceSpi', label: 'File-based swap'},
-            {value: undefined, label: 'Not set'}
+            {value: null, label: 'Not set'}
         ];
 
         $scope.eventGroups = igniteEventGroups;
 
         $scope.clusters = [];
 
-        function _clusterLbl (cluster) {
+        function _clusterLbl(cluster) {
             return cluster.name + ', ' + _.find($scope.discoveries, {value: cluster.discovery.kind}).label;
         }
 
@@ -93,46 +193,36 @@ consoleModule.controller('clustersController', [
                 $scope.selectItem($scope.clusters[0]);
         }
 
-        function clusterCaches(item) {
-            return _.reduce($scope.caches, function (memo, cache) {
-                if (item && _.includes(item.caches, cache.value)) {
-                    memo.push(cache.cache);
-                }
-
-                return memo;
-            }, []);
-        }
-
         $loading.start('loadingClustersScreen');
 
         // When landing on the page, get clusters and show them.
         $http.post('/api/v1/configuration/clusters/list')
-            .success(function (data) {
+            .success(function(data) {
                 $scope.spaces = data.spaces;
+                $scope.clusters = data.clusters;
+                $scope.caches = _.map(data.caches, (cache) => ({value: cache._id, label: cache.name, cache}));
+                $scope.igfss = _.map(data.igfss, (igfs) => ({value: igfs._id, label: igfs.name, igfs}));
 
-                _.forEach(data.clusters, function (cluster) {
+                _.forEach($scope.clusters, (cluster) => {
                     cluster.label = _clusterLbl(cluster);
-                });
 
-                $scope.clusters = data.clusters;
+                    if (!cluster.collision || !cluster.collision.kind)
+                        cluster.collision = {kind: 'Noop', JobStealing: {stealingEnabled: true}, PriorityQueue: {starvationPreventionEnabled: true}};
 
-                $scope.caches = _.map(data.caches, function (cache) {
-                    return {value: cache._id, label: cache.name, cache: cache};
-                });
+                    if (!cluster.failoverSpi)
+                        cluster.failoverSpi = [];
 
-                $scope.igfss = _.map(data.igfss, function (igfs) {
-                    return {value: igfs._id, label: igfs.name, igfs: igfs};
+                    if (!cluster.logger)
+                        cluster.logger = {Log4j: { mode: 'Default'}};
                 });
 
-                if ($state.params.id)
-                    $scope.createItem($state.params.id);
+                if ($state.params.linkId)
+                    $scope.createItem($state.params.linkId);
                 else {
-                    var lastSelectedCluster = angular.fromJson(sessionStorage.lastSelectedCluster);
+                    const lastSelectedCluster = angular.fromJson(sessionStorage.lastSelectedCluster);
 
                     if (lastSelectedCluster) {
-                        var idx = _.findIndex($scope.clusters, function (cluster) {
-                            return cluster._id === lastSelectedCluster;
-                        });
+                        const idx = _.findIndex($scope.clusters, (cluster) => cluster._id === lastSelectedCluster);
 
                         if (idx >= 0)
                             $scope.selectItem($scope.clusters[idx]);
@@ -147,39 +237,35 @@ consoleModule.controller('clustersController', [
                 }
 
                 $scope.$watch('ui.inputForm.$valid', function(valid) {
-                    if (valid && __original_value === JSON.stringify($cleanup($scope.backupItem))) {
+                    if (valid && _.isEqual(__original_value, $cleanup($scope.backupItem)))
                         $scope.ui.inputForm.$dirty = false;
-                    }
                 });
 
-                $scope.$watch('backupItem', function (val) {
-                    var form = $scope.ui.inputForm;
+                $scope.$watch('backupItem', function(val) {
+                    const form = $scope.ui.inputForm;
 
-                    if (form.$pristine || (form.$valid && __original_value === JSON.stringify($cleanup(val))))
+                    if (form.$pristine || (form.$valid && _.isEqual(__original_value, $cleanup(val))))
                         form.$setPristine();
                     else
                         form.$setDirty();
                 }, true);
 
-                if ($root.IgniteDemoMode) {
-                    if (sessionStorage.showDemoInfo !== 'true') {
-                        sessionStorage.showDemoInfo = 'true';
+                if ($root.IgniteDemoMode && sessionStorage.showDemoInfo !== 'true') {
+                    sessionStorage.showDemoInfo = 'true';
 
-                        DemoInfo.show();
-                    }
+                    DemoInfo.show();
                 }
-
             })
-            .catch(function (errMsg) {
+            .catch(function(errMsg) {
                 $common.showError(errMsg);
             })
-            .finally(function () {
+            .finally(function() {
                 $scope.ui.ready = true;
                 $scope.ui.inputForm.$setPristine();
                 $loading.finish('loadingClustersScreen');
             });
 
-        $scope.selectItem = function (item, backup) {
+        $scope.selectItem = function(item, backup) {
             function selectItem() {
                 $scope.selectedItem = item;
 
@@ -198,11 +284,11 @@ consoleModule.controller('clustersController', [
                 else if (item)
                     $scope.backupItem = angular.copy(item);
                 else
-                    $scope.backupItem = emptyCluster ;
+                    $scope.backupItem = emptyCluster;
 
                 $scope.backupItem = angular.merge({}, blank, $scope.backupItem);
 
-                __original_value = JSON.stringify($cleanup($scope.backupItem));
+                __original_value = $cleanup($scope.backupItem);
 
                 if ($common.getQueryVariable('new'))
                     $state.go('base.configuration.clusters');
@@ -211,202 +297,150 @@ consoleModule.controller('clustersController', [
             $common.confirmUnsavedChanges($scope.backupItem && $scope.ui.inputForm.$dirty, selectItem);
         };
 
-        function prepareNewItem(id) {
-            var newItem = {
-                discovery: {
-                    kind: 'Multicast',
-                    Vm: {addresses: ['127.0.0.1:47500..47510']},
-                    Multicast: {addresses: ['127.0.0.1:47500..47510']}
-                },
-                binaryConfiguration: {
-                    typeConfigurations: [],
-                    compactFooter: true
-                },
-                communication: {
-                    tcpNoDelay: true
-                },
-                connector: {
-                    noDelay: true
-                }
-            };
-
-            newItem = angular.merge({}, blank, newItem);
-
-            newItem.caches = id && _.find($scope.caches, {value: id}) ? [id] : [];
-            newItem.igfss = id && _.find($scope.igfss, {value: id}) ? [id] : [];
-            newItem.space = $scope.spaces[0]._id;
-
-            return newItem;
+        $scope.linkId = () => $scope.backupItem._id ? $scope.backupItem._id : 'create';
+
+        function prepareNewItem(linkId) {
+            return angular.merge({}, blank, {
+                space: $scope.spaces[0]._id,
+                discovery: {kind: 'Multicast', Vm: {addresses: ['127.0.0.1:47500..47510']}, Multicast: {addresses: ['127.0.0.1:47500..47510']}},
+                binaryConfiguration: {typeConfigurations: [], compactFooter: true},
+                communication: {tcpNoDelay: true},
+                connector: {noDelay: true},
+                collision: {kind: 'Noop', JobStealing: {stealingEnabled: true}, PriorityQueue: {starvationPreventionEnabled: true}},
+                failoverSpi: [],
+                logger: {Log4j: { mode: 'Default'}},
+                caches: linkId && _.find($scope.caches, {value: linkId}) ? [linkId] : [],
+                igfss: linkId && _.find($scope.igfss, {value: linkId}) ? [linkId] : []
+            });
         }
 
         // Add new cluster.
-        $scope.createItem = function(id) {
-            $timeout(function () {
-                $common.ensureActivePanel($scope.ui, "general", 'clusterName');
-            });
+        $scope.createItem = function(linkId) {
+            $timeout(() => $common.ensureActivePanel($scope.ui, 'general', 'clusterName'));
 
-            $scope.selectItem(undefined, prepareNewItem(id));
+            $scope.selectItem(null, prepareNewItem(linkId));
         };
 
-        $scope.indexOfCache = function (cacheId) {
-            return _.findIndex($scope.caches, function (cache) {
-                return cache.value === cacheId;
-            });
+        $scope.indexOfCache = function(cacheId) {
+            return _.findIndex($scope.caches, (cache) => cache.value === cacheId);
         };
 
-        // Check cluster logical consistency.
-        function validate(item) {
-            $common.hidePopover();
-
-            if ($common.isEmptyString(item.name))
-                return showPopoverMessage($scope.ui, 'general', 'clusterName', 'Cluster name should not be empty!');
-
-            var form = $scope.ui.inputForm;
-            var errors = form.$error;
-            var errKeys = Object.keys(errors);
-
-            if (errKeys && errKeys.length > 0) {
-                var firstErrorKey = errKeys[0];
-
-                var firstError = errors[firstErrorKey][0];
-                var actualError = firstError.$error[firstErrorKey][0];
-
-                var errNameFull = actualError.$name;
-                var errNameShort = errNameFull;
-
-                if (errNameShort.endsWith('TextInput'))
-                    errNameShort = errNameShort.substring(0, errNameShort.length - 9);
+        function clusterCaches(item) {
+            return _.filter(_.map($scope.caches, (scopeCache) => scopeCache.cache),
+                (cache) => _.includes(item.caches, cache._id));
+        }
 
-                var extractErrorMessage = function (errName) {
-                    try {
-                        return errors[firstErrorKey][0].$errorMessages[errName][firstErrorKey];
-                    }
-                    catch(ignored) {
-                        try {
-                            msg = form[firstError.$name].$errorMessages[errName][firstErrorKey];
-                        }
-                        catch(ignited) {
-                            return false;
-                        }
-                    }
-                };
+        function checkCacheDatasources(item) {
+            const caches = clusterCaches(item);
 
-                var msg = extractErrorMessage(errNameFull) || extractErrorMessage(errNameShort) || 'Invalid value!';
+            const checkRes = $common.checkCachesDataSources(caches);
 
-                return showPopoverMessage($scope.ui, firstError.$name, errNameFull, msg);
+            if (!checkRes.checked) {
+                return showPopoverMessage($scope.ui, 'general', 'caches',
+                    'Found caches "' + checkRes.firstCache.name + '" and "' + checkRes.secondCache.name + '" ' +
+                    'with the same data source bean name "' + checkRes.firstCache.cacheStoreFactory[checkRes.firstCache.cacheStoreFactory.kind].dataSourceBean +
+                    '" and different databases: "' + $common.cacheStoreJdbcDialectsLabel(checkRes.firstDB) + '" in "' + checkRes.firstCache.name + '" and "' +
+                    $common.cacheStoreJdbcDialectsLabel(checkRes.secondDB) + '" in "' + checkRes.secondCache.name + '"', 10000);
             }
 
-            var caches = _.filter(_.map($scope.caches, function (scopeCache) {
-                return scopeCache.cache;
-            }), function (cache) {
-                return _.includes($scope.backupItem.caches, cache._id);
-            });
+            return true;
+        }
 
-            var checkRes = $common.checkCachesDataSources(caches);
+        function checkCacheSQLSchemas(item) {
+            const caches = clusterCaches(item);
+
+            const checkRes = $common.checkCacheSQLSchemas(caches);
 
             if (!checkRes.checked) {
                 return showPopoverMessage($scope.ui, 'general', 'caches',
                     'Found caches "' + checkRes.firstCache.name + '" and "' + checkRes.secondCache.name + '" ' +
-                    'with the same data source bean name "' + checkRes.firstCache.cacheStoreFactory[checkRes.firstCache.cacheStoreFactory.kind].dataSourceBean +
-                    '" and different databases: "' + $common.cacheStoreJdbcDialectsLabel(checkRes.firstDB) + '" in "' + checkRes.firstCache.name + '" and "' +
-                    $common.cacheStoreJdbcDialectsLabel(checkRes.secondDB) + '" in "' + checkRes.secondCache.name + '"', 10000);
+                    'with the same SQL schema name "' + checkRes.firstCache.sqlSchema + '"', 10000);
             }
 
-            var b = item.binaryConfiguration;
+            return true;
+        }
+
+        function checkBinaryConfiguration(item) {
+            const b = item.binaryConfiguration;
 
             if ($common.isDefined(b)) {
                 if (!_.isEmpty(b.typeConfigurations)) {
-                    var sameName = function (t, ix) {
-                        return ix < typeIx && t.typeName === type.typeName;
-                    };
-
-                    for (var typeIx = 0; typeIx < b.typeConfigurations.length; typeIx++) {
-                        var type = b.typeConfigurations[typeIx];
+                    for (let typeIx = 0; typeIx < b.typeConfigurations.length; typeIx++) {
+                        const type = b.typeConfigurations[typeIx];
 
                         if ($common.isEmptyString(type.typeName))
                             return showPopoverMessage($scope.ui, 'binary', 'typeName' + typeIx, 'Type name should be specified!');
 
-                        if (_.find(b.typeConfigurations, sameName))
+                        if (_.find(b.typeConfigurations, (t, ix) => ix < typeIx && t.typeName === type.typeName))
                             return showPopoverMessage($scope.ui, 'binary', 'typeName' + typeIx, 'Type with such name is already specified!');
                     }
                 }
             }
 
-            var c = item.communication;
+            return true;
+        }
+
+        function checkCommunicationConfiguration(item) {
+            const c = item.communication;
 
             if ($common.isDefined(c)) {
                 if ($common.isDefined(c.unacknowledgedMessagesBufferSize)) {
-                    if ($common.isDefined(c.messageQueueLimit))
-                        if (c.unacknowledgedMessagesBufferSize < 5 * c.messageQueueLimit)
-                            return showPopoverMessage($scope.ui, 'communication', 'unacknowledgedMessagesBufferSize', 'Maximum number of stored unacknowledged messages should be at least 5 * message queue limit!');
+                    if ($common.isDefined(c.messageQueueLimit) && c.unacknowledgedMessagesBufferSize < 5 * c.messageQueueLimit)
+                        return showPopoverMessage($scope.ui, 'communication', 'unacknowledgedMessagesBufferSize', 'Maximum number of stored unacknowledged messages should be at least 5 * message queue limit!');
 
-                    if ($common.isDefined(c.ackSendThreshold))
-                        if (c.unacknowledgedMessagesBufferSize < 5 * c.ackSendThreshold)
-                            return showPopoverMessage($scope.ui, 'communication', 'unacknowledgedMessagesBufferSize', 'Maximum number of stored unacknowledged messages should be at least 5 * ack send threshold!');
+                    if ($common.isDefined(c.ackSendThreshold) && c.unacknowledgedMessagesBufferSize < 5 * c.ackSendThreshold)
+                        return showPopoverMessage($scope.ui, 'communication', 'unacknowledgedMessagesBufferSize', 'Maximum number of stored unacknowledged messages should be at least 5 * ack send threshold!');
                 }
 
                 if (c.sharedMemoryPort === 0)
                     return showPopoverMessage($scope.ui, 'communication', 'sharedMemoryPort', 'Shared memory port should be more than "0" or equals to "-1"!');
             }
 
-            var r = item.connector;
-
-            if ($common.isDefined(r)) {
-                if (r.sslEnabled && $common.isEmptyString(r.sslFactory))
-                    return showPopoverMessage($scope.ui, 'connector', 'connectorSslFactory', 'SSL factory should not be empty!');
-            }
+            return true;
+        }
 
-            var d = item.discovery;
+        function checkDiscoveryConfiguration(item) {
+            const d = item.discovery;
 
             if (d) {
-                if ((d.maxAckTimeout != undefined ? d.maxAckTimeout : 600000) < (d.ackTimeout || 5000))
+                if ((_.isNil(d.maxAckTimeout) ? 600000 : d.maxAckTimeout) < (d.ackTimeout || 5000))
                     return showPopoverMessage($scope.ui, 'discovery', 'ackTimeout', 'Acknowledgement timeout should be less than max acknowledgement timeout!');
 
                 if (d.kind === 'Vm' && d.Vm && d.Vm.addresses.length === 0)
                     return showPopoverMessage($scope.ui, 'general', 'addresses', 'Addresses are not specified!');
-
-                if (d.kind === 'S3' && d.S3 && $common.isEmptyString(d.S3.bucketName))
-                    return showPopoverMessage($scope.ui, 'general', 'bucketName', 'Bucket name should not be empty!');
-
-                if (d.kind === 'Cloud' && d.Cloud) {
-                    if ($common.isEmptyString(d.Cloud.identity))
-                        return showPopoverMessage($scope.ui, 'general', 'identity', 'Identity should not be empty!');
-
-                    if ($common.isEmptyString(d.Cloud.provider))
-                        return showPopoverMessage($scope.ui, 'general', 'provider', 'Provider should not be empty!');
-                }
-
-                if (d.kind === 'GoogleStorage' && d.GoogleStorage) {
-                    if ($common.isEmptyString(d.GoogleStorage.projectName))
-                        return showPopoverMessage($scope.ui, 'general', 'projectName', 'Project name should not be empty!');
-
-                    if ($common.isEmptyString(d.GoogleStorage.bucketName))
-                        return showPopoverMessage($scope.ui, 'general', 'bucketName', 'Bucket name should not be empty!');
-
-                    if ($common.isEmptyString(d.GoogleStorage.serviceAccountP12FilePath))
-                        return showPopoverMessage($scope.ui, 'general', 'serviceAccountP12FilePath', 'Private key path should not be empty!');
-
-                    if ($common.isEmptyString(d.GoogleStorage.serviceAccountId))
-                        return showPopoverMessage($scope.ui, 'general', 'serviceAccountId', 'Account ID should not be empty!');
-                }
             }
 
-            var swapKind = item.swapSpaceSpi && item.swapSpaceSpi.kind;
+            return true;
+        }
+
+        function checkSwapConfiguration(item) {
+            const swapKind = item.swapSpaceSpi && item.swapSpaceSpi.kind;
 
             if (swapKind && item.swapSpaceSpi[swapKind]) {
-                var swap = item.swapSpaceSpi[swapKind];
+                const swap = item.swapSpaceSpi[swapKind];
 
-                var sparsity = swap.maximumSparsity;
+                const sparsity = swap.maximumSparsity;
 
                 if ($common.isDefined(sparsity) && (sparsity < 0 || sparsity >= 1))
                     return showPopoverMessage($scope.ui, 'swap', 'maximumSparsity', 'Maximum sparsity should be more or equal 0 and less than 1!');
 
-                var readStripesNumber = swap.readStripesNumber;
+                const readStripesNumber = swap.readStripesNumber;
 
-                if (readStripesNumber && !(readStripesNumber == -1 || (readStripesNumber & (readStripesNumber - 1)) == 0))
+                if (readStripesNumber && !(readStripesNumber === -1 || (readStripesNumber & (readStripesNumber - 1)) === 0))
                     return showPopoverMessage($scope.ui, 'swap', 'readStripesNumber', 'Read stripe size must be positive and power of two!');
             }
 
+            return true;
+        }
+
+        function checkSslConfiguration(item) {
+            const r = item.connector;
+
+            if ($common.isDefined(r)) {
+                if (r.sslEnabled && $common.isEmptyString(r.sslFactory))
+                    return showPopoverMessage($scope.ui, 'connector', 'connectorSslFactory', 'SSL factory should not be empty!');
+            }
+
             if (item.sslEnabled) {
                 if (!$common.isDefined(item.sslContextFactory) || $common.isEmptyString(item.sslContextFactory.keyStoreFilePath))
                     return showPopoverMessage($scope.ui, 'sslConfiguration', 'keyStoreFilePath', 'Key store file should not be empty!');
@@ -415,23 +449,49 @@ consoleModule.controller('clustersController', [
                     return showPopoverMessage($scope.ui, 'sslConfiguration', 'sslConfiguration-title', 'Trust storage file or managers should be configured!');
             }
 
-            if (!swapKind && item.caches) {
-                for (var i = 0; i < item.caches.length; i++) {
-                    var idx = $scope.indexOfCache(item.caches[i]);
+            return true;
+        }
 
-                    if (idx >= 0) {
-                        var cache = $scope.caches[idx];
+        function checkPoolSizes(item) {
+            if (item.rebalanceThreadPoolSize && item.systemThreadPoolSize && item.systemThreadPoolSize <= item.rebalanceThreadPoolSize)
+                return showPopoverMessage($scope.ui, 'pools', 'rebalanceThreadPoolSize', 'Rebalance thread pool size exceed or equals System thread pool size!');
 
-                        if (cache.cache.swapEnabled)
-                            return showPopoverMessage($scope.ui, 'swap', 'swapSpaceSpi',
-                                'Swap space SPI is not configured, but cache "' + cache.label + '" configured to use swap!');
-                    }
-                }
-            }
+            return true;
+        }
 
-            if (item.rebalanceThreadPoolSize && item.systemThreadPoolSize && item.systemThreadPoolSize <= item.rebalanceThreadPoolSize)
-                return showPopoverMessage($scope.ui, 'pools', 'rebalanceThreadPoolSize',
-                    'Rebalance thread pool size exceed or equals System thread pool size!');
+        // Check cluster logical consistency.
+        function validate(item) {
+            $common.hidePopover();
+
+            if ($common.isEmptyString(item.name))
+                return showPopoverMessage($scope.ui, 'general', 'clusterName', 'Cluster name should not be empty!');
+
+            if (!$common.checkFieldValidators($scope.ui))
+                return false;
+
+            if (!checkCacheSQLSchemas(item))
+                return false;
+
+            if (!checkCacheDatasources(item))
+                return false;
+
+            if (!checkBinaryConfiguration(item))
+                return false;
+
+            if (!checkCommunicationConfiguration(item))
+                return false;
+
+            if (!checkDiscoveryConfiguration(item))
+                return false;
+
+            if (!checkSwapConfiguration(item))
+                return false;
+
+            if (!checkSslConfiguration(item))
+                return false;
+
+            if (!checkPoolSizes(item))
+                return false;
 
             return true;
         }
@@ -439,12 +499,12 @@ consoleModule.controller('clustersController', [
         // Save cluster in database.
         function save(item) {
             $http.post('/api/v1/configuration/clusters/save', item)
-                .success(function (_id) {
+                .success(function(_id) {
                     item.label = _clusterLbl(item);
 
                     $scope.ui.inputForm.$setPristine();
 
-                    var idx = _.findIndex($scope.clusters, (cluster) => cluster._id === _id);
+                    const idx = _.findIndex($scope.clusters, (cluster) => cluster._id === _id);
 
                     if (idx >= 0)
                         angular.merge($scope.clusters[idx], item);
@@ -453,6 +513,20 @@ consoleModule.controller('clustersController', [
                         $scope.clusters.push(item);
                     }
 
+                    _.forEach($scope.caches, (cache) => {
+                        if (_.includes(item.caches, cache.value))
+                            cache.cache.clusters = _.union(cache.cache.clusters, [_id]);
+                        else
+                            _.remove(cache.cache.clusters, (id) => id === _id);
+                    });
+
+                    _.forEach($scope.igfss, (igfs) => {
+                        if (_.includes(item.igfss, igfs.value))
+                            igfs.igfs.clusters = _.union(igfs.igfs.clusters, [_id]);
+                        else
+                            _.remove(igfs.igfs.clusters, (id) => id === _id);
+                    });
+
                     $scope.selectItem(item);
 
                     $common.showInfo('Cluster "' + item.name + '" saved.');
@@ -461,10 +535,10 @@ consoleModule.controller('clustersController', [
         }
 
         // Save cluster.
-        $scope.saveItem = function () {
-            var item = $scope.backupItem;
+        $scope.saveItem = function() {
+            const item = $scope.backupItem;
 
-            var swapSpi = $common.autoClusterSwapSpiConfiguration(item, clusterCaches(item));
+            const swapSpi = $common.autoClusterSwapSpiConfiguration(item, clusterCaches(item));
 
             if (swapSpi)
                 angular.extend(item, swapSpi);
@@ -474,16 +548,14 @@ consoleModule.controller('clustersController', [
         };
 
         function _clusterNames() {
-            return _.map($scope.clusters, function (cluster) {
-                return cluster.name;
-            });
+            return _.map($scope.clusters, (cluster) => cluster.name);
         }
 
         // Clone cluster with new name.
-        $scope.cloneItem = function () {
+        $scope.cloneItem = function() {
             if (validate($scope.backupItem)) {
-                $clone.confirm($scope.backupItem.name, _clusterNames()).then(function (newName) {
-                    var item = angular.copy($scope.backupItem);
+                $clone.confirm($scope.backupItem.name, _clusterNames()).then(function(newName) {
+                    const item = angular.copy($scope.backupItem);
 
                     delete item._id;
                     item.name = newName;
@@ -494,51 +566,58 @@ consoleModule.controller('clustersController', [
         };
 
         // Remove cluster from db.
-        $scope.removeItem = function () {
-            var selectedItem = $scope.selectedItem;
+        $scope.removeItem = function() {
+            const selectedItem = $scope.selectedItem;
 
             $confirm.confirm('Are you sure you want to remove cluster: "' + selectedItem.name + '"?')
-                .then(function () {
-                    var _id = selectedItem._id;
+                .then(function() {
+                    const _id = selectedItem._id;
 
-                    $http.post('/api/v1/configuration/clusters/remove', {_id: _id})
-                        .success(function () {
+                    $http.post('/api/v1/configuration/clusters/remove', {_id})
+                        .success(function() {
                             $common.showInfo('Cluster has been removed: ' + selectedItem.name);
 
-                            var clusters = $scope.clusters;
+                            const clusters = $scope.clusters;
 
-                            var idx = _.findIndex(clusters, function (cluster) {
-                                return cluster._id === _id;
-                            });
+                            const idx = _.findIndex(clusters, (cluster) => cluster._id === _id);
 
                             if (idx >= 0) {
                                 clusters.splice(idx, 1);
 
                                 if (clusters.length > 0)
                                     $scope.selectItem(clusters[0]);
-                                else
+                                else {
                                     $scope.backupItem = emptyCluster;
+                                    $scope.ui.inputForm.$setPristine();
+                                }
+
+                                _.forEach($scope.caches, (cache) => _.remove(cache.cache.clusters, (id) => id === _id));
+                                _.forEach($scope.igfss, (igfs) => _.remove(igfs.igfs.clusters, (id) => id === _id));
                             }
                         })
-                        .error(function (errMsg) {
+                        .error(function(errMsg) {
                             $common.showError(errMsg);
                         });
                 });
         };
 
         // Remove all clusters from db.
-        $scope.removeAllItems = function () {
+        $scope.removeAllItems = function() {
             $confirm.confirm('Are you sure you want to remove all clusters?')
-                .then(function () {
+                .then(function() {
                     $http.post('/api/v1/configuration/clusters/remove/all')
-                        .success(function () {
+                        .success(function() {
                             $common.showInfo('All clusters have been removed');
 
                             $scope.clusters = [];
+
+                            _.forEach($scope.caches, (cache) => cache.cache.clusters = []);
+                            _.forEach($scope.igfss, (igfs) => igfs.igfs.clusters = []);
+
                             $scope.backupItem = emptyCluster;
                             $scope.ui.inputForm.$setPristine();
                         })
-                        .error(function (errMsg) {
+                        .error(function(errMsg) {
                             $common.showError(errMsg);
                         });
                 });


[02/22] ignite git commit: IGNITE-3351: Hadoop: Fixed HadoopClassLoader leak into IGFS thread pool.

Posted by ak...@apache.org.
IGNITE-3351: Hadoop: Fixed HadoopClassLoader leak into IGFS thread pool.


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/8e6473a0
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/8e6473a0
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/8e6473a0

Branch: refs/heads/master
Commit: 8e6473a0488e4cf32bde84b2b0b4561ad5c4f157
Parents: bb7a672
Author: vozerov-gridgain <vo...@gridgain.com>
Authored: Wed Jun 22 12:18:38 2016 +0300
Committer: vozerov-gridgain <vo...@gridgain.com>
Committed: Wed Jun 22 16:54:28 2016 +0300

----------------------------------------------------------------------
 .../core/src/main/java/org/apache/ignite/internal/IgnitionEx.java | 3 +++
 1 file changed, 3 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/8e6473a0/modules/core/src/main/java/org/apache/ignite/internal/IgnitionEx.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/IgnitionEx.java b/modules/core/src/main/java/org/apache/ignite/internal/IgnitionEx.java
index 24249da..8062e6b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/IgnitionEx.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/IgnitionEx.java
@@ -1689,6 +1689,9 @@ public class IgnitionEx {
                 0,
                 new LinkedBlockingQueue<Runnable>());
 
+            // Pre-start all threads to avoid HadoopClassLoader leaks.
+            ((ThreadPoolExecutor)igfsExecSvc).prestartAllCoreThreads();
+
             // Note that we do not pre-start threads here as this pool may not be needed.
             callbackExecSvc = new IgniteStripedThreadPoolExecutor(
                 cfg.getAsyncCallbackPoolSize(),


[05/22] ignite git commit: ignite-3212 Remove tx from map if 'onStarted' failed.

Posted by ak...@apache.org.
ignite-3212 Remove tx from map if 'onStarted' failed.


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/a70ff4c4
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/a70ff4c4
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/a70ff4c4

Branch: refs/heads/master
Commit: a70ff4c40d9f149510b148467b1dd6ce3ad5db0d
Parents: 5e91594
Author: sboikov <sb...@gridgain.com>
Authored: Thu Jun 23 12:13:05 2016 +0300
Committer: sboikov <sb...@gridgain.com>
Committed: Thu Jun 23 12:13:05 2016 +0300

----------------------------------------------------------------------
 .../internal/processors/cache/transactions/IgniteTxManager.java  | 4 ++++
 1 file changed, 4 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/a70ff4c4/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxManager.java
index e8d20b6..63c9919 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxManager.java
@@ -568,6 +568,10 @@ public class IgniteTxManager extends GridCacheSharedManagerAdapter {
             ", tx=" + tx + ']';
 
         if (isCompleted(tx)) {
+            ConcurrentMap<GridCacheVersion, IgniteInternalTx> txIdMap = transactionMap(tx);
+
+            txIdMap.remove(tx.xidVersion(), tx);
+
             if (log.isDebugEnabled())
                 log.debug("Attempt to start a completed transaction (will ignore): " + tx);
 


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

Posted by ak...@apache.org.
Ignite Web Console beta2.


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/541e17d0
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/541e17d0
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/541e17d0

Branch: refs/heads/master
Commit: 541e17d07520e5dac7daec1b188abe6bd12d6aad
Parents: e7ebe0a
Author: Alexey Kuznetsov <ak...@apache.org>
Authored: Mon Jun 27 10:17:45 2016 +0700
Committer: Alexey Kuznetsov <ak...@apache.org>
Committed: Mon Jun 27 10:17:46 2016 +0700

----------------------------------------------------------------------
 .../visor/compute/VisorGatewayTask.java         |    3 +-
 modules/schema-import/README.txt                |   12 +-
 modules/web-agent/README.txt                    |   25 +-
 .../web-agent/assembly/release-web-agent.xml    |    2 -
 modules/web-agent/bin/ignite-web-agent.bat      |    2 +-
 modules/web-agent/bin/ignite-web-agent.sh       |    2 +-
 modules/web-agent/pom.xml                       |   22 +-
 .../console/agent/AgentConfiguration.java       |   53 +-
 .../ignite/console/agent/AgentLauncher.java     |   21 +-
 .../ignite/console/demo/AgentClusterDemo.java   |   82 +-
 modules/web-console/README.txt                  |    4 +-
 modules/web-console/src/main/js/.eslintrc       |    7 +-
 .../src/main/js/app/data/getting-started.json   |    2 +-
 .../src/main/js/app/data/pom-dependencies.json  |   20 +
 .../directives/ui-ace-docker/ui-ace-docker.jade |    4 +-
 .../ui-ace-java/ui-ace-java.directive.js        |    4 +-
 .../directives/ui-ace-pojos/ui-ace-pojos.jade   |    4 +-
 .../ui-ace-xml/ui-ace-xml.directive.js          |    4 +-
 .../src/main/js/app/helpers/jade/mixins.jade    |   17 +-
 modules/web-console/src/main/js/app/index.js    |   18 +-
 .../src/main/js/app/modules/Demo/Demo.module.js |   14 +-
 .../QueryNotebooks/QueryNotebooks.provider.js   |  115 -
 .../main/js/app/modules/agent/agent.module.js   |  323 +++
 .../configuration/generator/Pom.service.js      |   46 +-
 .../form/field/input/datalist.directive.js      |    6 +-
 .../modules/form/field/input/text.directive.js  |    9 +-
 .../js/app/modules/form/field/input/text.jade   |    2 +-
 .../app/modules/form/group/group.directive.js   |    6 +-
 .../form/validator/java-keywords.directive.js   |    9 +-
 .../src/main/js/app/modules/loading/loading.css |    2 +-
 .../query-notebooks/query-notebooks.module.js   |  115 +
 .../app/modules/states/configuration.state.js   |   32 +-
 .../states/configuration/caches/general.jade    |    2 +-
 .../states/configuration/caches/store.jade      |   37 +-
 .../clusters/attributes.directive.js            |   27 +
 .../configuration/clusters/attributes.jade      |   58 +
 .../states/configuration/clusters/binary.jade   |   12 +-
 .../clusters/collision.directive.js             |   27 +
 .../configuration/clusters/collision.jade       |   60 +
 .../clusters/collision/custom.directive.js      |   27 +
 .../clusters/collision/custom.jade              |   24 +
 .../clusters/collision/fifo-queue.directive.js  |   27 +
 .../clusters/collision/fifo-queue.jade          |   28 +
 .../collision/job-stealing.directive.js         |   27 +
 .../clusters/collision/job-stealing.jade        |   64 +
 .../collision/priority-queue.directive.js       |   27 +
 .../clusters/collision/priority-queue.jade      |   43 +
 .../configuration/clusters/communication.jade   |   30 +-
 .../configuration/clusters/deployment.jade      |   31 +-
 .../configuration/clusters/discovery.jade       |    4 +-
 .../clusters/failover.directive.js              |   27 +
 .../states/configuration/clusters/failover.jade |   82 +
 .../states/configuration/clusters/general.jade  |   20 +-
 .../clusters/general/discovery/cloud.jade       |    6 +-
 .../clusters/general/discovery/google.jade      |   11 +-
 .../clusters/general/discovery/s3.jade          |    4 +-
 .../clusters/general/discovery/zookeeper.jade   |   21 +-
 .../bounded-exponential-backoff.jade            |    6 +-
 .../discovery/zookeeper/retrypolicy/custom.jade |    6 +-
 .../retrypolicy/exponential-backoff.jade        |    6 +-
 .../zookeeper/retrypolicy/forever.jade          |    2 +-
 .../zookeeper/retrypolicy/n-times.jade          |    2 +-
 .../zookeeper/retrypolicy/one-time.jade         |    2 +-
 .../zookeeper/retrypolicy/until-elapsed.jade    |    4 +-
 .../states/configuration/clusters/igfs.jade     |    4 +-
 .../configuration/clusters/logger.directive.js  |   27 +
 .../states/configuration/clusters/logger.jade   |   65 +
 .../clusters/logger/custom.directive.js         |   27 +
 .../configuration/clusters/logger/custom.jade   |   24 +
 .../clusters/logger/log4j.directive.js          |   27 +
 .../configuration/clusters/logger/log4j.jade    |   49 +
 .../clusters/logger/log4j2.directive.js         |   27 +
 .../configuration/clusters/logger/log4j2.jade   |   38 +
 .../states/configuration/domains/query.jade     |    4 +-
 .../states/configuration/domains/store.jade     |    2 +-
 .../modules/states/configuration/igfs/misc.jade |    2 +-
 .../main/js/app/modules/user/Auth.service.js    |   19 +-
 .../js/app/services/AgentMonitor.service.js     |  337 ---
 .../src/main/js/app/services/cleanup.service.js |    4 +-
 .../src/main/js/build/system.config.js          |   55 +-
 .../src/main/js/controllers/admin-controller.js |   14 +-
 .../main/js/controllers/caches-controller.js    |  336 ++-
 .../main/js/controllers/clusters-controller.js  |  537 +++--
 .../src/main/js/controllers/common-module.js    | 1052 +++++----
 .../main/js/controllers/domains-controller.js   |  847 ++++---
 .../src/main/js/controllers/igfs-controller.js  |  212 +-
 .../main/js/controllers/profile-controller.js   |   60 +-
 .../src/main/js/controllers/sql-controller.js   | 2173 +++++++++---------
 .../src/main/js/generator/generator-common.js   |  151 +-
 .../src/main/js/generator/generator-java.js     |  998 ++++----
 .../src/main/js/generator/generator-optional.js |    2 +-
 .../main/js/generator/generator-properties.js   |   25 +-
 .../src/main/js/generator/generator-readme.js   |    6 +-
 .../src/main/js/generator/generator-xml.js      |  430 +++-
 .../main/js/gulpfile.babel.js/tasks/eslint.js   |    3 +
 .../src/main/js/public/images/cluster.png       |  Bin 29670 -> 29376 bytes
 .../src/main/js/public/images/query-table.png   |  Bin 42253 -> 29189 bytes
 .../src/main/js/public/stylesheets/style.scss   |   28 +
 modules/web-console/src/main/js/serve/agent.js  |  222 +-
 .../web-console/src/main/js/serve/browser.js    |  120 +-
 .../web-console/src/main/js/serve/configure.js  |    1 +
 modules/web-console/src/main/js/serve/mongo.js  |   58 +-
 .../src/main/js/serve/routes/agent.js           |    3 +-
 .../src/main/js/serve/routes/caches.js          |    4 +-
 .../src/main/js/serve/routes/clusters.js        |    8 +-
 .../src/main/js/serve/routes/igfs.js            |    2 +-
 .../src/main/js/serve/routes/profile.js         |   11 +-
 .../src/main/js/serve/routes/public.js          |   35 +-
 .../main/js/views/configuration/clusters.jade   |    4 +
 .../js/views/configuration/domains-import.jade  |  325 +--
 .../src/main/js/views/settings/profile.jade     |   14 +-
 .../web-console/src/main/js/views/signin.jade   |   26 +-
 .../src/main/js/views/sql/cache-metadata.jade   |    2 +-
 .../web-console/src/main/js/views/sql/sql.jade  |  205 +-
 114 files changed, 6087 insertions(+), 4279 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/core/src/main/java/org/apache/ignite/internal/visor/compute/VisorGatewayTask.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/compute/VisorGatewayTask.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/compute/VisorGatewayTask.java
index c59d299..f1b22ff 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/visor/compute/VisorGatewayTask.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/compute/VisorGatewayTask.java
@@ -355,7 +355,8 @@ public class VisorGatewayTask implements ComputeTask<Object[], Object> {
                 }
             }
 
-            return ignite.compute().execute(taskName, new VisorTaskArgument<>(nids, jobArgs, false));
+            return ignite.compute(ignite.cluster().forNodeIds(nids))
+                .execute(taskName, new VisorTaskArgument<>(nids, jobArgs, false));
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/schema-import/README.txt
----------------------------------------------------------------------
diff --git a/modules/schema-import/README.txt b/modules/schema-import/README.txt
index d4f2dbf..31a8ff0 100644
--- a/modules/schema-import/README.txt
+++ b/modules/schema-import/README.txt
@@ -19,12 +19,12 @@ For example you may use the following script for create sample type 'Person' in
 
 create table PERSON(id integer not null PRIMARY KEY, first_name varchar(50), last_name varchar(50), salary double);
 
-insert into PERSON(id, first_name, first_name, salary) values(1, 'Johannes', 'Kepler', 1000);
-insert into PERSON(id, first_name, first_name, salary) values(2, 'Galileo', 'Galilei', 1200);
-insert into PERSON(id, first_name, first_name, salary) values(3, 'Henry', 'More', 1150);
-insert into PERSON(id, first_name, first_name, salary) values(4, 'Polish', 'Brethren', 2000);
-insert into PERSON(id, first_name, first_name, salary) values(5, 'Robert', 'Boyle', 2500);
-insert into PERSON(id, first_name, first_name, salary) values(6, 'Isaac', 'Newton', 1300);
+insert into PERSON(id, first_name, last_name, salary) values(1, 'Johannes', 'Kepler', 1000);
+insert into PERSON(id, first_name, last_name, salary) values(2, 'Galileo', 'Galilei', 1200);
+insert into PERSON(id, first_name, last_name, salary) values(3, 'Henry', 'More', 1150);
+insert into PERSON(id, first_name, last_name, salary) values(4, 'Polish', 'Brethren', 2000);
+insert into PERSON(id, first_name, last_name, salary) values(5, 'Robert', 'Boyle', 2500);
+insert into PERSON(id, first_name, last_name, salary) values(6, 'Isaac', 'Newton', 1300);
 
 The Ignite Schema Import utility generates the following artifacts:
  # Java POJO key and value classes (enter "org.apache.ignite.schema" package name before generation).

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-agent/README.txt
----------------------------------------------------------------------
diff --git a/modules/web-agent/README.txt b/modules/web-agent/README.txt
index 0d8d4ac..c6e625b 100644
--- a/modules/web-agent/README.txt
+++ b/modules/web-agent/README.txt
@@ -17,22 +17,23 @@ Configuration file:
   Should be a file with simple line-oriented format as described here: http://docs.oracle.com/javase/7/docs/api/java/util/Properties.html#load(java.io.Reader)
 
   Available entries names:
-    token
+    tokens
     server-uri
     node-uri
     driver-folder
 
   Example configuration file:
-    token=1a2b3c4d5f
-    serverURI=http://console.example.com:3001
+    tokens=1a2b3c4d5f,2j1s134d12
+    serverURI=https://console.example.com:3001
 
-Security token:
-  1) By default token will be included into downloaded agent zip.
-  2) You can get/change token in your profile.
+Security tokens:
+  1) By default security token of current user will be included into "default.properties" inside downloaded "ignite-web-agent-x.x.x.zip".
+  2) One can get/reset token in Web Console profile (https://<your_console_address>/settings/profile).
+  3) One may specify several comma separated tokens using configuration file or command line arguments of web agent.
 
 Ignite Web agent requirements:
-  1) Ignite node should be started with REST server (move ignite-rest-http folder from lib/optional/ to lib/).
-  2) Pass Ignite node REST server URI to agent.
+  1) In order to communicate with web agent Ignite node should be started with REST server (move ignite-rest-http folder from lib/optional/ to lib/).
+  2) Configure web agent serverURI property by Ignite node REST server URI.
 
 Options:
   -h, --help
@@ -47,15 +48,15 @@ Options:
   -s, --server-uri
      URI for connect to Ignite Web Console via web-socket protocol, default
      value: http://localhost:3001
-  -t, --token
-     User's security token
+  -t, --tokens
+     User's security tokens
 
 How to build:
   To build from sources run following command in Ignite project root folder:
   mvn clean package -pl :ignite-web-agent -am -P web-console -DskipTests=true
 
 Demo of Ignite Web Agent:
- In order to simplify evaluation demo mode was implemented. To start demo, you need to to click button "Start demo".
+ In order to simplify evaluation demo mode was implemented. To start demo, you need to click button "Start demo".
  New tab will be open with prepared demo data.
 
  1) Demo for import domain model from database.
@@ -73,7 +74,7 @@ Demo of Ignite Web Agent:
      In this mode internal Ignite node will be started. Cache created and populated with data.
        2.1) Click "SQL" in Ignite Web Console top menu.
        2.2) "Demo" notebook with preconfigured queries will be opened.
-       2.3) You can also execute any SQL queries for tables: "Country, Department, Employee", "Parking, Car".
+       2.3) You can also execute any SQL queries for tables: "Country, Department, Employee, Parking, Car".
 
  For example:
    2.4) Enter SQL statement:

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-agent/assembly/release-web-agent.xml
----------------------------------------------------------------------
diff --git a/modules/web-agent/assembly/release-web-agent.xml b/modules/web-agent/assembly/release-web-agent.xml
index eb7da95..aa85b59 100644
--- a/modules/web-agent/assembly/release-web-agent.xml
+++ b/modules/web-agent/assembly/release-web-agent.xml
@@ -42,7 +42,6 @@
         <fileSet>
             <directory>${basedir}/bin</directory>
             <outputDirectory>/</outputDirectory>
-            <filtered>true</filtered>
             <includes>
                 <include>**/*.bat</include>
             </includes>
@@ -50,7 +49,6 @@
         <fileSet>
             <directory>${basedir}/bin</directory>
             <outputDirectory>/</outputDirectory>
-            <filtered>true</filtered>
             <fileMode>0755</fileMode>
             <includes>
                 <include>**/*.sh</include>

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-agent/bin/ignite-web-agent.bat
----------------------------------------------------------------------
diff --git a/modules/web-agent/bin/ignite-web-agent.bat b/modules/web-agent/bin/ignite-web-agent.bat
index 5b3f24c..f16eb35 100644
--- a/modules/web-agent/bin/ignite-web-agent.bat
+++ b/modules/web-agent/bin/ignite-web-agent.bat
@@ -55,7 +55,7 @@ goto error_finish
 ::
 if "%JVM_OPTS%" == "" set JVM_OPTS=-Xms1g -Xmx1g -server -XX:+AggressiveOpts -XX:MaxPermSize=256m
 
-"%JAVA_HOME%\bin\java.exe" %JVM_OPTS% -cp ignite-web-agent-${version}.jar org.apache.ignite.console.agent.AgentLauncher  %*
+"%JAVA_HOME%\bin\java.exe" %JVM_OPTS% -cp "*" org.apache.ignite.console.agent.AgentLauncher  %*
 
 set JAVA_ERRORLEVEL=%ERRORLEVEL%
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-agent/bin/ignite-web-agent.sh
----------------------------------------------------------------------
diff --git a/modules/web-agent/bin/ignite-web-agent.sh b/modules/web-agent/bin/ignite-web-agent.sh
index 6d0a1b5..3f2c2bc 100644
--- a/modules/web-agent/bin/ignite-web-agent.sh
+++ b/modules/web-agent/bin/ignite-web-agent.sh
@@ -84,4 +84,4 @@ if [ -z "$JVM_OPTS" ] ; then
     JVM_OPTS="-Xms1g -Xmx1g -server -XX:+AggressiveOpts -XX:MaxPermSize=256m"
 fi
 
-"$JAVA" ${JVM_OPTS} -cp ignite-web-agent-${version}.jar org.apache.ignite.console.agent.AgentLauncher "$@"
+"$JAVA" ${JVM_OPTS} -cp "*" org.apache.ignite.console.agent.AgentLauncher "$@"

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-agent/pom.xml
----------------------------------------------------------------------
diff --git a/modules/web-agent/pom.xml b/modules/web-agent/pom.xml
index cb55319..d87084f 100644
--- a/modules/web-agent/pom.xml
+++ b/modules/web-agent/pom.xml
@@ -47,7 +47,7 @@
         <dependency>
             <groupId>com.fasterxml.jackson.datatype</groupId>
             <artifactId>jackson-datatype-json-org</artifactId>
-            <version>2.7.1</version>
+            <version>${jackson2.version}</version>
         </dependency>
 
         <dependency>
@@ -88,6 +88,26 @@
 
         <dependency>
             <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-spring</artifactId>
+            <version>${project.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-aop</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-tx</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-jdbc</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
             <artifactId>ignite-log4j</artifactId>
             <version>${project.version}</version>
         </dependency>

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-agent/src/main/java/org/apache/ignite/console/agent/AgentConfiguration.java
----------------------------------------------------------------------
diff --git a/modules/web-agent/src/main/java/org/apache/ignite/console/agent/AgentConfiguration.java b/modules/web-agent/src/main/java/org/apache/ignite/console/agent/AgentConfiguration.java
index ffd30a5..d4787cc 100644
--- a/modules/web-agent/src/main/java/org/apache/ignite/console/agent/AgentConfiguration.java
+++ b/modules/web-agent/src/main/java/org/apache/ignite/console/agent/AgentConfiguration.java
@@ -23,6 +23,8 @@ import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.net.URL;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Properties;
 
 /**
@@ -45,11 +47,13 @@ public class AgentConfiguration {
     private static final String DFLT_NODE_URI = "http://localhost:8080";
 
     /** */
-    @Parameter(names = {"-t", "--token"}, description = "User's security token used to establish connection to Ignite Console.")
-    private String tok;
+    @Parameter(names = {"-t", "--tokens"},
+        description = "User's tokens separated by comma used to connect to Ignite Console.")
+    private List<String> tokens;
 
     /** */
-    @Parameter(names = {"-s", "--server-uri"}, description = "URI for connect to Ignite Console via web-socket protocol" +
+    @Parameter(names = {"-s", "--server-uri"},
+        description = "URI for connect to Ignite Console via web-socket protocol" +
         "           " +
         "      Default value: " + DFLT_SERVER_URI)
     private String srvUri;
@@ -80,17 +84,17 @@ public class AgentConfiguration {
     private Boolean help;
 
     /**
-     * @return Token.
+     * @return Tokens.
      */
-    public String token() {
-        return tok;
+    public List<String> tokens() {
+        return tokens;
     }
 
     /**
-     * @param tok Token.
+     * @param tokens Tokens.
      */
-    public void token(String tok) {
-        this.tok = tok;
+    public void tokens(List<String> tokens) {
+        this.tokens = tokens;
     }
 
     /**
@@ -173,10 +177,10 @@ public class AgentConfiguration {
             props.load(reader);
         }
 
-        String val = (String)props.remove("token");
+        String val = (String)props.remove("tokens");
 
         if (val != null)
-            token(val);
+            tokens(Arrays.asList(val.split(",")));
 
         val = (String)props.remove("server-uri");
 
@@ -198,8 +202,8 @@ public class AgentConfiguration {
      * @param cmd Command.
      */
     public void merge(AgentConfiguration cmd) {
-        if (tok == null)
-            token(cmd.token());
+        if (tokens == null)
+            tokens(cmd.tokens());
 
         if (srvUri == null)
             serverUri(cmd.serverUri());
@@ -221,16 +225,25 @@ public class AgentConfiguration {
     @Override public String toString() {
         StringBuilder sb = new StringBuilder();
 
-        if (tok != null && tok.length() > 0) {
-            sb.append("User's security token         : ");
+        if (tokens != null && tokens.size() > 0) {
+            sb.append("User's security tokens        : ");
 
-            if (tok.length() > 4) {
-                sb.append(new String(new char[tok.length() - 4]).replace('\0', '*'));
+            boolean first = true;
 
-                sb.append(tok.substring(tok.length() - 4));
+            for (String tok : tokens) {
+                if (first)
+                    first = false;
+                else
+                    sb.append(",");
+
+                if (tok.length() > 4) {
+                    sb.append(new String(new char[tok.length() - 4]).replace('\0', '*'));
+
+                    sb.append(tok.substring(tok.length() - 4));
+                }
+                else
+                    sb.append(new String(new char[tok.length()]).replace('\0', '*'));
             }
-            else
-                sb.append(new String(new char[tok.length()]).replace('\0', '*'));
 
             sb.append('\n');
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-agent/src/main/java/org/apache/ignite/console/agent/AgentLauncher.java
----------------------------------------------------------------------
diff --git a/modules/web-agent/src/main/java/org/apache/ignite/console/agent/AgentLauncher.java b/modules/web-agent/src/main/java/org/apache/ignite/console/agent/AgentLauncher.java
index 4b99a92..810fad4 100644
--- a/modules/web-agent/src/main/java/org/apache/ignite/console/agent/AgentLauncher.java
+++ b/modules/web-agent/src/main/java/org/apache/ignite/console/agent/AgentLauncher.java
@@ -26,7 +26,6 @@ import io.socket.emitter.Emitter;
 import java.io.File;
 import java.io.IOException;
 import java.net.ConnectException;
-import java.net.SocketException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
@@ -73,6 +72,9 @@ public class AgentLauncher {
     private static final String EVENT_SCHEMA_IMPORT_METADATA = "schemaImport:metadata";
 
     /** */
+    private static final String EVENT_AGENT_WARNING = "agent:warning";
+
+    /** */
     private static final String EVENT_AGENT_CLOSE = "agent:close";
 
     /** */
@@ -138,7 +140,7 @@ public class AgentLauncher {
      */
     private static final Emitter.Listener onDisconnect = new Emitter.Listener() {
         @Override public void call(Object... args) {
-            log.error(String.format("Connection closed: %s.", args[0]));
+            log.error(String.format("Connection closed: %s.", args));
         }
     };
 
@@ -198,7 +200,7 @@ public class AgentLauncher {
         System.out.println(cfg);
         System.out.println();
 
-        if (cfg.token() == null) {
+        if (cfg.tokens() == null) {
             String webHost;
 
             try {
@@ -213,9 +215,9 @@ public class AgentLauncher {
             System.out.println("Security token is required to establish connection to the web console.");
             System.out.println(String.format("It is available on the Profile page: https://%s/profile", webHost));
 
-            System.out.print("Enter security token: ");
+            System.out.print("Enter security tokens separated by comma: ");
 
-            cfg.token(System.console().readLine().trim());
+            cfg.tokens(Arrays.asList(System.console().readLine().trim().split(",")));
         }
 
         final RestHandler restHnd = new RestHandler(cfg);
@@ -258,7 +260,7 @@ public class AgentLauncher {
                         JSONObject authMsg = new JSONObject();
 
                         try {
-                            authMsg.put("token", cfg.token());
+                            authMsg.put("tokens", cfg.tokens());
 
                             String clsName = AgentLauncher.class.getSimpleName() + ".class";
 
@@ -280,7 +282,7 @@ public class AgentLauncher {
                                 @Override public void call(Object... args) {
                                     // Authentication failed if response contains args.
                                     if (args != null && args.length > 0) {
-                                        onDisconnect.call("Authentication failed: " + args[0]);
+                                        onDisconnect.call(args);
 
                                         System.exit(1);
                                     }
@@ -312,6 +314,11 @@ public class AgentLauncher {
                     .on(EVENT_SCHEMA_IMPORT_METADATA, dbHnd.metadataListener())
                     .on(EVENT_ERROR, onError)
                     .on(EVENT_DISCONNECT, onDisconnect)
+                    .on(EVENT_AGENT_WARNING, new Emitter.Listener() {
+                        @Override public void call(Object... args) {
+                            log.warn(args[0]);
+                        }
+                    })
                     .on(EVENT_AGENT_CLOSE, new Emitter.Listener() {
                         @Override public void call(Object... args) {
                             onDisconnect.call(args);

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-agent/src/main/java/org/apache/ignite/console/demo/AgentClusterDemo.java
----------------------------------------------------------------------
diff --git a/modules/web-agent/src/main/java/org/apache/ignite/console/demo/AgentClusterDemo.java b/modules/web-agent/src/main/java/org/apache/ignite/console/demo/AgentClusterDemo.java
index d09e5c7..bf0903a 100644
--- a/modules/web-agent/src/main/java/org/apache/ignite/console/demo/AgentClusterDemo.java
+++ b/modules/web-agent/src/main/java/org/apache/ignite/console/demo/AgentClusterDemo.java
@@ -18,10 +18,13 @@
 package org.apache.ignite.console.demo;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.Random;
+import java.util.Set;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
@@ -55,6 +58,8 @@ import org.apache.ignite.transactions.Transaction;
 import org.apache.log4j.Logger;
 
 import static org.apache.ignite.IgniteSystemProperties.IGNITE_ATOMIC_CACHE_DELETE_HISTORY_SIZE;
+import static org.apache.ignite.IgniteSystemProperties.IGNITE_PERFORMANCE_SUGGESTIONS_DISABLED;
+import static org.apache.ignite.IgniteSystemProperties.IGNITE_UPDATE_NOTIFIER;
 import static org.apache.ignite.IgniteSystemProperties.IGNITE_JETTY_PORT;
 import static org.apache.ignite.IgniteSystemProperties.IGNITE_NO_ASCII;
 import static org.apache.ignite.events.EventType.EVTS_DISCOVERY;
@@ -80,16 +85,24 @@ public class AgentClusterDemo {
 
     /** */
     private static final String COUNTRY_CACHE_NAME = "CountryCache";
+
     /** */
     private static final String DEPARTMENT_CACHE_NAME = "DepartmentCache";
+
     /** */
     private static final String EMPLOYEE_CACHE_NAME = "EmployeeCache";
+
     /** */
     private static final String PARKING_CACHE_NAME = "ParkingCache";
+
     /** */
     private static final String CAR_CACHE_NAME = "CarCache";
 
     /** */
+    private static final Set<String> DEMO_CACHES = new HashSet<>(Arrays.asList(COUNTRY_CACHE_NAME,
+        DEPARTMENT_CACHE_NAME, EMPLOYEE_CACHE_NAME, PARKING_CACHE_NAME, CAR_CACHE_NAME));
+
+    /** */
     private static final Random rnd = new Random();
 
     /** Countries count. */
@@ -320,9 +333,7 @@ public class AgentClusterDemo {
         IgniteConfiguration cfg = new IgniteConfiguration();
 
         cfg.setGridName((client ? "demo-server-" : "demo-client-") + gridIdx);
-
         cfg.setLocalHost("127.0.0.1");
-
         cfg.setIncludeEventTypes(EVTS_DISCOVERY);
 
         TcpDiscoveryVmIpFinder ipFinder = new TcpDiscoveryVmIpFinder();
@@ -333,7 +344,6 @@ public class AgentClusterDemo {
         TcpDiscoverySpi discoSpi = new TcpDiscoverySpi();
 
         discoSpi.setLocalPort(60900);
-
         discoSpi.setIpFinder(ipFinder);
 
         cfg.setDiscoverySpi(discoSpi);
@@ -341,21 +351,17 @@ public class AgentClusterDemo {
         TcpCommunicationSpi commSpi = new TcpCommunicationSpi();
 
         commSpi.setSharedMemoryPort(-1);
-
         commSpi.setLocalPort(60800);
 
         cfg.setCommunicationSpi(commSpi);
-
         cfg.setGridLogger(new Log4JLogger(log));
-
         cfg.setMetricsLogFrequency(0);
-
         cfg.getConnectorConfiguration().setPort(60700);
 
         if (client)
             cfg.setClientMode(true);
-        else
-            cfg.setCacheConfiguration(cacheCountry(), cacheDepartment(), cacheEmployee(), cacheParking(), cacheCar());
+
+        cfg.setCacheConfiguration(cacheCountry(), cacheDepartment(), cacheEmployee(), cacheParking(), cacheCar());
 
         return cfg;
     }
@@ -477,7 +483,6 @@ public class AgentClusterDemo {
         final long diff = new java.util.Date().getTime();
 
         populateCacheEmployee(ignite, diff);
-
         populateCacheCar(ignite);
 
         ScheduledExecutorService cachePool = newScheduledThreadPool(2, "demo-sql-load-cache-tasks");
@@ -485,32 +490,49 @@ public class AgentClusterDemo {
         cachePool.scheduleWithFixedDelay(new Runnable() {
             @Override public void run() {
                 try {
+                    for (String cacheName : ignite.cacheNames()) {
+                        if (!DEMO_CACHES.contains(cacheName)) {
+                            IgniteCache<Integer, String> otherCache = ignite.cache(cacheName);
+
+                            if (otherCache != null) {
+                                for (int i = 0, n = 1; i < cnt; i++, n++) {
+                                    Integer key = rnd.nextInt(1000);
+
+                                    String val = otherCache.get(key);
+
+                                    if (val == null)
+                                        otherCache.put(key, "other-" + key);
+                                    else if (rnd.nextInt(100) < 30)
+                                        otherCache.remove(key);
+                                }
+                            }
+                        }
+                    }
+
                     IgniteCache<Integer, Employee> cacheEmployee = ignite.cache(EMPLOYEE_CACHE_NAME);
 
-                    if (cacheEmployee == null)
-                        return;
+                    if (cacheEmployee != null)
+                        try(Transaction tx = ignite.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+                            for (int i = 0, n = 1; i < cnt; i++, n++) {
+                                Integer id = rnd.nextInt(EMPL_CNT);
 
-                    try(Transaction tx = ignite.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
-                        for (int i = 0, n = 1; i < cnt; i++, n++) {
-                            Integer id = rnd.nextInt(EMPL_CNT);
+                                Integer depId = rnd.nextInt(DEP_CNT);
 
-                            Integer depId = rnd.nextInt(DEP_CNT);
+                                double r = rnd.nextDouble();
 
-                            double r = rnd.nextDouble();
+                                cacheEmployee.put(id, new Employee(id, depId, depId, "First name employee #" + n,
+                                    "Last name employee #" + n, "Email employee #" + n, "Phone number employee #" + n,
+                                    new java.sql.Date((long)(r * diff)), "Job employee #" + n, 500 + round(r * 2000, 2)));
 
-                            cacheEmployee.put(id, new Employee(id, depId, depId, "First name employee #" + n,
-                                "Last name employee #" + n, "Email employee #" + n, "Phone number employee #" + n,
-                                new java.sql.Date((long)(r * diff)), "Job employee #" + n, 500 + round(r * 2000, 2)));
+                                if (rnd.nextBoolean())
+                                    cacheEmployee.remove(rnd.nextInt(EMPL_CNT));
 
-                            if (rnd.nextBoolean())
-                                cacheEmployee.remove(rnd.nextInt(EMPL_CNT));
+                                cacheEmployee.get(rnd.nextInt(EMPL_CNT));
+                            }
 
-                            cacheEmployee.get(rnd.nextInt(EMPL_CNT));
+                            if (rnd.nextInt(100) > 20)
+                                tx.commit();
                         }
-
-                        if (rnd.nextInt(100) > 20)
-                            tx.commit();
-                    }
                 }
                 catch (Throwable e) {
                     if (!e.getMessage().contains("cache is stopped"))
@@ -553,6 +575,8 @@ public class AgentClusterDemo {
             log.info("DEMO: Starting embedded nodes for demo...");
 
             System.setProperty(IGNITE_ATOMIC_CACHE_DELETE_HISTORY_SIZE, "1");
+            System.setProperty(IGNITE_PERFORMANCE_SUGGESTIONS_DISABLED, "true");
+            System.setProperty(IGNITE_UPDATE_NOTIFIER, "false");
 
             System.setProperty(IGNITE_JETTY_PORT, "60800");
             System.setProperty(IGNITE_NO_ASCII, "true");
@@ -598,12 +622,12 @@ public class AgentClusterDemo {
 
                 acfg.demoNodeUri(String.format("http://%s:%d", host, port));
 
-                log.info("DEMO: Embedded nodes for sql test-drive successfully started");
+                log.info("DEMO: Embedded nodes for sql and monitoring demo successfully started");
 
                 startLoad(ignite, 20);
             }
             catch (Exception e) {
-                log.error("DEMO: Failed to start embedded node for sql test-drive!", e);
+                log.error("DEMO: Failed to start embedded node for sql and monitoring demo!", e);
 
                 return false;
             }

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/README.txt
----------------------------------------------------------------------
diff --git a/modules/web-console/README.txt b/modules/web-console/README.txt
index ae509d6..286082c 100644
--- a/modules/web-console/README.txt
+++ b/modules/web-console/README.txt
@@ -7,7 +7,7 @@ The Apache Ignite Web Console includes an interactive configuration wizard which
  on your in-memory cache as well as view execution plans, in-memory schema, and streaming charts.
 
 In order to simplify evaluation of Web Console demo mode was implemented.
- To start demo, you need to to click button "Start demo". New tab will be open with prepared demo data on each screen.
+ To start demo, you need to click button "Start demo". New tab will be open with prepared demo data on each screen.
 
  Demo for import domain model from database.
   In this mode an in-memory H2 database will be started.
@@ -24,7 +24,7 @@ In order to simplify evaluation of Web Console demo mode was implemented.
     In this mode internal Ignite node will be started. Cache created and populated with data.
      1) Click "SQL" in Ignite Web Console top menu.
      2) "Demo" notebook with preconfigured queries will be opened.
-     3) You can also execute any SQL queries for tables: "Country, Department, Employee", "Parking, Car".
+     3) You can also execute any SQL queries for tables: "Country, Department, Employee, Parking, Car".
 
  For example:
   1) Enter SQL statement:

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/.eslintrc
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/.eslintrc b/modules/web-console/src/main/js/.eslintrc
index 00fa0ad..1882b44 100644
--- a/modules/web-console/src/main/js/.eslintrc
+++ b/modules/web-console/src/main/js/.eslintrc
@@ -20,6 +20,8 @@ ecmaFeatures:
 
 globals:
     _: true
+    $: true
+    d3: true
     io: true
     window: true
     global: true
@@ -123,7 +125,7 @@ rules:
     no-multiple-empty-lines: [0, {"max": 2}]
     no-native-reassign: 2
     no-negated-in-lhs: 2
-    no-nested-ternary: 2
+    no-nested-ternary: 0
     no-new: 2
     no-new-func: 2
     no-new-object: 2
@@ -195,3 +197,6 @@ rules:
     wrap-iife: 0
     wrap-regex: 0
     yoda: [2, "never"]
+
+parserOptions:
+    sourceType: module

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/data/getting-started.json
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/data/getting-started.json b/modules/web-console/src/main/js/app/data/getting-started.json
index 7aff0b4..1b435ab 100644
--- a/modules/web-console/src/main/js/app/data/getting-started.json
+++ b/modules/web-console/src/main/js/app/data/getting-started.json
@@ -81,7 +81,7 @@
           "</div>",
           "<div class='col-xs-5'>",
           " <ul>",
-          "  <li>Preview XML Configuration</li>",
+          "  <li>Preview XML configuration</li>",
           "  <li>Preview code configuration</li>",
           "  <li>Preview Docker file</li>",
           "  <li>Preview POM dependencies</li>",

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/data/pom-dependencies.json
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/data/pom-dependencies.json b/modules/web-console/src/main/js/app/data/pom-dependencies.json
new file mode 100644
index 0000000..7ab6c1b
--- /dev/null
+++ b/modules/web-console/src/main/js/app/data/pom-dependencies.json
@@ -0,0 +1,20 @@
+{
+    "Cloud": {"artifactId": "ignite-cloud"},
+    "S3": {"artifactId": "ignite-aws"},
+    "GoogleStorage": {"artifactId": "ignite-gce"},
+    "ZooKeeper": {"artifactId": "ignite-zookeeper"},
+
+    "Log4j": {"artifactId": "ignite-log4j"},
+    "Log4j2": {"artifactId": "ignite-log4j2"},
+    "JCL": {"artifactId": "ignite-jcl"},
+    "HadoopIgfsJcl": {"artifactId": "ignite-hadoop"},
+    "SLF4J": {"artifactId": "ignite-slf4j"},
+
+    "Generic": {"groupId": "com.mchange", "artifactId": "c3p0", "version": "0.9.5.1"},
+    "MySQL": {"groupId": "mysql", "artifactId": "mysql-connector-java", "version": "5.1.37"},
+    "PostgreSQL": {"groupId": "org.postgresql", "artifactId": "postgresql", "version": "9.4-1204-jdbc42"},
+    "H2": {"groupId": "com.h2database", "artifactId": "h2", "version": "1.3.175"},
+    "Oracle": {"groupId": "oracle", "artifactId": "jdbc", "version": "11.2", "jar": "ojdbc6.jar"},
+    "DB2": {"groupId": "ibm", "artifactId": "jdbc", "version": "4.19.26", "jar": "db2jcc4.jar"},
+    "SQLServer": {"groupId": "microsoft", "artifactId": "jdbc", "version": "4.1", "jar": "sqljdbc41.jar"}
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/directives/ui-ace-docker/ui-ace-docker.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/directives/ui-ace-docker/ui-ace-docker.jade b/modules/web-console/src/main/js/app/directives/ui-ace-docker/ui-ace-docker.jade
index f634aed..3b0e7b8 100644
--- a/modules/web-console/src/main/js/app/directives/ui-ace-docker/ui-ace-docker.jade
+++ b/modules/web-console/src/main/js/app/directives/ui-ace-docker/ui-ace-docker.jade
@@ -17,7 +17,7 @@
 mixin hard-link(ref, txt)
     a(style='color:#ec1c24' href=ref target='_blank') #{txt}
 
-div
+.panel-details-noborder
     .details-row
         p
             +hard-link('https://docs.docker.com/reference/builder', 'Docker')
@@ -28,4 +28,4 @@ div
             | . For more information about using Ignite with Docker please read&nbsp;
             +hard-link('http://apacheignite.readme.io/docs/docker-deployment', 'documentation')
             |.
-    div(ng-if='ctrl.data' ignite-ace='{onLoad: onLoad, mode: "dockerfile"}' ng-model='ctrl.data')
+    .details-row(ng-if='ctrl.data' ignite-ace='{onLoad: onLoad, mode: "dockerfile"}' ng-model='ctrl.data')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/directives/ui-ace-java/ui-ace-java.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/directives/ui-ace-java/ui-ace-java.directive.js b/modules/web-console/src/main/js/app/directives/ui-ace-java/ui-ace-java.directive.js
index 948f82c..cdce0fc 100644
--- a/modules/web-console/src/main/js/app/directives/ui-ace-java/ui-ace-java.directive.js
+++ b/modules/web-console/src/main/js/app/directives/ui-ace-java/ui-ace-java.directive.js
@@ -95,9 +95,9 @@ export default ['igniteUiAceJava', ['GeneratorJava', (generator) => {
             }
         }
 
-        if (typeof attrs.clusterCfg !== 'undefined') {
+        if (!_.isUndefined(attrs.clusterCfg)) {
             scope.$watch('cfg', (cfg) => {
-                if (typeof cfg !== 'undefined')
+                if (!_.isUndefined(cfg))
                     return;
 
                 scope.cfg = {};

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/directives/ui-ace-pojos/ui-ace-pojos.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/directives/ui-ace-pojos/ui-ace-pojos.jade b/modules/web-console/src/main/js/app/directives/ui-ace-pojos/ui-ace-pojos.jade
index 3e82f12..ed1432b 100644
--- a/modules/web-console/src/main/js/app/directives/ui-ace-pojos/ui-ace-pojos.jade
+++ b/modules/web-console/src/main/js/app/directives/ui-ace-pojos/ui-ace-pojos.jade
@@ -17,7 +17,7 @@
 mixin check-tooltip(message)
     i.tipLabel.fa.fa-question-circle(bs-tooltip='"#{message}"')
 
-div
+.panel-details-noborder
     .details-row
         .col-xs-2.col-sm-2.col-md-2
             label POJO class:
@@ -37,4 +37,4 @@ div
                 input(type='checkbox' ng-model='ctrl.includeKeyFields')
                 | Include key fields
             +check-tooltip("Generate key fields in POJO value class")
-    div(ng-if='ctrl.data' ignite-ace='{onLoad: onLoad, mode: "java"}' ng-model='ctrl.data')
+    .details-row(ng-if='ctrl.data' ignite-ace='{onLoad: onLoad, mode: "java"}' ng-model='ctrl.data')

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/directives/ui-ace-xml/ui-ace-xml.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/directives/ui-ace-xml/ui-ace-xml.directive.js b/modules/web-console/src/main/js/app/directives/ui-ace-xml/ui-ace-xml.directive.js
index 79787a2..1e913fb 100644
--- a/modules/web-console/src/main/js/app/directives/ui-ace-xml/ui-ace-xml.directive.js
+++ b/modules/web-console/src/main/js/app/directives/ui-ace-xml/ui-ace-xml.directive.js
@@ -95,9 +95,9 @@ export default ['igniteUiAceXml', ['GeneratorXml', (generator) => {
             }
         }
 
-        if (typeof attrs.clusterCfg !== 'undefined') {
+        if (!_.isUndefined(attrs.clusterCfg)) {
             scope.$watch('cfg', (cfg) => {
-                if (typeof cfg !== 'undefined')
+                if (!_.isUndefined(cfg))
                     return;
 
                 scope.cfg = {};

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/helpers/jade/mixins.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/helpers/jade/mixins.jade b/modules/web-console/src/main/js/app/helpers/jade/mixins.jade
index 341d351..2b8b282 100644
--- a/modules/web-console/src/main/js/app/helpers/jade/mixins.jade
+++ b/modules/web-console/src/main/js/app/helpers/jade/mixins.jade
@@ -353,7 +353,7 @@ mixin table-text-field(field, items, valid, save, placeholder, newItem)
     -var onBlur = valid + ' && ( ' + save + '); ' + resetOnBlur + ';'
 
     ignite-form-field-input-text(
-        data-name=field
+        data-name='#{field}{{ $index || "" }}'
         data-ng-model=field
         data-ng-required='true'
         data-placeholder=placeholder
@@ -376,7 +376,7 @@ mixin table-java-class-field(lbl, field, items, valid, save, newItem)
     -var onBlur = valid + ' && ( ' + save + '); ' + resetOnBlur + ';'
 
     ignite-form-field-input-text(
-        data-name=field
+        data-name='#{field}{{ $index || "" }}'
         data-ng-model=field
         data-ng-required='true'
         data-ignite-unique=items
@@ -411,7 +411,7 @@ mixin table-java-package-field(field, items, valid, save, newItem)
     -var onBlur = valid + ' && ( ' + save + '); ' + resetOnBlur + ';'
 
     ignite-form-field-input-text(
-        data-name=field
+        data-name='#{field}{{ $index || "" }}'
         data-ng-model=field
         data-ng-required='true'
         data-placeholder='Enter package name'
@@ -436,7 +436,7 @@ mixin table-address-field(field, items, valid, save, newItem, portRange)
     -var onBlur = valid + ' && ( ' + save + '); ' + resetOnBlur + ';'
 
     ignite-form-field-input-text(
-        data-name=field
+        data-name='#{field}{{ $index || "" }}'
         data-ng-model=field
         data-ng-required='true'
         data-placeholder='IP address:port'
@@ -466,8 +466,9 @@ mixin table-save-button(valid, save, newItem)
 //- Mixin for table remove button.
 mixin table-remove-conditional-button(items, show, tip)
     i.tipField.fa.fa-remove(
-        ng-show='#{show} && !field.edit'
-        bs-tooltip='"#{tip}"'
+        ng-hide='!#{show} || field.edit'
+        bs-tooltip
+        data-title=tip
         ng-click='#{items}.splice(#{items}.indexOf(model), 1)'
     )
 
@@ -519,12 +520,12 @@ mixin evictionPolicy(model, name, enabled, required, tip)
 
 //- Mixin for clusters dropdown.
 mixin clusters(model, tip)
-    +dropdown-multiple('<span>Clusters:</span>' + '<a ui-sref="base.configuration.clusters({id: ' + model + '._id})"> (add)</a>',
+    +dropdown-multiple('<span>Clusters:</span>' + '<a ui-sref="base.configuration.clusters({linkId: linkId()})"> (add)</a>',
         model + '.clusters', 'clusters', 'true', 'Choose clusters', 'No clusters configured', 'clusters', tip)
 
 //- Mixin for caches dropdown.
 mixin caches(model, tip)
-    +dropdown-multiple('<span>Caches:</span>' + '<a ui-sref="base.configuration.caches({id: ' + model + '._id})"> (add)</a>',
+    +dropdown-multiple('<span>Caches:</span>' + '<a ui-sref="base.configuration.caches({linkId: linkId()})"> (add)</a>',
         model + '.caches', 'caches', 'true', 'Choose caches', 'No caches configured', 'caches', tip)
 
 //- Mixin for XML and Java preview.

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/index.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/index.js b/modules/web-console/src/main/js/app/index.js
index a8bd741..397d25f 100644
--- a/modules/web-console/src/main/js/app/index.js
+++ b/modules/web-console/src/main/js/app/index.js
@@ -56,10 +56,11 @@ import 'angular-motion/dist/angular-motion.css!';
 import './decorator/select';
 import './decorator/tooltip';
 
-import './modules/Demo/Demo.module';
-import './modules/form/form.module';
 import './services/JavaTypes.service.js';
-import './modules/QueryNotebooks/QueryNotebooks.provider';
+import './modules/form/form.module';
+import './modules/agent/agent.module.js';
+import './modules/query-notebooks/query-notebooks.module';
+import './modules/Demo/Demo.module.js';
 
 import './modules/states/signin.state';
 import './modules/states/logout.state';
@@ -100,7 +101,6 @@ import confirm from './services/confirm.service';
 import IgniteInetAddress from './services/InetAddress.service';
 import IgniteCountries from './services/Countries.service';
 import IgniteChartColors from './services/ChartColors.service';
-import IgniteAgentMonitor from './services/AgentMonitor.service';
 import JavaTypes from './services/JavaTypes.service';
 
 // Providers.
@@ -149,13 +149,14 @@ angular
     'ui.router',
     'gridster',
     // Base modules.
+    'ignite-console.ace',
+    'ignite-console.Form',
     'ignite-console.user',
     'ignite-console.branding',
-    'ignite-console.Form',
-    'ignite-console.QueryNotebooks',
-    'ignite-console.ace',
-    'ignite-console.demo',
     'ignite-console.socket',
+    'ignite-console.agent',
+    'ignite-console.query-notebooks',
+    'ignite-console.demo',
     // States.
     'ignite-console.states.login',
     'ignite-console.states.logout',
@@ -193,7 +194,6 @@ angular
 .service(...IgniteInetAddress)
 .service(...IgniteCountries)
 .service(...IgniteChartColors)
-.service(...IgniteAgentMonitor)
 .service(...JavaTypes)
 // Providers.
 // Filters.

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/Demo/Demo.module.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/Demo/Demo.module.js b/modules/web-console/src/main/js/app/modules/Demo/Demo.module.js
index adf5a82..f08d84c 100644
--- a/modules/web-console/src/main/js/app/modules/Demo/Demo.module.js
+++ b/modules/web-console/src/main/js/app/modules/Demo/Demo.module.js
@@ -111,9 +111,11 @@ angular
         return items;
     }];
 }])
-.service('DemoInfo', ['$rootScope', '$modal', '$state', 'igniteDemoInfo', 'IgniteAgentMonitor', ($rootScope, $modal, $state, igniteDemoInfo, agentMonitor) => {
+.service('DemoInfo', ['$rootScope', '$modal', '$state', '$q', 'igniteDemoInfo', 'IgniteAgentMonitor', ($rootScope, $modal, $state, $q, igniteDemoInfo, agentMonitor) => {
     const scope = $rootScope.$new();
 
+    let closePromise = null;
+
     function _fillPage() {
         const model = igniteDemoInfo;
 
@@ -131,10 +133,8 @@ angular
 
     scope.close = () => {
         dialog.hide();
-    };
 
-    scope.gotoConfiguration = () => {
-        scope.$$postDigest(() => $state.go('base.configuration.clusters'));
+        closePromise && closePromise.resolve();
     };
 
     scope.downloadAgent = () => {
@@ -154,11 +154,13 @@ angular
 
     return {
         show: () => {
+            closePromise = $q.defer();
+
             _fillPage();
 
-            dialog.$promise
+            return dialog.$promise
                 .then(dialog.show)
-                .then(() => agentMonitor.awaitAgent())
+                .then(() => Promise.race([agentMonitor.awaitAgent(), closePromise.promise]))
                 .then(() => scope.hasAgents = true);
         }
     };

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/QueryNotebooks/QueryNotebooks.provider.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/QueryNotebooks/QueryNotebooks.provider.js b/modules/web-console/src/main/js/app/modules/QueryNotebooks/QueryNotebooks.provider.js
deleted file mode 100644
index 7e4e523..0000000
--- a/modules/web-console/src/main/js/app/modules/QueryNotebooks/QueryNotebooks.provider.js
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import angular from 'angular';
-
-angular
-    .module('ignite-console.QueryNotebooks', [
-
-    ])
-    .provider('QueryNotebooks', function() {
-        const _demoNotebook = {
-            name: 'SQL demo',
-            paragraphs: [
-                {
-                    name: 'Query with refresh rate',
-                    cacheName: 'CarCache',
-                    pageSize: 50,
-                    query: 'SELECT count(*)\nFROM "CarCache".Car',
-                    result: 'bar',
-                    timeLineSpan: '1',
-                    rate: {
-                        value: 3,
-                        unit: 1000,
-                        installed: true
-                    }
-                },
-                {
-                    name: 'Simple query',
-                    cacheName: 'CarCache',
-                    pageSize: 50,
-                    query: 'SELECT * FROM "CarCache".Car',
-                    result: 'table',
-                    timeLineSpan: '1',
-                    rate: {
-                        value: 30,
-                        unit: 1000,
-                        installed: false
-                    }
-                },
-                {
-                    name: 'Query with aggregates',
-                    cacheName: 'CarCache',
-                    pageSize: 50,
-                    query: 'SELECT p.name, count(*) AS cnt\nFROM "ParkingCache".Parking p\nINNER JOIN "CarCache".Car c\n  ON (p.id) = (c.parkingId)\nGROUP BY P.NAME',
-                    result: 'table',
-                    timeLineSpan: '1',
-                    rate: {
-                        value: 30,
-                        unit: 1000,
-                        installed: false
-                    }
-                }
-            ],
-            expandedParagraphs: [0, 1, 2]
-        };
-
-        this.$get = ['$q', '$http', '$rootScope', ($q, $http, $root) => {
-            return {
-                read(noteId) {
-                    if ($root.IgniteDemoMode)
-                        return $q.when(angular.copy(_demoNotebook));
-
-                    return $http.post('/api/v1/notebooks/get', {noteId})
-                        .then(({data}) => data)
-                        .catch(({data}) => $q.reject(data));
-                },
-                save(notebook) {
-                    if ($root.IgniteDemoMode)
-                        return $q.when();
-
-                    return $http.post('/api/v1/notebooks/save', notebook)
-                        .then(({data}) => data)
-                        .catch(({data}) => $q.reject(data));
-                },
-                remove(notebook) {
-                    if ($root.IgniteDemoMode)
-                        return $q.reject(`Removing "${notebook.name}" notebook is not supported.`);
-
-                    return $http.post('/api/v1/notebooks/remove', {_id: notebook._id})
-                        .then(() => {
-                            const idx = _.findIndex($root.notebooks, (item) => {
-                                return item._id === notebook._id;
-                            });
-
-                            if (idx >= 0) {
-                                $root.notebooks.splice(idx, 1);
-
-                                $root.rebuildDropdown();
-
-                                if (idx < $root.notebooks.length)
-                                    return $root.notebooks[idx];
-                            }
-
-                            if ($root.notebooks.length > 0)
-                                return $root.notebooks[$root.notebooks.length - 1];
-                        })
-                        .catch(({data}) => $q.reject(data));
-                }
-            };
-        }];
-    });

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/agent/agent.module.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/agent/agent.module.js b/modules/web-console/src/main/js/app/modules/agent/agent.module.js
new file mode 100644
index 0000000..4e3f7ef
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/agent/agent.module.js
@@ -0,0 +1,323 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import angular from 'angular';
+import io from 'socket.io-client'; // eslint-disable-line no-unused-vars
+
+class IgniteAgentMonitor {
+    constructor(socketFactory, $root, $q, $state, $modal, $common) {
+        this._scope = $root.$new();
+
+        $root.$watch('user', () => {
+            this._scope.user = $root.user;
+        });
+
+        $root.$on('$stateChangeStart', () => {
+            this.stopWatch();
+        });
+
+        // Pre-fetch modal dialogs.
+        this._downloadAgentModal = $modal({
+            scope: this._scope,
+            templateUrl: '/templates/agent-download.html',
+            show: false,
+            backdrop: 'static',
+            keyboard: false
+        });
+
+        const _modalHide = this._downloadAgentModal.hide;
+
+        /**
+         * Special dialog hide function.
+         */
+        this._downloadAgentModal.hide = () => {
+            $common.hideAlert();
+
+            _modalHide();
+        };
+
+        /**
+         * Close dialog and go by specified link.
+         */
+        this._scope.back = () => {
+            this.stopWatch();
+
+            if (this._scope.backState)
+                this._scope.$$postDigest(() => $state.go(this._scope.backState));
+        };
+
+        this._scope.downloadAgent = () => {
+            const lnk = document.createElement('a');
+
+            lnk.setAttribute('href', '/api/v1/agent/download/zip');
+            lnk.setAttribute('target', '_self');
+            lnk.setAttribute('download', null);
+            lnk.style.display = 'none';
+
+            document.body.appendChild(lnk);
+
+            lnk.click();
+
+            document.body.removeChild(lnk);
+        };
+
+        this._scope.hasAgents = null;
+        this._scope.showModal = false;
+
+        /**
+         * @type {Socket}
+         */
+        this._socket = null;
+
+        this._socketFactory = socketFactory;
+
+        this._$q = $q;
+
+        this._$common = $common;
+    }
+
+    /**
+     * @private
+     */
+    checkModal() {
+        if (this._scope.showModal && !this._scope.hasAgents)
+            this._downloadAgentModal.$promise.then(this._downloadAgentModal.show);
+        else if ((this._scope.hasAgents || !this._scope.showModal) && this._downloadAgentModal.$isShown)
+            this._downloadAgentModal.hide();
+    }
+
+    /**
+     * @returns {Promise}
+     */
+    awaitAgent() {
+        if (this._scope.hasAgents)
+            return this._$q.when();
+
+        const latch = this._$q.defer();
+
+        const offConnected = this._scope.$on('agent:watch', (event, state) => {
+            if (state !== 'DISCONNECTED')
+                offConnected();
+
+            if (state === 'CONNECTED')
+                return latch.resolve();
+
+            if (state === 'STOPPED')
+                return latch.reject('Agent watch stopped.');
+        });
+
+        return latch.promise;
+    }
+
+    init() {
+        this._socket = this._socketFactory();
+
+        const disconnectFn = () => {
+            this._scope.hasAgents = false;
+
+            this.checkModal();
+
+            this._scope.$broadcast('agent:watch', 'DISCONNECTED');
+        };
+
+        this._socket.on('connect_error', disconnectFn);
+        this._socket.on('disconnect', disconnectFn);
+
+        this._socket.on('agent:count', ({count}) => {
+            this._scope.hasAgents = count > 0;
+
+            this.checkModal();
+
+            this._scope.$broadcast('agent:watch', this._scope.hasAgents ? 'CONNECTED' : 'DISCONNECTED');
+        });
+    }
+
+    /**
+     * @param {Object} back
+     * @returns {Promise}
+     */
+    startWatch(back) {
+        this._scope.backState = back.state;
+        this._scope.backText = back.text;
+
+        this._scope.agentGoal = back.goal;
+
+        if (back.onDisconnect) {
+            this._scope.offDisconnect = this._scope.$on('agent:watch', (e, state) =>
+                state === 'DISCONNECTED' && back.onDisconnect());
+        }
+
+        this._scope.showModal = true;
+
+        // Remove blinking on init.
+        if (this._scope.hasAgents !== null)
+            this.checkModal();
+
+        return this.awaitAgent();
+    }
+
+    /**
+     *
+     * @param {String} event
+     * @param {Object} [args]
+     * @returns {Promise}
+     * @private
+     */
+    _emit(event, ...args) {
+        if (!this._socket)
+            return this._$q.reject('Failed to connect to server');
+
+        const latch = this._$q.defer();
+
+        const onDisconnect = () => {
+            this._socket.removeListener('disconnect', onDisconnect);
+
+            latch.reject('Connection to server was closed');
+        };
+
+        this._socket.on('disconnect', onDisconnect);
+
+        args.push((err, res) => {
+            this._socket.removeListener('disconnect', onDisconnect);
+
+            if (err)
+                latch.reject(err);
+
+            latch.resolve(res);
+        });
+
+        this._socket.emit(event, ...args);
+
+        return latch.promise;
+    }
+
+    drivers() {
+        return this._emit('schemaImport:drivers');
+    }
+
+    /**
+     *
+     * @param {Object} preset
+     * @returns {Promise}
+     */
+    schemas(preset) {
+        return this._emit('schemaImport:schemas', preset);
+    }
+
+    /**
+     *
+     * @param {Object} preset
+     * @returns {Promise}
+     */
+    tables(preset) {
+        return this._emit('schemaImport:tables', preset);
+    }
+
+    /**
+     * @param {Object} err
+     */
+    showNodeError(err) {
+        if (this._scope.showModal) {
+            this._downloadAgentModal.$promise.then(this._downloadAgentModal.show);
+
+            this._$common.showError(err);
+        }
+    }
+
+    /**
+     *
+     * @param {String} event
+     * @param {Object} [args]
+     * @returns {Promise}
+     * @private
+     */
+    _rest(event, ...args) {
+        return this._downloadAgentModal.$promise
+            .then(() => this._emit(event, ...args));
+    }
+
+    /**
+     * @param {Boolean} [attr]
+     * @param {Boolean} [mtr]
+     * @returns {Promise}
+     */
+    topology(attr, mtr) {
+        return this._rest('node:topology', !!attr, !!mtr);
+    }
+
+    /**
+     * @param {int} [queryId]
+     * @returns {Promise}
+     */
+    queryClose(queryId) {
+        return this._rest('node:query:close', queryId);
+    }
+
+    /**
+     * @param {String} cacheName Cache name.
+     * @param {int} pageSize
+     * @param {String} [query] Query if null then scan query.
+     * @returns {Promise}
+     */
+    query(cacheName, pageSize, query) {
+        return this._rest('node:query', _.isEmpty(cacheName) ? null : cacheName, pageSize, query);
+    }
+
+    /**
+     * @param {String} cacheName Cache name.
+     * @param {String} [query] Query if null then scan query.
+     * @returns {Promise}
+     */
+    queryGetAll(cacheName, query) {
+        return this._rest('node:query:getAll', _.isEmpty(cacheName) ? null : cacheName, query);
+    }
+
+    /**
+     * @param {String} [cacheName] Cache name.
+     * @returns {Promise}
+     */
+    metadata(cacheName) {
+        return this._rest('node:cache:metadata', _.isEmpty(cacheName) ? null : cacheName);
+    }
+
+    /**
+     * @param {int} queryId
+     * @param {int} pageSize
+     * @returns {Promise}
+     */
+    next(queryId, pageSize) {
+        return this._rest('node:query:fetch', queryId, pageSize);
+    }
+
+    stopWatch() {
+        this._scope.showModal = false;
+
+        this.checkModal();
+
+        this._scope.offDisconnect && this._scope.offDisconnect();
+
+        this._scope.$broadcast('agent:watch', 'STOPPED');
+    }
+}
+
+IgniteAgentMonitor.$inject = ['igniteSocketFactory', '$rootScope', '$q', '$state', '$modal', '$common'];
+
+angular
+    .module('ignite-console.agent', [
+
+    ])
+    .service('IgniteAgentMonitor', IgniteAgentMonitor);

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/configuration/generator/Pom.service.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/configuration/generator/Pom.service.js b/modules/web-console/src/main/js/app/modules/configuration/generator/Pom.service.js
index cf266e1..3508e59 100644
--- a/modules/web-console/src/main/js/app/modules/configuration/generator/Pom.service.js
+++ b/modules/web-console/src/main/js/app/modules/configuration/generator/Pom.service.js
@@ -15,6 +15,9 @@
  * limitations under the License.
  */
 
+// Java built-in class names.
+import POM_DEPENDENCIES from 'app/data/pom-dependencies.json!';
+
 /**
  * Pom file generation entry point.
  */
@@ -31,7 +34,8 @@ class GeneratorPom {
     }
 
     addDependency(deps, groupId, artifactId, version, jar) {
-        deps.push({groupId, artifactId, version, jar});
+        if (!_.find(deps, (dep) => dep.groupId === groupId && dep.artifactId === artifactId))
+            deps.push({groupId, artifactId, version, jar});
     }
 
     addResource(res, dir, exclude) {
@@ -132,8 +136,8 @@ class GeneratorPom {
      */
     generate(cluster, igniteVersion, res) {
         const caches = cluster.caches;
-        const dialect = {};
         const deps = [];
+        const storeDeps = [];
         const excludeGroupIds = ['org.apache.ignite'];
 
         const blobStoreFactory = {cacheStoreFactory: {kind: 'CacheHibernateBlobStoreFactory'}};
@@ -145,8 +149,11 @@ class GeneratorPom {
             if (cache.cacheStoreFactory && cache.cacheStoreFactory.kind) {
                 const storeFactory = cache.cacheStoreFactory[cache.cacheStoreFactory.kind];
 
-                if (storeFactory.dialect && (!storeFactory.connectVia || storeFactory.connectVia === 'DataSource'))
-                    dialect[storeFactory.dialect] = true;
+                if (storeFactory.dialect && (!storeFactory.connectVia || storeFactory.connectVia === 'DataSource')) {
+                    const dep = POM_DEPENDENCIES[storeFactory.dialect];
+
+                    this.addDependency(storeDeps, dep.groupId, dep.artifactId, dep.version, dep.jar);
+                }
             }
         });
 
@@ -172,14 +179,10 @@ class GeneratorPom {
         this.addDependency(deps, 'org.apache.ignite', 'ignite-indexing', igniteVersion);
         this.addDependency(deps, 'org.apache.ignite', 'ignite-rest-http', igniteVersion);
 
-        if (cluster.discovery.kind === 'Cloud')
-            this.addDependency(deps, 'org.apache.ignite', 'ignite-cloud', igniteVersion);
-        else if (cluster.discovery.kind === 'S3')
-            this.addDependency(deps, 'org.apache.ignite', 'ignite-aws', igniteVersion);
-        else if (cluster.discovery.kind === 'GoogleStorage')
-            this.addDependency(deps, 'org.apache.ignite', 'ignite-gce', igniteVersion);
-        else if (cluster.discovery.kind === 'ZooKeeper')
-            this.addDependency(deps, 'org.apache.ignite', 'ignite-zookeeper', igniteVersion);
+        let dep = POM_DEPENDENCIES[cluster.discovery.kind];
+
+        if (dep)
+            this.addDependency(deps, 'org.apache.ignite', dep.artifactId, igniteVersion);
 
         if (_.find(cluster.igfss, (igfs) => igfs.secondaryFileSystemEnabled))
             this.addDependency(deps, 'org.apache.ignite', 'ignite-hadoop', igniteVersion);
@@ -187,21 +190,14 @@ class GeneratorPom {
         if (_.find(caches, blobStoreFactory))
             this.addDependency(deps, 'org.apache.ignite', 'ignite-hibernate', igniteVersion);
 
-        dialect.Generic && this.addDependency(deps, 'com.mchange', 'c3p0', '0.9.5.1');
-
-        dialect.MySQL && this.addDependency(deps, 'mysql', 'mysql-connector-java', '5.1.37');
-
-        dialect.PostgreSQL && this.addDependency(deps, 'org.postgresql', 'postgresql', '9.4-1204-jdbc42');
+        if (cluster.logger && cluster.logger.kind) {
+            dep = POM_DEPENDENCIES[cluster.logger.kind];
 
-        dialect.H2 && this.addDependency(deps, 'com.h2database', 'h2', '1.3.175');
-
-        dialect.Oracle && this.addDependency(deps, 'oracle', 'jdbc', '11.2', 'ojdbc6.jar');
-
-        dialect.DB2 && this.addDependency(deps, 'ibm', 'jdbc', '4.19.26', 'db2jcc4.jar');
-
-        dialect.SQLServer && this.addDependency(deps, 'microsoft', 'jdbc', '4.1', 'sqljdbc41.jar');
+            if (dep)
+                this.addDependency(deps, 'org.apache.ignite', dep.artifactId, igniteVersion);
+        }
 
-        this.dependencies(res, cluster, deps);
+        this.dependencies(res, cluster, deps.concat(storeDeps));
 
         res.needEmptyLine = true;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/form/field/input/datalist.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/form/field/input/datalist.directive.js b/modules/web-console/src/main/js/app/modules/form/field/input/datalist.directive.js
index 0cd9d69..ce67897 100644
--- a/modules/web-console/src/main/js/app/modules/form/field/input/datalist.directive.js
+++ b/modules/web-console/src/main/js/app/modules/form/field/input/datalist.directive.js
@@ -74,10 +74,10 @@ export default ['igniteFormFieldInputDatalist', ['IgniteFormGUID', '$table', (gu
         scope.ngChange = () => {
             ngModel.$setViewValue(scope.value);
 
-            if (JSON.stringify(scope.value) !== JSON.stringify(form.$defaults[name]))
-                ngModel.$setDirty();
-            else
+            if (_.isEqual(scope.value, form.$defaults[name]))
                 ngModel.$setPristine();
+            else
+                ngModel.$setDirty();
 
             setTimeout(checkValid, 100); // Use setTimeout() workaround of problem of two controllers.
         };

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/form/field/input/text.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/form/field/input/text.directive.js b/modules/web-console/src/main/js/app/modules/form/field/input/text.directive.js
index ba4407f..adcc179 100644
--- a/modules/web-console/src/main/js/app/modules/form/field/input/text.directive.js
+++ b/modules/web-console/src/main/js/app/modules/form/field/input/text.directive.js
@@ -37,11 +37,14 @@ export default ['igniteFormFieldInputText', ['IgniteFormGUID', '$table', (guid,
             label.for = scope.id;
 
             scope.label = label;
+            scope.labelName = label.name;
 
             scope.$watch('required', (required) => {
                 label.required = required || false;
             });
         }
+        else
+            scope.labelName = attrs.igniteLabelName || 'Value';
 
         form.$defaults = form.$defaults || {};
 
@@ -75,10 +78,10 @@ export default ['igniteFormFieldInputText', ['IgniteFormGUID', '$table', (guid,
         scope.ngChange = () => {
             ngModel.$setViewValue(scope.value);
 
-            if (JSON.stringify(scope.value) !== JSON.stringify(form.$defaults[name]))
-                ngModel.$setDirty();
-            else
+            if (_.isEqual(scope.value, form.$defaults[name]))
                 ngModel.$setPristine();
+            else
+                ngModel.$setDirty();
 
             setTimeout(checkValid, 100); // Use setTimeout() workaround of problem of two controllers.
         };

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/form/field/input/text.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/form/field/input/text.jade b/modules/web-console/src/main/js/app/modules/form/field/input/text.jade
index 40aef79..8a1dfc2 100644
--- a/modules/web-console/src/main/js/app/modules/form/field/input/text.jade
+++ b/modules/web-console/src/main/js/app/modules/form/field/input/text.jade
@@ -43,6 +43,6 @@ mixin feedback(isCheckPristine, error, errorMessage)
         data-ng-focus='tableReset()'
     )
 
-    +feedback(true, 'required', '{{ label.name }} could not be empty!')
+    +feedback(true, 'required', '{{ labelName }} could not be empty!')
 
     span.transclude-here

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/form/group/group.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/form/group/group.directive.js b/modules/web-console/src/main/js/app/modules/form/group/group.directive.js
index 5039d6f..0971d44 100644
--- a/modules/web-console/src/main/js/app/modules/form/group/group.directive.js
+++ b/modules/web-console/src/main/js/app/modules/form/group/group.directive.js
@@ -50,10 +50,10 @@ export default ['igniteFormGroup', [() => {
         };
 
         const setAsDirty = () => {
-            if (JSON.stringify(scope.ngModel) !== JSON.stringify(parentFormCtrl.$defaults[name]))
-                ngModelCtrl.$setDirty();
-            else
+            if (_.isEqual(scope.ngModel, parentFormCtrl.$defaults[name]))
                 ngModelCtrl.$setPristine();
+            else
+                ngModelCtrl.$setDirty();
         };
 
         scope.$watch(() => parentFormCtrl.$pristine, setAsDefault);

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/form/validator/java-keywords.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/form/validator/java-keywords.directive.js b/modules/web-console/src/main/js/app/modules/form/validator/java-keywords.directive.js
index 841c48b..d97e59a 100644
--- a/modules/web-console/src/main/js/app/modules/form/validator/java-keywords.directive.js
+++ b/modules/web-console/src/main/js/app/modules/form/validator/java-keywords.directive.js
@@ -20,15 +20,14 @@ export default ['javaKeywords', ['JavaTypes', (JavaTypes) => {
         if (_.isUndefined(attrs.javaKeywords) || !attrs.javaKeywords)
             return;
 
+        const packageOnly = attrs.javaPackageName === 'package-only';
+
         ngModel.$validators.javaKeywords = (value) => {
             if (value) {
-                if (!JavaTypes.validIdentifier(value) || !JavaTypes.packageSpecified(value))
+                if (!JavaTypes.validIdentifier(value) || (!packageOnly && !JavaTypes.packageSpecified(value)))
                     return true;
 
-                for (const item of value.split('.')) {
-                    if (JavaTypes.isKeywords(item))
-                        return false;
-                }
+                return _.findIndex(value.split('.'), JavaTypes.isKeywords) < 0;
             }
 
             return true;

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/loading/loading.css
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/loading/loading.css b/modules/web-console/src/main/js/app/modules/loading/loading.css
index fac7517..87bbc6a 100644
--- a/modules/web-console/src/main/js/app/modules/loading/loading.css
+++ b/modules/web-console/src/main/js/app/modules/loading/loading.css
@@ -25,7 +25,7 @@
     left: 0;
     right: 0;
     top: 0;
-    z-index: 1002;
+    z-index: 1001;
     opacity: 0;
     visibility: hidden;
     background-color: white;

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/query-notebooks/query-notebooks.module.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/query-notebooks/query-notebooks.module.js b/modules/web-console/src/main/js/app/modules/query-notebooks/query-notebooks.module.js
new file mode 100644
index 0000000..5999948
--- /dev/null
+++ b/modules/web-console/src/main/js/app/modules/query-notebooks/query-notebooks.module.js
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import angular from 'angular';
+
+angular
+    .module('ignite-console.query-notebooks', [
+
+    ])
+    .provider('QueryNotebooks', function() {
+        const _demoNotebook = {
+            name: 'SQL demo',
+            paragraphs: [
+                {
+                    name: 'Query with refresh rate',
+                    cacheName: 'CarCache',
+                    pageSize: 50,
+                    query: 'SELECT count(*)\nFROM "CarCache".Car',
+                    result: 'bar',
+                    timeLineSpan: '1',
+                    rate: {
+                        value: 3,
+                        unit: 1000,
+                        installed: true
+                    }
+                },
+                {
+                    name: 'Simple query',
+                    cacheName: 'CarCache',
+                    pageSize: 50,
+                    query: 'SELECT * FROM "CarCache".Car',
+                    result: 'table',
+                    timeLineSpan: '1',
+                    rate: {
+                        value: 30,
+                        unit: 1000,
+                        installed: false
+                    }
+                },
+                {
+                    name: 'Query with aggregates',
+                    cacheName: 'CarCache',
+                    pageSize: 50,
+                    query: 'SELECT p.name, count(*) AS cnt\nFROM "ParkingCache".Parking p\nINNER JOIN "CarCache".Car c\n  ON (p.id) = (c.parkingId)\nGROUP BY P.NAME',
+                    result: 'table',
+                    timeLineSpan: '1',
+                    rate: {
+                        value: 30,
+                        unit: 1000,
+                        installed: false
+                    }
+                }
+            ],
+            expandedParagraphs: [0, 1, 2]
+        };
+
+        this.$get = ['$q', '$http', '$rootScope', ($q, $http, $root) => {
+            return {
+                read(noteId) {
+                    if ($root.IgniteDemoMode)
+                        return $q.when(angular.copy(_demoNotebook));
+
+                    return $http.post('/api/v1/notebooks/get', {noteId})
+                        .then(({data}) => data)
+                        .catch(({data}) => $q.reject(data));
+                },
+                save(notebook) {
+                    if ($root.IgniteDemoMode)
+                        return $q.when();
+
+                    return $http.post('/api/v1/notebooks/save', notebook)
+                        .then(({data}) => data)
+                        .catch(({data}) => $q.reject(data));
+                },
+                remove(notebook) {
+                    if ($root.IgniteDemoMode)
+                        return $q.reject(`Removing "${notebook.name}" notebook is not supported.`);
+
+                    return $http.post('/api/v1/notebooks/remove', {_id: notebook._id})
+                        .then(() => {
+                            const idx = _.findIndex($root.notebooks, (item) => {
+                                return item._id === notebook._id;
+                            });
+
+                            if (idx >= 0) {
+                                $root.notebooks.splice(idx, 1);
+
+                                $root.rebuildDropdown();
+
+                                if (idx < $root.notebooks.length)
+                                    return $root.notebooks[idx];
+                            }
+
+                            if ($root.notebooks.length > 0)
+                                return $root.notebooks[$root.notebooks.length - 1];
+                        })
+                        .catch(({data}) => $q.reject(data));
+                }
+            };
+        }];
+    });

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration.state.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration.state.js b/modules/web-console/src/main/js/app/modules/states/configuration.state.js
index 4a0578c..7e5e95e 100644
--- a/modules/web-console/src/main/js/app/modules/states/configuration.state.js
+++ b/modules/web-console/src/main/js/app/modules/states/configuration.state.js
@@ -55,6 +55,19 @@ import clustersSwap from './configuration/clusters/swap.directive';
 import clustersTime from './configuration/clusters/time.directive';
 import clustersThread from './configuration/clusters/thread.directive';
 import clustersTransactions from './configuration/clusters/transactions.directive';
+import clustersUserAttributes from './configuration/clusters/attributes.directive';
+import clustersCollision from './configuration/clusters/collision.directive';
+import clustersFailover from './configuration/clusters/failover.directive';
+import clustersLogger from './configuration/clusters/logger.directive';
+
+import clustersCollisionJobStealing from './configuration/clusters/collision/job-stealing.directive';
+import clustersCollisionFifoQueue from './configuration/clusters/collision/fifo-queue.directive';
+import clustersCollisionPriorityQueue from './configuration/clusters/collision/priority-queue.directive';
+import clustersCollisionCustom from './configuration/clusters/collision/custom.directive';
+
+import clustersLoggerLog4j2 from './configuration/clusters/logger/log4j2.directive';
+import clustersLoggerLog4j from './configuration/clusters/logger/log4j.directive';
+import clustersLoggerCustom from './configuration/clusters/logger/custom.directive';
 
 // Domains screen.
 import domainsGeneral from './configuration/domains/general.directive';
@@ -87,6 +100,17 @@ import summaryTabs from './configuration/summary/summary-tabs.directive';
 angular.module('ignite-console.states.configuration', ['ui.router'])
     // Clusters screen.
     .directive(...previewPanel)
+    .directive(...clustersLoggerCustom)
+    .directive(...clustersLoggerLog4j)
+    .directive(...clustersLoggerLog4j2)
+    .directive(...clustersLogger)
+    .directive(...clustersFailover)
+    .directive(...clustersCollisionCustom)
+    .directive(...clustersCollisionPriorityQueue)
+    .directive(...clustersCollisionFifoQueue)
+    .directive(...clustersCollisionJobStealing)
+    .directive(...clustersCollision)
+    .directive(...clustersUserAttributes)
     .directive(...clustersTransactions)
     .directive(...clustersThread)
     .directive(...clustersTime)
@@ -154,7 +178,7 @@ angular.module('ignite-console.states.configuration', ['ui.router'])
                 url: '/clusters',
                 templateUrl: '/configuration/clusters.html',
                 params: {
-                    id: null
+                    linkId: null
                 },
                 metaTags: {
                     title: 'Configure Clusters'
@@ -164,7 +188,7 @@ angular.module('ignite-console.states.configuration', ['ui.router'])
                 url: '/caches',
                 templateUrl: '/configuration/caches.html',
                 params: {
-                    id: null
+                    linkId: null
                 },
                 metaTags: {
                     title: 'Configure Caches'
@@ -174,7 +198,7 @@ angular.module('ignite-console.states.configuration', ['ui.router'])
                 url: '/domains',
                 templateUrl: '/configuration/domains.html',
                 params: {
-                    id: null
+                    linkId: null
                 },
                 metaTags: {
                     title: 'Configure Domain Model'
@@ -184,7 +208,7 @@ angular.module('ignite-console.states.configuration', ['ui.router'])
                 url: '/igfs',
                 templateUrl: '/configuration/igfs.html',
                 params: {
-                    id: null
+                    linkId: null
                 },
                 metaTags: {
                     title: 'Configure IGFS'

http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/app/modules/states/configuration/caches/general.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/app/modules/states/configuration/caches/general.jade b/modules/web-console/src/main/js/app/modules/states/configuration/caches/general.jade
index 2285825..b5e0797 100644
--- a/modules/web-console/src/main/js/app/modules/states/configuration/caches/general.jade
+++ b/modules/web-console/src/main/js/app/modules/states/configuration/caches/general.jade
@@ -31,7 +31,7 @@ form.panel.panel-default(name='general' novalidate)
                 .settings-row
                     +clusters(model, 'Associate clusters with the current cache')
                 .settings-row
-                    +dropdown-multiple('<span>Domain models:</span><a ui-sref="base.configuration.domains({id: ' + model + '._id})"> (add)</a>',
+                    +dropdown-multiple('<span>Domain models:</span><a ui-sref="base.configuration.domains({linkId: linkId()})"> (add)</a>',
                         model + '.domains', 'domains', 'true', 'Choose domain models', 'No domain models configured', 'domains',
                         'Select domain models to describe types in cache')
                 .settings-row


[21/22] ignite git commit: Merge branch 'gridgain-7.6.1' of https://github.com/gridgain/apache-ignite

Posted by ak...@apache.org.
Merge branch 'gridgain-7.6.1' of https://github.com/gridgain/apache-ignite


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/6e5061d1
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/6e5061d1
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/6e5061d1

Branch: refs/heads/master
Commit: 6e5061d169ad80bfad6d26d3f0337b0d634afde8
Parents: 8f28382 541e17d
Author: Alexey Kuznetsov <ak...@apache.org>
Authored: Mon Jun 27 10:20:03 2016 +0700
Committer: Alexey Kuznetsov <ak...@apache.org>
Committed: Mon Jun 27 10:20:03 2016 +0700

----------------------------------------------------------------------
 .../configuration/HadoopConfiguration.java      |   35 +
 .../org/apache/ignite/internal/IgnitionEx.java  |    3 +
 .../processors/cache/GridCacheAdapter.java      |   16 +-
 .../distributed/near/GridNearGetFuture.java     |    8 +-
 .../transactions/IgniteTxLocalAdapter.java      |   12 +-
 .../cache/transactions/IgniteTxManager.java     |   20 +-
 .../processors/hadoop/HadoopJobInfo.java        |    3 +-
 .../processors/igfs/IgfsDataManager.java        |   14 +-
 .../processors/igfs/IgfsOutputStreamImpl.java   |   10 +-
 .../internal/processors/igfs/IgfsProcessor.java |   24 -
 .../ignite/internal/util/IgniteUtils.java       |   45 +-
 .../visor/compute/VisorGatewayTask.java         |    3 +-
 .../ignite/spi/discovery/tcp/ServerImpl.java    |    7 +-
 .../spi/discovery/tcp/TcpDiscoveryImpl.java     |    3 +-
 .../IgniteCacheTxIteratorSelfTest.java          |  241 ++
 .../cluster/GridAddressResolverSelfTest.java    |   97 +
 .../igfs/IgfsDataManagerSelfTest.java           |    6 +-
 .../igfs/IgfsProcessorValidationSelfTest.java   |   26 -
 .../vm/TcpDiscoveryVmIpFinderSelfTest.java      |   75 +
 .../testsuites/IgniteCacheTestSuite5.java       |    2 +
 .../testsuites/IgniteKernalSelfTestSuite.java   |    2 +
 .../processors/hadoop/HadoopClassLoader.java    |   26 +-
 .../processors/hadoop/HadoopDefaultJobInfo.java |    8 +-
 .../hadoop/jobtracker/HadoopJobTracker.java     |   14 +-
 .../child/HadoopChildProcessRunner.java         |    2 +-
 .../processors/hadoop/v2/HadoopV2Job.java       |   14 +-
 .../hadoop/HadoopClassLoaderTest.java           |    2 +-
 .../processors/hadoop/HadoopSnappyTest.java     |    2 +-
 .../processors/hadoop/HadoopTasksV1Test.java    |    2 +-
 .../processors/hadoop/HadoopTasksV2Test.java    |    2 +-
 .../processors/hadoop/HadoopV2JobSelfTest.java  |    2 +-
 .../collections/HadoopAbstractMapTest.java      |    6 +-
 .../include/ignite/binary/binary_containers.h   |  191 +-
 .../include/ignite/binary/binary_raw_reader.h   |   25 +-
 .../include/ignite/binary/binary_raw_writer.h   |   13 +
 .../include/ignite/binary/binary_reader.h       |   13 +
 .../include/ignite/binary/binary_writer.h       |   13 +
 .../platforms/cpp/common/include/ignite/date.h  |    2 +-
 .../cpp/common/include/ignite/ignite_error.h    |   19 +-
 .../cpp/common/include/ignite/timestamp.h       |    2 +-
 .../platforms/cpp/common/src/ignite_error.cpp   |   12 +-
 .../cpp/core/include/ignite/cache/cache.h       |  186 +-
 .../cpp/core/include/ignite/cache/cache_entry.h |   14 +-
 .../include/ignite/cache/query/query_argument.h |   33 +-
 .../include/ignite/cache/query/query_cursor.h   |   17 +-
 .../ignite/cache/query/query_fields_cursor.h    |   11 +-
 .../ignite/cache/query/query_fields_row.h       |   22 +-
 .../include/ignite/cache/query/query_scan.h     |   10 +-
 .../core/include/ignite/cache/query/query_sql.h |   15 +-
 .../ignite/cache/query/query_sql_fields.h       |   15 +-
 .../platforms/cpp/core/include/ignite/ignite.h  |   21 +-
 .../core/include/ignite/ignite_configuration.h  |    4 +-
 .../cpp/core/include/ignite/ignition.h          |    2 +-
 .../include/ignite/transactions/transaction.h   |   68 +-
 .../ignite/transactions/transaction_consts.h    |   84 +-
 .../ignite/transactions/transaction_metrics.h   |   13 +-
 .../include/ignite/transactions/transactions.h  |   36 +-
 modules/platforms/cpp/core/namespaces.dox       |    2 +-
 modules/platforms/cpp/cpp.dxg                   |    4 +-
 modules/schema-import/README.txt                |   12 +-
 modules/web-agent/README.txt                    |   25 +-
 .../web-agent/assembly/release-web-agent.xml    |    2 -
 modules/web-agent/bin/ignite-web-agent.bat      |    2 +-
 modules/web-agent/bin/ignite-web-agent.sh       |    2 +-
 modules/web-agent/pom.xml                       |   22 +-
 .../console/agent/AgentConfiguration.java       |   53 +-
 .../ignite/console/agent/AgentLauncher.java     |   21 +-
 .../ignite/console/demo/AgentClusterDemo.java   |   82 +-
 modules/web-console/README.txt                  |    4 +-
 modules/web-console/src/main/js/.eslintrc       |    7 +-
 .../src/main/js/app/data/getting-started.json   |    2 +-
 .../src/main/js/app/data/pom-dependencies.json  |   20 +
 .../directives/ui-ace-docker/ui-ace-docker.jade |    4 +-
 .../ui-ace-java/ui-ace-java.directive.js        |    4 +-
 .../directives/ui-ace-pojos/ui-ace-pojos.jade   |    4 +-
 .../ui-ace-xml/ui-ace-xml.directive.js          |    4 +-
 .../src/main/js/app/helpers/jade/mixins.jade    |   17 +-
 modules/web-console/src/main/js/app/index.js    |   18 +-
 .../src/main/js/app/modules/Demo/Demo.module.js |   14 +-
 .../QueryNotebooks/QueryNotebooks.provider.js   |  115 -
 .../main/js/app/modules/agent/agent.module.js   |  323 +++
 .../configuration/generator/Pom.service.js      |   46 +-
 .../form/field/input/datalist.directive.js      |    6 +-
 .../modules/form/field/input/text.directive.js  |    9 +-
 .../js/app/modules/form/field/input/text.jade   |    2 +-
 .../app/modules/form/group/group.directive.js   |    6 +-
 .../form/validator/java-keywords.directive.js   |    9 +-
 .../src/main/js/app/modules/loading/loading.css |    2 +-
 .../query-notebooks/query-notebooks.module.js   |  115 +
 .../app/modules/states/configuration.state.js   |   32 +-
 .../states/configuration/caches/general.jade    |    2 +-
 .../states/configuration/caches/store.jade      |   37 +-
 .../clusters/attributes.directive.js            |   27 +
 .../configuration/clusters/attributes.jade      |   58 +
 .../states/configuration/clusters/binary.jade   |   12 +-
 .../clusters/collision.directive.js             |   27 +
 .../configuration/clusters/collision.jade       |   60 +
 .../clusters/collision/custom.directive.js      |   27 +
 .../clusters/collision/custom.jade              |   24 +
 .../clusters/collision/fifo-queue.directive.js  |   27 +
 .../clusters/collision/fifo-queue.jade          |   28 +
 .../collision/job-stealing.directive.js         |   27 +
 .../clusters/collision/job-stealing.jade        |   64 +
 .../collision/priority-queue.directive.js       |   27 +
 .../clusters/collision/priority-queue.jade      |   43 +
 .../configuration/clusters/communication.jade   |   30 +-
 .../configuration/clusters/deployment.jade      |   31 +-
 .../configuration/clusters/discovery.jade       |    4 +-
 .../clusters/failover.directive.js              |   27 +
 .../states/configuration/clusters/failover.jade |   82 +
 .../states/configuration/clusters/general.jade  |   20 +-
 .../clusters/general/discovery/cloud.jade       |    6 +-
 .../clusters/general/discovery/google.jade      |   11 +-
 .../clusters/general/discovery/s3.jade          |    4 +-
 .../clusters/general/discovery/zookeeper.jade   |   21 +-
 .../bounded-exponential-backoff.jade            |    6 +-
 .../discovery/zookeeper/retrypolicy/custom.jade |    6 +-
 .../retrypolicy/exponential-backoff.jade        |    6 +-
 .../zookeeper/retrypolicy/forever.jade          |    2 +-
 .../zookeeper/retrypolicy/n-times.jade          |    2 +-
 .../zookeeper/retrypolicy/one-time.jade         |    2 +-
 .../zookeeper/retrypolicy/until-elapsed.jade    |    4 +-
 .../states/configuration/clusters/igfs.jade     |    4 +-
 .../configuration/clusters/logger.directive.js  |   27 +
 .../states/configuration/clusters/logger.jade   |   65 +
 .../clusters/logger/custom.directive.js         |   27 +
 .../configuration/clusters/logger/custom.jade   |   24 +
 .../clusters/logger/log4j.directive.js          |   27 +
 .../configuration/clusters/logger/log4j.jade    |   49 +
 .../clusters/logger/log4j2.directive.js         |   27 +
 .../configuration/clusters/logger/log4j2.jade   |   38 +
 .../states/configuration/domains/query.jade     |    4 +-
 .../states/configuration/domains/store.jade     |    2 +-
 .../modules/states/configuration/igfs/misc.jade |    2 +-
 .../main/js/app/modules/user/Auth.service.js    |   19 +-
 .../js/app/services/AgentMonitor.service.js     |  337 ---
 .../src/main/js/app/services/cleanup.service.js |    4 +-
 .../src/main/js/build/system.config.js          |   55 +-
 .../src/main/js/controllers/admin-controller.js |   14 +-
 .../main/js/controllers/caches-controller.js    |  336 ++-
 .../main/js/controllers/clusters-controller.js  |  537 +++--
 .../src/main/js/controllers/common-module.js    | 1052 +++++----
 .../main/js/controllers/domains-controller.js   |  847 ++++---
 .../src/main/js/controllers/igfs-controller.js  |  212 +-
 .../main/js/controllers/profile-controller.js   |   60 +-
 .../src/main/js/controllers/sql-controller.js   | 2173 +++++++++---------
 .../src/main/js/generator/generator-common.js   |  151 +-
 .../src/main/js/generator/generator-java.js     |  998 ++++----
 .../src/main/js/generator/generator-optional.js |    2 +-
 .../main/js/generator/generator-properties.js   |   25 +-
 .../src/main/js/generator/generator-readme.js   |    6 +-
 .../src/main/js/generator/generator-xml.js      |  430 +++-
 .../main/js/gulpfile.babel.js/tasks/eslint.js   |    3 +
 .../src/main/js/public/images/cluster.png       |  Bin 29670 -> 29376 bytes
 .../src/main/js/public/images/query-table.png   |  Bin 42253 -> 29189 bytes
 .../src/main/js/public/stylesheets/style.scss   |   28 +
 modules/web-console/src/main/js/serve/agent.js  |  222 +-
 .../web-console/src/main/js/serve/browser.js    |  120 +-
 .../web-console/src/main/js/serve/configure.js  |    1 +
 modules/web-console/src/main/js/serve/mongo.js  |   58 +-
 .../src/main/js/serve/routes/agent.js           |    3 +-
 .../src/main/js/serve/routes/caches.js          |    4 +-
 .../src/main/js/serve/routes/clusters.js        |    8 +-
 .../src/main/js/serve/routes/igfs.js            |    2 +-
 .../src/main/js/serve/routes/profile.js         |   11 +-
 .../src/main/js/serve/routes/public.js          |   35 +-
 .../main/js/views/configuration/clusters.jade   |    4 +
 .../js/views/configuration/domains-import.jade  |  325 +--
 .../src/main/js/views/settings/profile.jade     |   14 +-
 .../web-console/src/main/js/views/signin.jade   |   26 +-
 .../src/main/js/views/sql/cache-metadata.jade   |    2 +-
 .../web-console/src/main/js/views/sql/sql.jade  |  205 +-
 172 files changed, 7375 insertions(+), 4567 deletions(-)
----------------------------------------------------------------------



[22/22] ignite git commit: Merge remote-tracking branch 'origin/master'

Posted by ak...@apache.org.
Merge remote-tracking branch 'origin/master'


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/34fc31e8
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/34fc31e8
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/34fc31e8

Branch: refs/heads/master
Commit: 34fc31e87b3e5f5c31f68421d7200c8848bb6e20
Parents: 6e5061d 8ec6db5
Author: Alexey Kuznetsov <ak...@apache.org>
Authored: Mon Jun 27 10:22:16 2016 +0700
Committer: Alexey Kuznetsov <ak...@apache.org>
Committed: Mon Jun 27 10:22:16 2016 +0700

----------------------------------------------------------------------
 .../GridAffinityFunctionContextImpl.java        |   9 +
 .../platform/PlatformProcessorImpl.java         |  35 +--
 .../affinity/PlatformAffinityFunction.java      | 242 +++++++++++++++
 .../callback/PlatformCallbackGateway.java       |  90 +++++-
 .../callback/PlatformCallbackUtils.java         |  46 +++
 .../dotnet/PlatformDotNetCacheStore.java        |  27 +-
 .../utils/PlatformConfigurationUtils.java       |  13 +-
 .../apache/ignite/resources/SpringResource.java |  15 +-
 .../platforms/cpp/jni/include/ignite/jni/java.h |  19 +-
 modules/platforms/cpp/jni/src/java.cpp          |  35 ++-
 .../Apache.Ignite.Core.Tests.csproj             |   5 +-
 .../Binary/BinarySelfTest.cs                    | 138 +++++---
 .../Cache/Affinity/AffinityFieldTest.cs         | 199 ++++++++++++
 .../Cache/Affinity/AffinityFunctionTest.cs      | 282 +++++++++++++++++
 .../Cache/Affinity/AffinityTest.cs              | 138 ++++++++
 .../Cache/CacheAffinityFieldTest.cs             | 199 ------------
 .../Cache/CacheAffinityTest.cs                  | 139 ---------
 .../Cache/CacheConfigurationTest.cs             |   6 +-
 .../Cache/Store/CacheStoreTest.cs               |  11 +-
 .../native-client-test-cache-affinity.xml       |   2 +-
 .../IgniteConfigurationSerializerTest.cs        |  14 +-
 .../Apache.Ignite.Core.Tests/LifecycleTest.cs   |   9 +
 .../Apache.Ignite.Core.Tests/ReconnectTest.cs   |   9 +
 .../Apache.Ignite.Core.Tests/TestRunner.cs      |   6 +-
 .../Apache.Ignite.Core.Tests/TestUtils.cs       |   4 +-
 .../Apache.Ignite.Core.csproj                   |   3 +
 .../Cache/Affinity/AffinityFunctionBase.cs      | 102 +++++-
 .../Cache/Affinity/AffinityFunctionContext.cs   | 116 +++++++
 .../Cache/Affinity/AffinityTopologyVersion.cs   | 138 ++++++++
 .../Cache/Affinity/IAffinityFunction.cs         |  55 +++-
 .../Cache/Configuration/CacheConfiguration.cs   |   9 +-
 .../Apache.Ignite.Core/Events/EventBase.cs      |   2 +-
 .../Apache.Ignite.Core/Events/EventReader.cs    |   8 +-
 .../dotnet/Apache.Ignite.Core/IIgnite.cs        |  25 ++
 .../IgniteConfigurationSection.xsd              |   4 +-
 .../dotnet/Apache.Ignite.Core/Ignition.cs       |  25 +-
 .../Impl/Binary/BinaryReflectiveActions.cs      |  10 +-
 .../dotnet/Apache.Ignite.Core/Impl/Ignite.cs    |  38 ++-
 .../Apache.Ignite.Core/Impl/IgniteProxy.cs      |  37 ++-
 .../Impl/LifecycleBeanHolder.cs                 |   2 +-
 .../Impl/Unmanaged/UnmanagedCallbackHandlers.cs |   6 +
 .../Impl/Unmanaged/UnmanagedCallbacks.cs        | 120 ++++++-
 .../Lifecycle/ClientReconnectEventArgs.cs       |  47 +++
 .../dotnet/Apache.Ignite.sln.DotSettings        |   7 +-
 .../GridResourceSpringBeanInjector.java         |  39 ++-
 .../GridSpringResourceInjectionSelfTest.java    | 311 ++++++++++++++++---
 .../spring-resource-with-duplicate-beans.xml    |  30 ++
 .../processors/resource/spring-resource.xml     |   2 +-
 48 files changed, 2299 insertions(+), 529 deletions(-)
----------------------------------------------------------------------



[07/22] ignite git commit: IGNITE-3113: CPP: Binary containers documentation. This closes #711.

Posted by ak...@apache.org.
IGNITE-3113: CPP: Binary containers documentation. This closes #711.


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/93445607
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/93445607
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/93445607

Branch: refs/heads/master
Commit: 934456070d336fe2a5081f44429ec22d8fc22603
Parents: 5e91594
Author: isapego <is...@gridgain.com>
Authored: Thu Jun 23 15:49:45 2016 +0300
Committer: vozerov-gridgain <vo...@gridgain.com>
Committed: Thu Jun 23 15:49:45 2016 +0300

----------------------------------------------------------------------
 .../include/ignite/binary/binary_containers.h   | 191 +++++++++++++++----
 .../include/ignite/binary/binary_raw_reader.h   |  25 ++-
 .../include/ignite/binary/binary_raw_writer.h   |  13 ++
 .../include/ignite/binary/binary_reader.h       |  13 ++
 .../include/ignite/binary/binary_writer.h       |  13 ++
 .../platforms/cpp/common/include/ignite/date.h  |   2 +-
 .../cpp/common/include/ignite/ignite_error.h    |  19 +-
 .../cpp/common/include/ignite/timestamp.h       |   2 +-
 .../platforms/cpp/common/src/ignite_error.cpp   |  12 +-
 .../cpp/core/include/ignite/cache/cache.h       | 186 ++++++++++++++++--
 .../cpp/core/include/ignite/cache/cache_entry.h |  14 +-
 .../include/ignite/cache/query/query_argument.h |  33 ++--
 .../include/ignite/cache/query/query_cursor.h   |  17 +-
 .../ignite/cache/query/query_fields_cursor.h    |  11 +-
 .../ignite/cache/query/query_fields_row.h       |  22 ++-
 .../include/ignite/cache/query/query_scan.h     |  10 +-
 .../core/include/ignite/cache/query/query_sql.h |  15 +-
 .../ignite/cache/query/query_sql_fields.h       |  15 +-
 .../platforms/cpp/core/include/ignite/ignite.h  |  21 +-
 .../core/include/ignite/ignite_configuration.h  |   4 +-
 .../cpp/core/include/ignite/ignition.h          |   2 +-
 .../include/ignite/transactions/transaction.h   |  68 ++++++-
 .../ignite/transactions/transaction_consts.h    |  84 ++++++--
 .../ignite/transactions/transaction_metrics.h   |  13 +-
 .../include/ignite/transactions/transactions.h  |  36 +++-
 modules/platforms/cpp/core/namespaces.dox       |   2 +-
 modules/platforms/cpp/cpp.dxg                   |   4 +-
 27 files changed, 687 insertions(+), 160 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/binary/include/ignite/binary/binary_containers.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/binary/include/ignite/binary/binary_containers.h b/modules/platforms/cpp/binary/include/ignite/binary/binary_containers.h
index 946101c..8f26416 100644
--- a/modules/platforms/cpp/binary/include/ignite/binary/binary_containers.h
+++ b/modules/platforms/cpp/binary/include/ignite/binary/binary_containers.h
@@ -37,37 +37,51 @@ namespace ignite
     {
         /**
          * Binary string array writer.
+         *
+         * Can be used to write array of strings one by one.
+         *
+         * Use Write() method to write array string by string, then finilize
+         * the writing by calling Close() method. Once the Close() method have
+         * been called, instance is not usable and will throw an IgniteError
+         * on any subsequent attempt to use it.
          */
         class IGNITE_IMPORT_EXPORT BinaryStringArrayWriter
         {
         public:
             /**
              * Constructor.
-             * 
+             * Internal call. Should not be used by user.
+             *
+             * @param impl Writer implementation.
              * @param id Identifier.
-             * @param impl Writer.
              */
             BinaryStringArrayWriter(impl::binary::BinaryWriterImpl* impl, int32_t id);
 
             /**
-             * Write string.
+             * Write null-terminated string.
+             *
+             * @param val Null-terminated character sequence to write.
              *
-             * @param val Null-terminated character sequence.
+             * @throw IgniteError if the writer instance is closed already.
              */
             void Write(const char* val);
 
             /**
              * Write string.
              *
-             * @param val String.
-             * @param len String length (characters).
+             * @param val String to write.
+             * @param len String length in bytes.
+             *
+             * @throw IgniteError if the writer instance is closed already.
              */
             void Write(const char* val, int32_t len);
 
             /**
              * Write string.
              *
-             * @param val String.
+             * @param val String to write.
+             *
+             * @throw IgniteError if the writer instance is closed already.
              */
             void Write(const std::string& val)
             {
@@ -76,18 +90,32 @@ namespace ignite
 
             /**
              * Close the writer.
+             *
+             * This method should be called to finilize writing
+             * of the array.
+             *
+             * @throw IgniteError if the writer instance is closed already.
              */
             void Close();
+
         private:
             /** Implementation delegate. */
             impl::binary::BinaryWriterImpl* impl; 
 
-            /** Idnetifier. */
+            /** Identifier. */
             const int32_t id;    
         };
 
         /**
-         * Binary collection writer.
+         * Binary array writer.
+         *
+         * Can be used to write array of values of the specific type one by
+         * one.
+         *
+         * Use Write() method to write array value by value, then finilize
+         * the writing by calling Close() method. Once the Close() method have
+         * been called, instance is not usable and will throw an IgniteError
+         * on any subsequent attempt to use it.
          */
         template<typename T>
         class IGNITE_IMPORT_EXPORT BinaryArrayWriter
@@ -95,11 +123,13 @@ namespace ignite
         public:
             /**
              * Constructor.
+             * Internal call. Should not be used by user.
              *
-             * @param impl Writer.
+             * @param impl Writer implementation.
              * @param id Identifier.
              */
-            BinaryArrayWriter(impl::binary::BinaryWriterImpl* impl, int32_t id) : impl(impl), id(id)
+            BinaryArrayWriter(impl::binary::BinaryWriterImpl* impl, int32_t id) :
+                impl(impl), id(id)
             {
                 // No-op.
             }
@@ -107,7 +137,9 @@ namespace ignite
             /**
              * Write a value.
              *
-             * @param val Value.
+             * @param val Value to write.
+             *
+             * @throw IgniteError if the writer instance is closed already.
              */
             void Write(const T& val)
             {
@@ -116,11 +148,17 @@ namespace ignite
 
             /**
              * Close the writer.
+             *
+             * This method should be called to finilize writing
+             * of the array.
+             *
+             * @throw IgniteError if the writer instance is closed already.
              */
             void Close()
             {
                 impl->CommitContainer(id);
             }
+
         private:
             /** Implementation delegate. */
             impl::binary::BinaryWriterImpl* impl; 
@@ -131,6 +169,14 @@ namespace ignite
 
         /**
          * Binary collection writer.
+         *
+         * Can be used to write collection of values of the specific type one by
+         * one.
+         *
+         * Use Write() method to write collection value by value, then finilize
+         * the writing by calling Close() method. Once the Close() method have
+         * been called, instance is not usable and will throw an IgniteError
+         * on any subsequent attempt to use it.
          */
         template<typename T>
         class IGNITE_IMPORT_EXPORT BinaryCollectionWriter
@@ -138,11 +184,13 @@ namespace ignite
         public:
             /**
              * Constructor.
+             * Internal call. Should not be used by user.
              *
-             * @param impl Writer.
+             * @param impl Writer implementation.
              * @param id Identifier.
              */
-            BinaryCollectionWriter(impl::binary::BinaryWriterImpl* impl, int32_t id) : impl(impl), id(id)
+            BinaryCollectionWriter(impl::binary::BinaryWriterImpl* impl, int32_t id) :
+                impl(impl), id(id)
             {
                 // No-op.
             }
@@ -150,7 +198,9 @@ namespace ignite
             /**
              * Write a value.
              *
-             * @param val Value.
+             * @param val Value to write.
+             *
+             * @throw IgniteError if the writer instance is closed already.
              */
             void Write(const T& val)
             {
@@ -159,6 +209,11 @@ namespace ignite
 
             /**
              * Close the writer.
+             *
+             * This method should be called to finilize writing
+             * of the collection.
+             *
+             * @throw IgniteError if the writer instance is closed already.
              */
             void Close()
             {
@@ -174,6 +229,13 @@ namespace ignite
 
         /**
          * Binary map writer.
+         *
+         * Can be used to write map element by element.
+         *
+         * Use Write() method to write map value by value, then finilize
+         * the writing by calling Close() method. Once the Close() method have
+         * been called, instance is not usable and will throw an IgniteError
+         * on any subsequent attempt to use it.
          */
         template<typename K, typename V>
         class IGNITE_IMPORT_EXPORT BinaryMapWriter
@@ -181,19 +243,24 @@ namespace ignite
         public:
             /**
              * Constructor.
+             * Internal call. Should not be used by user.
              *
-             * @param impl Writer.
+             * @param impl Writer implementation.
+             * @param id Identifier.
              */
-            BinaryMapWriter(impl::binary::BinaryWriterImpl* impl, int32_t id) : impl(impl), id(id)
+            BinaryMapWriter(impl::binary::BinaryWriterImpl* impl, int32_t id) :
+                impl(impl), id(id)
             {
                 // No-op.
             }
 
             /**
-             * Write a value.
+             * Write a map entry.
+             *
+             * @param key Key element of the map entry.
+             * @param val Value element of the map entry.
              *
-             * @param key Key.
-             * @param val Value.
+             * @throw IgniteError if the writer instance is closed already.
              */
             void Write(const K& key, const V& val)
             {
@@ -202,6 +269,10 @@ namespace ignite
 
             /**
              * Close the writer.
+             *
+             * This method should be called to finilize writing of the map.
+             *
+             * @throw IgniteError if the writer instance is closed already.
              */
             void Close()
             {
@@ -217,14 +288,20 @@ namespace ignite
 
         /**
          * Binary string array reader.
+         *
+         * Can be used to read array of strings string by string.
+         *
+         * Use GetNext() method to read array value by value while HasNext()
+         * method returns true.
          */
         class IGNITE_IMPORT_EXPORT BinaryStringArrayReader
         {
         public:
             /**
              * Constructor.
+             * Internal call. Should not be used by user.
              *
-             * @param impl Reader.
+             * @param impl Reader implementation.
              * @param id Identifier.
              * @param size Array size.
              */
@@ -240,20 +317,24 @@ namespace ignite
             /**
              * Get next element.
              *
-             * @param res Array to store data to. 
+             * @param res Buffer to store data to. 
              * @param len Expected length of string. NULL terminator will be set in case len is 
              *     greater than real string length.
              * @return Actual amount of elements read. If "len" argument is less than actual
              *     array size or resulting array is set to null, nothing will be written
              *     to resulting array and returned value will contain required array length.
              *     -1 will be returned in case array in stream was null.
+             *
+             * @throw IgniteError if there is no element to read.
              */
             int32_t GetNext(char* res, int32_t len);
 
             /**
              * Get next element.
              *
-             * @return String. 
+             * @return String.
+             *
+             * @throw IgniteError if there is no element to read.
              */
             std::string GetNext()
             {
@@ -279,22 +360,30 @@ namespace ignite
             int32_t GetSize() const;
 
             /**
-             * Whether array is NULL.
+             * Check whether array is NULL.
+             *
+             * @return True if the array is NULL.
              */
             bool IsNull() const;
+
         private:
             /** Implementation delegate. */
             impl::binary::BinaryReaderImpl* impl;  
 
             /** Identifier. */
-            const int32_t id;    
+            const int32_t id;
 
             /** Size. */
-            const int32_t size;                              
+            const int32_t size;
         };
 
         /**
          * Binary array reader.
+         *
+         * Can be used to read array of values of the specific type one by one.
+         *
+         * Use GetNext() method to read array value by value while HasNext()
+         * method returns true.
          */
         template<typename T>
         class BinaryArrayReader
@@ -302,8 +391,9 @@ namespace ignite
         public:
             /**
              * Constructor.
+             * Internal call. Should not be used by user.
              *
-             * @param impl Reader.
+             * @param impl Reader implementation.
              * @param id Identifier.
              * @param size Array size.
              */
@@ -327,6 +417,8 @@ namespace ignite
              * Read next element.
              *
              * @return Next element.
+             *
+             * @throw IgniteError if there is no element to read.
              */
             T GetNext()
             {
@@ -344,7 +436,9 @@ namespace ignite
             }
 
             /**
-             * Whether array is NULL.
+             * Check whether array is NULL.
+             *
+             * @return True if the array is NULL.
              */
             bool IsNull()
             {
@@ -363,6 +457,12 @@ namespace ignite
 
         /**
          * Binary collection reader.
+         *
+         * Can be used to read collection of values of the specific type
+         * one by one.
+         *
+         * Use GetNext() method to read array value by value while HasNext()
+         * method returns true.
          */
         template<typename T>
         class BinaryCollectionReader
@@ -370,8 +470,9 @@ namespace ignite
         public:
             /**
              * Constructor.
+             * Internal call. Should not be used by user.
              *
-             * @param impl Reader.
+             * @param impl Reader implementation.
              * @param id Identifier.
              * @param type Collection type.
              * @param size Collection size.
@@ -396,6 +497,8 @@ namespace ignite
              * Read next element.
              *
              * @return Next element.
+             *
+             * @throw IgniteError if there is no element to read.
              */
             T GetNext()
             {
@@ -405,7 +508,8 @@ namespace ignite
             /**
              * Get collection type.
              *
-             * @return Type.
+             * @return Collection type. See CollectionType for the list of
+             *     available values and their description.
              */
             CollectionType GetType()
             {
@@ -423,7 +527,9 @@ namespace ignite
             }
 
             /**
-             * Whether collection is NULL.
+             * Check whether collection is NULL.
+             *
+             * @return True if the collection is NULL.
              */
             bool IsNull()
             {
@@ -445,6 +551,11 @@ namespace ignite
 
         /**
          * Binary map reader.
+         *
+         * Can be used to read map entry by entry.
+         *
+         * Use GetNext() method to read array value by value while HasNext()
+         * method returns true.
          */
         template<typename K, typename V>
         class BinaryMapReader
@@ -452,8 +563,9 @@ namespace ignite
         public:
             /**
              * Constructor.
+             * Internal call. Should not be used by user.
              *
-             * @param impl Reader.
+             * @param impl Reader implementation.
              * @param id Identifier.
              * @param type Map type.
              * @param size Map size.
@@ -477,8 +589,12 @@ namespace ignite
             /**
              * Read next element.
              *
-             * @param key Key.
-             * @param val Value.
+             * @param key Pointer to buffer where key element should be stored.
+             *     Should not be null.
+             * @param val Pointer to buffer where value element should be
+             *     stored. Should not be null.
+             *
+             * @throw IgniteError if there is no element to read.
              */
             void GetNext(K* key, V* val)
             {
@@ -488,7 +604,8 @@ namespace ignite
             /**
              * Get map type.
              *
-             * @return Type.
+             * @return Map type. See MapType for the list of available values
+             *     and their description.
              */
             MapType GetType()
             {
@@ -506,7 +623,9 @@ namespace ignite
             }
 
             /**
-             * Whether map is NULL.
+             * Check whether map is NULL.
+             *
+             * @return True if the map is NULL.
              */
             bool IsNull()
             {

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/binary/include/ignite/binary/binary_raw_reader.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/binary/include/ignite/binary/binary_raw_reader.h b/modules/platforms/cpp/binary/include/ignite/binary/binary_raw_reader.h
index 473be3d..3104437 100644
--- a/modules/platforms/cpp/binary/include/ignite/binary/binary_raw_reader.h
+++ b/modules/platforms/cpp/binary/include/ignite/binary/binary_raw_reader.h
@@ -41,6 +41,17 @@ namespace ignite
     {
         /**
          * Binary raw reader.
+         *
+         * This class implemented as a reference to an implementation so copying
+         * of this class instance will only create another reference to the same
+         * underlying object.
+         *
+         * @note User should not store copy of this instance as it can be
+         *     invalidated as soon as the initially passed to user instance has
+         *     been destructed. For example this means that if user received an
+         *     instance of this class as a function argument then he should not
+         *     store and use copy of this class out of the scope of this
+         *     function.
          */
         class IGNITE_IMPORT_EXPORT BinaryRawReader
         {
@@ -48,6 +59,8 @@ namespace ignite
             /**
              * Constructor.
              *
+             * Internal method. Should not be used by user.
+             *
              * @param impl Implementation.
              */
             BinaryRawReader(ignite::impl::binary::BinaryReaderImpl* impl);
@@ -205,14 +218,14 @@ namespace ignite
             int32_t ReadDoubleArray(double* res, int32_t len);
 
             /**
-             * Read Guid. Maps to "UUID" type in Java.
+             * Read Guid. Maps to "java.util.UUID" type in Java.
              *
              * @return Result.
              */
             Guid ReadGuid();
 
             /**
-             * Read array of Guids. Maps to "UUID[]" type in Java.
+             * Read array of Guids. Maps to "java.util.UUID[]" type in Java.
              *
              * @param res Array to store data to.
              * @param len Expected length of array.             
@@ -224,14 +237,14 @@ namespace ignite
             int32_t ReadGuidArray(Guid* res, int32_t len);
 
             /**
-             * Read Date. Maps to "Date" type in Java.
+             * Read Date. Maps to "java.util.Date" type in Java.
              *
              * @return Result.
              */
             Date ReadDate();
 
             /**
-             * Read array of Dates. Maps to "Date[]" type in Java.
+             * Read array of Dates. Maps to "java.util.Date[]" type in Java.
              *
              * @param res Array to store data to.
              * @param len Expected length of array.             
@@ -243,14 +256,14 @@ namespace ignite
             int32_t ReadDateArray(Date* res, int32_t len);
 
             /**
-             * Read Timestamp. Maps to "Timestamp" type in Java.
+             * Read Timestamp. Maps to "java.sql.Timestamp" type in Java.
              *
              * @return Result.
              */
             Timestamp ReadTimestamp();
 
             /**
-             * Read array of Timestamps. Maps to "Timestamp[]" type in Java.
+             * Read array of Timestamps. Maps to "java.sql.Timestamp[]" type in Java.
              *
              * @param res Array to store data to.
              * @param len Expected length of array.             

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/binary/include/ignite/binary/binary_raw_writer.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/binary/include/ignite/binary/binary_raw_writer.h b/modules/platforms/cpp/binary/include/ignite/binary/binary_raw_writer.h
index 41cfef7..c960406 100644
--- a/modules/platforms/cpp/binary/include/ignite/binary/binary_raw_writer.h
+++ b/modules/platforms/cpp/binary/include/ignite/binary/binary_raw_writer.h
@@ -40,6 +40,17 @@ namespace ignite
     {
         /**
          * Binary raw writer.
+         *
+         * This class implemented as a reference to an implementation so copying
+         * of this class instance will only create another reference to the same
+         * underlying object.
+         *
+         * @note User should not store copy of this instance as it can be
+         *     invalidated as soon as the initially passed to user instance has
+         *     been destructed. For example this means that if user received an
+         *     instance of this class as a function argument then he should not
+         *     store and use copy of this class out of the scope of this
+         *     function.
          */
         class IGNITE_IMPORT_EXPORT BinaryRawWriter
         {
@@ -47,6 +58,8 @@ namespace ignite
             /**
              * Constructor.
              *
+             * Internal method. Should not be used by user.
+             *
              * @param impl Implementation.
              */
             BinaryRawWriter(ignite::impl::binary::BinaryWriterImpl* impl);

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/binary/include/ignite/binary/binary_reader.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/binary/include/ignite/binary/binary_reader.h b/modules/platforms/cpp/binary/include/ignite/binary/binary_reader.h
index 3e5bbb1..ac70f39 100644
--- a/modules/platforms/cpp/binary/include/ignite/binary/binary_reader.h
+++ b/modules/platforms/cpp/binary/include/ignite/binary/binary_reader.h
@@ -39,6 +39,17 @@ namespace ignite
     {
         /**
          * Binary reader.
+         *
+         * This class implemented as a reference to an implementation so copying
+         * of this class instance will only create another reference to the same
+         * underlying object.
+         *
+         * @note User should not store copy of this instance as it can be
+         *     invalidated as soon as the initially passed to user instance has
+         *     been destructed. For example this means that if user received an
+         *     instance of this class as a function argument then he should not
+         *     store and use copy of this class out of the scope of this
+         *     function.
          */
         class IGNITE_IMPORT_EXPORT BinaryReader
         {
@@ -46,6 +57,8 @@ namespace ignite
             /**
              * Constructor.
              *
+             * Internal method. Should not be used by user.
+             *
              * @param impl Implementation.
              */
             BinaryReader(ignite::impl::binary::BinaryReaderImpl* impl);

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/binary/include/ignite/binary/binary_writer.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/binary/include/ignite/binary/binary_writer.h b/modules/platforms/cpp/binary/include/ignite/binary/binary_writer.h
index 1bee82a..1923694 100644
--- a/modules/platforms/cpp/binary/include/ignite/binary/binary_writer.h
+++ b/modules/platforms/cpp/binary/include/ignite/binary/binary_writer.h
@@ -36,6 +36,17 @@ namespace ignite
     {
         /**
          * Binary writer.
+         *
+         * This class implemented as a reference to an implementation so copying
+         * of this class instance will only create another reference to the same
+         * underlying object.
+         *
+         * @note User should not store copy of this instance as it can be
+         *     invalidated as soon as the initially passed to user instance has
+         *     been destructed. For example this means that if user received an
+         *     instance of this class as a function argument then he should not
+         *     store and use copy of this class out of the scope of this
+         *     function.
          */
         class IGNITE_IMPORT_EXPORT BinaryWriter
         {
@@ -43,6 +54,8 @@ namespace ignite
             /**
              * Constructor.
              *
+             * Internal method. Should not be used by user.
+             *
              * @param impl Implementation.
              */
             BinaryWriter(ignite::impl::binary::BinaryWriterImpl* impl);

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/common/include/ignite/date.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/common/include/ignite/date.h b/modules/platforms/cpp/common/include/ignite/date.h
index 31fe5d0..ffdebd3 100644
--- a/modules/platforms/cpp/common/include/ignite/date.h
+++ b/modules/platforms/cpp/common/include/ignite/date.h
@@ -30,7 +30,7 @@
 namespace ignite
 {
     /**
-     * Date type.
+     * %Date type.
      */
     class IGNITE_IMPORT_EXPORT Date
     {

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/common/include/ignite/ignite_error.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/common/include/ignite/ignite_error.h b/modules/platforms/cpp/common/include/ignite/ignite_error.h
index edba67a..4c0e263 100644
--- a/modules/platforms/cpp/common/include/ignite/ignite_error.h
+++ b/modules/platforms/cpp/common/include/ignite/ignite_error.h
@@ -76,7 +76,7 @@ namespace ignite
 {
     namespace java
     {
-        /* Error constants. */
+        /* JNI error constants. */
         const int IGNITE_JNI_ERR_SUCCESS = 0;
         const int IGNITE_JNI_ERR_GENERIC = 1;
         const int IGNITE_JNI_ERR_JVM_INIT = 2;
@@ -84,7 +84,7 @@ namespace ignite
     }
 
     /**
-     * Ignite error information.
+     * %Ignite error information.
      */
     class IGNITE_IMPORT_EXPORT IgniteError : public std::exception
     {
@@ -119,7 +119,7 @@ namespace ignite
         /** Binary error. */
         static const int IGNITE_ERR_BINARY = 1002;
 
-        /** Generic Ignite error. */
+        /** Generic %Ignite error. */
         static const int IGNITE_ERR_GENERIC = 2000;
 
         /** Illegal argument passed. */
@@ -202,12 +202,13 @@ namespace ignite
         static void ThrowIfNeeded(IgniteError& err);
 
         /**
-         * Create empty error.
+         * Default constructor.
+         * Creates empty error. Code is IGNITE_SUCCESS and message is NULL.
          */
         IgniteError();
 
         /**
-         * Create error with specific code.
+         * Create error with specific code. Message is set to NULL.
          *
          * @param code Error code.
          */
@@ -232,7 +233,7 @@ namespace ignite
          * Assignment operator.
          *
          * @param other Other instance.
-         * @return Assignment result.
+         * @return *this.
          */
         IgniteError& operator=(const IgniteError& other);
 
@@ -251,7 +252,7 @@ namespace ignite
         /**
          * Get error message.
          *
-         * @return Error message.
+         * @return Error message. Can be NULL.
          */
         const char* GetText() const IGNITE_NO_THROW;
 
@@ -264,12 +265,12 @@ namespace ignite
         virtual const char* what() const IGNITE_NO_THROW;
 
         /**
-         * Set error.
+         * Initializes IgniteError instance from the JNI error.
          *
          * @param jniCode Error code.
          * @param jniCls Error class.
          * @param jniMsg Error message.
-         * @param err Error.
+         * @param err Error. Can not be NULL.
          */
         static void SetError(const int jniCode, const char* jniCls, const char* jniMsg, IgniteError* err);
     private:

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/common/include/ignite/timestamp.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/common/include/ignite/timestamp.h b/modules/platforms/cpp/common/include/ignite/timestamp.h
index 4528e53..14b83fa 100644
--- a/modules/platforms/cpp/common/include/ignite/timestamp.h
+++ b/modules/platforms/cpp/common/include/ignite/timestamp.h
@@ -32,7 +32,7 @@
 namespace ignite
 {
     /**
-     * Timestamp type.
+     * %Timestamp type.
      */
     class IGNITE_IMPORT_EXPORT Timestamp
     {

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/common/src/ignite_error.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/common/src/ignite_error.cpp b/modules/platforms/cpp/common/src/ignite_error.cpp
index 722214b..5acbed2 100644
--- a/modules/platforms/cpp/common/src/ignite_error.cpp
+++ b/modules/platforms/cpp/common/src/ignite_error.cpp
@@ -14,6 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include <utility>
+
 #include <ignite/ignite_error.h>
 #include <ignite/common/utils.h>
 
@@ -61,14 +63,8 @@ namespace ignite
         {
             IgniteError tmp(other);
 
-            int tmpCode = code;
-            char* tmpMsg = msg;
-            
-            code = tmp.code;
-            msg = tmp.msg;
-
-            tmp.code = tmpCode;
-            tmp.msg = tmpMsg;
+            std::swap(code, tmp.code);
+            std::swap(msg, tmp.msg);
         }
 
         return *this;

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/core/include/ignite/cache/cache.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/core/include/ignite/cache/cache.h b/modules/platforms/cpp/core/include/ignite/cache/cache.h
index e60c843..59b7a6a 100644
--- a/modules/platforms/cpp/core/include/ignite/cache/cache.h
+++ b/modules/platforms/cpp/core/include/ignite/cache/cache.h
@@ -46,6 +46,15 @@ namespace ignite
     {
         /**
          * Main entry point for all Data Grid APIs.
+         *
+         * Both key and value types should be default-constructable,
+         * copy-constructable and assignable. Also BinaryType class
+         * template should be specialized for both types.
+         *
+         * This class implemented as a reference to an implementation so copying
+         * of this class instance will only create another reference to the same
+         * underlying object. Underlying object released automatically once all
+         * the instances are destructed.
          */
         template<typename K, typename V>
         class IGNITE_IMPORT_EXPORT Cache
@@ -53,14 +62,23 @@ namespace ignite
         public:
             /**
              * Constructor.
+             *
+             * Internal method. Should not be used by user.
+             *
+             * @param impl Implementation.
              */
-            Cache(impl::cache::CacheImpl* impl) : impl(ignite::common::concurrent::SharedPointer<impl::cache::CacheImpl>(impl))
+            Cache(impl::cache::CacheImpl* impl) :
+                impl(impl)
             {
                 // No-op.
             }
 
             /**
-             * Name of this cache (null for default cache).
+             * Get name of this cache (null for default cache).
+             *
+             * This method should only be used on the valid instance.
+             *
+             * @return Name of this cache (null for default cache).
              */
             const char* GetName() const
             {
@@ -71,6 +89,8 @@ namespace ignite
              * Checks whether this cache contains no key-value mappings.
              * Semantically equals to Cache.Size(IGNITE_PEEK_MODE_PRIMARY) == 0.
              *
+             * This method should only be used on the valid instance.
+             *
              * @return True if cache is empty.
              */
             bool IsEmpty()
@@ -88,6 +108,8 @@ namespace ignite
              * Checks whether this cache contains no key-value mappings.
              * Semantically equals to Cache.Size(IGNITE_PEEK_MODE_PRIMARY) == 0.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param err Error.
              * @return True if cache is empty.
              */
@@ -99,6 +121,8 @@ namespace ignite
             /**
              * Check if cache contains mapping for this key.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key.
              * @return True if cache contains mapping for this key.
              */
@@ -116,6 +140,8 @@ namespace ignite
             /**
              * Check if cache contains mapping for this key.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key.
              * @param err Error.
              * @return True if cache contains mapping for this key.
@@ -130,6 +156,8 @@ namespace ignite
             /**
              * Check if cache contains mapping for these keys.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param keys Keys.
              * @return True if cache contains mapping for all these keys.
              */
@@ -147,6 +175,8 @@ namespace ignite
             /**
              * Check if cache contains mapping for these keys.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param keys Keys.
              * @param err Error.
              * @return True if cache contains mapping for all these keys.
@@ -165,6 +195,8 @@ namespace ignite
              * This method does not participate in any transactions, however, it may peek at transactional
              * value depending on the peek modes used.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key.
              * @param peekModes Peek modes.
              * @return Value.
@@ -187,6 +219,8 @@ namespace ignite
              * This method does not participate in any transactions, however, it may peek at transactional
              * value depending on the peek modes used.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key.
              * @param peekModes Peek modes.
              * @param err Error.
@@ -209,6 +243,8 @@ namespace ignite
              * will be loaded from persistent store.
              * This method is transactional and will enlist the entry into ongoing transaction if there is one.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key.
              * @return Value.
              */
@@ -230,6 +266,8 @@ namespace ignite
              * will be loaded from persistent store.
              * This method is transactional and will enlist the entry into ongoing transaction if there is one.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key.
              * @param err Error.
              * @return Value.
@@ -251,6 +289,8 @@ namespace ignite
              * will be loaded from persistent store.
              * This method is transactional and will enlist the entry into ongoing transaction if there is one.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param keys Keys.
              * @return Map of key-value pairs.
              */
@@ -272,6 +312,8 @@ namespace ignite
              * will be loaded from persistent store.
              * This method is transactional and will enlist the entry into ongoing transaction if there is one.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param keys Keys.
              * @param err Error.
              * @return Map of key-value pairs.
@@ -291,6 +333,8 @@ namespace ignite
              * If the cache previously contained a mapping for the key,
              * the old value is replaced by the specified value.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key with which the specified value is to be associated.
              * @param val Value to be associated with the specified key.
              */
@@ -308,6 +352,8 @@ namespace ignite
              * If the cache previously contained a mapping for the key,
              * the old value is replaced by the specified value.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key with which the specified value is to be associated.
              * @param val Value to be associated with the specified key.
              * @param err Error.
@@ -324,6 +370,8 @@ namespace ignite
              * If write-through is enabled, the stored values will be persisted to store.
              * This method is transactional and will enlist the entry into ongoing transaction if there is one.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param vals Key-value pairs to store in cache.
              */
             void PutAll(const std::map<K, V>& vals)
@@ -340,6 +388,8 @@ namespace ignite
              * If write-through is enabled, the stored values will be persisted to store.
              * This method is transactional and will enlist the entry into ongoing transaction if there is one.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param vals Key-value pairs to store in cache.
              * @param err Error.
              */
@@ -354,6 +404,8 @@ namespace ignite
              * Associates the specified value with the specified key in this cache,
              * returning an existing value if one existed.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key with which the specified value is to be associated.
              * @param val Value to be associated with the specified key.
              * @return The value associated with the key at the start of the
@@ -374,6 +426,8 @@ namespace ignite
              * Associates the specified value with the specified key in this cache,
              * returning an existing value if one existed.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key with which the specified value is to be associated.
              * @param val Value to be associated with the specified key.
              * @param err Error.
@@ -394,6 +448,8 @@ namespace ignite
              * Atomically replaces the value for a given key if and only if there is
              * a value currently mapped by the key.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key with which the specified value is to be associated.
              * @param val Value to be associated with the specified key.
              * @return The previous value associated with the specified key, or
@@ -414,6 +470,8 @@ namespace ignite
              * Atomically replaces the value for a given key if and only if there is
              * a value currently mapped by the key.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key with which the specified value is to be associated.
              * @param val Value to be associated with the specified key.
              * @param err Error.
@@ -433,6 +491,8 @@ namespace ignite
             /**
              * Atomically removes the entry for a key only if currently mapped to some value.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key with which the specified value is associated.
              * @return The value if one existed or null if no mapping existed for this key.
              */
@@ -450,6 +510,8 @@ namespace ignite
             /**
              * Atomically removes the entry for a key only if currently mapped to some value.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key with which the specified value is associated.
              * @param err Error.
              * @return The value if one existed or null if no mapping existed for this key.
@@ -468,6 +530,8 @@ namespace ignite
              * Atomically associates the specified key with the given value if it is not
              * already associated with a value.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key with which the specified value is to be associated.
              * @param val Value to be associated with the specified key.
              * @return True if a value was set.
@@ -487,6 +551,8 @@ namespace ignite
              * Atomically associates the specified key with the given value if it is not
              * already associated with a value.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key with which the specified value is to be associated.
              * @param val Value to be associated with the specified key.
              * @param err Error.
@@ -510,6 +576,8 @@ namespace ignite
              * If write-through is enabled, the stored value will be persisted to store.
              * This method is transactional and will enlist the entry into ongoing transaction if there is one.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key to store in cache.
              * @param val Value to be associated with the given key.
              * @return Previously contained value regardless of whether put happened or not
@@ -537,6 +605,8 @@ namespace ignite
              * If write-through is enabled, the stored value will be persisted to store.
              * This method is transactional and will enlist the entry into ongoing transaction if there is one.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key to store in cache.
              * @param val Value to be associated with the given key.
              * @param err Error.
@@ -562,6 +632,8 @@ namespace ignite
              * If write-through is enabled, the stored value will be persisted to store.
              * This method is transactional and will enlist the entry into ongoing transaction if there is one.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key to store in cache.
              * @param val Value to be associated with the given key.
              * @return True if the value was replaced.
@@ -586,6 +658,8 @@ namespace ignite
              * If write-through is enabled, the stored value will be persisted to store.
              * This method is transactional and will enlist the entry into ongoing transaction if there is one.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key to store in cache.
              * @param val Value to be associated with the given key.
              * @param err Error.
@@ -603,6 +677,8 @@ namespace ignite
              * old value passed as argument.
              * This method is transactional and will enlist the entry into ongoing transaction if there is one.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key to store in cache.
              * @param oldVal Old value to match.
              * @param newVal Value to be associated with the given key.
@@ -624,6 +700,8 @@ namespace ignite
              * old value passed as argument.
              * This method is transactional and will enlist the entry into ongoing transaction if there is one.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key to store in cache.
              * @param oldVal Old value to match.
              * @param newVal Value to be associated with the given key.
@@ -638,8 +716,12 @@ namespace ignite
             }
 
             /**
-             * Attempts to evict all entries associated with keys. Note, that entry will be evicted only
-             * if it's not used (not participating in any locks or transactions).
+             * Attempts to evict all entries associated with keys.
+             *
+             * @note Entry will be evicted only if it's not used (not
+             * participating in any locks or transactions).
+             *
+             * This method should only be used on the valid instance.
              *
              * @param keys Keys to evict from cache.
              */
@@ -653,8 +735,12 @@ namespace ignite
             }
 
             /**
-             * Attempts to evict all entries associated with keys. Note, that entry will be evicted only
-             * if it's not used (not participating in any locks or transactions).
+             * Attempts to evict all entries associated with keys.
+             *
+             * @note Entry will be evicted only if it's not used (not
+             * participating in any locks or transactions).
+             *
+             * This method should only be used on the valid instance.
              *
              * @param keys Keys to evict from cache.
              * @param err Error.
@@ -668,6 +754,8 @@ namespace ignite
 
             /**
              * Clear cache.
+             *
+             * This method should only be used on the valid instance.
              */
             void Clear()
             {
@@ -681,6 +769,8 @@ namespace ignite
             /**
              * Clear cache.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param err Error.
              */
             void Clear(IgniteError& err)
@@ -692,6 +782,8 @@ namespace ignite
              * Clear entry from the cache and swap storage, without notifying listeners or CacheWriters.
              * Entry is cleared only if it is not currently locked, and is not participating in a transaction.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key to clear.
              */
             void Clear(const K& key)
@@ -707,6 +799,8 @@ namespace ignite
              * Clear entry from the cache and swap storage, without notifying listeners or CacheWriters.
              * Entry is cleared only if it is not currently locked, and is not participating in a transaction.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key to clear.
              * @param err Error.
              */
@@ -721,6 +815,8 @@ namespace ignite
              * Clear entries from the cache and swap storage, without notifying listeners or CacheWriters.
              * Entry is cleared only if it is not currently locked, and is not participating in a transaction.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param keys Keys to clear.
              */
             void ClearAll(const std::set<K>& keys)
@@ -736,6 +832,8 @@ namespace ignite
              * Clear entries from the cache and swap storage, without notifying listeners or CacheWriters.
              * Entry is cleared only if it is not currently locked, and is not participating in a transaction.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param keys Keys to clear.
              * @param err Error.
              */
@@ -749,9 +847,12 @@ namespace ignite
             /**
              * Clear entry from the cache and swap storage, without notifying listeners or CacheWriters.
              * Entry is cleared only if it is not currently locked, and is not participating in a transaction.
-             * Note that this operation is local as it merely clears an entry from local cache, it does not
+             *
+             * @note This operation is local as it merely clears an entry from local cache, it does not
              * remove entries from remote caches.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key to clear.
              */
             void LocalClear(const K& key)
@@ -766,9 +867,12 @@ namespace ignite
             /**
              * Clear entry from the cache and swap storage, without notifying listeners or CacheWriters.
              * Entry is cleared only if it is not currently locked, and is not participating in a transaction.
-             * Note that this operation is local as it merely clears an entry from local cache, it does not
+             *
+             * @note This operation is local as it merely clears an entry from local cache, it does not
              * remove entries from remote caches.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key to clear.
              * @param err Error.
              */
@@ -782,9 +886,12 @@ namespace ignite
             /**
              * Clear entries from the cache and swap storage, without notifying listeners or CacheWriters.
              * Entry is cleared only if it is not currently locked, and is not participating in a transaction.
-             * Note that this operation is local as it merely clears entries from local cache, it does not
+             *
+             * @note This operation is local as it merely clears entries from local cache, it does not
              * remove entries from remote caches.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param keys Keys to clear.
              */
             void LocalClearAll(const std::set<K>& keys)
@@ -799,9 +906,12 @@ namespace ignite
             /**
              * Clear entries from the cache and swap storage, without notifying listeners or CacheWriters.
              * Entry is cleared only if it is not currently locked, and is not participating in a transaction.
-             * Note that this operation is local as it merely clears entries from local cache, it does not
+             *
+             * @note This operation is local as it merely clears entries from local cache, it does not
              * remove entries from remote caches.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param keys Keys to clear.
              * @param err Error.
              */
@@ -822,6 +932,8 @@ namespace ignite
              * If write-through is enabled, the value will be removed from store.
              * This method is transactional and will enlist the entry into ongoing transaction if there is one.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key whose mapping is to be removed from cache.
              * @return False if there was no matching key.
              */
@@ -846,6 +958,8 @@ namespace ignite
              * If write-through is enabled, the value will be removed from store.
              * This method is transactional and will enlist the entry into ongoing transaction if there is one.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key whose mapping is to be removed from cache.
              * @param err Error.
              * @return False if there was no matching key.
@@ -862,6 +976,8 @@ namespace ignite
              * If write-through is enabled, the value will be removed from store.
              * This method is transactional and will enlist the entry into ongoing transaction if there is one.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key whose mapping is to be removed from cache.
              * @param val Value to match against currently cached value.
              * @return True if entry was removed, false otherwise.
@@ -882,6 +998,8 @@ namespace ignite
              * If write-through is enabled, the value will be removed from store.
              * This method is transactional and will enlist the entry into ongoing transaction if there is one.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param key Key whose mapping is to be removed from cache.
              * @param val Value to match against currently cached value.
              * @param err Error.
@@ -899,6 +1017,8 @@ namespace ignite
              * If write-through is enabled, the value will be removed from store.
              * This method is transactional and will enlist the entry into ongoing transaction if there is one.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param keys Keys whose mappings are to be removed from cache.
              */
             void RemoveAll(const std::set<K>& keys)
@@ -915,6 +1035,8 @@ namespace ignite
              * If write-through is enabled, the value will be removed from store.
              * This method is transactional and will enlist the entry into ongoing transaction if there is one.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param keys Keys whose mappings are to be removed from cache.
              * @param err Error.
              */
@@ -930,6 +1052,8 @@ namespace ignite
              * If write-through is enabled, the value will be removed from store.
              * This method is transactional and will enlist the entry into ongoing transaction if there is one.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param err Error.
              */
             void RemoveAll()
@@ -946,6 +1070,8 @@ namespace ignite
              * If write-through is enabled, the value will be removed from store.
              * This method is transactional and will enlist the entry into ongoing transaction if there is one.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param err Error.
              */
             void RemoveAll(IgniteError& err)
@@ -956,6 +1082,8 @@ namespace ignite
             /**
              * Gets the number of all entries cached on this node.
              *
+             * This method should only be used on the valid instance.
+             *
              * @return Cache size on this node.
              */
             int32_t LocalSize()
@@ -966,6 +1094,8 @@ namespace ignite
             /**
              * Gets the number of all entries cached on this node.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param err Error.
              * @return Cache size on this node.
              */
@@ -977,6 +1107,8 @@ namespace ignite
             /**
              * Gets the number of all entries cached on this node.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param Peek modes.
              * @return Cache size on this node.
              */
@@ -994,6 +1126,8 @@ namespace ignite
             /**
              * Gets the number of all entries cached on this node.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param Peek modes.
              * @param err Error.
              * @return Cache size on this node.
@@ -1005,7 +1139,9 @@ namespace ignite
 
             /**
              * Gets the number of all entries cached across all nodes.
-             * NOTE: this operation is distributed and will query all participating nodes for their cache sizes.
+             * @note this operation is distributed and will query all participating nodes for their cache sizes.
+             *
+             * This method should only be used on the valid instance.
              *
              * @return Cache size across all nodes.
              */
@@ -1016,7 +1152,9 @@ namespace ignite
 
             /**
              * Gets the number of all entries cached across all nodes.
-             * NOTE: this operation is distributed and will query all participating nodes for their cache sizes.
+             * @note This operation is distributed and will query all participating nodes for their cache sizes.
+             *
+             * This method should only be used on the valid instance.
              *
              * @param err Error.
              * @return Cache size across all nodes.
@@ -1028,7 +1166,9 @@ namespace ignite
 
             /**
              * Gets the number of all entries cached across all nodes.
-             * NOTE: this operation is distributed and will query all participating nodes for their cache sizes.
+             * @note This operation is distributed and will query all participating nodes for their cache sizes.
+             *
+             * This method should only be used on the valid instance.
              *
              * @param Peek modes.
              * @return Cache size across all nodes.
@@ -1046,7 +1186,9 @@ namespace ignite
 
             /**
              * Gets the number of all entries cached across all nodes.
-             * NOTE: this operation is distributed and will query all participating nodes for their cache sizes.
+             * @note This operation is distributed and will query all participating nodes for their cache sizes.
+             *
+             * This method should only be used on the valid instance.
              *
              * @param Peek modes.
              * @param err Error.
@@ -1060,6 +1202,8 @@ namespace ignite
             /**
              * Perform SQL query.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param qry Query.
              * @return Query cursor.
              */
@@ -1077,6 +1221,8 @@ namespace ignite
             /**
              * Perform SQL query.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param qry Query.
              * @param err Error.
              * @return Query cursor.
@@ -1091,6 +1237,8 @@ namespace ignite
             /**
              * Perform text query.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param qry Query.
              * @return Query cursor.
              */
@@ -1108,6 +1256,8 @@ namespace ignite
             /**
              * Perform text query.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param qry Query.
              * @param err Error.
              * @return Query cursor.
@@ -1122,6 +1272,8 @@ namespace ignite
             /**
              * Perform scan query.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param qry Query.
              * @return Query cursor.
              */
@@ -1139,6 +1291,8 @@ namespace ignite
             /**
              * Perform scan query.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param qry Query.
              * @param err Error.
              * @return Query cursor.
@@ -1153,6 +1307,8 @@ namespace ignite
             /**
              * Perform sql fields query.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param qry Query.
              * @return Query cursor.
              */
@@ -1170,6 +1326,8 @@ namespace ignite
             /**
              * Perform sql fields query.
              *
+             * This method should only be used on the valid instance.
+             *
              * @param qry Query.
              * @param err Error.
              * @return Query cursor.

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/core/include/ignite/cache/cache_entry.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/core/include/ignite/cache/cache_entry.h b/modules/platforms/cpp/core/include/ignite/cache/cache_entry.h
index f709650..9810600 100644
--- a/modules/platforms/cpp/core/include/ignite/cache/cache_entry.h
+++ b/modules/platforms/cpp/core/include/ignite/cache/cache_entry.h
@@ -30,7 +30,7 @@ namespace ignite
     namespace cache
     {
         /**
-         * Cache entry class template.
+         * %Cache entry class template.
          *
          * Both key and value types should be default-constructable,
          * copy-constructable and assignable.
@@ -67,10 +67,10 @@ namespace ignite
              *
              * @param other Other instance.
              */
-            CacheEntry(const CacheEntry& other)
+            CacheEntry(const CacheEntry& other) :
+                key(other.key), val(other.val)
             {
-                key = other.key;
-                val = other.val;
+                // No-op.
             }
 
             /**
@@ -82,10 +82,8 @@ namespace ignite
             {
                 if (this != &other)
                 {
-                    CacheEntry tmp(other);
-
-                    std::swap(key, tmp.key);
-                    std::swap(val, tmp.val);
+                    key = other.key;
+                    val = other.val;
                 }
 
                 return *this;

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/core/include/ignite/cache/query/query_argument.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/core/include/ignite/cache/query/query_argument.h b/modules/platforms/cpp/core/include/ignite/cache/query/query_argument.h
index 933bd60..65578ce 100644
--- a/modules/platforms/cpp/core/include/ignite/cache/query/query_argument.h
+++ b/modules/platforms/cpp/core/include/ignite/cache/query/query_argument.h
@@ -27,11 +27,11 @@
 #include "ignite/binary/binary_raw_writer.h"
 
 namespace ignite
-{    
+{
     namespace cache
     {
         namespace query
-        {            
+        {
             /**
              * Base class for all query arguments.
              */
@@ -49,18 +49,24 @@ namespace ignite
                 /**
                  * Copy argument. 
                  *
-                 * @return Copy.
+                 * @return Copy of this argument instance.
                  */
                 virtual QueryArgumentBase* Copy() const = 0;
 
                 /**
-                 * Write argument.
+                 * Write argument using provided writer.
+                 *
+                 * @param writer Writer to use to write this argument.
                  */
                 virtual void Write(ignite::binary::BinaryRawWriter& writer) = 0;
             };
 
             /**
-             * Query argument.
+             * Query argument class template.
+             *
+             * Template argument type should be copy-constructable and
+             * assignable. Also BinaryType class template should be specialized
+             * for this type.
              */
             template<typename T>
             class QueryArgument : public QueryArgumentBase
@@ -71,7 +77,8 @@ namespace ignite
                  *
                  * @param val Value.
                  */
-                QueryArgument(const T& val) : val(val)
+                QueryArgument(const T& val) :
+                    val(val)
                 {
                     // No-op.
                 }
@@ -81,26 +88,22 @@ namespace ignite
                  *
                  * @param other Other instance.
                  */
-                QueryArgument(const QueryArgument& other)
+                QueryArgument(const QueryArgument& other) :
+                    val(other.val)
                 {
-                    val = other.val;
+                    // No-op.
                 }
 
                 /**
                  * Assignment operator.
                  *
                  * @param other Other instance.
+                 * @return *this.
                  */
                 QueryArgument& operator=(const QueryArgument& other) 
                 {
                     if (this != &other)
-                    {
-                        QueryArgument tmp(other);
-
-                        T val0 = val;
-                        val = tmp.val;
-                        tmp.val = val0;
-                    }
+                        val = other.val;
 
                     return *this;
                 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/core/include/ignite/cache/query/query_cursor.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/core/include/ignite/cache/query/query_cursor.h b/modules/platforms/cpp/core/include/ignite/cache/query/query_cursor.h
index 45eb54a..4c46662 100644
--- a/modules/platforms/cpp/core/include/ignite/cache/query/query_cursor.h
+++ b/modules/platforms/cpp/core/include/ignite/cache/query/query_cursor.h
@@ -42,7 +42,13 @@ namespace ignite
              * Query cursor class template.
              *
              * Both key and value types should be default-constructable,
-             * copy-constructable and assignable.
+             * copy-constructable and assignable. Also BinaryType class
+             * template should be specialized for both types.
+             *
+             * This class implemented as a reference to an implementation so copying
+             * of this class instance will only create another reference to the same
+             * underlying object. Underlying object released automatically once all
+             * the instances are destructed.
              */
             template<typename K, typename V>
             class QueryCursor
@@ -73,11 +79,12 @@ namespace ignite
 
                 /**
                  * Check whether next entry exists.
-                 * Throws IgniteError class instance in case of failure.
                  *
                  * This method should only be used on the valid instance.
                  *
                  * @return True if next entry exists.
+                 *
+                 * @throw IgniteError class instance in case of failure.
                  */
                 bool HasNext()
                 {
@@ -117,11 +124,12 @@ namespace ignite
 
                 /**
                  * Get next entry.
-                 * Throws IgniteError class instance in case of failure.
                  *
                  * This method should only be used on the valid instance.
                  *
                  * @return Next entry.
+                 *
+                 * @throw IgniteError class instance in case of failure.
                  */
                 CacheEntry<K, V> GetNext()
                 {
@@ -175,11 +183,12 @@ namespace ignite
 
                 /**
                  * Get all entries.
-                 * Throws IgniteError class instance in case of failure.
                  *
                  * This method should only be used on the valid instance.
                  *
                  * @param Vector where query entries will be stored.
+                 *
+                 * @throw IgniteError class instance in case of failure.
                  */
                 void GetAll(std::vector<CacheEntry<K, V>>& res)
                 {

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/core/include/ignite/cache/query/query_fields_cursor.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/core/include/ignite/cache/query/query_fields_cursor.h b/modules/platforms/cpp/core/include/ignite/cache/query/query_fields_cursor.h
index 3952ece..3946e1c 100644
--- a/modules/platforms/cpp/core/include/ignite/cache/query/query_fields_cursor.h
+++ b/modules/platforms/cpp/core/include/ignite/cache/query/query_fields_cursor.h
@@ -41,6 +41,11 @@ namespace ignite
         {
             /**
              * Query fields cursor.
+             *
+             * This class implemented as a reference to an implementation so copying
+             * of this class instance will only create another reference to the same
+             * underlying object. Underlying object released automatically once all
+             * the instances are destructed.
              */
             class QueryFieldsCursor
             {
@@ -70,11 +75,12 @@ namespace ignite
                 
                 /**
                  * Check whether next entry exists.
-                 * Throws IgniteError class instance in case of failure.
                  *
                  * This method should only be used on the valid instance.
                  *
                  * @return True if next entry exists.
+                 *
+                 * @throw IgniteError class instance in case of failure.
                  */
                 bool HasNext()
                 {
@@ -114,11 +120,12 @@ namespace ignite
 
                 /**
                  * Get next entry.
-                 * Throws IgniteError class instance in case of failure.
                  *
                  * This method should only be used on the valid instance.
                  *
                  * @return Next entry.
+                 *
+                 * @throw IgniteError class instance in case of failure.
                  */
                 QueryFieldsRow GetNext()
                 {

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/core/include/ignite/cache/query/query_fields_row.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/core/include/ignite/cache/query/query_fields_row.h b/modules/platforms/cpp/core/include/ignite/cache/query/query_fields_row.h
index 521da76..d3ac2de 100644
--- a/modules/platforms/cpp/core/include/ignite/cache/query/query_fields_row.h
+++ b/modules/platforms/cpp/core/include/ignite/cache/query/query_fields_row.h
@@ -40,6 +40,11 @@ namespace ignite
         {
             /**
              * Query fields cursor.
+             *
+             * This class implemented as a reference to an implementation so copying
+             * of this class instance will only create another reference to the same
+             * underlying object. Underlying object released automatically once all
+             * the instances are destructed.
              */
             class QueryFieldsRow
             {
@@ -47,8 +52,7 @@ namespace ignite
                 /**
                  * Default constructor.
                  *
-                 * Constructed instance is not valid and thus can not be used
-                 * as a cursor.
+                 * Constructed instance is not valid and thus can not be used.
                  */
                 QueryFieldsRow() : impl(0)
                 {
@@ -69,11 +73,12 @@ namespace ignite
 
                 /**
                  * Check whether next entry exists.
-                 * Throws IgniteError class instance in case of failure.
                  *
                  * This method should only be used on the valid instance.
                  *
                  * @return True if next entry exists.
+                 *
+                 * @throw IgniteError class instance in case of failure.
                  */
                 bool HasNext()
                 {
@@ -113,11 +118,16 @@ namespace ignite
 
                 /**
                  * Get next entry.
-                 * Throws IgniteError class instance in case of failure.
+                 *
+                 * Template argument type should be default-constructable,
+                 * copy-constructable and assignable. Also BinaryType class
+                 * template should be specialized for this type.
                  *
                  * This method should only be used on the valid instance.
                  *
                  * @return Next entry.
+                 *
+                 * @throw IgniteError class instance in case of failure.
                  */
                 template<typename T>
                 T GetNext()
@@ -135,6 +145,10 @@ namespace ignite
                  * Get next entry.
                  * Properly sets error param in case of failure.
                  *
+                 * Template argument type should be default-constructable,
+                 * copy-constructable and assignable. Also BinaryType class
+                 * template should be specialized for this type.
+                 *
                  * This method should only be used on the valid instance.
                  *
                  * @param err Used to set operation result.

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/core/include/ignite/cache/query/query_scan.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/core/include/ignite/cache/query/query_scan.h b/modules/platforms/cpp/core/include/ignite/cache/query/query_scan.h
index d4dd565..4228ba5 100644
--- a/modules/platforms/cpp/core/include/ignite/cache/query/query_scan.h
+++ b/modules/platforms/cpp/core/include/ignite/cache/query/query_scan.h
@@ -29,11 +29,11 @@
 #include "ignite/binary/binary_raw_writer.h"
 
 namespace ignite
-{    
+{
     namespace cache
     {
         namespace query
-        {         
+        {
             /**
              * Scan query.
              */
@@ -47,7 +47,7 @@ namespace ignite
                 {
                     // No-op.
                 }
-                
+
                 /**
                  * Constructor.
                  *
@@ -57,7 +57,7 @@ namespace ignite
                 {
                     // No-op.
                 }
-                
+
                 /**
                  * Get partition to scan.
                  *
@@ -117,7 +117,7 @@ namespace ignite
                 {
                     this->loc = loc;
                 }
-                
+
                 /**
                  * Write query info to the stream.
                  *

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/core/include/ignite/cache/query/query_sql.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/core/include/ignite/cache/query/query_sql.h b/modules/platforms/cpp/core/include/ignite/cache/query/query_sql.h
index d80fa51..f7a00fa 100644
--- a/modules/platforms/cpp/core/include/ignite/cache/query/query_sql.h
+++ b/modules/platforms/cpp/core/include/ignite/cache/query/query_sql.h
@@ -31,11 +31,11 @@
 #include "ignite/binary/binary_raw_writer.h"
 
 namespace ignite
-{    
+{
     namespace cache
     {
         namespace query
-        {         
+        {
             /**
              * Sql query.
              */
@@ -48,8 +48,8 @@ namespace ignite
                  * @param type Type name.
                  * @param sql SQL string.
                  */
-                SqlQuery(const std::string& type, const std::string& sql) : type(type), sql(sql), pageSize(1024), 
-                    loc(false), args()
+                SqlQuery(const std::string& type, const std::string& sql)
+                    : type(type), sql(sql), pageSize(1024),  loc(false), args()
                 {
                     // No-op.
                 }
@@ -59,7 +59,8 @@ namespace ignite
                  *
                  * @param other Other instance.
                  */
-                SqlQuery(const SqlQuery& other) : type(other.type), sql(other.sql), pageSize(other.pageSize),
+                SqlQuery(const SqlQuery& other) :
+                    type(other.type), sql(other.sql), pageSize(other.pageSize),
                     loc(other.loc), args()
                 {
                     args.reserve(other.args.size());
@@ -199,6 +200,10 @@ namespace ignite
                 /**
                  * Add argument.
                  *
+                 * Template argument type should be copy-constructable and
+                 * assignable. Also BinaryType class template should be specialized
+                 * for this type.
+                 *
                  * @param arg Argument.
                  */
                 template<typename T>

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/core/include/ignite/cache/query/query_sql_fields.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/core/include/ignite/cache/query/query_sql_fields.h b/modules/platforms/cpp/core/include/ignite/cache/query/query_sql_fields.h
index 4792d34..e21fc93 100644
--- a/modules/platforms/cpp/core/include/ignite/cache/query/query_sql_fields.h
+++ b/modules/platforms/cpp/core/include/ignite/cache/query/query_sql_fields.h
@@ -47,7 +47,8 @@ namespace ignite
                  *
                  * @param sql SQL string.
                  */
-                SqlFieldsQuery(const std::string& sql) : sql(sql), pageSize(1024), loc(false), args()
+                SqlFieldsQuery(const std::string& sql) :
+                    sql(sql), pageSize(1024), loc(false), args()
                 {
                     // No-op.
                 }
@@ -58,7 +59,8 @@ namespace ignite
                  * @param sql SQL string.
                  * @param loc Whether query should be executed locally.
                  */
-                SqlFieldsQuery(const std::string& sql, bool loc) : sql(sql), pageSize(1024), loc(false), args()
+                SqlFieldsQuery(const std::string& sql, bool loc) :
+                    sql(sql), pageSize(1024), loc(false), args()
                 {
                     // No-op.
                 }
@@ -68,7 +70,8 @@ namespace ignite
                  *
                  * @param other Other instance.
                  */
-                SqlFieldsQuery(const SqlFieldsQuery& other) : sql(other.sql), pageSize(other.pageSize), loc(other.loc),
+                SqlFieldsQuery(const SqlFieldsQuery& other) :
+                    sql(other.sql), pageSize(other.pageSize), loc(other.loc),
                     args()
                 {
                     args.reserve(other.args.size());
@@ -106,7 +109,7 @@ namespace ignite
                     for (std::vector<QueryArgumentBase*>::iterator it = args.begin(); it != args.end(); ++it)
                         delete *it;
                 }
-                
+
                 /**
                  * Get SQL string.
                  *
@@ -170,6 +173,10 @@ namespace ignite
                 /**
                  * Add argument.
                  *
+                 * Template argument type should be copy-constructable and
+                 * assignable. Also BinaryType class template should be specialized
+                 * for this type.
+                 *
                  * @param arg Argument.
                  */
                 template<typename T>

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/core/include/ignite/ignite.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/core/include/ignite/ignite.h b/modules/platforms/cpp/core/include/ignite/ignite.h
index e4f9208..311dff2 100644
--- a/modules/platforms/cpp/core/include/ignite/ignite.h
+++ b/modules/platforms/cpp/core/include/ignite/ignite.h
@@ -31,7 +31,12 @@
 namespace ignite
 {
     /**
-     * Main interface to operate with Ignite.
+     * Main interface to operate with %Ignite.
+     *
+     * This class implemented as a reference to an implementation so copying
+     * of this class instance will only create another reference to the same
+     * underlying object. Underlying object released automatically once all
+     * the instances are destructed.
      */
     class IGNITE_IMPORT_EXPORT Ignite
     {
@@ -57,6 +62,8 @@ namespace ignite
         /**
          * Get cache.
          *
+         * This method should only be used on the valid instance.
+         *
          * @param name Cache name.
          * @return Cache.
          */
@@ -75,6 +82,8 @@ namespace ignite
         /**
          * Get cache.
          *
+         * This method should only be used on the valid instance.
+         *
          * @param name Cache name.
          * @param err Error;
          * @return Cache.
@@ -90,6 +99,8 @@ namespace ignite
         /**
          * Get or create cache.
          *
+         * This method should only be used on the valid instance.
+         *
          * @param name Cache name.
          * @return Cache.
          */
@@ -108,6 +119,8 @@ namespace ignite
         /**
          * Get or create cache.
          *
+         * This method should only be used on the valid instance.
+         *
          * @param name Cache name.
          * @param err Error;
          * @return Cache.
@@ -123,6 +136,8 @@ namespace ignite
         /**
          * Create cache.
          *
+         * This method should only be used on the valid instance.
+         *
          * @param name Cache name.
          * @return Cache.
          */
@@ -141,6 +156,8 @@ namespace ignite
         /**
          * Create cache.
          *
+         * This method should only be used on the valid instance.
+         *
          * @param name Cache name.
          * @param err Error;
          * @return Cache.
@@ -156,6 +173,8 @@ namespace ignite
         /**
          * Get transactions.
          *
+         * This method should only be used on the valid instance.
+         *
          * @return Transaction class instance.
          */
         transactions::Transactions GetTransactions();

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/core/include/ignite/ignite_configuration.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/core/include/ignite/ignite_configuration.h b/modules/platforms/cpp/core/include/ignite/ignite_configuration.h
index ee59c11..65c4550 100644
--- a/modules/platforms/cpp/core/include/ignite/ignite_configuration.h
+++ b/modules/platforms/cpp/core/include/ignite/ignite_configuration.h
@@ -32,7 +32,7 @@
 namespace ignite
 {
     /**
-     * Ignite configuration.
+     * %Ignite configuration.
      */
     struct IgniteConfiguration
     {
@@ -58,7 +58,7 @@ namespace ignite
         std::list<std::string> jvmOpts;
 
         /**
-         * Constructor.
+         * Default constructor.
          */
         IgniteConfiguration() : igniteHome(), springCfgPath(), jvmLibPath(), jvmClassPath(),
             jvmInitMem(512), jvmMaxMem(1024), jvmOpts()

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/core/include/ignite/ignition.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/core/include/ignite/ignition.h b/modules/platforms/cpp/core/include/ignite/ignition.h
index 31f5b0b..f88efe5 100644
--- a/modules/platforms/cpp/core/include/ignite/ignition.h
+++ b/modules/platforms/cpp/core/include/ignite/ignition.h
@@ -31,7 +31,7 @@
 namespace ignite
 {
     /**
-     * This class defines a factory for the main Ignite API.
+     * This class defines a factory for the main %Ignite API.
      */
     class IGNITE_IMPORT_EXPORT Ignition
     {


[06/22] ignite git commit: IGNITE-3113: CPP: Binary containers documentation. This closes #711.

Posted by ak...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/core/include/ignite/transactions/transaction.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/core/include/ignite/transactions/transaction.h b/modules/platforms/cpp/core/include/ignite/transactions/transaction.h
index f68470e..b51a42c 100644
--- a/modules/platforms/cpp/core/include/ignite/transactions/transaction.h
+++ b/modules/platforms/cpp/core/include/ignite/transactions/transaction.h
@@ -24,7 +24,6 @@
 #define _IGNITE_TRANSACTIONS_TRANSACTION
 
 #include <ignite/common/concurrent.h>
-#include <ignite/jni/java.h>
 
 #include "ignite/impl/transactions/transaction_impl.h"
 #include "ignite/transactions/transaction_consts.h"
@@ -34,13 +33,26 @@ namespace ignite
     namespace transactions
     {
         /**
-         * Transaction.
+         * %Ignite cache transaction.
+         * Cache transactions have a default 2PC (two-phase-commit) behavior.
+         *
+         * @see TransactionConcurrency and TransactionIsolation for details on
+         * the supported isolation levels and concurrency models.
+         *
+         * This class implemented as a reference to an implementation so copying
+         * of this class instance will only create another reference to the same
+         * underlying object. Underlying object released automatically once all
+         * the instances are destructed.
          */
         class IGNITE_FRIEND_EXPORT Transaction
         {
         public:
             /**
              * Constructor.
+             *
+             * Internal method. Should not be used by user.
+             *
+             * @param impl Implementation.
              */
             Transaction(common::concurrent::SharedPointer<impl::transactions::TransactionImpl> impl);
 
@@ -66,36 +78,60 @@ namespace ignite
 
             /**
              * Commit the transaction.
+             *
+             * This method should only be used on the valid instance.
+             *
+             * @throw IgniteError class instance in case of failure.
              */
             void Commit();
 
             /**
              * Commit the transaction.
              *
+             * Properly sets error param in case of failure.
+             *
+             * This method should only be used on the valid instance.
+             *
              * @param err Error.
              */
             void Commit(IgniteError& err);
 
             /**
              * Rollback the transaction.
+             *
+             * This method should only be used on the valid instance.
+             *
+             * @throw IgniteError class instance in case of failure.
              */
             void Rollback();
 
             /**
              * Rollback the transaction.
              *
+             * Properly sets error param in case of failure.
+             *
+             * This method should only be used on the valid instance.
+             *
              * @param err Error.
              */
             void Rollback(IgniteError& err);
 
             /**
              * Close the transaction.
+             *
+             * This method should only be used on the valid instance.
+             *
+             * @throw IgniteError class instance in case of failure.
              */
             void Close();
 
             /**
              * Close the transaction.
              *
+             * Properly sets error param in case of failure.
+             *
+             * This method should only be used on the valid instance.
+             *
              * @param err Error.
              */
             void Close(IgniteError& err);
@@ -106,6 +142,10 @@ namespace ignite
              * After transaction have been marked as rollback-only it may
              * only be rolled back. Error occurs if such transaction is
              * being commited.
+             *
+             * This method should only be used on the valid instance.
+             *
+             * @throw IgniteError class instance in case of failure.
              */
             void SetRollbackOnly();
 
@@ -116,6 +156,10 @@ namespace ignite
              * only be rolled back. Error occurs if such transaction is
              * being commited.
              *
+             * Properly sets error param in case of failure.
+             *
+             * This method should only be used on the valid instance.
+             *
              * @param err Error.
              */
             void SetRollbackOnly(IgniteError& err);
@@ -128,6 +172,8 @@ namespace ignite
              * being commited.
              *
              * @return True if the transaction is rollback-only.
+             *
+             * @throw IgniteError class instance in case of failure.
              */
             bool IsRollbackOnly();
 
@@ -138,6 +184,10 @@ namespace ignite
              * only be rolled back. Error occurs if such transaction is
              * being commited.
              *
+             * Properly sets error param in case of failure.
+             *
+             * This method should only be used on the valid instance.
+             *
              * @param err Error.
              * @return True if the transaction is rollback-only.
              */
@@ -146,13 +196,21 @@ namespace ignite
             /**
              * Get current state.
              *
+             * This method should only be used on the valid instance.
+             *
              * @return Transaction state.
+             *
+             * @throw IgniteError class instance in case of failure.
              */
             TransactionState GetState();
 
             /**
              * Get current state.
              *
+             * Properly sets error param in case of failure.
+             *
+             * This method should only be used on the valid instance.
+             *
              * @param err Error.
              * @return Transaction state.
              */
@@ -161,6 +219,8 @@ namespace ignite
             /**
              * Get concurrency.
              *
+             * This method should only be used on the valid instance.
+             *
              * @return Concurrency.
              */
             TransactionConcurrency GetConcurrency() const
@@ -171,6 +231,8 @@ namespace ignite
             /**
              * Get isolation.
              *
+             * This method should only be used on the valid instance.
+             *
              * @return Isolation.
              */
             TransactionIsolation GetIsolation() const
@@ -181,6 +243,8 @@ namespace ignite
             /**
              * Get timeout.
              *
+             * This method should only be used on the valid instance.
+             *
              * @return Timeout in milliseconds. Zero if timeout is infinite.
              */
             int64_t GetTimeout() const

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/core/include/ignite/transactions/transaction_consts.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/core/include/ignite/transactions/transaction_consts.h b/modules/platforms/cpp/core/include/ignite/transactions/transaction_consts.h
index 5799b9f..8b25d3a 100644
--- a/modules/platforms/cpp/core/include/ignite/transactions/transaction_consts.h
+++ b/modules/platforms/cpp/core/include/ignite/transactions/transaction_consts.h
@@ -17,7 +17,7 @@
 
 /**
  * @file
- * Declares ignite::transactions::TransactionState enumeration.
+ * Declares Transaction-related enumerations.
  */
 
 #ifndef _IGNITE_TRANSACTIONS_TRANSACTION_CONSTS
@@ -28,14 +28,37 @@ namespace ignite
     namespace transactions
     {
         /**
-         * Transaction concurrency control.
+         * Transaction concurrency control model.
          */
         enum TransactionConcurrency
         {
-            /** Optimistic concurrency control. */
+            /**
+             * Optimistic concurrency model. In this mode all cache operations
+             * are not distributed to other nodes until Transaction::Commit()
+             * is called. In this mode one @c 'PREPARE' message will be sent to
+             * participating cache nodes to start acquiring per-transaction
+             * locks, and once all nodes reply @c 'OK', a one-way @c 'COMMIT'
+             * message is sent without waiting for reply.
+             *
+             * Note that in this mode, optimistic failures are only possible in
+             * conjunction with ::IGNITE_TX_ISOLATION_SERIALIZABLE isolation 
+             * level. In all other cases, optimistic transactions will never
+             * fail optimistically and will always be identically ordered on all
+             * participating grid nodes.
+             */
             IGNITE_TX_CONCURRENCY_OPTIMISTIC = 0,
 
-            /** Pessimistic concurrency control. */
+            /**
+             * Pessimistic concurrency model. In this mode a lock is acquired
+             * on all cache operations with exception of read operations in
+             * ::IGNITE_TX_ISOLATION_READ_COMMITTED mode. All optional filters
+             * passed into cache operations will be evaluated after successful
+             * lock acquisition. Whenever Transaction::Commit() is called, a
+             * single one-way @c 'COMMIT' message is sent to participating cache
+             * nodes without waiting for reply. Note that there is no reason for
+             * distributed @c 'PREPARE' step, as all locks have been already
+             * acquired.
+             */
             IGNITE_TX_CONCURRENCY_PESSIMISTIC = 1
         };
 
@@ -44,13 +67,42 @@ namespace ignite
          */
         enum TransactionIsolation
         {
-            /** Read committed isolation level. */
+            /**
+             * Read committed isolation level. This isolation level means that
+             * always a committed value will be provided for read operations.
+             * With this isolation level values are always read from cache
+             * global memory or persistent store every time a value is accessed.
+             * In other words, if the same key is accessed more than once within
+             * the same transaction, it may have different value every time
+             * since global cache memory may be updated concurrently by other
+             * threads.
+             */
             IGNITE_TX_ISOLATION_READ_COMMITTED = 0,
 
-            /** Repeatable read isolation level. */
+            /**
+             * Repeatable read isolation level. This isolation level means that
+             * if a value was read once within transaction, then all consecutive
+             * reads will provide the same in-transaction value. With this
+             * isolation level accessed values are stored within in-transaction
+             * memory, so consecutive access to the same key within the same
+             * transaction will always return the value that was previously read
+             * or updated within this transaction. If concurrency is
+             * ::IGNITE_TX_CONCURRENCY_PESSIMISTIC, then a lock on the key will
+             * be acquired prior to accessing the value.
+             */
             IGNITE_TX_ISOLATION_REPEATABLE_READ = 1,
 
-            /** Serializable isolation level. */
+            /**
+             * Serializable isolation level. This isolation level means that all
+             * transactions occur in a completely isolated fashion, as if all
+             * transactions in the system had executed serially, one after the
+             * other. Read access with this level happens the same way as with
+             * ::IGNITE_TX_ISOLATION_REPEATABLE_READ level. However, in
+             * ::IGNITE_TX_CONCURRENCY_OPTIMISTIC mode, if some transactions
+             * cannot be serially isolated from each other, then one winner will
+             * be picked and the other transactions in conflict will result in
+             * IgniteError being thrown.
+             */
             IGNITE_TX_ISOLATION_SERIALIZABLE = 2
         };
 
@@ -59,31 +111,31 @@ namespace ignite
          */
         enum TransactionState
         {
-            /** Transaction started. */
+            /** %Transaction started. */
             IGNITE_TX_STATE_ACTIVE,
 
-            /** Transaction validating. */
+            /** %Transaction validating. */
             IGNITE_TX_STATE_PREPARING,
 
-            /** Transaction validation succeeded. */
+            /** %Transaction validation succeeded. */
             IGNITE_TX_STATE_PREPARED,
 
-            /** Transaction is marked for rollback. */
+            /** %Transaction is marked for rollback. */
             IGNITE_TX_STATE_MARKED_ROLLBACK,
 
-            /** Transaction commit started (validating finished). */
+            /** %Transaction commit started (validating finished). */
             IGNITE_TX_STATE_COMMITTING,
 
-            /** Transaction commit succeeded. */
+            /** %Transaction commit succeeded. */
             IGNITE_TX_STATE_COMMITTED,
 
-            /** Transaction rollback started (validation failed). */
+            /** %Transaction rollback started (validation failed). */
             IGNITE_TX_STATE_ROLLING_BACK,
 
-            /** Transaction rollback succeeded. */
+            /** %Transaction rollback succeeded. */
             IGNITE_TX_STATE_ROLLED_BACK,
 
-            /** Transaction rollback failed or is otherwise unknown state. */
+            /** %Transaction rollback failed or is otherwise unknown state. */
             IGNITE_TX_STATE_UNKNOWN
         };
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/core/include/ignite/transactions/transaction_metrics.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/core/include/ignite/transactions/transaction_metrics.h b/modules/platforms/cpp/core/include/ignite/transactions/transaction_metrics.h
index 4986a64..00ebd10 100644
--- a/modules/platforms/cpp/core/include/ignite/transactions/transaction_metrics.h
+++ b/modules/platforms/cpp/core/include/ignite/transactions/transaction_metrics.h
@@ -32,13 +32,15 @@ namespace ignite
     namespace transactions
     {
         /**
-         * Transaction metrics.
+         * %Transaction metrics, shared across all caches.
          */
         class IGNITE_IMPORT_EXPORT TransactionMetrics
         {
         public:
             /**
              * Default constructor.
+             *
+             * Constructed instance is not valid.
              */
             TransactionMetrics() :
                 valid(false),
@@ -71,6 +73,8 @@ namespace ignite
 
             /**
              * Copy constructor.
+             *
+             * @param other Another instance.
              */
             TransactionMetrics(const TransactionMetrics& other) :
                 valid(other.valid),
@@ -84,6 +88,9 @@ namespace ignite
 
             /**
              * Assignment operator.
+             *
+             * @param other Another instance.
+             * @return @c *this.
              */
             TransactionMetrics& operator=(const TransactionMetrics& other)
             {
@@ -105,7 +112,7 @@ namespace ignite
             {
                 return commitTime;
             }
-            
+
             /**
              * Get rollback time.
              *
@@ -145,7 +152,7 @@ namespace ignite
              * in case of error. Invalid instances also often can be
              * created using default constructor.
              *
-             * @return True if the instance contains valid data.
+             * @return @c true if the instance contains valid data.
              */
             bool IsValid() const
             {

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/core/include/ignite/transactions/transactions.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/core/include/ignite/transactions/transactions.h b/modules/platforms/cpp/core/include/ignite/transactions/transactions.h
index 130116a..98282bd 100644
--- a/modules/platforms/cpp/core/include/ignite/transactions/transactions.h
+++ b/modules/platforms/cpp/core/include/ignite/transactions/transactions.h
@@ -35,13 +35,22 @@ namespace ignite
     namespace transactions
     {
         /**
-         * Transactions.
+         * %Transactions facade.
+         *
+         * This class implemented as a reference to an implementation so copying
+         * of this class instance will only create another reference to the same
+         * underlying object. Underlying object released automatically once all
+         * the instances are destructed.
          */
         class IGNITE_FRIEND_EXPORT Transactions
         {
         public:
             /**
              * Constructor.
+             *
+             * Internal method. Should not be used by user.
+             *
+             * @param impl Implementation.
              */
             Transactions(ignite::common::concurrent::SharedPointer<impl::transactions::TransactionsImpl> impl);
 
@@ -75,14 +84,16 @@ namespace ignite
             Transaction GetTx();
 
             /**
-             * Start new transaction.
+             * Start new transaction with default isolation, concurrency
+             * and timeout.
              *
              * @return New transaction instance.
              */
             Transaction TxStart();
 
             /**
-             * Start new transaction.
+             * Start new transaction with default isolation, concurrency
+             * and timeout.
              *
              * @param err Error.
              * @return New transaction instance.
@@ -90,7 +101,8 @@ namespace ignite
             Transaction TxStart(IgniteError& err);
 
             /**
-             * Start new transaction.
+             * Starts new transaction with the specified concurrency and
+             * isolation.
              *
              * @param concurrency Concurrency.
              * @param isolation Isolation.
@@ -100,7 +112,8 @@ namespace ignite
                 TransactionIsolation isolation);
 
             /**
-             * Start new transaction.
+             * Starts new transaction with the specified concurrency and
+             * isolation.
              *
              * @param concurrency Concurrency.
              * @param isolation Isolation.
@@ -111,12 +124,14 @@ namespace ignite
                 TransactionIsolation isolation, IgniteError& err);
 
             /**
-             * Start new transaction.
+             * Starts transaction with specified isolation, concurrency,
+             * timeout, and number of participating entries.
              *
              * @param concurrency Concurrency.
              * @param isolation Isolation.
              * @param timeout Timeout. Zero if for infinite timeout.
-             * @param txSize Number of entries participating in transaction (may be approximate).
+             * @param txSize Number of entries participating in transaction
+             *     (may be approximate).
              * @return New transaction instance.
              */
             Transaction TxStart(TransactionConcurrency concurrency,
@@ -129,7 +144,8 @@ namespace ignite
              * @param concurrency Concurrency.
              * @param isolation Isolation.
              * @param timeout Timeout. Zero if for infinite timeout.
-             * @param txSize Number of entries participating in transaction (may be approximate).
+             * @param txSize Number of entries participating in transaction
+             *     (may be approximate).
              * @param err Error.
              * @return New transaction instance.
              */
@@ -138,14 +154,14 @@ namespace ignite
                 int32_t txSize, IgniteError& err);
 
             /**
-             * Get metrics.
+             * Get transaction metrics.
              *
              * @return Metrics instance.
              */
             TransactionMetrics GetMetrics();
 
             /**
-             * Get metrics.
+             * Get transaction metrics.
              *
              * @param err Error.
              * @return Metrics instance.

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/core/namespaces.dox
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/core/namespaces.dox b/modules/platforms/cpp/core/namespaces.dox
index 20aa5ba..0f5f11f 100644
--- a/modules/platforms/cpp/core/namespaces.dox
+++ b/modules/platforms/cpp/core/namespaces.dox
@@ -37,7 +37,7 @@
 	 }
 
 	 /**
-	  * %Ignite Transaction API.
+	  * %Ignite %Transaction API.
 	  */
 	 namespace transactions
 	 {

http://git-wip-us.apache.org/repos/asf/ignite/blob/93445607/modules/platforms/cpp/cpp.dxg
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/cpp.dxg b/modules/platforms/cpp/cpp.dxg
index 605a613..d25d978 100644
--- a/modules/platforms/cpp/cpp.dxg
+++ b/modules/platforms/cpp/cpp.dxg
@@ -1715,8 +1715,8 @@ GENERATE_LEGEND        = YES
 
 DOT_CLEANUP            = YES
 
-;INPUT=core binary
-;EXCLUDE=core/include/ignite/impl core/os/linux/include/ignite/impl core/os/linux/src/impl core/os/win/include/ignite/impl core/os/win/src/impl core/src/impl binary/include/ignite/impl binary/src/impl
+;INPUT=core binary common
+;EXCLUDE=core/include/ignite/impl core/os/linux/include/ignite/impl core/os/linux/src/impl core/os/win/include/ignite/impl core/os/win/src/impl core/src/impl binary/include/ignite/impl binary/src/impl common/include/ignite/common common/os
 ;STRIP_FROM_PATH=core/include/ignite core/src binary/include/ignite binary/src
 ;OUTPUT_DIRECTORY=../../clients/target/cppdoc
 ;PROJECT_LOGO=../../../assembly/docfiles/ignite_logo.png