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 2015/08/03 12:49:04 UTC

incubator-ignite git commit: IGNITE-843 Added checks for valid java names. Set focus to field on what check failed.

Repository: incubator-ignite
Updated Branches:
  refs/heads/ignite-843 824b7d74d -> 6036af68b


IGNITE-843 Added checks for valid java names. Set focus to field on what check failed.


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

Branch: refs/heads/ignite-843
Commit: 6036af68b84e40a80c94182b086d731e31907c10
Parents: 824b7d7
Author: AKuznetsov <ak...@gridgain.com>
Authored: Mon Aug 3 17:49:05 2015 +0700
Committer: AKuznetsov <ak...@gridgain.com>
Committed: Mon Aug 3 17:49:05 2015 +0700

----------------------------------------------------------------------
 .../main/js/controllers/caches-controller.js    |  35 +--
 .../main/js/controllers/clusters-controller.js  |   6 +-
 .../src/main/js/controllers/common-module.js    | 138 +++++++++---
 .../main/js/controllers/metadata-controller.js  | 216 +++++++++++--------
 .../main/js/controllers/models/metadata.json    |  12 +-
 .../src/main/js/controllers/sql-controller.js   |   2 -
 .../src/main/js/views/includes/controls.jade    |  28 +--
 7 files changed, 284 insertions(+), 153 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/6036af68/modules/control-center-web/src/main/js/controllers/caches-controller.js
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/controllers/caches-controller.js b/modules/control-center-web/src/main/js/controllers/caches-controller.js
index 4f1e342..7844e02 100644
--- a/modules/control-center-web/src/main/js/controllers/caches-controller.js
+++ b/modules/control-center-web/src/main/js/controllers/caches-controller.js
@@ -18,7 +18,7 @@
 controlCenterModule.controller('cachesController', ['$scope', '$http', '$common', '$focus', '$confirm', '$copy', '$table', function ($scope, $http, $common, $focus, $confirm, $copy, $table) {
         $scope.joinTip = $common.joinTip;
         $scope.getModel = $common.getModel;
-        $scope.javaBuildInTypes = $common.javaBuildInTypes;
+        $scope.javaBuildInClasses = $common.javaBuildInClasses;
 
         $scope.tableReset = $table.tableReset;
         $scope.tableNewItem = $table.tableNewItem;
@@ -126,21 +126,26 @@ controlCenterModule.controller('cachesController', ['$scope', '$http', '$common'
             return false;
         };
 
+        function focusInvalidField(index, newId, curId) {
+            $focus(index < 0 ? newId : curId);
+
+            return false;
+        }
+
         $scope.tableSimpleValid = function (item, field, fx, index) {
+            if (!$common.isValidJavaClass('SQL function', fx, false))
+                return focusInvalidField(index, 'newSqlFxField', 'curSqlFxField');
+
             var model = item[field.model];
 
             if ($common.isDefined(model)) {
                 var idx = _.indexOf(model, fx);
 
-                // Found itself.
-                if (index >= 0 && index == idx)
-                    return true;
-
                 // Found duplicate.
-                if (idx >= 0) {
-                    $common.showError('SQL function such class name already exists!');
+                if (idx >= 0 && idx != index) {
+                    $common.showError('SQL function with such class name already exists!');
 
-                    return false;
+                    return focusInvalidField(index, 'newSqlFxField', 'curSqlFxField');
                 }
             }
 
@@ -148,6 +153,12 @@ controlCenterModule.controller('cachesController', ['$scope', '$http', '$common'
         };
 
         $scope.tablePairValid = function (item, field, keyCls, valCls, index) {
+            if (!$common.isValidJavaClass('Indexed type key', keyCls, true))
+                return focusInvalidField(index, 'newIndexedType', 'curIndexedType');
+
+            if (!$common.isValidJavaClass('Indexed type value', valCls, true))
+                return focusInvalidField(index, 'newIndexedType_next', 'curIndexedType_next');
+
             var model = item[field.model];
 
             if ($common.isDefined(model)) {
@@ -155,15 +166,11 @@ controlCenterModule.controller('cachesController', ['$scope', '$http', '$common'
                     return pair.keyClass == keyCls
                 });
 
-                // Found itself.
-                if (index >= 0 && index == idx)
-                    return true;
-
                 // Found duplicate.
-                if (idx >= 0) {
+                if (idx >= 0 && idx != index) {
                     $common.showError('Indexed type with such key class already exists!');
 
-                    return false;
+                    return focusInvalidField(index, 'newIndexedType', 'curIndexedType');
                 }
             }
 

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/6036af68/modules/control-center-web/src/main/js/controllers/clusters-controller.js
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/controllers/clusters-controller.js b/modules/control-center-web/src/main/js/controllers/clusters-controller.js
index 1bc6633..bcbdac2 100644
--- a/modules/control-center-web/src/main/js/controllers/clusters-controller.js
+++ b/modules/control-center-web/src/main/js/controllers/clusters-controller.js
@@ -101,12 +101,8 @@ controlCenterModule.controller('clustersController', ['$scope', '$http', '$commo
             if ($common.isDefined(model)) {
                 var idx = _.indexOf(model, val);
 
-                // Found itself.
-                if (index >= 0 && index == idx)
-                    return true;
-
                 // Found duplicate.
-                if (idx >= 0) {
+                if (idx >= 0 && idx != index) {
                     var msg = 'Such IP address already exists!';
 
                     if (field.model == 'regions')

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/6036af68/modules/control-center-web/src/main/js/controllers/common-module.js
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/controllers/common-module.js b/modules/control-center-web/src/main/js/controllers/common-module.js
index bc4005a..744ea28 100644
--- a/modules/control-center-web/src/main/js/controllers/common-module.js
+++ b/modules/control-center-web/src/main/js/controllers/common-module.js
@@ -58,14 +58,83 @@ controlCenterModule.config(function ($alertProvider) {
 
 // Common functions to be used in controllers.
 controlCenterModule.service('$common', ['$alert', function ($alert) {
+    function isDefined(v) {
+        return !(v === undefined || v === null);
+    }
+
+    function isEmptyArray(arr) {
+        if (isDefined(arr))
+            return arr.length == 0;
+
+        return true;
+    }
+
+    function isEmptyString(s) {
+        if (isDefined(s))
+            return s.trim().length == 0;
+
+        return true;
+    }
+
     var msgModal = undefined;
 
     function errorMessage(errMsg) {
         return errMsg ? errMsg : 'Internal server error.';
     }
 
-    function isDefined(v) {
-        return !(v === undefined || v === null);
+    function showError(msg) {
+        if (msgModal)
+            msgModal.hide();
+
+        msgModal = $alert({title: errorMessage(msg)});
+
+        return false;
+    }
+
+    var javaBuildInClasses = [
+        'BigDecimal, Boolean', 'Byte', 'Date', 'Double', 'Float', 'Integer', 'Long', 'Short', 'String', 'Time', 'Timestamp', 'UUID'
+    ];
+
+    var javaBuildInFullNameClasses = [
+        '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.Short', 'java.lang.String',
+        'java.sql.Time', 'java.sql.Timestamp', 'java.util.UUID'
+    ];
+
+    function isJavaBuildInClass(cls) {
+        if (isEmptyString(cls))
+            return false;
+
+        return _.contains(javaBuildInClasses, cls) || _.contains(javaBuildInFullNameClasses, cls);
+    }
+
+    var javaKeywords = [
+        'abstract',     'assert',        'boolean',      'break',           'byte',
+        'case',         'catch',         'char',         'class',           'const',
+        'continue',     'default',       'do',           'double',          'else',
+        'enum',         'extends',       'false',        'final',           'finally',
+        'float',        'for',           'goto',         'if',              'implements',
+        'import',       'instanceof',    'int',          'interface',       'long',
+        'native',       'new',           'null',         'package',         'private',
+        'protected',    'public',        'return',       'short',           'static',
+        'strictfp',     'super',         'switch',       'synchronized',    'this',
+        'throw',        'throws',        'transient',    'true',            'try',
+        'void',         'volatile',      'while'
+    ];
+
+    var VALID_JAVA_IDENTIFIER = new RegExp('^[a-zA-Z_$][a-zA-Z\d_$]*');
+
+    function isValidJavaIdentifier(msg, ident) {
+        if (isEmptyString(ident))
+            return showError(msg + ' could not be empty!');
+
+        if (_.contains(javaKeywords, ident))
+            return showError(msg + ' could not contains reserved java keyword: "' + ident + '"!');
+
+        if (!VALID_JAVA_IDENTIFIER.test(ident))
+            return showError(msg + ' contains invalid identifier: "' + ident + '"!');
+
+        return true;
     }
 
     return {
@@ -107,19 +176,13 @@ controlCenterModule.service('$common', ['$alert', function ($alert) {
                 return rtrimmed;
             });
 
-            return lines.join("");
+            return lines.join('');
         },
         isDefined: isDefined,
-        isNonEmpty: function (s) {
-            return isDefined(s) && s.trim().length > 0;
-        },
+        isEmptyArray: isEmptyArray,
+        isEmptyString: isEmptyString,
         errorMessage: errorMessage,
-        showError: function (msg) {
-            if (msgModal)
-                msgModal.hide();
-
-            msgModal = $alert({title: errorMessage(msg)});
-        },
+        showError: showError,
         showInfo: function (msg) {
             if (msgModal)
                 msgModal.hide();
@@ -130,9 +193,32 @@ controlCenterModule.service('$common', ['$alert', function ($alert) {
                 duration: 2
             });
         },
-        javaBuildInTypes: [
-            'Boolean', 'Byte', 'Date', 'Double', 'Float', 'Integer', 'Long', 'Short', 'String', 'Time', 'Timestamp', 'UUID'
-        ]
+        javaBuildInClasses: javaBuildInClasses,
+        isJavaBuildInClass: isJavaBuildInClass,
+        isValidJavaIdentifier: isValidJavaIdentifier,
+        isValidJavaClass: function (msg, ident, allowBuildInClass) {
+            if (isEmptyString(ident))
+                return showError(msg + ' could not be empty!');
+
+            var parts = ident.split('.');
+
+            var len = parts.length;
+
+            if (!allowBuildInClass && isJavaBuildInClass(ident))
+                return showError(msg + ' should not be the Java build-in class!');
+
+            if (len < 2 && !isJavaBuildInClass(ident))
+                return showError(msg + ' does not have package specified!');
+
+            for (var i = 0; i < parts.length; i++) {
+                var part = parts[i];
+
+                if (!isValidJavaIdentifier(msg, part))
+                    return false;
+            }
+
+            return true;
+        }
     }
 }]);
 
@@ -165,7 +251,7 @@ controlCenterModule.service('$confirm', function ($modal, $rootScope, $q) {
     return confirmModal;
 });
 
-// "Save as" popup service.
+// 'Save as' popup service.
 controlCenterModule.service('$copy', function ($modal, $rootScope, $q) {
     var scope = $rootScope.$new();
 
@@ -260,7 +346,7 @@ controlCenterModule.service('$table', ['$common', function ($common) {
             }
         },
         tableSimpleSaveVisible: function (newValue) {
-            return $common.isNonEmpty(newValue);
+            return !$common.isEmptyString(newValue);
         },
         tableSimpleUp: function (item, field, index) {
             _tableReset();
@@ -299,7 +385,7 @@ controlCenterModule.service('$table', ['$common', function ($common) {
             }
         },
         tablePairSaveVisible: function (newKey, newValue) {
-            return $common.isNonEmpty(newKey) && $common.isNonEmpty(newValue);
+            return !$common.isEmptyString(newKey) && !$common.isEmptyString(newValue);
         }
     }
 }]);
@@ -335,10 +421,10 @@ controlCenterModule.filter('displayValue', function () {
  */
 controlCenterModule.filter('compact', function () {
     return function (s) {
-        return s.replace("org.apache.ignite.internal.visor.", "o.a.i.i.v.").
-            replace("org.apache.ignite.internal.", "o.a.i.i.").
-            replace("org.apache.ignite.scalar.", "o.a.i.s.").
-            replace("org.apache.ignite.", "o.a.i.");
+        return s.replace('org.apache.ignite.internal.visor.', 'o.a.i.i.v.').
+            replace('org.apache.ignite.internal.', 'o.a.i.i.').
+            replace('org.apache.ignite.scalar.', 'o.a.i.s.').
+            replace('org.apache.ignite.', 'o.a.i.');
     }
 });
 
@@ -426,7 +512,7 @@ controlCenterModule.factory('$focus', function ($timeout, $window) {
 // Directive to focus next element on ENTER key.
 controlCenterModule.directive('enterFocusNext', function ($focus) {
     return function (scope, element, attrs) {
-        element.bind("keydown keypress", function (event) {
+        element.bind('keydown keypress', function (event) {
             if (event.which === 13) {
                 event.preventDefault();
 
@@ -468,13 +554,13 @@ controlCenterModule.controller('auth', [
 
         $scope.valid = false;
 
-        $scope.userDropdown = [{"text": "Profile", "href": "/profile"}];
+        $scope.userDropdown = [{text: 'Profile', href: '/profile'}];
 
         if (!$scope.becomeUsed) {
             if ($scope.user && $scope.user.admin)
-                $scope.userDropdown.push({"text": "Admin Panel", "href": "/admin"});
+                $scope.userDropdown.push({text: 'Admin Panel', href: '/admin'});
 
-            $scope.userDropdown.push({"text": "Log Out", "href": "/logout"});
+            $scope.userDropdown.push({text: 'Log Out', href: '/logout'});
         }
 
         // Pre-fetch an external template populated with a custom scope

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/6036af68/modules/control-center-web/src/main/js/controllers/metadata-controller.js
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/controllers/metadata-controller.js b/modules/control-center-web/src/main/js/controllers/metadata-controller.js
index 91d8abf..5af7ff3 100644
--- a/modules/control-center-web/src/main/js/controllers/metadata-controller.js
+++ b/modules/control-center-web/src/main/js/controllers/metadata-controller.js
@@ -15,10 +15,10 @@
  * limitations under the License.
  */
 
-controlCenterModule.controller('metadataController', ['$scope', '$http', '$common', '$confirm', '$copy', '$table', function ($scope, $http, $common, $confirm, $copy, $table) {
+controlCenterModule.controller('metadataController', ['$scope', '$http', '$common', '$focus', '$confirm', '$copy', '$table', function ($scope, $http, $common, $focus, $confirm, $copy, $table) {
         $scope.joinTip = $common.joinTip;
         $scope.getModel = $common.getModel;
-        $scope.javaBuildInTypes = $common.javaBuildInTypes;
+        $scope.javaBuildInClasses = $common.javaBuildInClasses;
 
         $scope.tableReset = $table.tableReset;
         $scope.tableNewItem = $table.tableNewItem;
@@ -258,11 +258,23 @@ controlCenterModule.controller('metadataController', ['$scope', '$http', '$commo
 
         $scope.metadatas = [];
 
-        $scope.isJavaBuildInType = function () {
+        $scope.required = function (field) {
+            var model = $common.isDefined(field.path) ? field.path + '.' + field.model : field.model;
+
+            var item = $scope.backupItem;
+
+            if (item && item.kind && item.kind != 'query') {
+                return model == 'databaseSchema' || model == 'databaseTable';
+            }
+
+            return false;
+        };
+
+        $scope.isJavaBuildInClass = function () {
             var item = $scope.backupItem;
 
             if (item && item.keyType)
-                return _.contains($common.javaBuildInTypes, item.keyType);
+                return $common.isJavaBuildInClass(item.keyType);
 
             return false;
         };
@@ -343,23 +355,30 @@ controlCenterModule.controller('metadataController', ['$scope', '$http', '$commo
             $scope.backupItem.space = $scope.spaces[0]._id;
         };
 
-        function isEmpty(arr) {
-            if ($common.isDefined(arr))
-                return arr.length == 0;
-            else
-                return true;
-        }
-
         // Check metadata logical consistency.
         function validate(item) {
             var kind = item.kind;
 
+            if (!$common.isValidJavaClass('Key type', item.keyType, true)) {
+                $focus('keyType');
+
+                return false;
+            }
+
+
+            if (!$common.isValidJavaClass('Value type', item.valueType, false)) {
+                $focus('valueType');
+
+                return false;
+            }
+
             if (kind == 'query' || kind == 'both') {
-                if (isEmpty(item.queryFields) && isEmpty(item.ascendingFields) && isEmpty(item.descendingFields)
-                    && isEmpty(item.textFields) && isEmpty(item.groups)) {
-                        $common.showError('SQL fields are not specified!');
+                if ($common.isEmptyArray(item.queryFields) && $common.isEmptyArray(item.ascendingFields) &&
+                    $common.isEmptyArray(item.descendingFields) && $common.isEmptyArray(item.textFields) &&
+                    $common.isEmptyArray(item.groups)) {
+                    $common.showError('SQL fields are not specified!');
 
-                        return false;
+                    return false;
                 }
 
                 var groups = item.groups;
@@ -368,7 +387,7 @@ controlCenterModule.controller('metadataController', ['$scope', '$http', '$commo
                         var group = groups[i];
                         var fields = group.fields;
 
-                        if (isEmpty(fields)) {
+                        if ($common.isEmptyArray(fields)) {
                             $common.showError('Group "' + group.name + '" has no fields.');
 
                             return false;
@@ -384,13 +403,13 @@ controlCenterModule.controller('metadataController', ['$scope', '$http', '$commo
             }
 
             if (kind == 'store' || kind == 'both') {
-                if (isEmpty(item.keyFields) && !_.contains($common.javaBuildInTypes, item.keyType)) {
+                if ($common.isEmptyArray(item.keyFields) && !$common.isJavaBuildInClass(item.keyType)) {
                     $common.showError('Key fields are not specified!');
 
                     return false;
                 }
 
-                if (isEmpty(item.valueFields)) {
+                if ($common.isEmptyArray(item.valueFields)) {
                     $common.showError('Value fields are not specified!');
 
                     return false;
@@ -488,44 +507,55 @@ controlCenterModule.controller('metadataController', ['$scope', '$http', '$commo
                 });
         };
 
+        function focusInvalidField(index, newId, curId) {
+            $focus(index < 0 ? newId : curId);
+
+            return false;
+        }
+
         $scope.tableSimpleValid = function (item, field, name, index) {
             var model = item[field.model];
 
             if ($common.isDefined(model)) {
                 var idx = _.indexOf(model, name);
 
-                // Found itself.
-                if (index >= 0 && index == idx)
-                    return true;
-
                 // Found duplicate.
-                if (idx >= 0) {
+                if (idx >= 0 && idx != index) {
                     $common.showError('Field with such name already exists!');
 
-                    return false;
+                    return focusInvalidField(index, 'newTextField', 'curTextField');
                 }
             }
 
             return true;
         };
 
+        var pairFields = {
+            queryFields: {msg: 'Query field class', newId: 'newQryField', curId: 'curQryField'},
+            ascendingFields: {msg: 'Ascending field class', newId: 'newAscField', curId: 'curAscField'},
+            descendingFields: {msg: 'Descending field class', newId: 'newDescField', curId: 'curDescField'}
+        };
+
         $scope.tablePairValid = function (item, field, name, clsName, index) {
-            var model = item[field.model];
+            var pairField = pairFields[field.model];
 
-            if ($common.isDefined(model)) {
-                var idx = _.findIndex(model, function (pair) {
-                    return pair.name == name
-                });
+            if (pairField) {
+                if (!$common.isValidJavaClass(pairField.msg, clsName, true))
+                    return focusInvalidField(index, pairField.newId + '_next', pairField.curId + '_next');
 
-                // Found itself.
-                if (index >= 0 && index == idx)
-                    return true;
+                var model = item[field.model];
 
-                // Found duplicate.
-                if (idx >= 0) {
-                    $common.showError('Field with such name already exists!');
+                if ($common.isDefined(model)) {
+                    var idx = _.findIndex(model, function (pair) {
+                        return pair.name == name
+                    });
 
-                    return false;
+                    // Found duplicate.
+                    if (idx >= 0 && idx != index) {
+                        $common.showError('Field with such name already exists!');
+
+                        return focusInvalidField(index, pairField.newId, pairField.curId);
+                    }
                 }
             }
 
@@ -533,52 +563,68 @@ controlCenterModule.controller('metadataController', ['$scope', '$http', '$commo
         };
 
         $scope.tableDbFieldSaveVisible = function (databaseName, databaseType, javaName, javaType) {
-            return $common.isNonEmpty(databaseName) && $common.isDefined(databaseType) &&
-                $common.isNonEmpty(javaName) && $common.isDefined(javaType);
+            return !$common.isEmptyString(databaseName) && $common.isDefined(databaseType) && !$common.isEmptyString(javaName) && $common.isDefined(javaType);
+        };
+
+        var dbFields = {
+            keyFields: {msg: 'Key field', newId: 'newKeyField', curId: 'curKeyField'},
+            valueFields: {msg: 'Value field', newId: 'newValField', curId: 'curValField'}
         };
 
         $scope.tableDbFieldSave = function (field, newDatabaseName, newDatabaseType, newJavaName, newJavaType, index) {
-            var item = $scope.backupItem;
+            var dbField = dbFields[field.model];
 
-            var model = item[field.model];
+            if (dbField) {
+                var backupItem = $scope.backupItem;
 
-            var newItem = {databaseName: newDatabaseName, databaseType: newDatabaseType, javaName: newJavaName, javaType: newJavaType};
+                var model = backupItem[field.model];
 
-            if ($common.isDefined(model)) {
-                var idx = _.findIndex(model, function (dbMeta) {
-                    return dbMeta.databaseName == newDatabaseName
-                });
+                var newItem = {
+                    databaseName: newDatabaseName,
+                    databaseType: newDatabaseType,
+                    javaName: newJavaName,
+                    javaType: newJavaType
+                };
 
-                // Found duplicate.
-                if (idx >= 0 && index != idx) {
-                    $common.showError('DB field with such name already exists!');
+                if (!$common.isValidJavaIdentifier(dbField.msg + ' java name', newJavaName))
+                    return focusInvalidField(index, dbField.newId + '_next', dbField.curId + '_next');
 
-                    return;
-                }
+                if ($common.isDefined(model)) {
+                    var idx = _.findIndex(model, function (dbMeta) {
+                        return dbMeta.databaseName == newDatabaseName
+                    });
 
-                if (index < 0) {
-                    if (model)
-                        model.push(newItem);
-                    else
-                        item[field.model] = [newItem];
-                }
-                else {
-                    var dbField = model[index];
+                    // Found duplicate.
+                    if (idx >= 0 && index != idx) {
+                        $common.showError('DB field with such name already exists!');
 
-                    dbField.databaseName = newDatabaseName;
-                    dbField.databaseType = newDatabaseType;
-                    dbField.javaName = newJavaName;
-                    dbField.javaType = newJavaType;
+                        return focusInvalidField(index, dbField.newId, dbField.curId);
+                    }
+
+                    if (index < 0) {
+                        if (model)
+                            model.push(newItem);
+                        else
+                            backupItem[field.model] = [newItem];
+                    }
+                    else {
+                        var item = model[index];
+
+                        item.databaseName = newDatabaseName;
+                        item.databaseType = newDatabaseType;
+                        item.javaName = newJavaName;
+                        item.javaType = newJavaType;
+                    }
                 }
-            }
-            else
-                item[field.model] = [newItem];
+                else
+                    backupItem[field.model] = [newItem];
 
-            $table.tableReset();
+                $table.tableReset();
+            }
         };
 
         $scope.tableGroupSaveVisible = function (group) {
-            return $common.isNonEmpty(group);
+            return !$common.isEmptyString(group);
         };
 
         function tableGroupValid(groupName, index) {
@@ -589,15 +635,11 @@ controlCenterModule.controller('metadataController', ['$scope', '$http', '$commo
                     return group.name == groupName;
                 });
 
-                // Found itself.
-                if (index >= 0 && index == idx)
-                    return true;
-
                 // Found duplicate.
-                if (idx >= 0) {
+                if (idx >= 0 && idx != index) {
                     $common.showError('Group with such name already exists!');
 
-                    return false;
+                    return focusInvalidField(index, 'newGroupName', 'curGroupName');
                 }
             }
 
@@ -667,26 +709,25 @@ controlCenterModule.controller('metadataController', ['$scope', '$http', '$commo
         };
 
         $scope.tableGroupItemSaveVisible = function (fieldName, className) {
-            return $common.isNonEmpty(fieldName) && $common.isNonEmpty(className);
+            return !$common.isEmptyString(fieldName) && !$common.isEmptyString(className);
         };
 
-        function tableGroupItemValid(fieldName, groupIndex, index) {
-            var groupItems = $scope.backupItem.groups[groupIndex].fields;
+        function tableGroupItemValid(fieldName, className, groupIndex, index) {
+            if (!$common.isValidJavaClass('Group field', className, true))
+                return focusInvalidField(index, 'newFieldName_next', 'curFieldName_next');
 
-            if ($common.isDefined(groupItems)) {
-                var idx = _.findIndex(groupItems, function (groupItem) {
-                    return groupItem.name == fieldName;
-                });
+            var fields = $scope.backupItem.groups[groupIndex].fields;
 
-                // Found itself.
-                if (index >= 0 && index == idx)
-                    return true;
+            if ($common.isDefined(fields)) {
+                var idx = _.findIndex(fields, function (field) {
+                    return field.name == fieldName;
+                });
 
                 // Found duplicate.
-                if (idx >= 0) {
+                if (idx >= 0 && idx != index) {
                     $common.showError('Field with such name already exists in group!');
 
-                    return false;
+                    return focusInvalidField(index, 'newFieldName', 'curFieldName');
                 }
             }
 
@@ -694,7 +735,7 @@ controlCenterModule.controller('metadataController', ['$scope', '$http', '$commo
         }
 
         $scope.tableGroupItemSave = function (fieldName, className, direction, groupIndex, index) {
-            if (tableGroupItemValid(fieldName, groupIndex, index)) {
+            if (tableGroupItemValid(fieldName, className, groupIndex, index)) {
                 $table.tableReset();
 
                 var group = $scope.backupItem.groups[groupIndex];
@@ -769,4 +810,5 @@ controlCenterModule.controller('metadataController', ['$scope', '$http', '$commo
             }
         };
     }]
-);
\ No newline at end of file
+)
+;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/6036af68/modules/control-center-web/src/main/js/controllers/models/metadata.json
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/controllers/models/metadata.json b/modules/control-center-web/src/main/js/controllers/models/metadata.json
index 34157d0..ff16347 100644
--- a/modules/control-center-web/src/main/js/controllers/models/metadata.json
+++ b/modules/control-center-web/src/main/js/controllers/models/metadata.json
@@ -71,6 +71,7 @@
       "label": "Key type",
       "type": "withJavaBuildInTypes",
       "model": "keyType",
+      "id": "keyType",
       "required": true,
       "placeholder": "Full class name for Key",
       "tip": [
@@ -81,6 +82,7 @@
       "label": "Value type",
       "type": "text",
       "model": "valueType",
+      "id": "valueType",
       "required": true,
       "placeholder": "Full class name for Value",
       "tip": [
@@ -93,9 +95,9 @@
       "model": "keyFields",
       "keyName": "name",
       "valueName": "className",
-      "hide": "backupItem.kind == 'query' || isJavaBuildInType()",
-      "focusNewItemId": "newKeyFields",
-      "focusCurItemId": "curKeyFields",
+      "hide": "backupItem.kind == 'query' || isJavaBuildInClass()",
+      "focusNewItemId": "newKeyField",
+      "focusCurItemId": "curKeyField",
       "addTip": "Add key field.",
       "removeTip": "Remove key field.",
       "tip": [
@@ -109,8 +111,8 @@
       "keyName": "name",
       "valueName": "className",
       "hide": "backupItem.kind != 'both' && backupItem.kind == 'query'",
-      "focusNewItemId": "newValFields",
-      "focusCurItemId": "curValFields",
+      "focusNewItemId": "newValField",
+      "focusCurItemId": "curValField",
       "addTip": "Add value field.",
       "removeTip": "Remove value field.",
       "tip": [

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/6036af68/modules/control-center-web/src/main/js/controllers/sql-controller.js
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/controllers/sql-controller.js b/modules/control-center-web/src/main/js/controllers/sql-controller.js
index 527a194..c5f5a4a 100644
--- a/modules/control-center-web/src/main/js/controllers/sql-controller.js
+++ b/modules/control-center-web/src/main/js/controllers/sql-controller.js
@@ -76,7 +76,6 @@ controlCenterModule.controller('sqlController', ['$scope', '$controller', '$http
         .success(function (clusters) {
             var node = clusters[0];
 
-            console.log(clusters);
             $scope.caches = node.caches;
 
             if ($scope.tabs.length == 0)
@@ -85,7 +84,6 @@ controlCenterModule.controller('sqlController', ['$scope', '$controller', '$http
         .error(function (errMsg) {
             $scope.caches = undefined;
 
-            console.log(errMsg);
             $common.showError(errMsg);
         });
 

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/6036af68/modules/control-center-web/src/main/js/views/includes/controls.jade
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/views/includes/controls.jade b/modules/control-center-web/src/main/js/views/includes/controls.jade
index dca31bc..e70f7c9 100644
--- a/modules/control-center-web/src/main/js/views/includes/controls.jade
+++ b/modules/control-center-web/src/main/js/views/includes/controls.jade
@@ -59,7 +59,7 @@ mixin table-pair-edit(keyModel, valModel, keyPlaceholder, valPlaceholder, keyJav
         label.fieldSep /
         .input-tip
             if keyJavaBuildInTypes
-                input.form-control(id=focusId enter-focus-next enter-focus-next-id=focusIdNext type='text' ng-model=keyModel placeholder=keyPlaceholder bs-typeahead data-min-length='1' bs-options='javaType for javaType in javaBuildInTypes' on-escape='tableReset()')
+                input.form-control(id=focusId enter-focus-next enter-focus-next-id=focusIdNext type='text' ng-model=keyModel placeholder=keyPlaceholder bs-typeahead data-min-length='1' bs-options='javaClass for javaClass in javaBuildInClasses' on-escape='tableReset()')
             else
                 input.form-control(id=focusId enter-focus-next enter-focus-next-id=focusIdNext type='text' ng-model=keyModel placeholder=keyPlaceholder on-escape='tableReset()')
     .col-sm-6
@@ -71,7 +71,7 @@ mixin table-pair-edit(keyModel, valModel, keyPlaceholder, valPlaceholder, keyJav
         +btn-save(btnVisible, btnSave)
         .input-tip
             if valueJavaBuildInTypes
-                input.form-control(id=focusIdNext type='text' ng-model=valModel placeholder=valPlaceholder bs-typeahead data-min-length='1' bs-options='javaType for javaType in javaBuildInTypes' on-enter=btnVisibleAndSave on-escape='tableReset()')
+                input.form-control(id=focusIdNext type='text' ng-model=valModel placeholder=valPlaceholder bs-typeahead data-min-length='1' bs-options='javaClass for javaClass in javaBuildInClasses' on-enter=btnVisibleAndSave on-escape='tableReset()')
             else
                 input.form-control(id=focusIdNext type='text' ng-model=valModel placeholder=valPlaceholder on-enter=btnVisibleAndSave on-escape='tableReset()')
 
@@ -187,9 +187,9 @@ mixin table-db-field-edit(databaseName, databaseType, javaName, javaType, focusI
         .input-tip
             button.form-control(ng-model=javaType bs-select data-placeholder='Java type' bs-options='item.value as item.label for item in {{javaTypes}}')
 
-mixin table-group-item-edit(fieldName, className, direction, focusId, index)
-    -var focusIdNext = focusId + '_next'
-    - var args = fieldName + ', ' + className
+mixin table-group-item-edit(fieldName, className, direction, index)
+    -var focusIdNext = fieldName + '_next'
+    -var args = fieldName + ', ' + className
     -var btnVisible = 'tableGroupItemSaveVisible(' + args + ')'
     -var btnSave = 'tableGroupItemSave(' + args + ', ' + direction + ', groupIndex, ' + index +')'
     -var btnVisibleAndSave = btnVisible + ' && ' + btnSave
@@ -197,11 +197,11 @@ mixin table-group-item-edit(fieldName, className, direction, focusId, index)
     .col-sm-4
         label.fieldSep /
         .input-tip
-            input.form-control(id=focusId enter-focus-next enter-focus-next-id=focusIdNext type='text' ng-model=fieldName placeholder='Field name' on-escape='tableReset()')
+            input.form-control(id=fieldName enter-focus-next enter-focus-next-id=focusIdNext type='text' ng-model=fieldName placeholder='Field name' on-escape='tableReset()')
     .col-sm-5
         label.fieldSep /
         .input-tip
-            input.form-control(id=focusIdNext type='text' ng-model=className placeholder='Class name' bs-typeahead data-min-length='1' bs-options='javaType for javaType in javaBuildInTypes' on-enter=btnVisibleAndSave on-escape='tableReset()')
+            input.form-control(id=focusIdNext type='text' ng-model=className placeholder='Class name' bs-typeahead data-min-length='1' bs-options='javaClass for javaClass in javaBuildInClasses' on-enter=btnVisibleAndSave on-escape='tableReset()')
     .col-sm-3
         +btn-save(btnVisible, btnSave)
         .input-tip
@@ -235,7 +235,7 @@ mixin form-row-custom(lblClasses, fieldClasses)
             div(class=fieldClasses)
                 +tipField('field.tip')
                 .input-tip
-                    input.form-control(type='text' placeholder='{{::field.placeholder}}' bs-typeahead data-min-length='1' bs-options='javaType for javaType in javaBuildInTypes')&attributes(fieldCommon)
+                    input.form-control(id='{{::field.id}}' type='text' placeholder='{{::field.placeholder}}' bs-typeahead data-min-length='1' bs-options='javaClass for javaClass in javaBuildInClasses')&attributes(fieldCommon)
         div(ng-switch-when='password' ng-hide=fieldHide)
             label(class=lblClasses ng-class=fieldRequiredClass) {{::field.label}}:
             div(class=fieldClasses)
@@ -333,7 +333,7 @@ mixin form-row-custom(lblClasses, fieldClasses)
             .col-sm-6
                 label.table-header {{::field.label}}:
                 +tipLabel('field.tip')
-                +btn-add('tableNewItem(field)', 'field.addTip', 'newGroupId')
+                +btn-add('tableNewItem(field)', 'field.addTip', 'newGroupName')
             .col-sm-12(ng-show='(#{fieldMdl} && #{fieldMdl}.length > 0) || tableNewItemActive(field)')
                 .col-sm-6
                     .table-details
@@ -343,14 +343,14 @@ mixin form-row-custom(lblClasses, fieldClasses)
                                     td.col-sm-12
                                         div
                                             .col-sm-12(ng-show='!tableEditing(field, $index)')
-                                                a.labelFormField(event-focus='click' event-focus-id='curGroupId' ng-click='curGroup = tableStartEdit(backupItem, field, $index); curGroupName = curGroup.name; curFields = curGroup.fields') {{$index + 1}}) {{group.name}}
+                                                a.labelFormField(event-focus='click' event-focus-id='curGroupName' ng-click='curGroup = tableStartEdit(backupItem, field, $index); curGroupName = curGroup.name; curFields = curGroup.fields') {{$index + 1}}) {{group.name}}
                                                 +btn-remove('tableRemove(backupItem, field, $index)', 'field.removeTip')
                                                 +btn-add('tableGroupNewItem($index)', 'field.addItemTip', 'newGroupItemId')
                                             div(ng-if='tableEditing(field, $index)')
                                                 label.labelField {{$index + 1}})
                                                 +btn-save('tableGroupSaveVisible(curGroupName)', 'tableGroupSave(curGroupName, $index)')
                                                 .input-tip
-                                                    input#curGroupId.form-control(type='text' ng-model='curGroupName' placeholder='Index name' on-enter='tableGroupSaveVisible(curGroupName) && tableGroupSave(curGroupName, $index)' on-escape='tableReset()')
+                                                    input#curGroupName.form-control(type='text' ng-model='curGroupName' placeholder='Index name' on-enter='tableGroupSaveVisible(curGroupName) && tableGroupSave(curGroupName, $index)' on-escape='tableReset()')
                                             div
                                                 table.links-edit.col-sm-12(st-table='group.fields' ng-init='groupIndex = $index')
                                                     tbody
@@ -360,14 +360,14 @@ mixin form-row-custom(lblClasses, fieldClasses)
                                                                     a.labelFormField(event-focus='click' event-focus-id='curGroupItemId' ng-click='curGroupItem = tableGroupItemStartEdit(groupIndex, $index); curFieldName = curGroupItem.name; curClassName = curGroupItem.className; curDirection = curGroupItem.direction') {{$index + 1}}) {{groupItem.name}} / {{groupItem.className}} / {{groupItem.direction ? "DESC" : "ASC"}}
                                                                     +btn-remove('tableRemoveGroupItem(group, $index)', 'field.removeItemTip')
                                                                 div(ng-if='tableGroupItemEditing(groupIndex, $index)')
-                                                                    +table-group-item-edit('curFieldName', 'curClassName', 'curDirection', 'curGroupItemId', '$index')
+                                                                    +table-group-item-edit('curFieldName', 'curClassName', 'curDirection', '$index')
                                                     tfoot(ng-if='tableGroupNewItemActive(groupIndex)')
                                                         tr.col-sm-12(style='padding-left: 18px')
                                                             td
-                                                                +table-group-item-edit('newFieldName', 'newClassName', 'newDirection', 'newGroupItemId', '-1')
+                                                                +table-group-item-edit('newFieldName', 'newClassName', 'newDirection', '-1')
                             tfoot(ng-show='tableNewItemActive(field)')
                                 tr
                                     td.col-sm-12
                                         +btn-save('tableGroupSaveVisible(newGroupName)', 'tableGroupSave(newGroupName, -1)')
                                         .input-tip
-                                            input#newGroupId.form-control(type='text' ng-model='newGroupName' placeholder='Group name' on-enter='tableGroupSaveVisible(newGroupName) && tableGroupSave(newGroupName, -1)' on-escape='tableReset()')
+                                            input#newGroupName.form-control(type='text' ng-model='newGroupName' placeholder='Group name' on-enter='tableGroupSaveVisible(newGroupName) && tableGroupSave(newGroupName, -1)' on-escape='tableReset()')