You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ni...@apache.org on 2023/06/27 12:12:04 UTC

[ignite] branch IGNITE-19753 updated: IGNITE-19753 merge and resolve conflicts (#10808)

This is an automated email from the ASF dual-hosted git repository.

nizhikov pushed a commit to branch IGNITE-19753
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/IGNITE-19753 by this push:
     new 4eb45637850 IGNITE-19753 merge and resolve conflicts (#10808)
4eb45637850 is described below

commit 4eb45637850d1ae91c76c8ea16e283da9d70fd3c
Author: Valery Shorin <va...@gmail.com>
AuthorDate: Tue Jun 27 15:11:55 2023 +0300

    IGNITE-19753 merge and resolve conflicts (#10808)
---
 docs/_docs/monitoring-metrics/new-metrics.adoc     |  26 ++
 .../query/calcite/CalciteQueryProcessor.java       |  39 ++-
 .../internal/processors/query/calcite/Query.java   |  24 +-
 .../processors/query/calcite/RootQuery.java        |  30 +-
 .../calcite/exec/ExecutionCancelledException.java  |  24 --
 .../query/calcite/exec/ExecutionServiceImpl.java   |   3 +-
 .../query/calcite/exec/rel/AbstractNode.java       |   6 +-
 .../query/calcite/exec/rel/RootNode.java           |  13 +-
 .../query/calcite/prepare/QueryPlanCacheImpl.java  |  16 +-
 .../integration/RunningQueriesIntegrationTest.java |  41 ++-
 .../integration/SqlDiagnosticIntegrationTest.java  | 163 ++++++++++
 .../ignite/jdbc/thin/JdbcThinMetadataSelfTest.java |   1 +
 .../internal/commandline/ArgumentParser.java       | 180 ++---------
 ...{CommandInvoker.java => CliCommandInvoker.java} | 200 ++++--------
 .../internal/commandline/CommandHandler.java       | 117 +++----
 .../commandline/ConnectionAndSslParameters.java    |  23 +-
 .../argument/parser/CLIArgumentParser.java         |   2 +-
 .../apache/ignite/events/BaselineEventsTest.java   |  18 +-
 .../commandline/CommandHandlerParsingTest.java     |   6 +-
 .../indexreader/IgniteIndexReaderTest.java         |   7 +
 .../GridCommandHandlerSslWithSecurityTest.java     |  31 +-
 .../org/apache/ignite/util/CdcCommandTest.java     |  41 ++-
 .../util/GridCommandHandlerAbstractTest.java       |  13 +-
 ...CommandHandlerCheckIncrementalSnapshotTest.java |  25 +-
 .../util/GridCommandHandlerClusterByClassTest.java |  64 +++-
 ...ridCommandHandlerClusterByClassWithSSLTest.java |   9 +
 .../GridCommandHandlerConsistencyCountersTest.java |  25 +-
 ...sistencyRepairCorrectnessTransactionalTest.java |  15 +-
 .../util/GridCommandHandlerConsistencyTest.java    |  17 +-
 .../GridCommandHandlerDefragmentationTest.java     |  13 +-
 .../GridCommandHandlerFactoryAbstractTest.java     | 338 ++++++++++++++++++++
 ...ridCommandHandlerIncompatibleSslConfigTest.java |   8 +
 .../GridCommandHandlerIndexForceRebuildTest.java   |   4 +-
 .../util/GridCommandHandlerIndexListTest.java      |  25 +-
 .../GridCommandHandlerIndexRebuildStatusTest.java  |   7 +-
 .../GridCommandHandlerIndexingWithSSLTest.java     |   9 +
 .../GridCommandHandlerInterruptCommandTest.java    |   3 +
 .../util/GridCommandHandlerMetadataTest.java       |  14 +-
 .../util/GridCommandHandlerPropertiesTest.java     |  31 +-
 .../ignite/util/GridCommandHandlerSslTest.java     |  11 +-
 .../apache/ignite/util/GridCommandHandlerTest.java |  93 +++---
 ...GridCommandHandlerTracingConfigurationTest.java |   7 +-
 .../util/GridCommandHandlerWithSslFactoryTest.java |  10 +
 .../ignite/util/GridCommandHandlerWithSslTest.java |   9 +
 .../ignite/util/KillCommandsControlShTest.java     |  21 +-
 .../org/apache/ignite/util/MetricCommandTest.java  |  32 +-
 .../apache/ignite/util/SystemViewCommandTest.java  |  41 ++-
 ...ndHandlerFactoryAbstractTest$TestCommandHandler |   2 +
 .../java/org/apache/ignite/internal/IgniteEx.java  |   7 +
 .../org/apache/ignite/internal/IgniteKernal.java   |  10 +
 .../ignite/internal/cluster/IgniteClusterEx.java   |  14 +-
 .../ignite/internal/cluster/IgniteClusterImpl.java |   7 +-
 .../internal/management/ActivateCommand.java       |  19 +-
 .../internal/management/DeactivateCommand.java     |  28 +-
 .../internal/management/SetStateCommand.java       |  18 +-
 .../ignite/internal/management/StateCommand.java   |  38 ++-
 .../internal/management/SystemViewCommand.java     |  18 +-
 .../internal/management/api/CommandInvoker.java    | 110 +++++++
 .../internal/management/api/CommandMBean.java      | 239 ++++++++++++++
 .../management/api/CommandRegistryImpl.java        |  32 +-
 .../internal/management/api/CommandUtils.java      | 349 ++++++++++++++++++++-
 .../internal/management/api/ComputeCommand.java    |   4 +-
 .../internal/management/api/LocalCommand.java      |  19 +-
 .../internal/management/api/PreparableCommand.java |   5 +-
 .../baseline/AbstractBaselineCommand.java          |   3 +-
 .../management/baseline/BaselineCommand.java       |   4 +-
 .../cache/CacheCheckIndexInlineSizesCommand.java   |   2 +-
 .../management/cache/CacheContentionCommand.java   |   7 +-
 .../management/cache/CacheDestroyCommand.java      |  17 +-
 .../management/cache/CacheDistributionCommand.java |   9 +-
 .../cache/CacheIndexesForceRebuildCommand.java     |  13 +-
 .../management/cache/CacheIndexesListCommand.java  |   8 +-
 .../cache/CacheIndexesRebuildStatusCommand.java    |   8 +-
 .../management/cache/CacheListCommand.java         |  43 +--
 .../cache/CacheScheduleIndexesRebuildCommand.java  |  12 +-
 .../cache/CacheValidateIndexesCommand.java         |   9 +-
 .../cache/CacheValidateIndexesCommandArg.java      |   6 +
 .../cdc/CdcDeleteLostSegmentLinksCommand.java      |   8 +-
 .../consistency/ConsistencyRepairCommand.java      |  33 +-
 .../consistency/ConsistencyStatusCommand.java      |   4 +-
 .../DefragmentationScheduleCommand.java            |   9 +-
 .../diagnostic/DiagnosticConnectivityCommand.java  |   5 +-
 .../diagnostic/DiagnosticPagelocksCommand.java     |  12 +-
 .../encryption/CacheGroupEncryptionCommand.java    |   4 +-
 .../EncryptionReencryptionRateLimitCommand.java    |   4 +-
 .../management/kill/KillClientCommand.java         |  11 +-
 .../management/kill/KillConsistencyCommand.java    |   6 +-
 .../management/kill/KillTransactionCommand.java    |   5 +-
 .../internal/management/metric/MetricCommand.java  |   5 +-
 .../metric/MetricConfigureHistogramCommand.java    |   5 +-
 .../metric/MetricConfigureHitrateCommand.java      |   5 +-
 .../PerformanceStatisticsStatusCommand.java        |   6 +
 .../AbstractTracingConfigurationCommand.java       |   4 +-
 .../tracing/TracingConfigurationCommand.java       |   4 +-
 .../internal/management/tx/TxInfoCommand.java      |  31 +-
 .../internal/managers/IgniteMBeansManager.java     |  48 +++
 .../managers/systemview/SystemViewMBean.java       |   3 +-
 .../systemview/walker/ServiceViewWalker.java       |   5 +-
 .../query}/QueryParserMetricsHolder.java           |   4 +-
 .../internal/processors/service/ServiceInfo.java   |   1 -
 .../apache/ignite/internal/util/IgniteUtils.java   |  23 ++
 .../ignite/mxbean/BaselineAutoAdjustMXBean.java    |  27 +-
 .../ignite/mxbean/ClientProcessorMXBean.java       |   4 +
 .../org/apache/ignite/mxbean/ComputeMXBean.java    |   4 +
 .../ignite/mxbean/DefragmentationMXBean.java       |   4 +
 .../org/apache/ignite/mxbean/EncryptionMXBean.java |   4 +
 .../apache/ignite/mxbean/IgniteClusterMXBean.java  |   4 +
 .../org/apache/ignite/mxbean/MetricsMxBean.java    |   4 +
 .../ignite/mxbean/PerformanceStatisticsMBean.java  |   4 +
 .../java/org/apache/ignite/mxbean/QueryMXBean.java |   4 +
 .../org/apache/ignite/mxbean/ServiceMXBean.java    |   4 +
 .../org/apache/ignite/mxbean/SnapshotMXBean.java   |   4 +
 .../apache/ignite/mxbean/TransactionsMXBean.java   |   7 +
 .../org/apache/ignite/mxbean/WarmUpMXBean.java     |   4 +
 .../ignite/spi/systemview/view/ServiceView.java    |   9 +
 .../ignite/internal/metric/JmxExporterSpiTest.java |   1 +
 .../ignite/internal/metric/SystemViewSelfTest.java |   4 +-
 .../testframework/junits/GridAbstractTest.java     |  19 +-
 .../junits/multijvm/IgniteClusterProcessProxy.java |   5 +
 .../junits/multijvm/IgniteProcessProxy.java        |   6 +
 .../commandline/CommandsProviderExtImpl.java       |   3 +-
 .../internal/processors/query/h2/QueryParser.java  |   1 +
 .../cache/metric/SqlViewExporterSpiTest.java       |   4 +-
 .../query/h2/QueryParserMetricsHolderSelfTest.java |   3 +-
 .../apache/ignite/util/KillCommandsMXBeanTest.java |  11 +-
 .../apache/ignite/util/KillCommandsSQLTest.java    |  11 +-
 .../org/apache/ignite/util/KillCommandsTests.java  |  13 +-
 parent/pom.xml                                     |   2 +-
 128 files changed, 2436 insertions(+), 894 deletions(-)

diff --git a/docs/_docs/monitoring-metrics/new-metrics.adoc b/docs/_docs/monitoring-metrics/new-metrics.adoc
index 7eae8cd24da..1b260d435e0 100644
--- a/docs/_docs/monitoring-metrics/new-metrics.adoc
+++ b/docs/_docs/monitoring-metrics/new-metrics.adoc
@@ -471,3 +471,29 @@ Register name: `cache`
 |LastDataVer| long | The latest data version on the node.
 |DataVersionClusterId| integer | Data version cluster id.
 |===
+
+== SQL parser metrics
+
+Register name: `sql.parser.cache`
+
+[cols="2,1,3",opts="header"]
+|===
+|Name|    Type|    Description
+|hits| long | The number of SQL queries that were found in the parsers cache (doesn't require to be parsed and planned before execution).
+|misses| long | The number of SQL queries that were parsed and planned.
+|===
+
+== SQL executor metrics
+
+Register name: `sql.queries.user`
+
+[cols="2,1,3",opts="header"]
+|===
+|Name|    Type|    Description
+|success| long | The number of succesfully executed SQL queries.
+|failed| long | The number of failed SQL queries (including canceled).
+|canceled| long | The number of canceled SQL queries.
+|===
+
+
+
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java
index c52c8f4180e..6bc9cccf5fa 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java
@@ -23,6 +23,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.BiFunction;
 import java.util.function.Function;
 import org.apache.calcite.DataContexts;
@@ -49,6 +50,7 @@ import org.apache.calcite.tools.FrameworkConfig;
 import org.apache.calcite.tools.Frameworks;
 import org.apache.ignite.SystemProperty;
 import org.apache.ignite.cache.query.FieldsQueryCursor;
+import org.apache.ignite.cache.query.QueryCancelledException;
 import org.apache.ignite.calcite.CalciteQueryEngineConfiguration;
 import org.apache.ignite.configuration.QueryEngineConfiguration;
 import org.apache.ignite.events.SqlQueryExecutionEvent;
@@ -60,6 +62,7 @@ import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata;
 import org.apache.ignite.internal.processors.query.IgniteSQLException;
 import org.apache.ignite.internal.processors.query.QueryContext;
 import org.apache.ignite.internal.processors.query.QueryEngine;
+import org.apache.ignite.internal.processors.query.QueryParserMetricsHolder;
 import org.apache.ignite.internal.processors.query.QueryUtils;
 import org.apache.ignite.internal.processors.query.calcite.exec.ArrayRowHandler;
 import org.apache.ignite.internal.processors.query.calcite.exec.ExchangeService;
@@ -177,6 +180,9 @@ public class CalciteQueryProcessor extends GridProcessorAdapter implements Query
     /** */
     private final QueryPlanCache qryPlanCache;
 
+    /** */
+    private final QueryParserMetricsHolder parserMetrics;
+
     /** */
     private final QueryTaskExecutor taskExecutor;
 
@@ -225,6 +231,7 @@ public class CalciteQueryProcessor extends GridProcessorAdapter implements Query
         failureProcessor = ctx.failure();
         schemaHolder = new SchemaHolderImpl(ctx);
         qryPlanCache = new QueryPlanCacheImpl(ctx);
+        parserMetrics = new QueryParserMetricsHolder(ctx.metric());
         mailboxRegistry = new MailboxRegistryImpl(ctx);
         taskExecutor = new QueryTaskExecutorImpl(ctx);
         executionSvc = new ExecutionServiceImpl<>(ctx, ArrayRowHandler.INSTANCE);
@@ -422,9 +429,18 @@ public class CalciteQueryProcessor extends GridProcessorAdapter implements Query
 
                 @Override public QueryPlan apply(RootQuery<Object[]> qry, Object[] params) {
                     if (plan == null) {
-                        plan = queryPlanCache().queryPlan(new CacheKey(schema.getName(), sql, null, params), () ->
-                            prepareSvc.prepareSingle(qryNode, qry.planningContext())
-                        );
+                        AtomicBoolean miss = new AtomicBoolean();
+
+                        plan = queryPlanCache().queryPlan(new CacheKey(schema.getName(), sql, null, params), () -> {
+                            miss.set(true);
+
+                            return prepareSvc.prepareSingle(qryNode, qry.planningContext());
+                        });
+
+                        if (miss.get())
+                            parserMetrics.countCacheMiss();
+                        else
+                            parserMetrics.countCacheHit();
                     }
 
                     return plan;
@@ -457,11 +473,15 @@ public class CalciteQueryProcessor extends GridProcessorAdapter implements Query
         QueryPlan plan = queryPlanCache().queryPlan(new CacheKey(schema.getName(), sql, null, params));
 
         if (plan != null) {
+            parserMetrics.countCacheHit();
+
             return Collections.singletonList(
                 processQuery(qryCtx, qry -> action.apply(qry, plan), schema.getName(), plan.query(), null, params)
             );
         }
 
+        parserMetrics.countCacheMiss();
+
         SqlNodeList qryList = Commons.parse(sql, FRAMEWORK_CONFIG.getParserConfig());
 
         List<T> res = new ArrayList<>(qryList.size());
@@ -555,12 +575,17 @@ public class CalciteQueryProcessor extends GridProcessorAdapter implements Query
             if (qrys != null)
                 qrys.forEach(RootQuery::cancel);
 
-            qryReg.unregister(qry.id(), e);
+            if (isCanceled) {
+                qryReg.unregister(qry.id(), new QueryCancelledException());
+
+                throw new IgniteSQLException("The query was cancelled while planning",
+                    IgniteQueryErrorCode.QUERY_CANCELED, e);
+            }
+            else {
+                qryReg.unregister(qry.id(), e);
 
-            if (isCanceled)
-                throw new IgniteSQLException("The query was cancelled while planning", IgniteQueryErrorCode.QUERY_CANCELED, e);
-            else
                 throw e;
+            }
         }
     }
 
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/Query.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/Query.java
index 9d42e051ac6..38f67f59d1f 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/Query.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/Query.java
@@ -28,11 +28,11 @@ import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.BiConsumer;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.cache.query.QueryCancelledException;
 import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
 import org.apache.ignite.internal.processors.query.GridQueryCancel;
 import org.apache.ignite.internal.processors.query.IgniteSQLException;
 import org.apache.ignite.internal.processors.query.calcite.exec.ExchangeService;
-import org.apache.ignite.internal.processors.query.calcite.exec.ExecutionCancelledException;
 import org.apache.ignite.internal.processors.query.calcite.exec.tracker.MemoryTracker;
 import org.apache.ignite.internal.processors.query.calcite.exec.tracker.NoOpMemoryTracker;
 import org.apache.ignite.internal.processors.query.calcite.exec.tracker.QueryMemoryTracker;
@@ -165,9 +165,9 @@ public class Query<RowT> {
         }
 
         for (RunningFragment<RowT> frag : fragments)
-            frag.context().execute(() -> frag.root().onError(new ExecutionCancelledException()), frag.root()::onError);
+            frag.context().execute(() -> frag.root().onError(new QueryCancelledException()), frag.root()::onError);
 
-        tryClose(new ExecutionCancelledException());
+        tryClose(queryCanceledException());
     }
 
     /** */
@@ -176,13 +176,8 @@ public class Query<RowT> {
             if (state == QueryState.INITED)
                 state = QueryState.EXECUTING;
 
-            if (state == QueryState.CLOSING || state == QueryState.CLOSED) {
-                throw new IgniteSQLException(
-                    "The query was cancelled",
-                    IgniteQueryErrorCode.QUERY_CANCELED,
-                    new ExecutionCancelledException()
-                );
-            }
+            if (state == QueryState.CLOSING || state == QueryState.CLOSED)
+                throw queryCanceledException();
 
             fragments.add(f);
         }
@@ -193,6 +188,15 @@ public class Query<RowT> {
         return cancel.isCanceled();
     }
 
+    /** */
+    protected IgniteSQLException queryCanceledException() {
+        return new IgniteSQLException(
+            "The query was cancelled",
+            IgniteQueryErrorCode.QUERY_CANCELED,
+            new QueryCancelledException()
+        );
+    }
+
     /** */
     public void onNodeLeft(UUID nodeId) {
         if (initNodeId.equals(nodeId))
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/RootQuery.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/RootQuery.java
index c1f5b2d16df..22c1764a77b 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/RootQuery.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/RootQuery.java
@@ -42,7 +42,6 @@ import org.apache.ignite.internal.processors.query.IgniteSQLException;
 import org.apache.ignite.internal.processors.query.QueryContext;
 import org.apache.ignite.internal.processors.query.QueryUtils;
 import org.apache.ignite.internal.processors.query.calcite.exec.ExchangeService;
-import org.apache.ignite.internal.processors.query.calcite.exec.ExecutionCancelledException;
 import org.apache.ignite.internal.processors.query.calcite.exec.ExecutionContext;
 import org.apache.ignite.internal.processors.query.calcite.exec.rel.Node;
 import org.apache.ignite.internal.processors.query.calcite.exec.rel.RootNode;
@@ -174,12 +173,8 @@ public class RootQuery<RowT> extends Query<RowT> implements TrackableQuery {
      */
     public void mapping() {
         synchronized (mux) {
-            if (state == QueryState.CLOSED) {
-                throw new IgniteSQLException(
-                    "The query was cancelled while executing.",
-                    IgniteQueryErrorCode.QUERY_CANCELED
-                );
-            }
+            if (state == QueryState.CLOSED)
+                throw queryCanceledException();
 
             state = QueryState.MAPPING;
         }
@@ -190,12 +185,8 @@ public class RootQuery<RowT> extends Query<RowT> implements TrackableQuery {
      */
     public void run(ExecutionContext<RowT> ctx, MultiStepPlan plan, Node<RowT> root) {
         synchronized (mux) {
-            if (state == QueryState.CLOSED) {
-                throw new IgniteSQLException(
-                    "The query was cancelled while executing.",
-                    IgniteQueryErrorCode.QUERY_CANCELED
-                );
-            }
+            if (state == QueryState.CLOSED)
+                throw queryCanceledException();
 
             planningTime = U.currentTimeMillis() - startTs;
 
@@ -278,7 +269,7 @@ public class RootQuery<RowT> extends Query<RowT> implements TrackableQuery {
                     log.warning("An exception occures during the query cancel", wrpEx);
             }
             finally {
-                super.tryClose(failure);
+                super.tryClose(failure == null && root != null ? root.failure() : failure);
             }
         }
     }
@@ -287,18 +278,15 @@ public class RootQuery<RowT> extends Query<RowT> implements TrackableQuery {
     @Override public void cancel() {
         cancel.cancel();
 
-        tryClose(new ExecutionCancelledException());
+        U.closeQuiet(root);
+        tryClose(queryCanceledException());
     }
 
     /** */
     public PlanningContext planningContext() {
         synchronized (mux) {
-            if (state == QueryState.CLOSED || state == QueryState.CLOSING) {
-                throw new IgniteSQLException(
-                    "The query was cancelled while executing.",
-                    IgniteQueryErrorCode.QUERY_CANCELED
-                );
-            }
+            if (state == QueryState.CLOSED || state == QueryState.CLOSING)
+                throw queryCanceledException();
 
             if (state == QueryState.EXECUTING || state == QueryState.MAPPING) {
                 throw new IgniteSQLException(
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionCancelledException.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionCancelledException.java
deleted file mode 100644
index fb4a8d8a9c4..00000000000
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionCancelledException.java
+++ /dev/null
@@ -1,24 +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.
- */
-
-package org.apache.ignite.internal.processors.query.calcite.exec;
-
-import org.apache.ignite.IgniteCheckedException;
-
-/** */
-public class ExecutionCancelledException extends IgniteCheckedException {
-}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java
index 4d31c32ef66..92c0b70a10b 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java
@@ -34,6 +34,7 @@ import org.apache.calcite.tools.Frameworks;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.cache.query.FieldsQueryCursor;
+import org.apache.ignite.cache.query.QueryCancelledException;
 import org.apache.ignite.calcite.CalciteQueryEngineConfiguration;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.events.CacheQueryReadEvent;
@@ -833,7 +834,7 @@ public class ExecutionServiceImpl<Row> extends AbstractService implements Execut
 
             Exception e = new RemoteException(nodeId, msg.queryId(), msg.fragmentId(), msg.error());
 
-            if (X.hasCause(msg.error(), ExecutionCancelledException.class)) {
+            if (X.hasCause(msg.error(), QueryCancelledException.class)) {
                 e = new IgniteSQLException(
                     "The query was cancelled while executing.",
                     IgniteQueryErrorCode.QUERY_CANCELED,
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/AbstractNode.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/AbstractNode.java
index 5a4006f42c5..19c2927cbd1 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/AbstractNode.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/AbstractNode.java
@@ -22,8 +22,8 @@ import java.util.Comparator;
 import java.util.List;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.ignite.IgniteSystemProperties;
+import org.apache.ignite.cache.query.QueryCancelledException;
 import org.apache.ignite.internal.IgniteInterruptedCheckedException;
-import org.apache.ignite.internal.processors.query.calcite.exec.ExecutionCancelledException;
 import org.apache.ignite.internal.processors.query.calcite.exec.ExecutionContext;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.U;
@@ -146,7 +146,7 @@ public abstract class AbstractNode<Row> implements Node<Row> {
      * @param e Exception.
      */
     public void onError(Throwable e) {
-        if (e instanceof ExecutionCancelledException)
+        if (e instanceof QueryCancelledException)
             U.warn(context().logger(), "Execution is cancelled.", e);
         else
             onErrorInternal(e);
@@ -184,7 +184,7 @@ public abstract class AbstractNode<Row> implements Node<Row> {
     /** */
     protected void checkState() throws Exception {
         if (context().isCancelled())
-            throw new ExecutionCancelledException();
+            throw new QueryCancelledException();
         if (Thread.interrupted())
             throw new IgniteInterruptedCheckedException("Thread was interrupted.");
         if (!U.assertionsEnabled())
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/RootNode.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/RootNode.java
index 0ee802873bb..5449e19eb38 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/RootNode.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/RootNode.java
@@ -31,12 +31,14 @@ import java.util.function.Function;
 
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.ignite.IgniteInterruptedException;
+import org.apache.ignite.cache.query.QueryCancelledException;
 import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
 import org.apache.ignite.internal.processors.query.IgniteSQLException;
 import org.apache.ignite.internal.processors.query.calcite.exec.ExecutionContext;
 import org.apache.ignite.internal.processors.query.calcite.util.TypeUtils;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.U;
+import org.jetbrains.annotations.Nullable;
 
 import static org.apache.ignite.cache.query.QueryCancelledException.ERR_MSG;
 
@@ -114,8 +116,10 @@ public class RootNode<Row> extends AbstractNode<Row> implements SingleNode<Row>,
 
         lock.lock();
         try {
-            if (waiting != -1)
-                ex.compareAndSet(null, new IgniteSQLException(ERR_MSG, IgniteQueryErrorCode.QUERY_CANCELED));
+            if (waiting != -1) {
+                ex.compareAndSet(null, new IgniteSQLException(ERR_MSG, IgniteQueryErrorCode.QUERY_CANCELED,
+                    new QueryCancelledException()));
+            }
 
             closed = true; // an exception has to be set first to get right check order
 
@@ -128,6 +132,11 @@ public class RootNode<Row> extends AbstractNode<Row> implements SingleNode<Row>,
         onClose.accept(ex.get());
     }
 
+    /** */
+    public @Nullable Throwable failure() {
+        return ex.get();
+    }
+
     /** {@inheritDoc} */
     @Override protected boolean isClosed() {
         return closed;
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/QueryPlanCacheImpl.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/QueryPlanCacheImpl.java
index 178faf8cd5e..a1b038abfff 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/QueryPlanCacheImpl.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/QueryPlanCacheImpl.java
@@ -38,7 +38,7 @@ public class QueryPlanCacheImpl extends AbstractService implements QueryPlanCach
     private static final int CACHE_SIZE = 1024;
 
     /** */
-    private GridInternalSubscriptionProcessor subscriptionProcessor;
+    private final GridInternalSubscriptionProcessor subscriptionProc;
 
     /** */
     private volatile Map<CacheKey, QueryPlan> cache;
@@ -50,21 +50,14 @@ public class QueryPlanCacheImpl extends AbstractService implements QueryPlanCach
         super(ctx);
 
         cache = new GridBoundedConcurrentLinkedHashMap<>(CACHE_SIZE);
-        subscriptionProcessor(ctx.internalSubscriptionProcessor());
+        subscriptionProc = ctx.internalSubscriptionProcessor();
 
         init();
     }
 
-    /**
-     * @param subscriptionProcessor Subscription processor.
-     */
-    public void subscriptionProcessor(GridInternalSubscriptionProcessor subscriptionProcessor) {
-        this.subscriptionProcessor = subscriptionProcessor;
-    }
-
     /** {@inheritDoc} */
     @Override public void init() {
-        subscriptionProcessor.registerSchemaChangeListener(new SchemaListener());
+        subscriptionProc.registerSchemaChangeListener(new SchemaListener());
     }
 
     /** {@inheritDoc} */
@@ -74,8 +67,6 @@ public class QueryPlanCacheImpl extends AbstractService implements QueryPlanCach
 
     /** {@inheritDoc} */
     @Override public QueryPlan queryPlan(CacheKey key, Supplier<QueryPlan> planSupplier) {
-        Map<CacheKey, QueryPlan> cache = this.cache;
-
         QueryPlan plan = cache.computeIfAbsent(key, k -> planSupplier.get());
 
         return plan.copy();
@@ -83,7 +74,6 @@ public class QueryPlanCacheImpl extends AbstractService implements QueryPlanCach
 
     /** {@inheritDoc} */
     @Override public QueryPlan queryPlan(CacheKey key) {
-        Map<CacheKey, QueryPlan> cache = this.cache;
         QueryPlan plan = cache.get(key);
         return plan != null ? plan.copy() : null;
     }
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/RunningQueriesIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/RunningQueriesIntegrationTest.java
index bd841718a15..b069eea3622 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/RunningQueriesIntegrationTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/RunningQueriesIntegrationTest.java
@@ -40,6 +40,7 @@ import org.apache.ignite.calcite.CalciteQueryEngineConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.processors.metric.MetricRegistry;
 import org.apache.ignite.internal.processors.query.IgniteSQLException;
 import org.apache.ignite.internal.processors.query.QueryUtils;
 import org.apache.ignite.internal.processors.query.calcite.CalciteQueryProcessor;
@@ -51,19 +52,20 @@ import org.apache.ignite.internal.processors.query.calcite.schema.CacheTableImpl
 import org.apache.ignite.internal.processors.query.calcite.schema.IgniteCacheTable;
 import org.apache.ignite.internal.processors.query.calcite.schema.IgniteTable;
 import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.spi.metric.LongMetric;
 import org.apache.ignite.spi.systemview.view.SqlQueryView;
 import org.apache.ignite.spi.systemview.view.SystemView;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.apache.ignite.testframework.junits.WithSystemProperty;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
-import org.junit.Assert;
 import org.junit.Test;
 
 import static java.util.stream.Collectors.joining;
 import static org.apache.ignite.IgniteSystemProperties.getLong;
 import static org.apache.ignite.internal.processors.query.calcite.CalciteQueryProcessor.IGNITE_CALCITE_PLANNER_TIMEOUT;
 import static org.apache.ignite.internal.processors.query.running.RunningQueryManager.SQL_QRY_VIEW;
+import static org.apache.ignite.internal.processors.query.running.RunningQueryManager.SQL_USER_QUERIES_REG_NAME;
 
 /**
  *
@@ -100,6 +102,9 @@ public class RunningQueriesIntegrationTest extends AbstractBasicIntegrationTest
      */
     @Test
     public void testCancelAtPlanningPhase() throws IgniteCheckedException {
+        MetricRegistry mreg = client.context().metric().registry(SQL_USER_QUERIES_REG_NAME);
+        mreg.reset();
+
         CalciteQueryProcessor engine = queryProcessor(client);
         int cnt = 9;
 
@@ -111,7 +116,7 @@ public class RunningQueriesIntegrationTest extends AbstractBasicIntegrationTest
 
         IgniteInternalFuture<List<List<?>>> fut = GridTestUtils.runAsync(() -> sql(sql));
 
-        Assert.assertTrue(GridTestUtils.waitForCondition(
+        assertTrue(GridTestUtils.waitForCondition(
             () -> !engine.runningQueries().isEmpty() || fut.isDone(), TIMEOUT_IN_MS));
 
         Collection<? extends Query<?>> running = engine.runningQueries();
@@ -123,15 +128,17 @@ public class RunningQueriesIntegrationTest extends AbstractBasicIntegrationTest
         assertSame(qry, engine.runningQuery(qry.id()));
 
         // Waits for planning.
-        Assert.assertTrue(GridTestUtils.waitForCondition(
+        assertTrue(GridTestUtils.waitForCondition(
             () -> qry.state() == QueryState.PLANNING, TIMEOUT_IN_MS));
 
         qry.cancel();
 
-        Assert.assertTrue(GridTestUtils.waitForCondition(
+        assertTrue(GridTestUtils.waitForCondition(
             () -> engine.runningQueries().isEmpty(), TIMEOUT_IN_MS));
 
         GridTestUtils.assertThrowsAnyCause(log, () -> fut.get(0), IgniteSQLException.class, "The query was cancelled while planning");
+
+        assertEquals(1, ((LongMetric)mreg.findMetric("canceled")).value());
     }
 
     /**
@@ -140,6 +147,9 @@ public class RunningQueriesIntegrationTest extends AbstractBasicIntegrationTest
      */
     @Test
     public void testCancelAtExecutionPhase() throws Exception {
+        MetricRegistry mreg = client.context().metric().registry(SQL_USER_QUERIES_REG_NAME);
+        mreg.reset();
+
         CalciteQueryProcessor cliEngine = queryProcessor(client);
         CalciteQueryProcessor srvEngine = queryProcessor(srv);
 
@@ -199,10 +209,10 @@ public class RunningQueriesIntegrationTest extends AbstractBasicIntegrationTest
 
             qry.cancel();
 
-            Assert.assertTrue(GridTestUtils.waitForCondition(
+            assertTrue(GridTestUtils.waitForCondition(
                 () -> srvEngine.runningQueries().isEmpty(), TIMEOUT_IN_MS));
 
-            Assert.assertTrue(GridTestUtils.waitForCondition(
+            assertTrue(GridTestUtils.waitForCondition(
                 () -> cliEngine.runningQueries().isEmpty(), TIMEOUT_IN_MS));
         }
         finally {
@@ -210,7 +220,9 @@ public class RunningQueriesIntegrationTest extends AbstractBasicIntegrationTest
         }
 
         GridTestUtils.assertThrowsAnyCause(log,
-            () -> fut.get(100), IgniteSQLException.class, "The query was cancelled while executing.");
+            () -> fut.get(100), IgniteSQLException.class, "The query was cancelled");
+
+        assertEquals(1, ((LongMetric)mreg.findMetric("canceled")).value());
     }
 
     /**
@@ -220,6 +232,9 @@ public class RunningQueriesIntegrationTest extends AbstractBasicIntegrationTest
      */
     @Test
     public void testCancelByRemoteFragment() throws IgniteCheckedException {
+        MetricRegistry mreg = client.context().metric().registry(SQL_USER_QUERIES_REG_NAME);
+        mreg.reset();
+
         CalciteQueryProcessor clientEngine = queryProcessor(client);
         CalciteQueryProcessor serverEngine = queryProcessor(srv);
         int cnt = 6;
@@ -236,7 +251,7 @@ public class RunningQueriesIntegrationTest extends AbstractBasicIntegrationTest
 
         IgniteInternalFuture<List<List<?>>> fut = GridTestUtils.runAsync(() -> sql(sql));
 
-        Assert.assertTrue(GridTestUtils.waitForCondition(
+        assertTrue(GridTestUtils.waitForCondition(
             () -> {
                 Collection<? extends Query<?>> queries = clientEngine.runningQueries();
 
@@ -244,7 +259,7 @@ public class RunningQueriesIntegrationTest extends AbstractBasicIntegrationTest
             },
             TIMEOUT_IN_MS));
 
-        Assert.assertTrue(GridTestUtils.waitForCondition(() -> !serverEngine.runningQueries().isEmpty(), TIMEOUT_IN_MS));
+        assertTrue(GridTestUtils.waitForCondition(() -> !serverEngine.runningQueries().isEmpty(), TIMEOUT_IN_MS));
 
         Collection<? extends Query<?>> running = serverEngine.runningQueries();
         Query<?> qry = F.first(running);
@@ -253,13 +268,15 @@ public class RunningQueriesIntegrationTest extends AbstractBasicIntegrationTest
 
         qry.cancel();
 
-        Assert.assertTrue(GridTestUtils.waitForCondition(
+        assertTrue(GridTestUtils.waitForCondition(
             () -> clientEngine.runningQueries().isEmpty(), TIMEOUT_IN_MS));
 
-        Assert.assertTrue(GridTestUtils.waitForCondition(
+        assertTrue(GridTestUtils.waitForCondition(
             () -> serverEngine.runningQueries().isEmpty(), TIMEOUT_IN_MS));
 
-        GridTestUtils.assertThrowsAnyCause(log, () -> fut.get(100), IgniteSQLException.class, "The query was cancelled while executing.");
+        GridTestUtils.assertThrowsAnyCause(log, () -> fut.get(100), IgniteSQLException.class, "The query was cancelled");
+
+        assertEquals(1, ((LongMetric)mreg.findMetric("canceled")).value());
     }
 
     /** */
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/SqlDiagnosticIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/SqlDiagnosticIntegrationTest.java
index 63294238a3b..2bb661e01d1 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/SqlDiagnosticIntegrationTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/SqlDiagnosticIntegrationTest.java
@@ -18,6 +18,10 @@
 
 package org.apache.ignite.internal.processors.query.calcite.integration;
 
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.Statement;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -29,10 +33,12 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicIntegerArray;
 import java.util.concurrent.atomic.AtomicLong;
+import org.apache.ignite.cache.query.FieldsQueryCursor;
 import org.apache.ignite.cache.query.SqlFieldsQuery;
 import org.apache.ignite.cache.query.annotations.QuerySqlFunction;
 import org.apache.ignite.calcite.CalciteQueryEngineConfiguration;
 import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.ClientConnectorConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.configuration.SqlConfiguration;
 import org.apache.ignite.events.CacheQueryExecutedEvent;
@@ -40,12 +46,15 @@ import org.apache.ignite.events.CacheQueryReadEvent;
 import org.apache.ignite.events.SqlQueryExecutionEvent;
 import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.processors.cache.query.GridCacheQueryType;
+import org.apache.ignite.internal.processors.metric.MetricRegistry;
 import org.apache.ignite.internal.processors.performancestatistics.AbstractPerformanceStatisticsTest;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
 import org.apache.ignite.internal.processors.query.QueryUtils;
 import org.apache.ignite.internal.processors.query.calcite.Query;
 import org.apache.ignite.internal.processors.query.calcite.QueryRegistry;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.spi.metric.LongMetric;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.apache.ignite.testframework.ListeningTestLogger;
 import org.apache.ignite.testframework.LogListener;
@@ -58,15 +67,20 @@ import static org.apache.ignite.internal.processors.cache.query.GridCacheQueryTy
 import static org.apache.ignite.internal.processors.performancestatistics.AbstractPerformanceStatisticsTest.cleanPerformanceStatisticsDir;
 import static org.apache.ignite.internal.processors.performancestatistics.AbstractPerformanceStatisticsTest.startCollectStatistics;
 import static org.apache.ignite.internal.processors.performancestatistics.AbstractPerformanceStatisticsTest.stopCollectStatisticsAndRead;
+import static org.apache.ignite.internal.processors.query.QueryParserMetricsHolder.QUERY_PARSER_METRIC_GROUP_NAME;
 import static org.apache.ignite.internal.processors.query.running.HeavyQueriesTracker.BIG_RESULT_SET_MSG;
 import static org.apache.ignite.internal.processors.query.running.HeavyQueriesTracker.LONG_QUERY_ERROR_MSG;
 import static org.apache.ignite.internal.processors.query.running.HeavyQueriesTracker.LONG_QUERY_EXEC_MSG;
 import static org.apache.ignite.internal.processors.query.running.HeavyQueriesTracker.LONG_QUERY_FINISHED_MSG;
+import static org.apache.ignite.internal.processors.query.running.RunningQueryManager.SQL_USER_QUERIES_REG_NAME;
 
 /**
  * Test SQL diagnostic tools.
  */
 public class SqlDiagnosticIntegrationTest extends AbstractBasicIntegrationTest {
+    /** */
+    private static final String jdbcUrl = "jdbc:ignite:thin://127.0.0.1:" + ClientConnectorConfiguration.DFLT_PORT;
+
     /** */
     private static final long LONG_QRY_TIMEOUT = 1_000L;
 
@@ -114,6 +128,155 @@ public class SqlDiagnosticIntegrationTest extends AbstractBasicIntegrationTest {
         return 2;
     }
 
+    /** */
+    @Test
+    public void testParserMetrics() {
+        MetricRegistry mreg0 = grid(0).context().metric().registry(QUERY_PARSER_METRIC_GROUP_NAME);
+        MetricRegistry mreg1 = grid(1).context().metric().registry(QUERY_PARSER_METRIC_GROUP_NAME);
+        mreg0.reset();
+        mreg1.reset();
+
+        LongMetric hits0 = mreg0.findMetric("hits");
+        LongMetric hits1 = mreg1.findMetric("hits");
+        LongMetric misses0 = mreg0.findMetric("misses");
+        LongMetric misses1 = mreg1.findMetric("misses");
+
+        // Parse and plan on client.
+        sql("CREATE TABLE test_parse(a INT)");
+
+        assertEquals(0, hits0.value());
+        assertEquals(0, hits1.value());
+        assertEquals(0, misses0.value());
+        assertEquals(0, misses1.value());
+
+        for (int i = 0; i < 10; i++)
+            sql(grid(0), "INSERT INTO test_parse VALUES (?)", i);
+
+        assertEquals(9, hits0.value());
+        assertEquals(0, hits1.value());
+        assertEquals(1, misses0.value());
+        assertEquals(0, misses1.value());
+
+        for (int i = 0; i < 10; i++)
+            sql(grid(1), "SELECT * FROM test_parse WHERE a = ?", i);
+
+        assertEquals(9, hits0.value());
+        assertEquals(9, hits1.value());
+        assertEquals(1, misses0.value());
+        assertEquals(1, misses1.value());
+    }
+
+    /** */
+    @Test
+    public void testBatchParserMetrics() throws Exception {
+        MetricRegistry mreg0 = grid(0).context().metric().registry(QUERY_PARSER_METRIC_GROUP_NAME);
+        MetricRegistry mreg1 = grid(1).context().metric().registry(QUERY_PARSER_METRIC_GROUP_NAME);
+        mreg0.reset();
+        mreg1.reset();
+
+        LongMetric hits0 = mreg0.findMetric("hits");
+        LongMetric hits1 = mreg1.findMetric("hits");
+        LongMetric misses0 = mreg0.findMetric("misses");
+        LongMetric misses1 = mreg1.findMetric("misses");
+
+        sql("CREATE TABLE test_batch(a INT)");
+
+        assertEquals(0, hits0.value());
+        assertEquals(0, hits1.value());
+        assertEquals(0, misses0.value());
+        assertEquals(0, misses1.value());
+
+        try (Connection conn = DriverManager.getConnection(jdbcUrl)) {
+            conn.setSchema("PUBLIC");
+
+            try (Statement stmt = conn.createStatement()) {
+                for (int i = 0; i < 10; i++)
+                    stmt.addBatch(String.format("INSERT INTO test_batch VALUES (%d)", i));
+
+                stmt.executeBatch();
+
+                assertEquals(0, hits0.value());
+                assertEquals(0, hits1.value());
+                assertEquals(10, misses0.value());
+                assertEquals(0, misses1.value());
+            }
+
+            String sql = "INSERT INTO test_batch VALUES (?)";
+
+            try (PreparedStatement stmt = conn.prepareStatement(sql)) {
+                for (int i = 10; i < 20; i++) {
+                    stmt.setInt(1, i);
+                    stmt.addBatch();
+                }
+
+                stmt.executeBatch();
+
+                assertEquals(0, hits0.value());
+                assertEquals(0, hits1.value());
+                assertEquals(11, misses0.value()); // Only one increment per batch.
+                assertEquals(0, misses1.value());
+            }
+
+            try (PreparedStatement stmt = conn.prepareStatement(sql)) {
+                for (int i = 20; i < 30; i++) {
+                    stmt.setInt(1, i);
+                    stmt.addBatch();
+                }
+
+                stmt.executeBatch();
+
+                assertEquals(1, hits0.value()); // Only one increment per batch.
+                assertEquals(0, hits1.value());
+                assertEquals(11, misses0.value());
+                assertEquals(0, misses1.value());
+            }
+        }
+    }
+
+    /** */
+    @Test
+    public void testUserQueriesMetrics() throws Exception {
+        sql(grid(0), "CREATE TABLE test_metric (a INT)");
+
+        MetricRegistry mreg0 = grid(0).context().metric().registry(SQL_USER_QUERIES_REG_NAME);
+        MetricRegistry mreg1 = grid(1).context().metric().registry(SQL_USER_QUERIES_REG_NAME);
+        mreg0.reset();
+        mreg1.reset();
+
+        AtomicInteger qryCnt = new AtomicInteger();
+        grid(0).context().query().runningQueryManager().registerQueryFinishedListener(q -> qryCnt.incrementAndGet());
+
+        sql(grid(0), "INSERT INTO test_metric VALUES (?)", 0);
+        sql(grid(0), "SELECT * FROM test_metric WHERE a = ?", 0);
+
+        try {
+            sql(grid(0), "SELECT * FROM test_fail");
+
+            fail();
+        }
+        catch (IgniteSQLException ignored) {
+            // Expected.
+        }
+
+        FieldsQueryCursor<?> cur = grid(0).getOrCreateCache("test_metric")
+            .query(new SqlFieldsQuery("SELECT * FROM table(system_range(1, 10000))"));
+
+        assertTrue(cur.iterator().hasNext());
+
+        cur.close();
+
+        // Query unregistering is async process, wait for it before metrics check.
+        assertTrue(GridTestUtils.waitForCondition(() -> qryCnt.get() == 4, 1_000L));
+
+        assertEquals(2, ((LongMetric)mreg0.findMetric("success")).value());
+        assertEquals(2, ((LongMetric)mreg0.findMetric("failed")).value()); // 1 error + 1 cancelled.
+        assertEquals(1, ((LongMetric)mreg0.findMetric("canceled")).value());
+
+        assertEquals(0, ((LongMetric)mreg1.findMetric("success")).value());
+        assertEquals(0, ((LongMetric)mreg1.findMetric("failed")).value());
+        assertEquals(0, ((LongMetric)mreg1.findMetric("canceled")).value());
+    }
+
     /** */
     @Test
     public void testPerformanceStatistics() throws Exception {
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinMetadataSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinMetadataSelfTest.java
index e8df4956a40..22b07cf579a 100644
--- a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinMetadataSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinMetadataSelfTest.java
@@ -895,6 +895,7 @@ public class JdbcThinMetadataSelfTest extends JdbcThinAbstractSelfTest {
                 "SYS.SERVICES.NODE_FILTER.null",
                 "SYS.SERVICES.STATICALLY_CONFIGURED.null",
                 "SYS.SERVICES.SERVICE_ID.null",
+                "SYS.SERVICES.TOPOLOGY_SNAPSHOT.null",
                 "SYS.TASKS.AFFINITY_CACHE_NAME.null",
                 "SYS.TASKS.INTERNAL.null",
                 "SYS.TASKS.END_TIME.null",
diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/ArgumentParser.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/ArgumentParser.java
index 885f70deee6..604f3a47d24 100644
--- a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/ArgumentParser.java
+++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/ArgumentParser.java
@@ -19,10 +19,11 @@
 package org.apache.ignite.internal.commandline;
 
 import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Deque;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -30,8 +31,6 @@ import java.util.Set;
 import java.util.function.BiConsumer;
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
-import java.util.function.Function;
-import org.apache.ignite.IgniteException;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.IgniteSystemProperties;
 import org.apache.ignite.internal.client.GridClientConfiguration;
@@ -41,13 +40,9 @@ import org.apache.ignite.internal.dto.IgniteDataTransferObject;
 import org.apache.ignite.internal.management.IgniteCommandRegistry;
 import org.apache.ignite.internal.management.api.Argument;
 import org.apache.ignite.internal.management.api.ArgumentGroup;
-import org.apache.ignite.internal.management.api.BeforeNodeStartCommand;
 import org.apache.ignite.internal.management.api.CliSubcommandsWithPrefix;
 import org.apache.ignite.internal.management.api.Command;
 import org.apache.ignite.internal.management.api.CommandsRegistry;
-import org.apache.ignite.internal.management.api.ComputeCommand;
-import org.apache.ignite.internal.management.api.HelpCommand;
-import org.apache.ignite.internal.management.api.LocalCommand;
 import org.apache.ignite.internal.management.api.Positional;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgniteExperimental;
@@ -63,13 +58,13 @@ import static org.apache.ignite.internal.commandline.argument.parser.CLIArgument
 import static org.apache.ignite.internal.management.api.CommandUtils.CMD_WORDS_DELIM;
 import static org.apache.ignite.internal.management.api.CommandUtils.NAME_PREFIX;
 import static org.apache.ignite.internal.management.api.CommandUtils.PARAM_WORDS_DELIM;
+import static org.apache.ignite.internal.management.api.CommandUtils.argument;
 import static org.apache.ignite.internal.management.api.CommandUtils.asOptional;
+import static org.apache.ignite.internal.management.api.CommandUtils.executable;
 import static org.apache.ignite.internal.management.api.CommandUtils.fromFormattedCommandName;
 import static org.apache.ignite.internal.management.api.CommandUtils.isBoolean;
-import static org.apache.ignite.internal.management.api.CommandUtils.parameterExample;
 import static org.apache.ignite.internal.management.api.CommandUtils.toFormattedCommandName;
 import static org.apache.ignite.internal.management.api.CommandUtils.toFormattedFieldName;
-import static org.apache.ignite.internal.management.api.CommandUtils.toFormattedNames;
 import static org.apache.ignite.internal.management.api.CommandUtils.visitCommandParams;
 import static org.apache.ignite.ssl.SslContextFactory.DFLT_SSL_PROTOCOL;
 
@@ -84,11 +79,8 @@ public class ArgumentParser {
     /** */
     private final IgniteCommandRegistry registry;
 
-    /** */
-    Command<?, ?> root;
-
-    /** */
-    Command<?, ?> cmd;
+    /** Path to the specific command. Command to execute is on top of stack, parent commands below. */
+    Deque<Command<?, ?>> cmdPath = new ArrayDeque<>();
 
     /** */
     static final String CMD_HOST = "--host";
@@ -255,7 +247,7 @@ public class ArgumentParser {
      * @return Arguments bean.
      * @throws IllegalArgumentException In case arguments aren't valid.
      */
-    <A extends IgniteDataTransferObject> ConnectionAndSslParameters<A> parseAndValidate(List<String> raw) {
+    public <A extends IgniteDataTransferObject> ConnectionAndSslParameters<A> parseAndValidate(List<String> raw) {
         List<String> args = new ArrayList<>(raw);
 
         findCommand(args.iterator());
@@ -265,12 +257,12 @@ public class ArgumentParser {
         parser.parse(args.iterator());
 
         A arg = (A)argument(
-            cmd.argClass(),
+            cmdPath.peek().argClass(),
             (fld, pos) -> parser.get(pos),
             fld -> parser.get(toFormattedFieldName(fld).toLowerCase())
         );
 
-        if (!parser.<Boolean>get(CMD_ENABLE_EXPERIMENTAL) && cmd.getClass().isAnnotationPresent(IgniteExperimental.class)) {
+        if (!parser.<Boolean>get(CMD_ENABLE_EXPERIMENTAL) && cmdPath.peek().getClass().isAnnotationPresent(IgniteExperimental.class)) {
             log.warning(
                 String.format("To use experimental command add " + CMD_ENABLE_EXPERIMENTAL + " parameter for %s",
                     UTILITY_NAME)
@@ -279,7 +271,7 @@ public class ArgumentParser {
             throw new IllegalArgumentException("Experimental commands disabled");
         }
 
-        return new ConnectionAndSslParameters<>((Command<A, ?>)cmd, root, arg, parser);
+        return new ConnectionAndSslParameters<>(cmdPath, arg, parser);
     }
 
     /**
@@ -288,29 +280,34 @@ public class ArgumentParser {
      * @param iter Iterator of CLI arguments.
      */
     protected void findCommand(Iterator<String> iter) {
-        assert cmd == null && root == null;
+        assert cmdPath.isEmpty();
 
-        while (iter.hasNext() && cmd == null) {
+        while (iter.hasNext() && cmdPath.isEmpty()) {
             String cmdName = iter.next();
 
             if (!cmdName.startsWith(NAME_PREFIX))
                 continue;
 
-            cmd = registry.command(fromFormattedCommandName(cmdName.substring(NAME_PREFIX.length()), CMD_WORDS_DELIM));
+            Command<?, ?> cmd = registry.command(fromFormattedCommandName(cmdName.substring(NAME_PREFIX.length()), CMD_WORDS_DELIM));
+
+            if (cmd == null)
+                continue;
+
+            cmdPath.push(cmd);
         }
 
-        if (cmd == null)
+        if (cmdPath.isEmpty())
             throw new IllegalArgumentException("No action was specified");
 
         // Remove command name parameter to exclude it from ongoing parsing.
         iter.remove();
 
-        while (cmd instanceof CommandsRegistry && iter.hasNext()) {
+        while (cmdPath.peek() instanceof CommandsRegistry && iter.hasNext()) {
             String name = iter.next();
 
             char delim = PARAM_WORDS_DELIM;
 
-            if (cmd.getClass().isAnnotationPresent(CliSubcommandsWithPrefix.class)) {
+            if (cmdPath.peek().getClass().isAnnotationPresent(CliSubcommandsWithPrefix.class)) {
                 if (!name.startsWith(NAME_PREFIX))
                     break;
 
@@ -319,31 +316,27 @@ public class ArgumentParser {
                 delim = CMD_WORDS_DELIM;
             }
 
-            Command<?, ?> cmd1 = ((CommandsRegistry<?, ?>)cmd).command(fromFormattedCommandName(name, delim));
+            Command<?, ?> cmd1 = ((CommandsRegistry<?, ?>)cmdPath.peek()).command(fromFormattedCommandName(name, delim));
 
             if (cmd1 == null)
                 break;
 
-            root = cmd;
-            cmd = cmd1;
+            cmdPath.push(cmd1);
 
             // Remove command name parameter to exclude it from ongoing parsing.
             iter.remove();
         }
 
-        if (!(cmd instanceof ComputeCommand)
-            && !(cmd instanceof LocalCommand)
-            && !(cmd instanceof BeforeNodeStartCommand)
-            && !(cmd instanceof HelpCommand)) {
+        if (!executable(cmdPath.peek())) {
             throw new IllegalArgumentException(
-                "Command " + toFormattedCommandName(cmd.getClass()) + " can't be executed"
+                "Command " + toFormattedCommandName(cmdPath.peek().getClass()) + " can't be executed"
             );
         }
     }
 
     /** */
     private CLIArgumentParser createArgumentParser() {
-        assert cmd != null;
+        assert !cmdPath.isEmpty();
 
         List<CLIArgument<?>> positionalArgs = new ArrayList<>();
         List<CLIArgument<?>> namedArgs = new ArrayList<>();
@@ -357,7 +350,7 @@ public class ArgumentParser {
             (name, val) -> {}
         );
 
-        ArgumentGroup argGrp = cmd.argClass().getAnnotation(ArgumentGroup.class);
+        ArgumentGroup argGrp = cmdPath.peek().argClass().getAnnotation(ArgumentGroup.class);
         Set<String> grpdFlds = argGrp == null
             ? Collections.emptySet()
             : new HashSet<>(Arrays.asList(argGrp.value()));
@@ -382,127 +375,10 @@ public class ArgumentParser {
                 namedArgCb.accept(fld);
         });
 
-        visitCommandParams(cmd.argClass(), positionalArgCb, namedArgCb, argGrpCb);
+        visitCommandParams(cmdPath.peek().argClass(), positionalArgCb, namedArgCb, argGrpCb);
 
         namedArgs.addAll(common);
 
         return new CLIArgumentParser(positionalArgs, namedArgs);
     }
-
-    /**
-     * Fill and vaildate command argument.
-     *
-     * @param argCls Argument class.
-     * @param positionalParamProvider Provider of positional parameters.
-     * @param paramProvider Provider of named parameters.
-     * @return Argument filled with parameters.
-     * @param <A> Argument type.
-     */
-    private static <A extends IgniteDataTransferObject> A argument(
-        Class<A> argCls,
-        BiFunction<Field, Integer, Object> positionalParamProvider,
-        Function<Field, Object> paramProvider
-    ) {
-        try {
-            ArgumentState<A> arg = new ArgumentState<>(argCls);
-
-            visitCommandParams(
-                argCls,
-                fld -> arg.accept(fld, positionalParamProvider.apply(fld, arg.nextIdx())),
-                fld -> arg.accept(fld, paramProvider.apply(fld)),
-                (argGrp, flds) -> flds.forEach(fld -> {
-                    if (fld.isAnnotationPresent(Positional.class))
-                        arg.accept(fld, positionalParamProvider.apply(fld, arg.nextIdx()));
-                    else
-                        arg.accept(fld, paramProvider.apply(fld));
-                })
-            );
-
-            if (arg.argGrp != null && (!arg.grpOptional() && !arg.grpFldExists))
-                throw new IllegalArgumentException("One of " + toFormattedNames(argCls, arg.grpdFlds) + " required");
-
-            return arg.res;
-        }
-        catch (InstantiationException | IllegalAccessException e) {
-            throw new IgniteException(e);
-        }
-    }
-
-    /** */
-    private static class ArgumentState<A extends IgniteDataTransferObject> implements BiConsumer<Field, Object> {
-        /** */
-        final A res;
-
-        /** */
-        final ArgumentGroup argGrp;
-
-        /** */
-        boolean grpFldExists;
-
-        /** */
-        int idx;
-
-        /** */
-        final Set<String> grpdFlds;
-
-        /** */
-        public ArgumentState(Class<A> argCls) throws InstantiationException, IllegalAccessException {
-            res = argCls.newInstance();
-            argGrp = argCls.getAnnotation(ArgumentGroup.class);
-            grpdFlds = argGrp == null
-                ? Collections.emptySet()
-                : new HashSet<>(Arrays.asList(argGrp.value()));
-        }
-
-        /** */
-        public boolean grpOptional() {
-            return argGrp == null || argGrp.optional();
-        }
-
-        /** */
-        private int nextIdx() {
-            int idx0 = idx;
-
-            idx++;
-
-            return idx0;
-        }
-
-        /** {@inheritDoc} */
-        @Override public void accept(Field fld, Object val) {
-            boolean grpdFld = grpdFlds.contains(fld.getName());
-
-            if (val == null) {
-                if (grpdFld || fld.getAnnotation(Argument.class).optional())
-                    return;
-
-                String name = fld.isAnnotationPresent(Positional.class)
-                    ? parameterExample(fld, false)
-                    : toFormattedFieldName(fld);
-
-                throw new IllegalArgumentException("Argument " + name + " required.");
-            }
-
-            if (grpdFld) {
-                if (grpFldExists && (argGrp != null && argGrp.onlyOneOf())) {
-                    throw new IllegalArgumentException(
-                        "Only one of " + toFormattedNames(res.getClass(), grpdFlds) + " allowed"
-                    );
-                }
-
-                grpFldExists = true;
-            }
-
-            try {
-                res.getClass().getMethod(fld.getName(), fld.getType()).invoke(res, val);
-            }
-            catch (NoSuchMethodException | IllegalAccessException e) {
-                throw new IgniteException(e);
-            }
-            catch (InvocationTargetException e) {
-                if (e.getTargetException() != null && e.getTargetException() instanceof RuntimeException)
-                    throw (RuntimeException)e.getTargetException();
-            }
-        }
-    }
 }
diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandInvoker.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CliCommandInvoker.java
similarity index 61%
rename from modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandInvoker.java
rename to modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CliCommandInvoker.java
index 06c66b3e015..aa7d4ec6333 100644
--- a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandInvoker.java
+++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CliCommandInvoker.java
@@ -21,11 +21,9 @@ import java.io.IOException;
 import java.net.InetAddress;
 import java.util.Collection;
 import java.util.List;
-import java.util.Map;
-import java.util.UUID;
+import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
-import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.internal.client.GridClient;
 import org.apache.ignite.internal.client.GridClientBeforeNodeStart;
 import org.apache.ignite.internal.client.GridClientCompute;
@@ -37,138 +35,92 @@ import org.apache.ignite.internal.client.GridClientNode;
 import org.apache.ignite.internal.dto.IgniteDataTransferObject;
 import org.apache.ignite.internal.management.api.BeforeNodeStartCommand;
 import org.apache.ignite.internal.management.api.Command;
-import org.apache.ignite.internal.management.api.ComputeCommand;
-import org.apache.ignite.internal.management.api.LocalCommand;
-import org.apache.ignite.internal.management.api.PreparableCommand;
+import org.apache.ignite.internal.management.api.CommandInvoker;
 import org.apache.ignite.internal.util.IgniteUtils;
 import org.apache.ignite.internal.util.typedef.F;
-import org.apache.ignite.internal.visor.VisorTaskArgument;
 import org.apache.ignite.lang.IgniteBiTuple;
 
-import static java.util.Collections.singleton;
-import static java.util.stream.Collectors.toMap;
 import static org.apache.ignite.internal.commandline.CommandHandler.DFLT_HOST;
 
 /**
  * Adapter of new management API command for legacy {@code control.sh} execution flow.
  */
-public class CommandInvoker<A extends IgniteDataTransferObject> {
-    /** Command to execute. */
-    private final Command<A, ?> cmd;
-
-    /** Parsed argument. */
-    private final A arg;
-
+public class CliCommandInvoker<A extends IgniteDataTransferObject> extends CommandInvoker<A> implements AutoCloseable {
     /** Client configuration. */
     private GridClientConfiguration clientCfg;
 
+    /** Client. */
+    private GridClient client;
+
     /** @param cmd Command to execute. */
-    public CommandInvoker(Command<A, ?> cmd, A arg, GridClientConfiguration clientCfg) {
-        this.cmd = cmd;
-        this.arg = arg;
+    public CliCommandInvoker(Command<A, ?> cmd, A arg, GridClientConfiguration clientCfg) {
+        super(cmd, arg, null);
         this.clientCfg = clientCfg;
     }
 
     /**
-     * Actual command execution with verbose mode if needed.
-     * Implement it if your command supports verbose mode.
-     *
-     * @param log Logger to use.
-     * @param verbose Use verbose mode or not
-     * @return Result of operation (mostly usable for tests).
-     * @throws Exception If error occur.
+     * @return Message text to show user for. {@code null} means that confirmantion is not required.
      */
-    public <R> R invoke(IgniteLogger log, boolean verbose) throws Exception {
-        try (GridClient client = startClient(clientCfg)) {
-            String deprecationMsg = cmd.deprecationMessage(arg);
-
-            if (deprecationMsg != null)
-                log.warning(deprecationMsg);
-
-            R res;
-
-            if (cmd instanceof LocalCommand)
-                res = ((LocalCommand<A, R>)cmd).execute(client, arg, log::info);
-            else if (cmd instanceof ComputeCommand) {
-                GridClientCompute compute = client.compute();
-
-                Map<UUID, GridClientNode> nodes = compute.nodes().stream()
-                    .collect(toMap(GridClientNode::nodeId, n -> n));
-
-                ComputeCommand<A, R> cmd = (ComputeCommand<A, R>)this.cmd;
-
-                Collection<UUID> cmdNodes = cmd.nodes(nodes, arg);
+    public String confirmationPrompt() {
+        return cmd.confirmationPrompt(arg);
+    }
 
-                if (cmdNodes == null)
-                    cmdNodes = singleton(defaultNode(client, clientCfg).nodeId());
+    /** */
+    public <R> R invokeBeforeNodeStart(Consumer<String> printer) throws Exception {
+        try (GridClientBeforeNodeStart client = startClientBeforeNodeStart(clientCfg)) {
+            return ((BeforeNodeStartCommand<A, R>)cmd).execute(client, arg, printer);
+        }
+        catch (GridClientDisconnectedException e) {
+            throw new GridClientException(e.getCause());
+        }
+    }
 
-                for (UUID id : cmdNodes) {
-                    if (!nodes.containsKey(id))
-                        throw new IllegalArgumentException("Node with id=" + id + " not found.");
-                }
+    /** {@inheritDoc} */
+    @Override protected GridClientNode defaultNode() throws GridClientException {
+        GridClientNode node;
 
-                Collection<GridClientNode> connectable = F.viewReadOnly(
-                    cmdNodes,
-                    nodes::get,
-                    id -> nodes.get(id).connectable()
-                );
+        // Prefer node from connect string.
+        final String cfgAddr = clientCfg.getServers().iterator().next();
 
-                if (!F.isEmpty(connectable))
-                    compute = compute.projection(connectable);
+        String[] parts = cfgAddr.split(":");
 
-                res = compute.execute(cmd.taskClass().getName(), new VisorTaskArgument<>(cmdNodes, arg, false));
+        if (DFLT_HOST.equals(parts[0])) {
+            InetAddress addr;
 
-                cmd.printResult(arg, res, log::info);
+            try {
+                addr = IgniteUtils.getLocalHost();
+            }
+            catch (IOException e) {
+                throw new GridClientException("Can't get localhost name.", e);
             }
-            else
-                throw new IllegalArgumentException("Unknown command type: " + cmd);
 
-            return res;
-        }
-        catch (Throwable e) {
-            log.error("Failed to perform operation.");
-            log.error(CommandLogger.errorMessage(e));
+            if (addr.isLoopbackAddress())
+                throw new GridClientException("Can't find localhost name.");
 
-            throw e;
-        }
-    }
+            String origAddr = addr.getHostName() + ":" + parts[1];
 
-    /** */
-    public boolean prepare(IgniteLogger log) throws Exception {
-        if (!(cmd instanceof PreparableCommand))
-            return true;
+            node = listHosts(client()).filter(tuple -> origAddr.equals(tuple.get2())).findFirst().map(IgniteBiTuple::get1).orElse(null);
 
-        try (GridClient client = startClient(clientCfg)) {
-            return ((PreparableCommand<A, ?>)cmd).prepare(client, arg, log::info);
+            if (node == null)
+                node = listHostsByClientNode(client()).filter(tuple -> tuple.get2().size() == 1 && cfgAddr.equals(tuple.get2().get(0))).
+                    findFirst().map(IgniteBiTuple::get1).orElse(null);
         }
-    }
+        else
+            node = listHosts(client()).filter(tuple -> cfgAddr.equals(tuple.get2())).findFirst().map(IgniteBiTuple::get1).orElse(null);
 
-    /**
-     * @return Message text to show user for. {@code null} means that confirmantion is not required.
-     */
-    public String confirmationPrompt() {
-        return cmd.confirmationPrompt(arg);
-    }
+        // Otherwise choose random node.
+        if (node == null)
+            node = balancedNode(client().compute());
 
-    /** */
-    public <R> R invokeBeforeNodeStart(IgniteLogger log) throws Exception {
-        try (GridClientBeforeNodeStart client = startClientBeforeNodeStart(clientCfg)) {
-            return ((BeforeNodeStartCommand<A, R>)cmd).execute(client, arg, log::info);
-        }
-        catch (GridClientDisconnectedException e) {
-            throw new GridClientException(e.getCause());
-        }
+        return node;
     }
 
-    /**
-     * Method to create thin client for communication with cluster.
-     *
-     * @param clientCfg Thin client configuration.
-     * @return Grid thin client instance which is already connected to cluster.
-     * @throws Exception If error occur.
-     */
-    private static GridClient startClient(GridClientConfiguration clientCfg) throws Exception {
-        GridClient client = GridClientFactory.start(clientCfg);
+    /** {@inheritDoc} */
+    @Override protected GridClient client() throws GridClientException {
+        if (client != null && client.connected())
+            return client;
+
+        client = GridClientFactory.start(clientCfg);
 
         // If connection is unsuccessful, fail before doing any operations:
         if (!client.connected()) {
@@ -187,6 +139,12 @@ public class CommandInvoker<A extends IgniteDataTransferObject> {
         return client;
     }
 
+    /** {@inheritDoc} */
+    @Override public void close() {
+        if (client != null)
+            client.close();
+    }
+
     /**
      * Method to create thin client for communication with node before it starts.
      * If node has already started, there will be an error.
@@ -217,46 +175,6 @@ public class CommandInvoker<A extends IgniteDataTransferObject> {
         return client;
     }
 
-    /** */
-    private static GridClientNode defaultNode(GridClient client, GridClientConfiguration clientCfg) throws GridClientException {
-        GridClientNode node;
-
-        // Prefer node from connect string.
-        final String cfgAddr = clientCfg.getServers().iterator().next();
-
-        String[] parts = cfgAddr.split(":");
-
-        if (DFLT_HOST.equals(parts[0])) {
-            InetAddress addr;
-
-            try {
-                addr = IgniteUtils.getLocalHost();
-            }
-            catch (IOException e) {
-                throw new GridClientException("Can't get localhost name.", e);
-            }
-
-            if (addr.isLoopbackAddress())
-                throw new GridClientException("Can't find localhost name.");
-
-            String origAddr = addr.getHostName() + ":" + parts[1];
-
-            node = listHosts(client).filter(tuple -> origAddr.equals(tuple.get2())).findFirst().map(IgniteBiTuple::get1).orElse(null);
-
-            if (node == null)
-                node = listHostsByClientNode(client).filter(tuple -> tuple.get2().size() == 1 && cfgAddr.equals(tuple.get2().get(0))).
-                    findFirst().map(IgniteBiTuple::get1).orElse(null);
-        }
-        else
-            node = listHosts(client).filter(tuple -> cfgAddr.equals(tuple.get2())).findFirst().map(IgniteBiTuple::get1).orElse(null);
-
-        // Otherwise choose random node.
-        if (node == null)
-            node = balancedNode(client.compute());
-
-        return node;
-    }
-
     /**
      * @param client Client.
      * @return List of hosts.
diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandHandler.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandHandler.java
index 318a4121c55..8a3257e3c5d 100644
--- a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandHandler.java
+++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandHandler.java
@@ -54,10 +54,8 @@ import org.apache.ignite.internal.management.api.CliSubcommandsWithPrefix;
 import org.apache.ignite.internal.management.api.Command;
 import org.apache.ignite.internal.management.api.CommandUtils;
 import org.apache.ignite.internal.management.api.CommandsRegistry;
-import org.apache.ignite.internal.management.api.ComputeCommand;
 import org.apache.ignite.internal.management.api.EnumDescription;
 import org.apache.ignite.internal.management.api.HelpCommand;
-import org.apache.ignite.internal.management.api.LocalCommand;
 import org.apache.ignite.internal.management.api.Positional;
 import org.apache.ignite.internal.management.cache.CacheCommand;
 import org.apache.ignite.internal.util.IgniteUtils;
@@ -90,6 +88,7 @@ import static org.apache.ignite.internal.management.api.CommandUtils.NAME_PREFIX
 import static org.apache.ignite.internal.management.api.CommandUtils.PARAM_WORDS_DELIM;
 import static org.apache.ignite.internal.management.api.CommandUtils.asOptional;
 import static org.apache.ignite.internal.management.api.CommandUtils.cmdText;
+import static org.apache.ignite.internal.management.api.CommandUtils.executable;
 import static org.apache.ignite.internal.management.api.CommandUtils.join;
 import static org.apache.ignite.internal.management.api.CommandUtils.parameterExample;
 import static org.apache.ignite.internal.management.api.CommandUtils.toFormattedCommandName;
@@ -253,68 +252,83 @@ public class CommandHandler {
 
             ConnectionAndSslParameters<A> args = new ArgumentParser(logger, registry).parseAndValidate(rawArgs);
 
-            commandName = toFormattedCommandName(args.root().getClass()).toUpperCase();
+            commandName = toFormattedCommandName(args.cmdPath().peekLast().getClass()).toUpperCase();
 
-            CommandInvoker<A> invoker = new CommandInvoker<>(args.command(), args.commandArg(), getClientConfiguration(args));
+            try (CliCommandInvoker<A> invoker = new CliCommandInvoker<>(args.command(), args.commandArg(), getClientConfiguration(args))) {
+                int tryConnectMaxCount = 3;
 
-            int tryConnectMaxCount = 3;
+                boolean suppliedAuth = !F.isEmpty(args.userName()) && !F.isEmpty(args.password());
 
-            boolean suppliedAuth = !F.isEmpty(args.userName()) && !F.isEmpty(args.password());
+                boolean credentialsRequested = false;
 
-            boolean credentialsRequested = false;
-
-            while (true) {
-                try {
-                    if (!invoker.prepare(logger))
-                        return EXIT_CODE_OK;
+                while (true) {
+                    try {
+                        if (!invoker.prepare(logger::info))
+                            return EXIT_CODE_OK;
 
-                    if (!args.autoConfirmation()) {
-                        if (!confirm(invoker.confirmationPrompt())) {
-                            logger.info("Operation cancelled.");
+                        if (!args.autoConfirmation()) {
+                            if (!confirm(invoker.confirmationPrompt())) {
+                                logger.info("Operation cancelled.");
 
-                            return EXIT_CODE_OK;
+                                return EXIT_CODE_OK;
+                            }
                         }
-                    }
-
-                    logger.info("Command [" + commandName + "] started");
-                    logger.info("Arguments: " + argumentsToString(rawArgs));
-                    logger.info(U.DELIM);
 
-                    if (args.command() instanceof HelpCommand)
-                        printUsage(logger, args.root());
-                    else if (args.command() instanceof BeforeNodeStartCommand)
-                        lastOperationRes = invoker.invokeBeforeNodeStart(logger);
-                    else
-                        lastOperationRes = invoker.invoke(logger, args.verbose());
+                        logger.info("Command [" + commandName + "] started");
+                        logger.info("Arguments: " + argumentsToString(rawArgs));
+                        logger.info(U.DELIM);
+
+                        String deprecationMsg = args.command().deprecationMessage(args.commandArg());
+
+                        if (deprecationMsg != null)
+                            logger.warning(deprecationMsg);
+
+                        if (args.command() instanceof HelpCommand)
+                            printUsage(logger, args.cmdPath().peekLast());
+                        else {
+                            try {
+                                if (args.command() instanceof BeforeNodeStartCommand)
+                                    lastOperationRes = invoker.invokeBeforeNodeStart(logger::info);
+                                else
+                                    lastOperationRes = invoker.invoke(logger::info, args.verbose());
+                            }
+                            catch (Throwable e) {
+                                logger.error("Failed to perform operation.");
+                                logger.error(CommandLogger.errorMessage(e));
+
+                                throw e;
+                            }
+                        }
 
-                    break;
-                }
-                catch (Throwable e) {
-                    if (!isAuthError(e))
-                        throw e;
+                        break;
+                    }
+                    catch (Throwable e) {
+                        if (!isAuthError(e))
+                            throw e;
 
-                    if (suppliedAuth)
-                        throw new GridClientAuthenticationException("Wrong credentials.");
+                        if (suppliedAuth)
+                            throw new GridClientAuthenticationException("Wrong credentials.");
 
-                    if (tryConnectMaxCount == 0) {
-                        throw new GridClientAuthenticationException("Maximum number of " +
-                            "retries exceeded");
-                    }
+                        if (tryConnectMaxCount == 0) {
+                            throw new GridClientAuthenticationException("Maximum number of " +
+                                "retries exceeded");
+                        }
 
-                    logger.info(credentialsRequested ?
-                        "Authentication error, please try again." :
-                        "This cluster requires authentication.");
+                        logger.info(credentialsRequested ?
+                            "Authentication error, please try again." :
+                            "This cluster requires authentication.");
 
-                    if (credentialsRequested)
-                        tryConnectMaxCount--;
+                        if (credentialsRequested)
+                            tryConnectMaxCount--;
 
-                    invoker.clientConfiguration(getClientConfiguration(
-                        retrieveUserName(args, invoker.clientConfiguration()),
-                        new String(requestPasswordFromConsole("password: ")),
-                        args
-                    ));
+                        invoker.clientConfiguration(getClientConfiguration(
+                            retrieveUserName(args, invoker.clientConfiguration()),
+                            new String(requestPasswordFromConsole("password: ")),
+                            args
+                        ));
 
-                    credentialsRequested = true;
+                        credentialsRequested = true;
+                    }
                 }
             }
 
@@ -827,10 +841,7 @@ public class CommandHandler {
      * @param logger Logger to print help to.
      */
     public static void usage(Command<?, ?> cmd, List<Command<?, ?>> parents, IgniteLogger logger) {
-        if (cmd instanceof LocalCommand
-            || cmd instanceof ComputeCommand
-            || cmd instanceof HelpCommand
-            || cmd instanceof BeforeNodeStartCommand) {
+        if (executable(cmd)) {
             logger.info("");
 
             if (cmd.getClass().isAnnotationPresent(IgniteExperimental.class))
diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/ConnectionAndSslParameters.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/ConnectionAndSslParameters.java
index 4dc5c5ab0ae..f9a9397d8b6 100644
--- a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/ConnectionAndSslParameters.java
+++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/ConnectionAndSslParameters.java
@@ -17,10 +17,12 @@
 
 package org.apache.ignite.internal.commandline;
 
+import java.util.Deque;
 import org.apache.ignite.internal.client.GridClientConfiguration;
 import org.apache.ignite.internal.commandline.argument.parser.CLIArgumentParser;
 import org.apache.ignite.internal.dto.IgniteDataTransferObject;
 import org.apache.ignite.internal.management.api.Command;
+import org.apache.ignite.internal.management.api.CommandsRegistry;
 
 import static org.apache.ignite.internal.commandline.ArgumentParser.CMD_AUTO_CONFIRMATION;
 import static org.apache.ignite.internal.commandline.ArgumentParser.CMD_HOST;
@@ -58,10 +60,7 @@ public class ConnectionAndSslParameters<A extends IgniteDataTransferObject> {
     private char[] sslTrustStorePassword;
 
     /** Command to execute. */
-    private final Command<A, ?> command;
-
-    /** Command to execute. */
-    private final Command<?, ?> root;
+    private final Deque<Command<?, ?>> cmdPath;
 
     /** Command argument. */
     private final A arg;
@@ -70,16 +69,16 @@ public class ConnectionAndSslParameters<A extends IgniteDataTransferObject> {
     private final CLIArgumentParser parser;
 
     /**
-     * @param command Command.
+     * @param cmdPath Path to the command in {@link CommandsRegistry} hierarchy.
+     * @param arg Command argument.
+     * @param parser CLI arguments parser.
      */
     public ConnectionAndSslParameters(
-        Command<A, ?> command,
-        Command<?, ?> root,
+        Deque<Command<?, ?>> cmdPath,
         A arg,
         CLIArgumentParser parser
     ) {
-        this.command = command;
-        this.root = root;
+        this.cmdPath = cmdPath;
         this.arg = arg;
         this.parser = parser;
 
@@ -93,14 +92,14 @@ public class ConnectionAndSslParameters<A extends IgniteDataTransferObject> {
      * @return High-level command which were defined by user to run.
      */
     public Command<A, ?> command() {
-        return command;
+        return (Command<A, ?>)cmdPath.peek();
     }
 
     /**
      * @return High-level command which were defined by user to run.
      */
-    public Command<?, ?> root() {
-        return root != null ? root : command;
+    public Deque<Command<?, ?>> cmdPath() {
+        return cmdPath;
     }
 
     /** */
diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/argument/parser/CLIArgumentParser.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/argument/parser/CLIArgumentParser.java
index f361f751d55..bf58ed927de 100644
--- a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/argument/parser/CLIArgumentParser.java
+++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/argument/parser/CLIArgumentParser.java
@@ -107,7 +107,7 @@ public class CLIArgumentParser {
             if (!bool && !argsIter.hasNext())
                 throw new IllegalArgumentException("Please specify a value for argument: " + arg);
 
-            String strVal = bool ? null : argsIter.next();
+            String strVal = bool ? "true" : argsIter.next();
 
             if (strVal != null && strVal.startsWith(NAME_PREFIX))
                 throw new IllegalArgumentException("Unexpected value: " + strVal);
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/events/BaselineEventsTest.java b/modules/control-utility/src/test/java/org/apache/ignite/events/BaselineEventsTest.java
index 135ac1b5648..4b9265fcce5 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/events/BaselineEventsTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/events/BaselineEventsTest.java
@@ -31,11 +31,11 @@ import org.apache.ignite.internal.cluster.IgniteClusterEx;
 import org.apache.ignite.internal.commandline.CommandHandler;
 import org.apache.ignite.lang.IgnitePredicate;
 import org.apache.ignite.testframework.GridTestUtils;
-import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.apache.ignite.util.GridCommandHandlerFactoryAbstractTest;
 import org.junit.Test;
 
 /** */
-public abstract class BaselineEventsTest extends GridCommonAbstractTest {
+public abstract class BaselineEventsTest extends GridCommandHandlerFactoryAbstractTest {
     /** */
     private int[] includedEvtTypes = EventType.EVTS_ALL;
 
@@ -102,7 +102,7 @@ public abstract class BaselineEventsTest extends GridCommonAbstractTest {
 
         assertEquals(
             CommandHandler.EXIT_CODE_OK,
-            new CommandHandler().execute(Arrays.asList("--baseline", "set", consistentIds, "--yes"))
+            newCommandHandler().execute(Arrays.asList("--baseline", "set", consistentIds, "--yes"))
         );
 
         assertTrue(GridTestUtils.waitForCondition(baselineChanged::get, 3_000));
@@ -187,15 +187,15 @@ public abstract class BaselineEventsTest extends GridCommonAbstractTest {
 
         assertEquals(
             CommandHandler.EXIT_CODE_OK,
-            new CommandHandler().execute(Arrays.asList("--baseline", "auto_adjust", "enable", "timeout", "10", "--yes"))
+            newCommandHandler().execute(Arrays.asList("--baseline", "auto_adjust", "enable", "timeout", "10", "--yes"))
         );
         assertTrue(GridTestUtils.waitForCondition(autoAdjustEnabled::get, 3_000));
 
         assertEquals(
             CommandHandler.EXIT_CODE_OK,
-            new CommandHandler().execute(Arrays.asList("--baseline", "auto_adjust", "disable", "--yes"))
+            newCommandHandler().execute(Arrays.asList("--baseline", "auto_adjust", "disable", "--yes"))
         );
-        assertFalse(autoAdjustEnabled.get());
+        assertTrue(GridTestUtils.waitForCondition(() -> !autoAdjustEnabled.get(), 3_000));
 
         cluster.baselineAutoAdjustEnabled(true);
         assertTrue(GridTestUtils.waitForCondition(autoAdjustEnabled::get, 3_000));
@@ -227,7 +227,7 @@ public abstract class BaselineEventsTest extends GridCommonAbstractTest {
 
         assertEquals(
             CommandHandler.EXIT_CODE_OK,
-            new CommandHandler().execute(Arrays.asList("--baseline", "auto_adjust", "enable", "timeout", "10", "--yes"))
+            newCommandHandler().execute(Arrays.asList("--baseline", "auto_adjust", "enable", "timeout", "10", "--yes"))
         );
         assertTrue(GridTestUtils.waitForCondition(() -> autoAdjustTimeout.get() == 10L, 3_000));
 
@@ -263,7 +263,7 @@ public abstract class BaselineEventsTest extends GridCommonAbstractTest {
 
         assertEquals(
             CommandHandler.EXIT_CODE_OK,
-            new CommandHandler().execute(Arrays.asList("--baseline", "set", consistentIds, "--yes"))
+            newCommandHandler().execute(Arrays.asList("--baseline", "set", consistentIds, "--yes"))
         );
 
         awaitPartitionMapExchange();
@@ -276,7 +276,7 @@ public abstract class BaselineEventsTest extends GridCommonAbstractTest {
 
         assertEquals(
             CommandHandler.EXIT_CODE_OK,
-            new CommandHandler().execute(Arrays.asList("--baseline", "auto_adjust", "enable", "timeout", "10", "--yes"))
+            newCommandHandler().execute(Arrays.asList("--baseline", "auto_adjust", "enable", "timeout", "10", "--yes"))
         );
 
         cluster.baselineAutoAdjustEnabled(false);
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java b/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java
index 2bae6e7946a..d150f9e75ab 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java
@@ -434,9 +434,9 @@ public class CommandHandlerParsingTest {
             Command<A, ?> cmdL = (Command<A, ?>)e.getValue();
 
             // SET_STATE command has mandatory argument used in confirmation message.
-            CommandInvoker<A> cmd = cmdL.getClass() != SetStateCommand.class
-                ? new CommandInvoker<>(cmdL, null, null)
-                : new CommandInvoker<>(parseArgs(asList(cmdText(cmdL), "ACTIVE")).command(), null, null);
+            CliCommandInvoker<A> cmd = cmdL.getClass() != SetStateCommand.class
+                ? new CliCommandInvoker<>(cmdL, null, null)
+                : new CliCommandInvoker<>(parseArgs(asList(cmdText(cmdL), "ACTIVE")).command(), null, null);
 
             if (!(cmd instanceof ComputeCommand)
                 && !(cmd instanceof LocalCommand)
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/indexreader/IgniteIndexReaderTest.java b/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/indexreader/IgniteIndexReaderTest.java
index 4816e4f5b36..a3f6022c9a8 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/indexreader/IgniteIndexReaderTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/indexreader/IgniteIndexReaderTest.java
@@ -67,6 +67,7 @@ import org.apache.ignite.lang.IgniteBiTuple;
 import org.apache.ignite.util.GridCommandHandlerAbstractTest;
 import org.jetbrains.annotations.Nullable;
 import org.junit.Test;
+import org.junit.runners.Parameterized;
 
 import static java.lang.String.format;
 import static java.lang.String.valueOf;
@@ -151,6 +152,12 @@ public class IgniteIndexReaderTest extends GridCommandHandlerAbstractTest {
     /** Work directory, containing cache group directories. */
     private static File workDir;
 
+    /** */
+    @Parameterized.Parameters(name = "cmdHnd={0}")
+    public static List<String> commandHandlers() {
+        return Collections.singletonList(CLI_CMD_HND);
+    }
+
     /** {@inheritDoc} */
     @Override protected void beforeTestsStarted() throws Exception {
         super.beforeTestsStarted();
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/internal/processors/security/GridCommandHandlerSslWithSecurityTest.java b/modules/control-utility/src/test/java/org/apache/ignite/internal/processors/security/GridCommandHandlerSslWithSecurityTest.java
index d697f48034e..dfcaaf113b2 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/internal/processors/security/GridCommandHandlerSslWithSecurityTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/internal/processors/security/GridCommandHandlerSslWithSecurityTest.java
@@ -28,10 +28,10 @@ import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.commandline.CommandHandler;
 import org.apache.ignite.internal.commandline.NoopConsole;
-import org.apache.ignite.internal.logger.IgniteLoggerEx;
 import org.apache.ignite.internal.processors.security.impl.TestSecurityPluginProvider;
-import org.apache.ignite.internal.util.typedef.internal.U;
-import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.util.GridCommandHandlerFactoryAbstractTest;
+import org.junit.Assume;
 import org.junit.Test;
 
 import static org.apache.ignite.cluster.ClusterState.ACTIVE;
@@ -45,7 +45,7 @@ import static org.apache.ignite.testframework.GridTestUtils.sslTrustedFactory;
 /**
  * Command line handler test with SSL and security.
  */
-public class GridCommandHandlerSslWithSecurityTest extends GridCommonAbstractTest {
+public class GridCommandHandlerSslWithSecurityTest extends GridCommandHandlerFactoryAbstractTest {
     /** Login. */
     private final String login = "testUsr";
 
@@ -70,6 +70,13 @@ public class GridCommandHandlerSslWithSecurityTest extends GridCommonAbstractTes
         sysOut = System.out;
     }
 
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        Assume.assumeTrue(commandHandler.equalsIgnoreCase(CLI_CMD_HND));
+
+        super.beforeTest();
+    }
+
     /** {@inheritDoc} */
     @Override protected void afterTest() throws Exception {
         super.afterTest();
@@ -88,14 +95,6 @@ public class GridCommandHandlerSslWithSecurityTest extends GridCommonAbstractTes
         System.setOut(new PrintStream(testOut));
     }
 
-    /**
-     * Flushes all Logger handlers to make log data available to test.
-     * @param hnd Command handler.
-     */
-    private void flushCommandOutput(CommandHandler hnd) {
-        U.<IgniteLoggerEx>field(hnd, "logger").flush();
-    }
-
     /** {@inheritDoc} */
     @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
         return super.getConfiguration(igniteInstanceName)
@@ -121,12 +120,12 @@ public class GridCommandHandlerSslWithSecurityTest extends GridCommonAbstractTes
 
         crd.cluster().state(ACTIVE);
 
-        CommandHandler cmd = new CommandHandler();
+        TestCommandHandler cmd = newCommandHandler();
 
         AtomicInteger keyStorePwdCnt = new AtomicInteger();
         AtomicInteger trustStorePwdCnt = new AtomicInteger();
 
-        cmd.console = new NoopConsole() {
+        ((CommandHandler)GridTestUtils.getFieldValue(cmd, "hnd")).console = new NoopConsole() {
             /** {@inheritDoc} */
             @Override public char[] readPassword(String fmt, Object... args) {
                 if (fmt.contains("keystore")) {
@@ -175,7 +174,7 @@ public class GridCommandHandlerSslWithSecurityTest extends GridCommonAbstractTes
 
         injectTestSystemOut();
 
-        CommandHandler hnd = new CommandHandler();
+        TestCommandHandler hnd = newCommandHandler();
 
         int exitCode = hnd.execute(Arrays.asList(
             "--state",
@@ -188,7 +187,7 @@ public class GridCommandHandlerSslWithSecurityTest extends GridCommonAbstractTes
 
         assertEquals(EXIT_CODE_OK, exitCode);
 
-        flushCommandOutput(hnd);
+        hnd.flushLogger();
 
         // Make sure all sensitive information is masked.
         String testOutput = testOut.toString();
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/CdcCommandTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/CdcCommandTest.java
index fb7835d2abc..5bf38d626a9 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/CdcCommandTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/CdcCommandTest.java
@@ -56,6 +56,7 @@ import org.apache.ignite.plugin.AbstractTestPluginProvider;
 import org.apache.ignite.plugin.PluginContext;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.jetbrains.annotations.Nullable;
+import org.junit.Assume;
 import org.junit.Test;
 import static org.apache.ignite.cdc.AbstractCdcTest.ChangeEventType.UPDATE;
 import static org.apache.ignite.cdc.AbstractCdcTest.KEYS_CNT;
@@ -203,9 +204,11 @@ public class CdcCommandTest extends GridCommandHandlerAbstractTest {
 
         appStarted.await(getTestTimeout(), TimeUnit.MILLISECONDS);
 
-        assertContains(log, executeCommand(EXIT_CODE_UNEXPECTED_ERROR,
-                CDC, DELETE_LOST_SEGMENT_LINKS, NODE_ID, srv0.localNode().id().toString()),
-            "Failed to delete lost segment CDC links. Unable to acquire lock to lock CDC folder.");
+        String out = executeCommand(EXIT_CODE_UNEXPECTED_ERROR,
+            CDC, DELETE_LOST_SEGMENT_LINKS, NODE_ID, srv0.localNode().id().toString());
+
+        if (commandHandler.equals(CLI_CMD_HND))
+            assertContains(log, out, "Failed to delete lost segment CDC links. Unable to acquire lock to lock CDC folder.");
 
         assertFalse(fut.isDone());
     }
@@ -324,9 +327,10 @@ public class CdcCommandTest extends GridCommandHandlerAbstractTest {
     public void testResendCachesNotExist() {
         injectTestSystemOut();
 
-        assertContains(log, executeCommand(EXIT_CODE_UNEXPECTED_ERROR,
-                CDC, RESEND, CACHES, "unknown_cache"),
-            "Cache does not exist");
+        String out = executeCommand(EXIT_CODE_UNEXPECTED_ERROR, CDC, RESEND, CACHES, "unknown_cache");
+
+        if (commandHandler.equals(CLI_CMD_HND))
+            assertContains(log, out, "Cache does not exist");
 
         String cdcDisabledCacheName = "cdcDisabledCache";
 
@@ -334,9 +338,10 @@ public class CdcCommandTest extends GridCommandHandlerAbstractTest {
             .setName(cdcDisabledCacheName)
             .setDataRegionName(CDC_DISABLED_DATA_REGION));
 
-        assertContains(log, executeCommand(EXIT_CODE_UNEXPECTED_ERROR,
-                CDC, RESEND, CACHES, cdcDisabledCacheName),
-            "CDC is not enabled for given cache");
+        out = executeCommand(EXIT_CODE_UNEXPECTED_ERROR, CDC, RESEND, CACHES, cdcDisabledCacheName);
+
+        if (commandHandler.equals(CLI_CMD_HND))
+            assertContains(log, out, "CDC is not enabled for given cache");
     }
 
     /** */
@@ -366,6 +371,8 @@ public class CdcCommandTest extends GridCommandHandlerAbstractTest {
     /** */
     @Test
     public void testResendCancelOnRebalanceInProgress() throws Exception {
+        Assume.assumeTrue(commandHandler.equals(CLI_CMD_HND));
+
         injectTestSystemOut();
 
         addData(srv0.cache(DEFAULT_CACHE_NAME), 0, KEYS_CNT);
@@ -415,9 +422,11 @@ public class CdcCommandTest extends GridCommandHandlerAbstractTest {
         }
 
         IgniteInternalFuture<Object> fut = GridTestUtils.runAsync(() -> {
-            assertContains(log, executeCommand(EXIT_CODE_UNEXPECTED_ERROR,
-                    CDC, RESEND, CACHES, DEFAULT_CACHE_NAME),
-                "CDC cache data resend cancelled. Topology changed");
+            String out = executeCommand(EXIT_CODE_UNEXPECTED_ERROR,
+                CDC, RESEND, CACHES, DEFAULT_CACHE_NAME);
+
+            if (commandHandler.equals(CLI_CMD_HND))
+                assertContains(log, out, "CDC cache data resend cancelled. Topology changed");
         });
 
         blocked.await();
@@ -453,9 +462,11 @@ public class CdcCommandTest extends GridCommandHandlerAbstractTest {
         };
 
         IgniteInternalFuture<Object> fut = GridTestUtils.runAsync(() -> {
-            assertContains(log, executeCommand(EXIT_CODE_UNEXPECTED_ERROR,
-                    CDC, RESEND, CACHES, DEFAULT_CACHE_NAME),
-                "CDC cache data resend cancelled. Topology changed");
+            String out = executeCommand(EXIT_CODE_UNEXPECTED_ERROR,
+                CDC, RESEND, CACHES, DEFAULT_CACHE_NAME);
+
+            if (commandHandler.equals(CLI_CMD_HND))
+                assertContains(log, out, "CDC cache data resend cancelled. Topology changed");
         });
 
         preload.await();
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerAbstractTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerAbstractTest.java
index efb93665a57..8bb0e670196 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerAbstractTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerAbstractTest.java
@@ -50,7 +50,6 @@ import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.TestRecordingCommunicationSpi;
 import org.apache.ignite.internal.client.GridClientFactory;
 import org.apache.ignite.internal.commandline.CommandHandler;
-import org.apache.ignite.internal.logger.IgniteLoggerEx;
 import org.apache.ignite.internal.management.cache.CacheIdleVerifyCommand;
 import org.apache.ignite.internal.processors.cache.GridCacheFuture;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxPrepareFuture;
@@ -65,8 +64,8 @@ import org.apache.ignite.logger.java.JavaLoggerFileHandler;
 import org.apache.ignite.spi.encryption.keystore.KeystoreEncryptionSpi;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.apache.ignite.testframework.junits.WithSystemProperty;
-import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
 import org.jetbrains.annotations.Nullable;
+
 import static java.lang.String.join;
 import static java.lang.System.lineSeparator;
 import static java.nio.file.Files.delete;
@@ -90,7 +89,7 @@ import static org.apache.ignite.internal.management.cache.VerifyBackupPartitions
  * {@link GridCommandHandlerClusterByClassAbstractTest}
  */
 @WithSystemProperty(key = IGNITE_ENABLE_EXPERIMENTAL_COMMAND, value = "true")
-public abstract class GridCommandHandlerAbstractTest extends GridCommonAbstractTest {
+public abstract class GridCommandHandlerAbstractTest extends GridCommandHandlerFactoryAbstractTest {
     /** */
     protected static final String CLIENT_NODE_NAME_PREFIX = "client";
 
@@ -347,7 +346,7 @@ public abstract class GridCommandHandlerAbstractTest extends GridCommonAbstractT
      * @return Result of execution
      */
     protected int execute(List<String> args) {
-        return execute(new CommandHandler(createTestLogger()), args);
+        return execute(newCommandHandler(createTestLogger()), args);
     }
 
     /**
@@ -357,14 +356,14 @@ public abstract class GridCommandHandlerAbstractTest extends GridCommonAbstractT
      * @param args Arguments.
      * @return Result of execution
      */
-    protected int execute(CommandHandler hnd, String... args) {
+    protected int execute(TestCommandHandler hnd, String... args) {
         return execute(hnd, new ArrayList<>(asList(args)));
     }
 
     /**
      * Before command executed {@link #testOut} reset.
      */
-    protected int execute(CommandHandler hnd, List<String> args) {
+    protected int execute(TestCommandHandler hnd, List<String> args) {
         if (!F.isEmpty(args) && !"--help".equalsIgnoreCase(args.get(0)))
             addExtraArguments(args);
 
@@ -374,7 +373,7 @@ public abstract class GridCommandHandlerAbstractTest extends GridCommonAbstractT
         lastOperationResult = hnd.getLastOperationResult();
 
         // Flush all Logger handlers to make log data available to test.
-        U.<IgniteLoggerEx>field(hnd, "logger").flush();
+        hnd.flushLogger();
 
         return exitCode;
     }
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerCheckIncrementalSnapshotTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerCheckIncrementalSnapshotTest.java
index 6cf4a640139..6fe71e0fc46 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerCheckIncrementalSnapshotTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerCheckIncrementalSnapshotTest.java
@@ -20,6 +20,7 @@ package org.apache.ignite.util;
 import java.util.List;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.function.Consumer;
+import java.util.stream.Collectors;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.cache.CacheAtomicityMode;
 import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
@@ -28,7 +29,6 @@ import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.TestRecordingCommunicationSpi;
-import org.apache.ignite.internal.commandline.CommandHandler;
 import org.apache.ignite.internal.management.cache.IdleVerifyResultV2;
 import org.apache.ignite.internal.pagemem.wal.IgniteWriteAheadLogManager;
 import org.apache.ignite.internal.pagemem.wal.record.DataRecord;
@@ -69,7 +69,7 @@ public class GridCommandHandlerCheckIncrementalSnapshotTest extends GridCommandH
     private static final String SNP = "testSnapshot";
 
     /** */
-    private CommandHandler cmd;
+    private TestCommandHandler cmd;
 
     /** */
     private volatile IgniteBiPredicate<Object, TxRecord> skipTxRec;
@@ -78,25 +78,34 @@ public class GridCommandHandlerCheckIncrementalSnapshotTest extends GridCommandH
     private volatile IgniteBiPredicate<Object, DataRecord> skipDataRec;
 
     /** Count of server nodes to start. */
-    @Parameterized.Parameter
+    @Parameterized.Parameter(1)
     public int nodesCnt;
 
     /** Count of primary nodes participated in a transaction. */
-    @Parameterized.Parameter(1)
+    @Parameterized.Parameter(2)
     public int txPrimNodesCnt;
 
     /** Count of primary nodes participated in a transaction. */
-    @Parameterized.Parameter(2)
+    @Parameterized.Parameter(3)
     public int backupsCnt;
 
     /** */
-    @Parameterized.Parameters(name = "nodesCnt={0}, primNodesCnt={1}, backupsCnt={2}")
+    @Parameterized.Parameters(name = "cmdHnd={0},nodesCnt={1},primNodesCnt={2},backupsCnt={3}")
     public static List<Object[]> params() {
         return F.asList(
             new Object[]{2, 1, 1},
             new Object[]{2, 2, 1},
             new Object[]{3, 1, 1},
-            new Object[]{3, 1, 2});
+            new Object[]{3, 1, 2}
+        ).stream().flatMap(row -> commandHandlers().stream().map(invoker -> {
+            Object[] res = new Object[row.length + 1];
+
+            res[0] = invoker;
+
+            System.arraycopy(row, 0, res, 1, row.length);
+
+            return res;
+        })).collect(Collectors.toList());
     }
 
 
@@ -132,7 +141,7 @@ public class GridCommandHandlerCheckIncrementalSnapshotTest extends GridCommandH
 
         injectTestSystemOut();
 
-        cmd = new CommandHandler();
+        cmd = newCommandHandler();
     }
 
     /** */
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerClusterByClassTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerClusterByClassTest.java
index 76379ea5365..49f4a7159d7 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerClusterByClassTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerClusterByClassTest.java
@@ -97,6 +97,7 @@ import org.apache.ignite.transactions.Transaction;
 import org.apache.ignite.transactions.TransactionRollbackException;
 import org.apache.ignite.transactions.TransactionState;
 import org.jetbrains.annotations.NotNull;
+import org.junit.Assume;
 import org.junit.Test;
 
 import static java.util.Arrays.asList;
@@ -367,6 +368,8 @@ public class GridCommandHandlerClusterByClassTest extends GridCommandHandlerClus
     /** */
     @Test
     public void testCacheHelp() throws Exception {
+        Assume.assumeTrue(commandHandler.equals(CLI_CMD_HND));
+
         injectTestSystemOut();
 
         assertEquals(EXIT_CODE_OK, execute("--cache", "help"));
@@ -418,6 +421,8 @@ public class GridCommandHandlerClusterByClassTest extends GridCommandHandlerClus
     /** */
     @Test
     public void testHelp() throws Exception {
+        Assume.assumeTrue(commandHandler.equals(CLI_CMD_HND));
+
         injectTestSystemOut();
 
         assertEquals(EXIT_CODE_OK, execute("--help"));
@@ -494,6 +499,8 @@ public class GridCommandHandlerClusterByClassTest extends GridCommandHandlerClus
     /** */
     @Test
     public void testPrintTimestampAtEndsOfExecution() {
+        Assume.assumeTrue(commandHandler.equals(CLI_CMD_HND));
+
         injectTestSystemOut();
 
         assertEquals(EXIT_CODE_OK, execute());
@@ -1300,9 +1307,11 @@ public class GridCommandHandlerClusterByClassTest extends GridCommandHandlerClus
 
         int expSize = G.allGrids().size();
 
-        assertContains(log, executeCommand(EXIT_CODE_UNEXPECTED_ERROR, "--cache", CREATE,
-                SPRING_XML_CONFIG, cfgPath + "/cache-create-with-spel.xml"),
-            "Spring expressions are prohibited.");
+        String out = executeCommand(EXIT_CODE_UNEXPECTED_ERROR, "--cache", CREATE,
+            SPRING_XML_CONFIG, cfgPath + "/cache-create-with-spel.xml");
+
+        if (commandHandler.equals(CLI_CMD_HND))
+            assertContains(log, out, "Spring expressions are prohibited.");
 
         assertEquals(expSize, G.allGrids().size());
     }
@@ -1313,10 +1322,15 @@ public class GridCommandHandlerClusterByClassTest extends GridCommandHandlerClus
         String warningMsgPrefix = "Warning! The command will destroy";
         String requiredArgsMsg = "One of [" + CACHES + ", " + DESTROY_ALL_ARG + "] required";
 
+        String cacheName = "temp-internal-cache";
+
         // Create some internal caches.
-        CacheConfiguration<Object, Object> internalCfg = new CacheConfiguration<>("temp-internal-cache");
-        crd.context().cache().dynamicStartCache(internalCfg, internalCfg.getName(), null, CacheType.INTERNAL, false,
-            true, true, false).get(getTestTimeout());
+        if (crd.context().cache().internalCache(cacheName) == null) {
+            CacheConfiguration<Object, Object> internalCfg = new CacheConfiguration<>(cacheName);
+            crd.context().cache().dynamicStartCache(internalCfg, internalCfg.getName(), null, CacheType.INTERNAL, false,
+                true, true, false).get(getTestTimeout());
+        }
+
         crd.countDownLatch("structure", 1, true, true);
 
         long internalCachesCnt = crd.context().cache().cacheDescriptors().values().stream().filter(
@@ -1371,7 +1385,9 @@ public class GridCommandHandlerClusterByClassTest extends GridCommandHandlerClus
         // Destroy all user-created caches.
         injectTestSystemIn(CONFIRM_MSG);
         assertEquals(EXIT_CODE_OK, execute("--cache", DESTROY, DESTROY_ALL_ARG));
-        assertContains(log, testOut.toString(), expConfirmation);
+
+        if (commandHandler.equals(CLI_CMD_HND))
+            assertContains(log, testOut.toString(), expConfirmation);
         assertTrue("Caches must be destroyed: " + crd.cacheNames().toString(), crd.cacheNames().isEmpty());
 
         autoConfirmation = true;
@@ -1398,14 +1414,16 @@ public class GridCommandHandlerClusterByClassTest extends GridCommandHandlerClus
         assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute("--cache", CLEAR, CACHES, "X,Y", "Z"));
         assertContains(log, testOut.toString(), "Unexpected argument: Z");
 
-        autoConfirmation = false;
+        if (commandHandler.equals(CLI_CMD_HND)) {
+            autoConfirmation = false;
 
-        String expConfirmation = String.format(CacheClearCommand.CONFIRM_MSG, 2, "cache1, cache2");
+            String expConfirmation = String.format(CacheClearCommand.CONFIRM_MSG, 2, "cache1, cache2");
 
-        // Ensure we cannot delete a cache groups.
-        injectTestSystemIn(CONFIRM_MSG);
-        assertEquals(EXIT_CODE_OK, execute("--cache", CLEAR, CACHES, "cache1,cache2"));
-        assertContains(log, testOut.toString(), expConfirmation);
+            // Ensure we cannot delete a cache groups.
+            injectTestSystemIn(CONFIRM_MSG);
+            assertEquals(EXIT_CODE_OK, execute("--cache", CLEAR, CACHES, "cache1,cache2"));
+            assertContains(log, testOut.toString(), expConfirmation);
+        }
 
         autoConfirmation = true;
 
@@ -1757,8 +1775,8 @@ public class GridCommandHandlerClusterByClassTest extends GridCommandHandlerClus
      * @param validateClo Validate clo.
      * @param args Args.
      */
-    private void validate(CommandHandler h, IgniteInClosure<Map<ClusterNode, TxTaskResult>> validateClo,
-        String... args) {
+    private void validate(TestCommandHandler h, IgniteInClosure<Map<ClusterNode, TxTaskResult>> validateClo,
+                          String... args) {
         assertEquals(EXIT_CODE_OK, execute(h, args));
 
         validateClo.apply(h.getLastOperationResult());
@@ -1935,6 +1953,8 @@ public class GridCommandHandlerClusterByClassTest extends GridCommandHandlerClus
     @Test
     @WithSystemProperty(key = IGNITE_ENABLE_EXPERIMENTAL_COMMAND, value = "true")
     public void testContainsNotExperimentalCmdInHelpOutputWhenEnableExperimentalTrue() {
+        Assume.assumeTrue(commandHandler.equals(CLI_CMD_HND));
+
         checkContainsNotExperimentalCmdInHelpOutput();
     }
 
@@ -1947,6 +1967,8 @@ public class GridCommandHandlerClusterByClassTest extends GridCommandHandlerClus
     @Test
     @WithSystemProperty(key = IGNITE_ENABLE_EXPERIMENTAL_COMMAND, value = "false")
     public void testContainsNotExperimentalCmdInHelpOutputWhenEnableExperimentalFalse() {
+        Assume.assumeTrue(commandHandler.equals(CLI_CMD_HND));
+
         checkContainsNotExperimentalCmdInHelpOutput();
     }
 
@@ -1957,6 +1979,8 @@ public class GridCommandHandlerClusterByClassTest extends GridCommandHandlerClus
     @Test
     @WithSystemProperty(key = IGNITE_ENABLE_EXPERIMENTAL_COMMAND, value = "true")
     public void testContainsExperimentalCmdInHelpOutput() {
+        Assume.assumeTrue(commandHandler.equals(CLI_CMD_HND));
+
         checkExperimentalCmdInHelpOutput(true);
     }
 
@@ -1967,6 +1991,8 @@ public class GridCommandHandlerClusterByClassTest extends GridCommandHandlerClus
     @Test
     @WithSystemProperty(key = IGNITE_ENABLE_EXPERIMENTAL_COMMAND, value = "false")
     public void testNotContainsExperimentalCmdInHelpOutput() {
+        Assume.assumeTrue(commandHandler.equals(CLI_CMD_HND));
+
         checkExperimentalCmdInHelpOutput(false);
     }
 
@@ -2040,6 +2066,8 @@ public class GridCommandHandlerClusterByClassTest extends GridCommandHandlerClus
      */
     @Test
     public void testErrInvalidArgumentsWithVerbose() {
+        Assume.assumeTrue(commandHandler.equals(CLI_CMD_HND));
+
         injectTestSystemOut();
 
         int resCode = EXIT_CODE_INVALID_ARGUMENTS;
@@ -2058,6 +2086,8 @@ public class GridCommandHandlerClusterByClassTest extends GridCommandHandlerClus
      */
     @Test
     public void testErrConnectionWithVerbose() {
+        Assume.assumeTrue(commandHandler.equals(CLI_CMD_HND));
+
         injectTestSystemOut();
 
         int resCode = EXIT_CODE_CONNECTION_FAILED;
@@ -2076,6 +2106,8 @@ public class GridCommandHandlerClusterByClassTest extends GridCommandHandlerClus
      */
     @Test
     public void testErrUnexpectedWithWithoutVerbose() {
+        Assume.assumeTrue(commandHandler.equals(CLI_CMD_HND));
+
         injectTestSystemOut();
 
         Logger log = GridCommandHandlerAbstractTest.initLogger(null);
@@ -2092,7 +2124,7 @@ public class GridCommandHandlerClusterByClassTest extends GridCommandHandlerClus
         }));
 
         int resCode = EXIT_CODE_UNEXPECTED_ERROR;
-        CommandHandler cmd = new CommandHandler(new JavaLogger(log, false));
+        TestCommandHandler cmd = newCommandHandler(new JavaLogger(log, false));
 
         assertEquals(resCode, execute(cmd, BASELINE));
         assertContains(GridAbstractTest.log, testOut.toString(), ERROR_STACK_TRACE_PREFIX);
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerClusterByClassWithSSLTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerClusterByClassWithSSLTest.java
index f91890eface..6a4a5f7c37c 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerClusterByClassWithSSLTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerClusterByClassWithSSLTest.java
@@ -17,6 +17,8 @@
 
 package org.apache.ignite.util;
 
+import org.junit.Assume;
+
 /**
  *
  */
@@ -25,4 +27,11 @@ public class GridCommandHandlerClusterByClassWithSSLTest extends GridCommandHand
     @Override protected boolean sslEnabled() {
         return true;
     }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        Assume.assumeTrue(commandHandler.equalsIgnoreCase(CLI_CMD_HND));
+
+        super.beforeTest();
+    }
 }
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerConsistencyCountersTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerConsistencyCountersTest.java
index 4a23ec62762..cbf37ca1d09 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerConsistencyCountersTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerConsistencyCountersTest.java
@@ -97,12 +97,23 @@ public class GridCommandHandlerConsistencyCountersTest extends GridCommandHandle
     public static Iterable<Object[]> data() {
         List<Object[]> res = new ArrayList<>();
 
+        int cntr = 0;
+        List<String> invokers = commandHandlers();
+
         for (ReadRepairStrategy strategy : ReadRepairStrategy.values()) {
             for (boolean reuse : new boolean[] {false, true}) {
                 for (boolean historical : new boolean[] {false, true}) {
                     for (CacheAtomicityMode atomicityMode : new CacheAtomicityMode[] {ATOMIC, TRANSACTIONAL}) {
-                        for (boolean walRestore: new boolean[] {false, true})
-                            res.add(new Object[] {strategy, reuse, historical, atomicityMode, walRestore});
+                        for (boolean walRestore: new boolean[] {false, true}) {
+                            res.add(new Object[]{
+                                invokers.get(cntr++ % invokers.size()),
+                                strategy,
+                                reuse,
+                                historical,
+                                atomicityMode,
+                                walRestore
+                            });
+                        }
                     }
                 }
             }
@@ -114,31 +125,31 @@ public class GridCommandHandlerConsistencyCountersTest extends GridCommandHandle
     /**
      * ReadRepair strategy
      */
-    @Parameterized.Parameter
+    @Parameterized.Parameter(1)
     public ReadRepairStrategy strategy;
 
     /**
      * When true, updates will reuse already existing keys.
      */
-    @Parameterized.Parameter(1)
+    @Parameterized.Parameter(2)
     public boolean reuseKeys;
 
     /**
      * When true, historical rebalance will be used instead of full.
      */
-    @Parameterized.Parameter(2)
+    @Parameterized.Parameter(3)
     public boolean historical;
 
     /**
      * Cache atomicity mode
      */
-    @Parameterized.Parameter(3)
+    @Parameterized.Parameter(4)
     public CacheAtomicityMode atomicityMode;
 
     /**
      * Ignite nodes use WAL for restoring logical updates at restart after the crash.
      */
-    @Parameterized.Parameter(4)
+    @Parameterized.Parameter(5)
     public boolean walRestore;
 
     /** Listening logger. */
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerConsistencyRepairCorrectnessTransactionalTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerConsistencyRepairCorrectnessTransactionalTest.java
index 5994abb2ffa..1d16195ed89 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerConsistencyRepairCorrectnessTransactionalTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerConsistencyRepairCorrectnessTransactionalTest.java
@@ -68,10 +68,13 @@ public class GridCommandHandlerConsistencyRepairCorrectnessTransactionalTest ext
     public static final String PARALLEL = "--parallel";
 
     /** Test parameters. */
-    @Parameterized.Parameters(name = "misses={0}, nulls={1}, strategy={2}, parallel={3}")
+    @Parameterized.Parameters(name = "cmdHnd={0}, misses={1}, nulls={2}, strategy={3}, parallel={4}")
     public static Iterable<Object[]> parameters() {
         List<Object[]> res = new ArrayList<>();
 
+        int cntr = 0;
+        List<String> invokers = commandHandlers();
+
         for (boolean misses : new boolean[] {false, true}) {
             for (boolean nulls : new boolean[] {false, true}) {
                 for (ReadRepairStrategy strategy : ReadRepairStrategy.values()) {
@@ -79,7 +82,7 @@ public class GridCommandHandlerConsistencyRepairCorrectnessTransactionalTest ext
                         if (parallel && strategy != ReadRepairStrategy.CHECK_ONLY)
                             continue; // see https://issues.apache.org/jira/browse/IGNITE-15316
 
-                        res.add(new Object[] {misses, nulls, strategy, parallel});
+                        res.add(new Object[] {invokers.get(cntr++ % invokers.size()), misses, nulls, strategy, parallel});
                     }
                 }
             }
@@ -89,19 +92,19 @@ public class GridCommandHandlerConsistencyRepairCorrectnessTransactionalTest ext
     }
 
     /** Misses. */
-    @Parameterized.Parameter
+    @Parameterized.Parameter(1)
     public boolean misses;
 
     /** Nulls. */
-    @Parameterized.Parameter(1)
+    @Parameterized.Parameter(2)
     public boolean nulls;
 
     /** Strategy. */
-    @Parameterized.Parameter(2)
+    @Parameterized.Parameter(3)
     public ReadRepairStrategy strategy;
 
     /** Parallel consistency check. */
-    @Parameterized.Parameter(3)
+    @Parameterized.Parameter(4)
     public boolean parallel;
 
     /** Partitions. */
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerConsistencyTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerConsistencyTest.java
index 5bff5c85fd9..670b925bb2c 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerConsistencyTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerConsistencyTest.java
@@ -92,16 +92,19 @@ public class GridCommandHandlerConsistencyTest extends GridCommandHandlerCluster
     protected final ListeningTestLogger listeningLog = new ListeningTestLogger(log);
 
     /** */
-    @Parameterized.Parameters(name = "strategy={0}, explicitGrp={1}, callByGrp={2}")
+    @Parameterized.Parameters(name = "cmdHnd={0}, strategy={1}, explicitGrp={2}, callByGrp={3}")
     public static Iterable<Object[]> data() {
         List<Object[]> res = new ArrayList<>();
 
+        int cntr = 0;
+        List<String> invokers = commandHandlers();
+
         for (ReadRepairStrategy strategy : ReadRepairStrategy.values()) {
-            for (boolean explicitGrp : new boolean[] {false, true}) {
+            for (boolean explicitGrp : new boolean[]{false, true}) {
                 if (explicitGrp)
-                    res.add(new Object[] {strategy, explicitGrp, true});
+                    res.add(new Object[]{invokers.get(cntr++ % invokers.size()), strategy, explicitGrp, true});
 
-                res.add(new Object[] {strategy, explicitGrp, false});
+                res.add(new Object[]{invokers.get(cntr++ % invokers.size()), strategy, explicitGrp, false});
             }
         }
 
@@ -111,19 +114,19 @@ public class GridCommandHandlerConsistencyTest extends GridCommandHandlerCluster
     /**
      *
      */
-    @Parameterized.Parameter
+    @Parameterized.Parameter(1)
     public ReadRepairStrategy strategy;
 
     /**
      * True when cache defined via group.
      */
-    @Parameterized.Parameter(1)
+    @Parameterized.Parameter(2)
     public boolean explicitGrp;
 
     /**
      * True when cache consistency repair called by group name.
      */
-    @Parameterized.Parameter(2)
+    @Parameterized.Parameter(3)
     public boolean callByGrp;
 
     /**
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerDefragmentationTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerDefragmentationTest.java
index 53ed5a07432..f250b3d6c06 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerDefragmentationTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerDefragmentationTest.java
@@ -35,7 +35,6 @@ import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.IgniteNodeAttributes;
-import org.apache.ignite.internal.commandline.CommandHandler;
 import org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager;
 import org.apache.ignite.internal.processors.cache.persistence.defragmentation.maintenance.DefragmentationParameters;
 import org.apache.ignite.internal.processors.cache.persistence.file.FileIOFactory;
@@ -92,7 +91,7 @@ public class GridCommandHandlerDefragmentationTest extends GridCommandHandlerClu
 
         ListeningTestLogger testLog = new ListeningTestLogger();
 
-        CommandHandler cmd = createCommandHandler(testLog);
+        TestCommandHandler cmd = createCommandHandler(testLog);
 
         LogListener logLsnr = LogListener.matches("Scheduling completed successfully.").build();
 
@@ -172,7 +171,7 @@ public class GridCommandHandlerDefragmentationTest extends GridCommandHandlerClu
 
         ListeningTestLogger testLog = new ListeningTestLogger();
 
-        CommandHandler cmd = createCommandHandler(testLog);
+        TestCommandHandler cmd = createCommandHandler(testLog);
 
         assertEquals(EXIT_CODE_OK, execute(
             cmd,
@@ -233,7 +232,7 @@ public class GridCommandHandlerDefragmentationTest extends GridCommandHandlerClu
 
         ListeningTestLogger testLog = new ListeningTestLogger();
 
-        CommandHandler cmd = createCommandHandler(testLog);
+        TestCommandHandler cmd = createCommandHandler(testLog);
 
         assertEquals(EXIT_CODE_OK, execute(
             cmd,
@@ -345,7 +344,7 @@ public class GridCommandHandlerDefragmentationTest extends GridCommandHandlerClu
 
         ListeningTestLogger testLog = new ListeningTestLogger();
 
-        CommandHandler cmd = createCommandHandler(testLog);
+        TestCommandHandler cmd = createCommandHandler(testLog);
 
         assertEquals(EXIT_CODE_OK, execute(
             cmd,
@@ -452,7 +451,7 @@ public class GridCommandHandlerDefragmentationTest extends GridCommandHandlerClu
     }
 
     /** */
-    private CommandHandler createCommandHandler(ListeningTestLogger testLog) {
+    private TestCommandHandler createCommandHandler(ListeningTestLogger testLog) {
         Logger log = GridCommandHandlerAbstractTest.initLogger(null);
 
         log.addHandler(new StreamHandler(System.out, new Formatter() {
@@ -466,6 +465,6 @@ public class GridCommandHandlerDefragmentationTest extends GridCommandHandlerClu
             }
         }));
 
-        return new CommandHandler(new JavaLogger(log, false));
+        return newCommandHandler(new JavaLogger(log, false));
     }
 }
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerFactoryAbstractTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerFactoryAbstractTest.java
new file mode 100644
index 00000000000..e360206a82a
--- /dev/null
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerFactoryAbstractTest.java
@@ -0,0 +1,338 @@
+/*
+ * 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.util;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.ServiceLoader;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import javax.management.DynamicMBean;
+import javax.management.MBeanException;
+import javax.management.ReflectionException;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.IgnitionEx;
+import org.apache.ignite.internal.commandline.ArgumentParser;
+import org.apache.ignite.internal.commandline.CommandHandler;
+import org.apache.ignite.internal.commandline.CommandLogger;
+import org.apache.ignite.internal.commandline.ConnectionAndSslParameters;
+import org.apache.ignite.internal.dto.IgniteDataTransferObject;
+import org.apache.ignite.internal.logger.IgniteLoggerEx;
+import org.apache.ignite.internal.management.IgniteCommandRegistry;
+import org.apache.ignite.internal.management.api.Command;
+import org.apache.ignite.internal.management.api.CommandsRegistry;
+import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.jetbrains.annotations.Nullable;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_REST_TCP_PORT;
+import static org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_INVALID_ARGUMENTS;
+import static org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_OK;
+import static org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_UNEXPECTED_ERROR;
+import static org.apache.ignite.internal.commandline.CommandHandler.setupJavaLogger;
+import static org.apache.ignite.internal.commandline.CommandLogger.errorMessage;
+import static org.apache.ignite.internal.management.api.CommandMBean.INVOKE;
+import static org.apache.ignite.internal.management.api.CommandMBean.LAST_RES_METHOD;
+import static org.apache.ignite.internal.management.api.CommandUtils.cmdKey;
+import static org.apache.ignite.internal.management.api.CommandUtils.isBoolean;
+import static org.apache.ignite.internal.management.api.CommandUtils.toFormattedCommandName;
+import static org.apache.ignite.internal.management.api.CommandUtils.visitCommandParams;
+
+/** Class to check command execution via all available handlers. */
+@RunWith(Parameterized.class)
+public class GridCommandHandlerFactoryAbstractTest extends GridCommonAbstractTest {
+    /** @see JmxCommandHandler */
+    public static final String JMX_CMD_HND = "jmx";
+
+    /** @see CliCommandHandler */
+    public static final String CLI_CMD_HND = "cli";
+
+    /** */
+    public static final Map<String, Function<IgniteLogger, TestCommandHandler>> CMD_HNDS = new HashMap<>();
+
+    static {
+        ServiceLoader<TestCommandHandler> svc = ServiceLoader.load(TestCommandHandler.class);
+
+        for (TestCommandHandler hnd : svc) {
+            CMD_HNDS.put(hnd.name(), log -> {
+                try {
+                    return hnd.getClass().getConstructor(IgniteLogger.class).newInstance(log);
+                }
+                catch (InstantiationException | IllegalAccessException | InvocationTargetException |
+                       NoSuchMethodException e) {
+                    throw new IgniteException(e);
+                }
+            });
+        }
+    }
+
+    /** */
+    @Parameterized.Parameter
+    public String commandHandler;
+
+    /** */
+    @Parameterized.Parameters(name = "cmdHnd={0}")
+    public static List<String> commandHandlers() {
+        return new ArrayList<>(CMD_HNDS.keySet());
+    }
+
+    /** */
+    protected TestCommandHandler newCommandHandler() {
+        return newCommandHandler(null);
+    }
+
+    /** Command executor factory. */
+    protected TestCommandHandler newCommandHandler(@Nullable IgniteLogger log) {
+        if (!CMD_HNDS.containsKey(commandHandler))
+            throw new IllegalArgumentException("Unknown handler: " + commandHandler);
+
+        return CMD_HNDS.get(commandHandler).apply(log);
+    }
+
+    /** */
+    public interface TestCommandHandler {
+        /** */
+        public <T> T getLastOperationResult();
+
+        /** */
+        public void flushLogger();
+
+        /** */
+        public int execute(List<String> rawArgs);
+
+        /** */
+        public String name();
+    }
+
+    /** */
+    public static class CliCommandHandler implements TestCommandHandler {
+        /** */
+        private final CommandHandler hnd;
+
+        /** */
+        public CliCommandHandler() {
+            this.hnd = new CommandHandler();
+        }
+
+        /** */
+        public CliCommandHandler(@Nullable IgniteLogger log) {
+            this.hnd = log == null ? new CommandHandler() : new CommandHandler(log);
+        }
+
+        /** {@inheritDoc} */
+        @Override public int execute(List<String> rawArgs) {
+            return hnd.execute(rawArgs);
+        }
+
+        /** {@inheritDoc} */
+        @Override public <T> T getLastOperationResult() {
+            return hnd.getLastOperationResult();
+        }
+
+        /** {@inheritDoc} */
+        @Override public void flushLogger() {
+            // Flush all Logger handlers to make log data available to test.
+            U.<IgniteLoggerEx>field(hnd, "logger").flush();
+        }
+
+        /** {@inheritDoc} */
+        @Override public String name() {
+            return CLI_CMD_HND;
+        }
+    }
+
+    /** */
+    public static class JmxCommandHandler implements TestCommandHandler {
+        /** */
+        private int port;
+
+        /** */
+        private IgniteEx ignite;
+
+        /** */
+        private IgniteLoggerEx log;
+
+        /** */
+        private Object res;
+
+        /** */
+        public JmxCommandHandler() {
+            this(null);
+        }
+
+        /** */
+        public JmxCommandHandler(@Nullable IgniteLogger log) {
+            this.log = (IgniteLoggerEx)(log == null ? setupJavaLogger("jmx-invoker", CommandHandler.class) : log);
+        }
+
+        /** {@inheritDoc} */
+        @Override public int execute(List<String> value) {
+            String commandName = null;
+
+            try {
+                ArgumentParser parser = new ArgumentParser(log, new IgniteCommandRegistry());
+
+                ConnectionAndSslParameters<IgniteDataTransferObject> p = parser.parseAndValidate(value);
+
+                commandName = toFormattedCommandName(p.cmdPath().peekLast().getClass()).toUpperCase();
+
+                Deque<Command<?, ?>> cmdPath = new ArrayDeque<>(p.cmdPath());
+
+                List<String> grps = new ArrayList<>();
+
+                while (!cmdPath.isEmpty()) {
+                    grps.add(cmdKey(
+                        cmdPath.pop().getClass(),
+                        !cmdPath.isEmpty() ? (Class<? extends CommandsRegistry<?, ?>>)cmdPath.peek().getClass() : null)
+                    );
+                }
+
+                String name = grps.remove(0);
+
+                Collections.reverse(grps);
+
+                DynamicMBean mbean = getMxBean(
+                    ignite(p).context().igniteInstanceName(),
+                    "management",
+                    grps,
+                    name,
+                    DynamicMBean.class
+                );
+
+                List<String> params = new ArrayList<>();
+
+                Consumer<Field> fldCnsmr = fld -> params.add(toString(U.field(p.commandArg(), fld.getName())));
+
+                visitCommandParams(p.command().argClass(), fldCnsmr, fldCnsmr, (grp, flds) -> flds.forEach(fldCnsmr));
+
+                String[] signature = new String[params.size()];
+
+                Arrays.fill(signature, String.class.getName());
+
+                String out = (String)mbean.invoke(INVOKE, params.toArray(X.EMPTY_OBJECT_ARRAY), signature);
+
+                log.info(out);
+
+                res = mbean.invoke(LAST_RES_METHOD, X.EMPTY_OBJECT_ARRAY, U.EMPTY_STRS);
+            }
+            catch (MBeanException | ReflectionException e) {
+                throw new IgniteException(e);
+            }
+            catch (Throwable e) {
+                log.error("Failed to perform operation.");
+                log.error(CommandLogger.errorMessage(e));
+
+                if (X.hasCause(e, IllegalArgumentException.class)) {
+                    IllegalArgumentException iae = X.cause(e, IllegalArgumentException.class);
+
+                    log.error("Check arguments. " + errorMessage(iae));
+                    log.info("Command [" + commandName + "] finished with code: " + EXIT_CODE_INVALID_ARGUMENTS);
+
+                    return EXIT_CODE_INVALID_ARGUMENTS;
+                }
+
+                return EXIT_CODE_UNEXPECTED_ERROR;
+            }
+
+            return EXIT_CODE_OK;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String name() {
+            return JMX_CMD_HND;
+        }
+
+        /** */
+        private IgniteEx ignite(ConnectionAndSslParameters<IgniteDataTransferObject> p) {
+            int port = p.port();
+
+            if (port == this.port)
+                return ignite;
+
+            for (Ignite node : IgnitionEx.allGrids()) {
+                Integer nodePort = ((IgniteEx)node).localNode().<Integer>attribute(ATTR_REST_TCP_PORT);
+
+                if (nodePort != null && port == nodePort) {
+                    this.port = port;
+                    ignite = (IgniteEx)node;
+
+                    return ignite;
+                }
+            }
+
+            throw new IllegalStateException("Unknown grid for port: " + port);
+        }
+
+        /** {@inheritDoc} */
+        @Override public <T> T getLastOperationResult() {
+            return (T)res;
+        }
+
+        /** {@inheritDoc} */
+        @Override public void flushLogger() {
+            log.flush();
+        }
+
+        /** */
+        private static String toString(Object val) {
+            if (val == null || (isBoolean(val.getClass()) && !(boolean)val))
+                return "";
+
+            if (val.getClass().isArray()) {
+                int length = Array.getLength(val);
+
+                if (length == 0)
+                    return "";
+
+                StringBuffer sb = new StringBuffer();
+
+                for (int i = 0; i < length; i++) {
+                    if (i != 0)
+                        sb.append(',');
+
+                    sb.append(toString(Array.get(val, i)));
+                }
+
+                return sb.toString();
+            }
+
+            return Objects.toString(val);
+        }
+    }
+
+    /** */
+    protected int commandHandlerExtraLines() {
+        return commandHandler.equals(CLI_CMD_HND) ? 11 : 0;
+    }
+}
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIncompatibleSslConfigTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIncompatibleSslConfigTest.java
index 2a3dcf39154..d8fc8f38743 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIncompatibleSslConfigTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIncompatibleSslConfigTest.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.util;
 
 import java.util.List;
+import org.junit.Assume;
 import org.junit.Test;
 
 import static org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_UNEXPECTED_ERROR;
@@ -40,6 +41,13 @@ public class GridCommandHandlerIncompatibleSslConfigTest extends GridCommandHand
         params.add("src/test/resources/some-file.jks");
     }
 
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        Assume.assumeTrue(commandHandler.equalsIgnoreCase(CLI_CMD_HND));
+
+        super.beforeTest();
+    }
+
     /** */
     @Test
     public void test() throws Exception {
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIndexForceRebuildTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIndexForceRebuildTest.java
index cc2ee650b50..3486d746826 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIndexForceRebuildTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIndexForceRebuildTest.java
@@ -246,7 +246,7 @@ public class GridCommandHandlerIndexForceRebuildTest extends GridCommandHandlerA
 
             validateOutputIndicesRebuildWasStarted(outputStr, F.asMap(GRP_NAME_1, F.asList(CACHE_NAME_1_1)));
 
-            assertEquals("Unexpected number of lines in output.", 19, outputStr.split("\n").length);
+            assertEquals("Unexpected number of lines in output.", 8 + commandHandlerExtraLines(), outputStr.split("\n").length);
 
             // Index rebuild must be triggered only for cache1_1 and only on node3.
             assertFalse(cache1Listeners[0].check());
@@ -311,7 +311,7 @@ public class GridCommandHandlerIndexForceRebuildTest extends GridCommandHandlerA
                 )
             );
 
-            assertEquals("Unexpected number of lines in outputStr.", 20, outputStr.split("\n").length);
+            assertEquals("Unexpected number of lines in outputStr.", 9 + commandHandlerExtraLines(), outputStr.split("\n").length);
 
             assertFalse(cache1Listeners[0].check());
             assertFalse(cache1Listeners[1].check());
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIndexListTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIndexListTest.java
index bff0d012c4f..50745cb181f 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIndexListTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIndexListTest.java
@@ -24,7 +24,6 @@ import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteDataStreamer;
 import org.apache.ignite.cluster.ClusterState;
 import org.apache.ignite.internal.IgniteEx;
-import org.apache.ignite.internal.commandline.CommandHandler;
 import org.apache.ignite.internal.management.cache.IndexListInfoContainer;
 import org.junit.Test;
 
@@ -77,21 +76,23 @@ public class GridCommandHandlerIndexListTest extends GridCommandHandlerAbstractT
     @Test
     public void testCacheIndexList() {
         final String idxName = "PERSON_ORGID_ASC_IDX";
-        final int expectedLinesNum = 15;
+        final int expectedLinesNum = 4 + commandHandlerExtraLines();
         final int expectedIndexDescrLinesNum = 2;
 
         injectTestSystemOut();
 
-        final CommandHandler handler = new CommandHandler(createTestLogger());
+        final TestCommandHandler handler = newCommandHandler(createTestLogger());
 
         assertEquals(EXIT_CODE_OK, execute(handler, "--cache", "indexes_list", "--index-name", idxName));
 
         String outStr = testOut.toString();
 
+        String typePattern = commandHandler.equals(CLI_CMD_HND) ? "ArrayList" : "LinkedKeySet";
+
         assertTrue(outStr.contains("grpName=" + GROUP_NAME + ", cacheName=" + CACHE_NAME + ", idxName=PERSON_ORGID_ASC_IDX, " +
-            "colsNames=ArrayList [ORGID, _KEY], tblName=PERSON"));
+            "colsNames=" + typePattern + " [ORGID, _KEY], tblName=PERSON"));
         assertTrue(outStr.contains("grpName=" + GROUP_NAME_SECOND + ", cacheName=" + CACHE_NAME_SECOND +
-            ", idxName=PERSON_ORGID_ASC_IDX, colsNames=ArrayList [ORGID, _KEY], tblName=PERSON"));
+            ", idxName=PERSON_ORGID_ASC_IDX, colsNames=" + typePattern + " [ORGID, _KEY], tblName=PERSON"));
 
         final String[] outputLines = outStr.split("\n");
 
@@ -130,7 +131,7 @@ public class GridCommandHandlerIndexListTest extends GridCommandHandlerAbstractT
 
         injectTestSystemOut();
 
-        final CommandHandler handler = new CommandHandler(createTestLogger());
+        final TestCommandHandler handler = newCommandHandler(createTestLogger());
 
         assertEquals(EXIT_CODE_OK, execute(handler, "--cache", "indexes_list",
             "--node-id", grid(0).localNode().id().toString(),
@@ -138,8 +139,10 @@ public class GridCommandHandlerIndexListTest extends GridCommandHandlerAbstractT
             "--cache-name", CACHE_NAME,
             "--index-name", idxName));
 
+        String typePattern = commandHandler.equals(CLI_CMD_HND) ? "ArrayList" : "LinkedKeySet";
+
         assertTrue(testOut.toString().contains("grpName=" + GROUP_NAME + ", cacheName=" + CACHE_NAME +
-            ", idxName=PERSON_ORGID_ASC_IDX, colsNames=ArrayList [ORGID, _KEY], tblName=PERSON"));
+            ", idxName=PERSON_ORGID_ASC_IDX, colsNames=" + typePattern + " [ORGID, _KEY], tblName=PERSON"));
     }
 
     /**
@@ -153,7 +156,7 @@ public class GridCommandHandlerIndexListTest extends GridCommandHandlerAbstractT
 
         injectTestSystemOut();
 
-        final CommandHandler handler = new CommandHandler(createTestLogger());
+        final TestCommandHandler handler = newCommandHandler(createTestLogger());
 
         try {
             ignite.createCache(tmpCacheName);
@@ -169,7 +172,7 @@ public class GridCommandHandlerIndexListTest extends GridCommandHandlerAbstractT
 
             String[] lines = str.split("\n");
 
-            assertEquals("Unexpected output size", 11, lines.length);
+            assertEquals("Unexpected output size", commandHandlerExtraLines(), lines.length);
         }
         finally {
             ignite.destroyCache(tmpCacheName);
@@ -212,7 +215,7 @@ public class GridCommandHandlerIndexListTest extends GridCommandHandlerAbstractT
 
     /** */
     private void checkGroup(String grpRegEx, Predicate<String> predicate, int expectedResNum) {
-        final CommandHandler handler = new CommandHandler(createTestLogger());
+        final TestCommandHandler handler = newCommandHandler(createTestLogger());
 
         assertEquals(EXIT_CODE_OK, execute(handler, "--cache", "indexes_list", "--group-name", grpRegEx));
 
@@ -259,7 +262,7 @@ public class GridCommandHandlerIndexListTest extends GridCommandHandlerAbstractT
 
     /** */
     private void checkCacheNameFilter(String cacheRegEx, Predicate<String> predicate, int expectedResNum) {
-        final CommandHandler handler = new CommandHandler(createTestLogger());
+        final TestCommandHandler handler = newCommandHandler(createTestLogger());
 
         assertEquals(EXIT_CODE_OK, execute(handler, "--cache", "indexes_list", "--cache-name", cacheRegEx));
 
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIndexRebuildStatusTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIndexRebuildStatusTest.java
index 5e4f91819d4..6206f44ba6d 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIndexRebuildStatusTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIndexRebuildStatusTest.java
@@ -30,7 +30,6 @@ import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.IgniteInterruptedCheckedException;
 import org.apache.ignite.internal.cache.query.index.IndexProcessor;
-import org.apache.ignite.internal.commandline.CommandHandler;
 import org.apache.ignite.internal.management.cache.IndexRebuildStatusInfoContainer;
 import org.apache.ignite.internal.managers.indexing.IndexesRebuildTask;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
@@ -137,7 +136,7 @@ public class GridCommandHandlerIndexRebuildStatusTest extends GridCommandHandler
         injectTestSystemOut();
         idxRebuildsStartedNum.set(0);
 
-        final CommandHandler handler = new CommandHandler(createTestLogger());
+        final TestCommandHandler handler = newCommandHandler(createTestLogger());
 
         stopGrid(GRIDS_NUM - 1);
         stopGrid(GRIDS_NUM - 2);
@@ -172,7 +171,7 @@ public class GridCommandHandlerIndexRebuildStatusTest extends GridCommandHandler
         injectTestSystemOut();
         idxRebuildsStartedNum.set(0);
 
-        final CommandHandler handler = new CommandHandler(createTestLogger());
+        final TestCommandHandler handler = newCommandHandler(createTestLogger());
 
         stopGrid(GRIDS_NUM - 1);
         stopGrid(GRIDS_NUM - 2);
@@ -219,7 +218,7 @@ public class GridCommandHandlerIndexRebuildStatusTest extends GridCommandHandler
      * @param handler CommandHandler used to run command.
      * @param nodeIds Ids to check.
      */
-    private void checkResult(CommandHandler handler, UUID... nodeIds) {
+    private void checkResult(TestCommandHandler handler, UUID... nodeIds) {
         String output = testOut.toString();
 
         Map<UUID, Set<IndexRebuildStatusInfoContainer>> cmdResult = handler.getLastOperationResult();
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIndexingWithSSLTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIndexingWithSSLTest.java
index 9ba66dc3ad7..178024460ea 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIndexingWithSSLTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIndexingWithSSLTest.java
@@ -17,6 +17,8 @@
 
 package org.apache.ignite.util;
 
+import org.junit.Assume;
+
 /**
  *
  */
@@ -25,4 +27,11 @@ public class GridCommandHandlerIndexingWithSSLTest extends GridCommandHandlerInd
     @Override protected boolean sslEnabled() {
         return true;
     }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        Assume.assumeTrue(commandHandler.equalsIgnoreCase(CLI_CMD_HND));
+
+        super.beforeTest();
+    }
 }
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerInterruptCommandTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerInterruptCommandTest.java
index 9c138996de6..736e2ca3e68 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerInterruptCommandTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerInterruptCommandTest.java
@@ -43,6 +43,7 @@ import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.apache.ignite.testframework.ListeningTestLogger;
 import org.apache.ignite.testframework.LogListener;
+import org.junit.Assume;
 import org.junit.Test;
 
 import static org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_UNEXPECTED_ERROR;
@@ -65,6 +66,8 @@ public class GridCommandHandlerInterruptCommandTest extends GridCommandHandlerAb
 
     /** {@inheritDoc} */
     @Override protected void beforeTest() throws Exception {
+        Assume.assumeTrue(commandHandler.equals(CLI_CMD_HND));
+
         super.beforeTest();
 
         cleanPersistenceDir();
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerMetadataTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerMetadataTest.java
index e320f09e74c..c2938b75e9e 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerMetadataTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerMetadataTest.java
@@ -167,9 +167,15 @@ public class GridCommandHandlerMetadataTest extends GridCommandHandlerClusterByC
      */
     @Test
     public void testMetadataForInternalClassesIsNotRegistered() {
-        IgniteCache<Object, Object> dfltCache = grid(0).getOrCreateCache(DEFAULT_CACHE_NAME);
+        IgniteCache<Object, Object> dfltCache = grid(0).getOrCreateCache(DEFAULT_CACHE_NAME).withKeepBinary();
 
-        dfltCache.put(1, new TestValue());
+        String typeName = TestValue.class.getName() + commandHandler;
+
+        BinaryObjectBuilder bldr = grid(0).binary().builder(typeName);
+
+        bldr.setField("val", 3);
+
+        dfltCache.put(1, bldr.build());
 
         Collection<BinaryType> metadata = crd.context().cacheObjects().metadata();
 
@@ -177,7 +183,7 @@ public class GridCommandHandlerMetadataTest extends GridCommandHandlerClusterByC
 
         assertEquals(metadata.toString(), 1, metadata.size());
 
-        assertContains(log, testOut.toString(), "typeName=" + TestValue.class.getTypeName());
+        assertContains(log, testOut.toString(), "typeName=" + typeName);
 
         grid(0).destroyCache(DEFAULT_CACHE_NAME);
 
@@ -185,7 +191,7 @@ public class GridCommandHandlerMetadataTest extends GridCommandHandlerClusterByC
 
         assertEquals(metadata.toString(), 1, metadata.size());
 
-        assertEquals(EXIT_CODE_OK, execute("--meta", "remove", "--typeName", TestValue.class.getTypeName()));
+        assertEquals(EXIT_CODE_OK, execute("--meta", "remove", "--typeName", typeName));
 
         metadata = crd.context().cacheObjects().metadata();
 
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerPropertiesTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerPropertiesTest.java
index 8fbcf2bf4ce..e331d3b934b 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerPropertiesTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerPropertiesTest.java
@@ -20,6 +20,7 @@ package org.apache.ignite.util;
 import java.io.Serializable;
 import java.util.Set;
 import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.processors.configuration.distributed.DistributedChangeableProperty;
 import org.apache.ignite.internal.processors.configuration.distributed.SimpleDistributedProperty;
@@ -27,6 +28,7 @@ import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
 import org.apache.ignite.internal.util.typedef.G;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.junit.After;
+import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -59,6 +61,8 @@ public class GridCommandHandlerPropertiesTest extends GridCommandHandlerClusterB
      */
     @Test
     public void testHelp() {
+        Assume.assumeTrue(commandHandler.equals(CLI_CMD_HND));
+
         assertEquals(EXIT_CODE_OK, execute("--property", "help"));
 
         String out = testOut.toString();
@@ -136,7 +140,9 @@ public class GridCommandHandlerPropertiesTest extends GridCommandHandlerClusterB
      * Checks the set command for property 'checkpoint.deviation'.
      */
     @Test
-    public void testPropertyCheckpointDeviation() {
+    public void testPropertyCheckpointDeviation() throws IgniteCheckedException {
+        String propName = "checkpoint.deviation";
+
         for (Ignite ign : G.allGrids()) {
             if (ign.configuration().isClientMode())
                 continue;
@@ -144,14 +150,15 @@ public class GridCommandHandlerPropertiesTest extends GridCommandHandlerClusterB
             SimpleDistributedProperty<Integer> cpFreqDeviation = U.field(((IgniteEx)ign).context().cache().context().database(),
                 "cpFreqDeviation");
 
-            assertNull(cpFreqDeviation.get());
+            if (cpFreqDeviation.get() != null)
+                ((IgniteEx)ign).context().distributedMetastorage().remove(propName);
         }
 
         assertEquals(
             EXIT_CODE_OK,
             execute(
                 "--property", "set",
-                "--name", "checkpoint.deviation",
+                "--name", propName,
                 "--val", "20"
             )
         );
@@ -207,6 +214,15 @@ public class GridCommandHandlerPropertiesTest extends GridCommandHandlerClusterB
      */
     @Test
     public void testPropertyWalRebalanceThreshold() {
+        assertEquals(
+            EXIT_CODE_OK,
+            execute(
+                "--property", "set",
+                "--name", HISTORICAL_REBALANCE_THRESHOLD_DMS_KEY,
+                "--val", Integer.toString(DFLT_PDS_WAL_REBALANCE_THRESHOLD)
+            )
+        );
+
         assertDistributedPropertyEquals(HISTORICAL_REBALANCE_THRESHOLD_DMS_KEY, DFLT_PDS_WAL_REBALANCE_THRESHOLD);
 
         int newVal = DFLT_PDS_WAL_REBALANCE_THRESHOLD * 2;
@@ -228,6 +244,15 @@ public class GridCommandHandlerPropertiesTest extends GridCommandHandlerClusterB
      */
     @Test
     public void testPropertySnapshotTransferRate() {
+        assertEquals(
+            EXIT_CODE_OK,
+            execute(
+                "--property", "set",
+                "--name", SNAPSHOT_TRANSFER_RATE_DMS_KEY,
+                "--val", Long.toString(DFLT_SNAPSHOT_TRANSFER_RATE_BYTES)
+            )
+        );
+
         assertDistributedPropertyEquals(SNAPSHOT_TRANSFER_RATE_DMS_KEY, DFLT_SNAPSHOT_TRANSFER_RATE_BYTES);
 
         long newVal = 1024;
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerSslTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerSslTest.java
index 1e2b61e4573..d41a43377b5 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerSslTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerSslTest.java
@@ -24,11 +24,11 @@ import org.apache.ignite.Ignite;
 import org.apache.ignite.configuration.ConnectorConfiguration;
 import org.apache.ignite.configuration.DataStorageConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
-import org.apache.ignite.internal.commandline.CommandHandler;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.ssl.SslContextFactory;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.jetbrains.annotations.NotNull;
+import org.junit.Assume;
 import org.junit.Test;
 
 import static org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_CONNECTION_FAILED;
@@ -53,6 +53,13 @@ public class GridCommandHandlerSslTest extends GridCommandHandlerClusterPerMetho
         return factory;
     }
 
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        Assume.assumeTrue(commandHandler.equalsIgnoreCase(CLI_CMD_HND));
+
+        super.beforeTest();
+    }
+
     /** {@inheritDoc} */
     @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
         IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
@@ -81,7 +88,7 @@ public class GridCommandHandlerSslTest extends GridCommandHandlerClusterPerMetho
 
         assertFalse(ignite.cluster().state().active());
 
-        final CommandHandler cmd = new CommandHandler();
+        final TestCommandHandler cmd = newCommandHandler();
 
         List<String> params = new ArrayList<>();
 
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java
index 9dbdf07329a..abb7a50c4fb 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java
@@ -78,7 +78,6 @@ import org.apache.ignite.internal.TestRecordingCommunicationSpi;
 import org.apache.ignite.internal.client.GridClientFactory;
 import org.apache.ignite.internal.client.impl.GridClientImpl;
 import org.apache.ignite.internal.client.util.GridConcurrentHashSet;
-import org.apache.ignite.internal.commandline.CommandHandler;
 import org.apache.ignite.internal.management.cache.FindAndDeleteGarbageInPersistenceTaskResult;
 import org.apache.ignite.internal.management.cache.IdleVerifyDumpTask;
 import org.apache.ignite.internal.management.snapshot.SnapshotTaskResult;
@@ -139,6 +138,7 @@ import org.apache.ignite.transactions.TransactionRollbackException;
 import org.apache.ignite.transactions.TransactionTimeoutException;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
+import org.junit.Assume;
 import org.junit.Test;
 
 import static java.io.File.separatorChar;
@@ -266,7 +266,8 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
 
         assertEquals(ACTIVE, ignite.cluster().state());
 
-        assertContains(log, testOut.toString(), "Command deprecated. Use --set-state instead.");
+        if (commandHandler.equals(CLI_CMD_HND))
+            assertContains(log, testOut.toString(), "Command deprecated. Use --set-state instead.");
     }
 
     /**
@@ -276,6 +277,8 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
      */
     @Test
     public void testClientsLeakage() throws Exception {
+        Assume.assumeTrue(commandHandler.equalsIgnoreCase(CLI_CMD_HND));
+
         startGrids(1);
 
         Map<UUID, GridClientImpl> clnts = U.field(GridClientFactory.class, "openClients");
@@ -742,7 +745,8 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
         assertFalse(ignite.cluster().state().active());
         assertEquals(INACTIVE, ignite.cluster().state());
 
-        assertContains(log, testOut.toString(), "Command deprecated. Use --set-state instead.");
+        if (commandHandler.equals(CLI_CMD_HND))
+            assertContains(log, testOut.toString(), "Command deprecated. Use --set-state instead.");
     }
 
     /**
@@ -869,11 +873,13 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
         assertEquals(EXIT_CODE_OK, execute("--deactivate"));
         assertFalse(igniteEx.cluster().state().active());
 
-        assertContains(
-            log,
-            testOut.toString(),
-            "Warning: the command will deactivate a cluster \"" + clusterName + "\"."
-        );
+        if (commandHandler.equals(CLI_CMD_HND)) {
+            assertContains(
+                log,
+                testOut.toString(),
+                "Warning: the command will deactivate a cluster \"" + clusterName + "\"."
+            );
+        }
     }
 
     /**
@@ -1696,7 +1702,7 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
 
         doSleep(5000);
 
-        CommandHandler h = new CommandHandler();
+        TestCommandHandler h = newCommandHandler();
 
         final TxInfo[] toKill = {null};
 
@@ -1962,7 +1968,7 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
             }, 10_000);
         }
 
-        CommandHandler h = new CommandHandler();
+        TestCommandHandler h = newCommandHandler();
 
         // Check listing.
         validate(h, map -> {
@@ -2048,7 +2054,9 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
                                         .noneMatch(s -> s.contains(getTestIgniteInstanceName() + "1"));
 
         assertTrue(testOutStr, testOutStr.contains("Node not found for consistent ID:"));
-        assertFalse(testOutStr, isInstanse1Found);
+
+        if (commandHandler.equals(CLI_CMD_HND))
+            assertFalse(testOutStr, isInstanse1Found);
     }
 
     /** */
@@ -2108,6 +2116,8 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
      */
     @Test
     public void testCacheIdleVerifyDumpWhenNodeFailing() throws Exception {
+        Assume.assumeTrue("CHECKME", CLI_CMD_HND.equalsIgnoreCase(commandHandler));
+
         Ignite ignite = startGrids(3);
 
         Ignite unstable = startGrid("unstable");
@@ -2413,7 +2423,7 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
      * @param validateClo Validate clo.
      * @param args Args.
      */
-    private void validate(CommandHandler h, IgniteInClosure<Map<ClusterNode, TxTaskResult>> validateClo,
+    private void validate(TestCommandHandler h, IgniteInClosure<Map<ClusterNode, TxTaskResult>> validateClo,
         String... args) {
         assertEquals(EXIT_CODE_OK, execute(h, args));
 
@@ -2456,15 +2466,17 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
 
         ignite.cluster().state(ACTIVE);
 
-        assertEquals(
-            EXIT_CODE_OK,
-            execute("--diagnostic")
-        );
+        if (commandHandler.equals(CLI_CMD_HND)) {
+            assertEquals(
+                EXIT_CODE_OK,
+                execute("--diagnostic")
+            );
 
-        assertEquals(
-            EXIT_CODE_OK,
-            execute("--diagnostic", "help")
-        );
+            assertEquals(
+                EXIT_CODE_OK,
+                execute("--diagnostic", "help")
+            );
+        }
 
         // Dump locks only on connected node to default path.
         assertEquals(
@@ -2700,7 +2712,7 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
 
         assertNotNull(tx0);
 
-        CommandHandler h = new CommandHandler();
+        TestCommandHandler h = newCommandHandler();
 
         validate(h, map -> {
             ClusterNode node = grid(0).cluster().localNode();
@@ -2733,7 +2745,6 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
      * @throws Exception If failed.
      */
     @Test
-    @SuppressWarnings("unchecked")
     public void setConsistenceIdsWithOfflineBaselineNode() throws Exception {
         Ignite ignite = startGrids(2);
 
@@ -3008,7 +3019,7 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
 
         Ignite ignite = startGrids(1);
 
-        CommandHandler h = new CommandHandler(createTestLogger());
+        TestCommandHandler h = newCommandHandler(createTestLogger());
 
         assertEquals(EXIT_CODE_OK, execute(h, "--encryption", "get_master_key_name"));
 
@@ -3165,7 +3176,7 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
 
         injectTestSystemOut();
 
-        CommandHandler h = new CommandHandler(createTestLogger());
+        TestCommandHandler h = newCommandHandler(createTestLogger());
 
         // Invalid command syntax check.
         assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute(h, "--snapshot", "create", snpName, "blah"));
@@ -3239,7 +3250,7 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
 
         createCacheAndPreload(startCli, 100);
 
-        CommandHandler h = new CommandHandler();
+        TestCommandHandler h = newCommandHandler();
 
         // Cancel snapshot using operation ID.
         doSnapshotCancellationTest(startCli, Collections.singletonList(srv), startCli.cache(DEFAULT_CACHE_NAME),
@@ -3268,7 +3279,7 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
         snp(ig).createSnapshot(snpName)
             .get();
 
-        CommandHandler h = new CommandHandler();
+        TestCommandHandler h = newCommandHandler();
 
         assertEquals(EXIT_CODE_OK, execute(h, "--snapshot", "check", snpName));
 
@@ -3316,16 +3327,20 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
 
         // Cache exists.
         assertEquals(EXIT_CODE_UNEXPECTED_ERROR, execute("--snapshot", "restore", snpName, "--start", "--sync"));
-        assertContains(log, testOut.toString(), "Command option '--start' is redundant and must be avoided.");
-        assertContains(log, testOut.toString(), "Unable to restore cache group - directory is not empty. " +
-            "Cache group should be destroyed manually before perform restore operation [group=" + cacheName);
+        if (commandHandler.equals(CLI_CMD_HND)) {
+            assertContains(log, testOut.toString(), "Command option '--start' is redundant and must be avoided.");
+            assertContains(log, testOut.toString(), "Unable to restore cache group - directory is not empty. " +
+                "Cache group should be destroyed manually before perform restore operation [group=" + cacheName);
+        }
 
         ig.cache(cacheName).destroy();
         awaitPartitionMapExchange();
 
         assertEquals(EXIT_CODE_OK, execute("--snapshot", "restore", snpName, "--sync"));
-        assertNotContains(log, testOut.toString(), "Command option '--start' is redundant and must be avoided.");
-        assertContains(log, testOut.toString(), "Snapshot cache group restore operation completed successfully");
+        if (commandHandler.equals(CLI_CMD_HND)) {
+            assertNotContains(log, testOut.toString(), "Command option '--start' is redundant and must be avoided.");
+            assertContains(log, testOut.toString(), "Snapshot cache group restore operation completed successfully");
+        }
 
         IgniteCache<Object, Object> cache = ig.cache(cacheName);
 
@@ -3373,7 +3388,7 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
         assertNull(ig.cache(cacheName2));
         assertNull(ig.cache(cacheName3));
 
-        CommandHandler h = new CommandHandler(createTestLogger());
+        TestCommandHandler h = newCommandHandler(createTestLogger());
 
         assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute(h, "--snapshot", "restore", snpName, cacheName1));
         assertContains(log, testOut.toString(), "Unexpected argument: " + cacheName1);
@@ -3442,9 +3457,11 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
             else
                 assertEquals(EXIT_CODE_OK, execute(h, "--snapshot", "restore", snpName));
 
-            String out = testOut.toString();
-            assertContains(log, out, "Warning: command will restore ALL USER-CREATED CACHE GROUPS from the snapshot");
-            assertContains(log, out, "Snapshot cache group restore operation started [name=" + snpName);
+            if (commandHandler.equals(CLI_CMD_HND)) {
+                String out = testOut.toString();
+                assertContains(log, out, "Warning: command will restore ALL USER-CREATED CACHE GROUPS from the snapshot");
+                assertContains(log, out, "Snapshot cache group restore operation started [name=" + snpName);
+            }
 
             waitForCondition(() -> ig.cache(cacheName1) != null, getTestTimeout());
             waitForCondition(() -> ig.cache(cacheName2) != null, getTestTimeout());
@@ -3851,6 +3868,8 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
      */
     @Test
     public void testSuccessStopWarmUp() throws Exception {
+        Assume.assumeTrue(CLI_CMD_HND.equalsIgnoreCase(commandHandler));
+
         WarmUpTestPluginProvider provider = new WarmUpTestPluginProvider();
 
         IgniteConfiguration cfg = getConfiguration(getTestIgniteInstanceName(0)).setPluginProviders(provider);
@@ -3889,6 +3908,8 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
      */
     @Test
     public void testFailStopWarmUp() throws Exception {
+        Assume.assumeTrue(commandHandler.equalsIgnoreCase(CLI_CMD_HND));
+
         startGrid(0);
 
         assertEquals(EXIT_CODE_UNEXPECTED_ERROR, execute("--warm-up", "--stop"));
@@ -3903,7 +3924,7 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
         IgniteEx ignite,
         boolean delFoundGarbage
     ) {
-        CommandHandler hnd = new CommandHandler();
+        TestCommandHandler hnd = newCommandHandler();
 
         List<String> args = new ArrayList<>(Arrays.asList("--yes", "--port", "11212", "--cache", "find_garbage",
             ignite.localNode().id().toString()));
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTracingConfigurationTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTracingConfigurationTest.java
index cb8e5d3afb3..de56018044e 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTracingConfigurationTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTracingConfigurationTest.java
@@ -23,7 +23,6 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import org.apache.ignite.internal.IgniteEx;
-import org.apache.ignite.internal.commandline.CommandHandler;
 import org.apache.ignite.internal.management.tracing.TracingConfigurationCommand;
 import org.apache.ignite.internal.management.tracing.TracingConfigurationTaskResult;
 import org.apache.ignite.spi.tracing.Scope;
@@ -94,15 +93,13 @@ public class GridCommandHandlerTracingConfigurationTest extends GridCommandHandl
     protected IgniteEx ignite;
 
     /** */
-    private static CommandHandler hnd;
+    private TestCommandHandler hnd;
 
     /** {@inheritDoc} */
     @Override protected void beforeTestsStarted() throws Exception {
         super.beforeTestsStarted();
 
         ignite = startGrids(2);
-
-        hnd = new CommandHandler();
     }
 
     /** {@inheritDoc} */
@@ -116,6 +113,8 @@ public class GridCommandHandlerTracingConfigurationTest extends GridCommandHandl
     @Override protected void beforeTest() throws Exception {
         super.beforeTest();
 
+        hnd = newCommandHandler();
+
         // Cleanup configuration.
         grid(0).tracingConfiguration().resetAll(null);
 
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerWithSslFactoryTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerWithSslFactoryTest.java
index a8709a8ba7b..f16e7a886fa 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerWithSslFactoryTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerWithSslFactoryTest.java
@@ -24,6 +24,7 @@ import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.ssl.SslContextFactory;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.junit.AfterClass;
+import org.junit.Assume;
 
 /**
  *
@@ -46,6 +47,13 @@ public class GridCommandHandlerWithSslFactoryTest extends GridCommandHandlerWith
         super.beforeTestsStarted();
     }
 
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        Assume.assumeTrue(commandHandler.equalsIgnoreCase(CLI_CMD_HND));
+
+        super.beforeTest();
+    }
+
     /** */
     @AfterClass
     public static void tearDown() {
@@ -55,6 +63,8 @@ public class GridCommandHandlerWithSslFactoryTest extends GridCommandHandlerWith
 
     /** {@inheritDoc} */
     @Override protected void afterTest() throws Exception {
+        Assume.assumeTrue(commandHandler.equalsIgnoreCase(CLI_CMD_HND));
+
         super.afterTest();
 
         assertTrue(factoryUsed);
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerWithSslTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerWithSslTest.java
index 82f35c956a2..ba3c1467ba8 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerWithSslTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerWithSslTest.java
@@ -17,6 +17,8 @@
 
 package org.apache.ignite.util;
 
+import org.junit.Assume;
+
 /**
  *
  */
@@ -25,4 +27,11 @@ public class GridCommandHandlerWithSslTest extends GridCommandHandlerTest {
     @Override protected boolean sslEnabled() {
         return true;
     }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        Assume.assumeTrue(commandHandler.equalsIgnoreCase(CLI_CMD_HND));
+
+        super.beforeTest();
+    }
 }
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/KillCommandsControlShTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/KillCommandsControlShTest.java
index ef4bcde0bcb..d57f8f8b157 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/KillCommandsControlShTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/KillCommandsControlShTest.java
@@ -22,6 +22,7 @@ import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.cache.CacheAtomicityMode;
 import org.apache.ignite.cache.ReadRepairStrategy;
@@ -34,11 +35,13 @@ import org.apache.ignite.internal.management.consistency.ConsistencyRepairTask;
 import org.apache.ignite.internal.management.consistency.ConsistencyStatusTask;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridNearGetRequest;
 import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.T3;
 import org.apache.ignite.lang.IgnitePredicate;
 import org.apache.ignite.lang.IgniteUuid;
 import org.apache.ignite.spi.systemview.view.ComputeJobView;
 import org.apache.ignite.spi.systemview.view.SystemView;
 import org.apache.ignite.testframework.GridTestUtils;
+import org.junit.Assume;
 import org.junit.Test;
 
 import static java.util.Arrays.asList;
@@ -77,6 +80,14 @@ public class KillCommandsControlShTest extends GridCommandHandlerClusterByClassA
     /** */
     private static List<IgniteEx> srvs;
 
+    /** */
+    private Consumer<T3<UUID, String, Long>> killScan = args -> {
+        int res = execute("--kill", "scan", args.get1().toString(), args.get2(), args.get3().toString());
+
+        assertEquals(EXIT_CODE_OK, res);
+    };
+
+
     /** {@inheritDoc} */
     @Override protected void beforeTestsStarted() throws Exception {
         super.beforeTestsStarted();
@@ -109,11 +120,7 @@ public class KillCommandsControlShTest extends GridCommandHandlerClusterByClassA
     /** @throws Exception If failed. */
     @Test
     public void testCancelScanQuery() throws Exception {
-        doTestScanQueryCancel(client, srvs, args -> {
-            int res = execute("--kill", "scan", args.get1().toString(), args.get2(), args.get3().toString());
-
-            assertEquals(EXIT_CODE_OK, res);
-        });
+        doTestScanQueryCancel(client, srvs, killScan);
     }
 
     /** @throws Exception If failed. */
@@ -163,7 +170,7 @@ public class KillCommandsControlShTest extends GridCommandHandlerClusterByClassA
             int res = execute("--kill", "continuous", nodeId.toString(), routineId.toString());
 
             assertEquals(EXIT_CODE_OK, res);
-        });
+        }, killScan);
     }
 
     /** @throws Exception If failed. */
@@ -280,6 +287,8 @@ public class KillCommandsControlShTest extends GridCommandHandlerClusterByClassA
      *
      */
     private void testCancelConsistencyTask(boolean parallel) throws InterruptedException {
+        Assume.assumeTrue(commandHandler.equals(CLI_CMD_HND));
+
         String consistencyCacheName = "consistencyCache";
 
         CacheConfiguration<Integer, Integer> cfg = new CacheConfiguration<>();
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/MetricCommandTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/MetricCommandTest.java
index cc4217c164d..83ad568fdbe 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/MetricCommandTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/MetricCommandTest.java
@@ -182,6 +182,8 @@ public class MetricCommandTest extends GridCommandHandlerClusterByClassAbstractT
     public void testConfigureHistogram() {
         String mregName = "configure-registry";
 
+        ignite0.context().metric().remove(mregName);
+
         MetricRegistry mreg = ignite0.context().metric().registry(mregName);
 
         long[] bounds = new long[] {50, 500};
@@ -209,6 +211,8 @@ public class MetricCommandTest extends GridCommandHandlerClusterByClassAbstractT
     public void testConfigureHitrate() {
         String mregName = "configure-registry";
 
+        ignite0.context().metric().remove(mregName);
+
         MetricRegistry mreg = ignite0.context().metric().registry(mregName);
 
         HitRateMetric hitrate = mreg.hitRateMetric("hitrate", null, 500, 5);
@@ -225,6 +229,8 @@ public class MetricCommandTest extends GridCommandHandlerClusterByClassAbstractT
     public void testHistogramMetrics() {
         String mregName = "histogram-registry";
 
+        ignite0.context().metric().remove(mregName);
+
         MetricRegistry mreg = ignite0.context().metric().registry(mregName);
 
         long[] bounds = new long[] {50, 500};
@@ -280,6 +286,8 @@ public class MetricCommandTest extends GridCommandHandlerClusterByClassAbstractT
     public void testRegistryMetrics() {
         String mregName = "test-metric-registry";
 
+        ignite0.context().metric().remove(mregName);
+
         MetricRegistry mreg = ignite0.context().metric().registry(mregName);
 
         mreg.booleanMetric("boolean-metric", "");
@@ -327,6 +335,8 @@ public class MetricCommandTest extends GridCommandHandlerClusterByClassAbstractT
     public void testLongMetrics() {
         String mregName = "long-metric-registry";
 
+        ignite0.context().metric().remove(mregName);
+
         MetricRegistry mreg = ignite0.context().metric().registry(mregName);
 
         mreg.longMetric("long-metric", "").add(Long.MAX_VALUE);
@@ -343,6 +353,8 @@ public class MetricCommandTest extends GridCommandHandlerClusterByClassAbstractT
     public void testIntegerMetrics() {
         String mregName = "int-metric-registry";
 
+        ignite0.context().metric().remove(mregName);
+
         MetricRegistry mreg = ignite0.context().metric().registry(mregName);
 
         mreg.intMetric("int-metric", "").add(Integer.MAX_VALUE);
@@ -359,6 +371,8 @@ public class MetricCommandTest extends GridCommandHandlerClusterByClassAbstractT
     public void testDoubleMetrics() {
         String mregName = "int-double-registry";
 
+        ignite0.context().metric().remove(mregName);
+
         MetricRegistry mreg = ignite0.context().metric().registry(mregName);
 
         mreg.doubleMetric("double-metric", "").add(111.222);
@@ -397,6 +411,8 @@ public class MetricCommandTest extends GridCommandHandlerClusterByClassAbstractT
     public void testHitrateMetrics() {
         String mregName = "hitrate-registry";
 
+        ignite0.context().metric().remove(mregName);
+
         MetricRegistry mreg = ignite0.context().metric().registry(mregName);
 
         mreg.hitRateMetric("hitrate-metric", "", getTestTimeout(), 2).add(Integer.MAX_VALUE);
@@ -445,14 +461,18 @@ public class MetricCommandTest extends GridCommandHandlerClusterByClassAbstractT
      * @return Metric values.
      */
     private Map<String, String> parseMetricCommandOutput(String out) {
-        String outStart = "--------------------------------------------------------------------------------";
+        if (commandHandler.equals(CLI_CMD_HND)) {
+            String outStart = "--------------------------------------------------------------------------------";
 
-        String outEnd = "Command [METRIC] finished with code: " + EXIT_CODE_OK;
+            String outEnd = "Command [METRIC] finished with code: " + EXIT_CODE_OK;
+
+            out = out.substring(
+                out.indexOf(outStart) + outStart.length() + 1,
+                out.indexOf(outEnd) - 1
+            );
+        }
 
-        String[] rows = out.substring(
-            out.indexOf(outStart) + outStart.length() + 1,
-            out.indexOf(outEnd) - 1
-        ).split(U.nl());
+        String[] rows = out.split(U.nl());
 
         Map<String, String> res = new HashMap<>();
 
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/SystemViewCommandTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/SystemViewCommandTest.java
index 03f02209c12..54e8e3fc63d 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/SystemViewCommandTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/SystemViewCommandTest.java
@@ -347,6 +347,7 @@ public class SystemViewCommandTest extends GridCommandHandlerClusterByClassAbstr
         assertEquals(srvcCfg.getName(), sysView.get(1)); // name
         assertEquals(DummyService.class.getName(), sysView.get(2)); // serviceClass
         assertEquals(Integer.toString(srvcCfg.getMaxPerNodeCount()), sysView.get(6)); // maxPerNodeCount
+        assertEquals(F.first(ignite0.services().serviceDescriptors()).topologySnapshot().toString(), sysView.get(10));
     }
 
     /** */
@@ -1066,7 +1067,7 @@ public class SystemViewCommandTest extends GridCommandHandlerClusterByClassAbstr
         IgniteCache<Integer, TestObjectAllTypes> c1 = ignite0.createCache("test-cache");
         IgniteCache<Integer, TestObjectEnum> c2 = ignite0.createCache("test-enum-cache");
 
-        executeSql(ignite0, "CREATE TABLE T1(ID LONG PRIMARY KEY, NAME VARCHAR(40), ACCOUNT BIGINT)");
+        executeSql(ignite0, "CREATE TABLE IF NOT EXISTS T1(ID LONG PRIMARY KEY, NAME VARCHAR(40), ACCOUNT BIGINT)");
         executeSql(ignite0, "INSERT INTO T1(ID, NAME, ACCOUNT) VALUES(1, 'test', 1)");
 
         c1.put(1, new TestObjectAllTypes());
@@ -1074,13 +1075,17 @@ public class SystemViewCommandTest extends GridCommandHandlerClusterByClassAbstr
 
         List<List<String>> binaryMetaView = systemView(ignite0, BINARY_METADATA_VIEW);
 
-        assertEquals(3, binaryMetaView.size());
+        assertTrue(binaryMetaView.size() >= 3);
+
+        int cntr = 0;
 
         for (List<String> row : binaryMetaView) {
             if (Objects.equals(TestObjectEnum.class.getName(), row.get(1))) {
                 assertTrue(Boolean.parseBoolean(row.get(6)));
 
                 assertEquals("0", row.get(3));
+
+                cntr++;
             }
             else if (Objects.equals(TestObjectAllTypes.class.getName(), row.get(1))) {
                 assertFalse(Boolean.parseBoolean(row.get(6)));
@@ -1091,16 +1096,22 @@ public class SystemViewCommandTest extends GridCommandHandlerClusterByClassAbstr
 
                 for (Field field : fields)
                     assertTrue(row.get(4).contains(field.getName()));
+
+                cntr++;
             }
-            else {
+            else if (row.get(1).contains("T1")) {
                 assertFalse(Boolean.parseBoolean(row.get(6)));
 
                 assertEquals("2", row.get(3));
 
                 assertTrue(row.get(4).contains("NAME"));
                 assertTrue(row.get(4).contains("ACCOUNT"));
+
+                cntr++;
             }
         }
+
+        assertTrue(cntr >= 3);
     }
 
     /** */
@@ -1150,17 +1161,19 @@ public class SystemViewCommandTest extends GridCommandHandlerClusterByClassAbstr
     public void testSnapshotView() throws Exception {
         int srvCnt = ignite0.cluster().forServers().nodes().size();
 
-        String snap0 = "testSnapshot0";
+        int sz = systemView(ignite0, SNAPSHOT_SYS_VIEW).size();
+
+        String snap0 = "testSnapshot" + commandHandler;
 
         ignite0.snapshot().createSnapshot(snap0).get();
 
-        assertEquals(srvCnt, systemView(ignite0, SNAPSHOT_SYS_VIEW).size());
+        assertEquals(srvCnt + sz, systemView(ignite0, SNAPSHOT_SYS_VIEW).size());
     }
 
     /** */
     @Test
     public void testMultipleNodes() {
-        checkNodesResult(Collections.singleton(ignite0), NODE_IDS);
+        //checkNodesResult(Collections.singleton(ignite0), NODE_IDS);
         checkNodesResult(Collections.singleton(client), NODE_IDS);
 
         checkNodesResult(F.asList(ignite0, ignite1), NODE_IDS);
@@ -1269,14 +1282,18 @@ public class SystemViewCommandTest extends GridCommandHandlerClusterByClassAbstr
      * @return System view values.
      */
     private Map<UUID, List<List<String>>> parseSystemViewCommandOutput(String out) {
-        String outStart = "--------------------------------------------------------------------------------";
+        if (commandHandler.equals(CLI_CMD_HND)) {
+            String outStart = "--------------------------------------------------------------------------------";
 
-        String outEnd = "Command [SYSTEM-VIEW] finished with code: " + EXIT_CODE_OK;
+            String outEnd = "Command [SYSTEM-VIEW] finished with code: " + EXIT_CODE_OK;
+
+            out = out.substring(
+                out.indexOf(outStart) + outStart.length() + 1,
+                out.indexOf(outEnd) - 1
+            );
+        }
 
-        String[] rows = out.substring(
-            out.indexOf(outStart) + outStart.length() + 1,
-            out.indexOf(outEnd) - 1
-        ).split(U.nl());
+        String[] rows = out.split(U.nl());
 
         Pattern nodePtrn = Pattern.compile("Results from node with ID: (.*)");
         String tableDelim = "---";
diff --git a/modules/control-utility/src/test/resources/META-INF/services/org.apache.ignite.util.GridCommandHandlerFactoryAbstractTest$TestCommandHandler b/modules/control-utility/src/test/resources/META-INF/services/org.apache.ignite.util.GridCommandHandlerFactoryAbstractTest$TestCommandHandler
new file mode 100644
index 00000000000..adfad811015
--- /dev/null
+++ b/modules/control-utility/src/test/resources/META-INF/services/org.apache.ignite.util.GridCommandHandlerFactoryAbstractTest$TestCommandHandler
@@ -0,0 +1,2 @@
+org.apache.ignite.util.GridCommandHandlerFactoryAbstractTest$JmxCommandHandler
+org.apache.ignite.util.GridCommandHandlerFactoryAbstractTest$CliCommandHandler
\ No newline at end of file
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/IgniteEx.java b/modules/core/src/main/java/org/apache/ignite/internal/IgniteEx.java
index 5d982290852..ff61fe1ace8 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/IgniteEx.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/IgniteEx.java
@@ -26,6 +26,7 @@ import org.apache.ignite.IgniteSet;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.internal.cluster.IgniteClusterEx;
+import org.apache.ignite.internal.management.IgniteCommandRegistry;
 import org.apache.ignite.internal.processors.cache.GridCacheUtilityKey;
 import org.apache.ignite.internal.processors.cache.IgniteInternalCache;
 import org.apache.ignite.lang.IgniteBiTuple;
@@ -172,4 +173,10 @@ public interface IgniteEx extends Ignite {
      * @throws IgniteException If set could not be fetched or created.
      */
     public <T> IgniteSet<T> set(String name, int cacheId, boolean collocated, boolean separated) throws IgniteException;
+
+    /**
+     * @return Registry with all management commands known by node.
+     * @see org.apache.ignite.internal.management.api.Command
+     */
+    public IgniteCommandRegistry commandsRegistry();
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java b/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
index 39f4ad5be07..e61a1a8772f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
@@ -94,6 +94,7 @@ import org.apache.ignite.internal.cache.query.index.IndexProcessor;
 import org.apache.ignite.internal.cluster.ClusterGroupAdapter;
 import org.apache.ignite.internal.cluster.IgniteClusterEx;
 import org.apache.ignite.internal.maintenance.MaintenanceProcessor;
+import org.apache.ignite.internal.management.IgniteCommandRegistry;
 import org.apache.ignite.internal.managers.GridManager;
 import org.apache.ignite.internal.managers.IgniteMBeansManager;
 import org.apache.ignite.internal.managers.checkpoint.GridCheckpointManager;
@@ -398,6 +399,10 @@ public class IgniteKernal implements IgniteEx, Externalizable {
     @GridToStringExclude
     private IgniteMBeansManager mBeansMgr;
 
+    /** Registry with all management commands known by node. */
+    @GridToStringExclude
+    private final IgniteCommandRegistry cmdReg = new IgniteCommandRegistry();
+
     /** Ignite configuration instance. */
     private IgniteConfiguration cfg;
 
@@ -3093,6 +3098,11 @@ public class IgniteKernal implements IgniteEx, Externalizable {
         }
     }
 
+    /** {@inheritDoc} */
+    @Override public IgniteCommandRegistry commandsRegistry() {
+        return cmdReg;
+    }
+
     /**
      * The {@code ctx.gateway().readLock()} is used underneath.
      */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cluster/IgniteClusterEx.java b/modules/core/src/main/java/org/apache/ignite/internal/cluster/IgniteClusterEx.java
index bc728730e96..88abcd71514 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cluster/IgniteClusterEx.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cluster/IgniteClusterEx.java
@@ -18,10 +18,22 @@
 package org.apache.ignite.internal.cluster;
 
 import org.apache.ignite.IgniteCluster;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.cluster.ClusterState;
 
 /**
  *
  */
 public interface IgniteClusterEx extends IgniteCluster, ClusterGroupEx {
-    // No-op.
+    /**
+     * Changes current cluster state to given {@code newState} cluster state.
+     * <p>
+     * <b>NOTE:</b>
+     * Deactivation clears in-memory caches (without persistence) including the system caches.
+     *
+     * @param newState New cluster state.
+     * @param force Force deactivation.
+     * @throws IgniteException If there is an already started transaction or lock in the same thread.
+     */
+    public void state(ClusterState newState, boolean force) throws IgniteException;
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cluster/IgniteClusterImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/cluster/IgniteClusterImpl.java
index b094d8df42b..f2cf3d298ca 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cluster/IgniteClusterImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cluster/IgniteClusterImpl.java
@@ -396,10 +396,15 @@ public class IgniteClusterImpl extends ClusterGroupAdapter implements IgniteClus
 
     /** {@inheritDoc} */
     @Override public void state(ClusterState newState) throws IgniteException {
+        state(newState, true);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void state(ClusterState newState, boolean force) throws IgniteException {
         guard();
 
         try {
-            ctx.state().changeGlobalState(newState, true, serverNodes(), false).get();
+            ctx.state().changeGlobalState(newState, force, serverNodes(), false).get();
         }
         catch (IgniteCheckedException e) {
             throw U.convertException(e);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/ActivateCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/ActivateCommand.java
index 59f7f8c45ae..f87aa812085 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/ActivateCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/ActivateCommand.java
@@ -18,10 +18,14 @@
 package org.apache.ignite.internal.management;
 
 import java.util.function.Consumer;
+import org.apache.ignite.Ignite;
 import org.apache.ignite.internal.client.GridClient;
 import org.apache.ignite.internal.client.GridClientClusterState;
+import org.apache.ignite.internal.client.GridClientException;
 import org.apache.ignite.internal.management.api.LocalCommand;
 import org.apache.ignite.internal.management.api.NoArg;
+import org.jetbrains.annotations.Nullable;
+
 import static org.apache.ignite.cluster.ClusterState.ACTIVE;
 
 /** */
@@ -38,10 +42,19 @@ public class ActivateCommand implements LocalCommand<NoArg, NoArg> {
     }
 
     /** {@inheritDoc} */
-    @Override public NoArg execute(GridClient cli, NoArg arg, Consumer<String> printer) throws Exception {
-        GridClientClusterState state = cli.state();
+    @Override public NoArg execute(
+        @Nullable GridClient cli,
+        @Nullable Ignite ignite,
+        NoArg arg,
+        Consumer<String> printer
+    ) throws GridClientException {
+        if (cli != null) {
+            GridClientClusterState state = cli.state();
 
-        state.state(ACTIVE, false);
+            state.state(ACTIVE, false);
+        }
+        else
+            ignite.cluster().state(ACTIVE);
 
         printer.accept("Cluster activated");
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/DeactivateCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/DeactivateCommand.java
index 61368c3c268..24e7c9c3ef8 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/DeactivateCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/DeactivateCommand.java
@@ -18,12 +18,16 @@
 package org.apache.ignite.internal.management;
 
 import java.util.function.Consumer;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.client.GridClient;
 import org.apache.ignite.internal.client.GridClientClusterState;
 import org.apache.ignite.internal.client.GridClientException;
+import org.apache.ignite.internal.cluster.IgniteClusterEx;
 import org.apache.ignite.internal.management.api.LocalCommand;
 import org.apache.ignite.internal.management.api.NoArg;
 import org.apache.ignite.internal.management.api.PreparableCommand;
+import org.jetbrains.annotations.Nullable;
 
 import static org.apache.ignite.cluster.ClusterState.INACTIVE;
 
@@ -46,10 +50,19 @@ public class DeactivateCommand implements LocalCommand<DeactivateCommandArg, NoA
     }
 
     /** {@inheritDoc} */
-    @Override public NoArg execute(GridClient cli, DeactivateCommandArg arg, Consumer<String> printer) throws Exception {
-        GridClientClusterState state = cli.state();
+    @Override public NoArg execute(
+        @Nullable GridClient cli,
+        @Nullable Ignite ignite,
+        DeactivateCommandArg arg,
+        Consumer<String> printer
+    ) throws GridClientException {
+        if (cli != null) {
+            GridClientClusterState state = cli.state();
 
-        state.state(INACTIVE, arg.force());
+            state.state(INACTIVE, arg.force());
+        }
+        else
+            ((IgniteClusterEx)ignite.cluster()).state(INACTIVE, arg.force());
 
         printer.accept("Cluster deactivated");
 
@@ -62,8 +75,13 @@ public class DeactivateCommand implements LocalCommand<DeactivateCommandArg, NoA
     }
 
     /** {@inheritDoc} */
-    @Override public boolean prepare(GridClient cli, DeactivateCommandArg arg, Consumer<String> printer) throws GridClientException {
-        arg.clusterName(cli.state().clusterName());
+    @Override public boolean prepare(
+        @Nullable GridClient cli,
+        @Nullable Ignite ignite,
+        DeactivateCommandArg arg,
+        Consumer<String> printer
+    ) throws GridClientException {
+        arg.clusterName(cli != null ? cli.state().clusterName() : ((IgniteEx)ignite).context().cluster().clusterName());
 
         return true;
     }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/SetStateCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/SetStateCommand.java
index e03a7e0ea98..f6ee5994cbd 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/SetStateCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/SetStateCommand.java
@@ -18,9 +18,13 @@
 package org.apache.ignite.internal.management;
 
 import java.util.function.Consumer;
+import org.apache.ignite.Ignite;
 import org.apache.ignite.cluster.ClusterState;
 import org.apache.ignite.internal.client.GridClient;
+import org.apache.ignite.internal.client.GridClientException;
+import org.apache.ignite.internal.cluster.IgniteClusterEx;
 import org.apache.ignite.internal.management.api.LocalCommand;
+import org.jetbrains.annotations.Nullable;
 
 /** */
 public class SetStateCommand implements LocalCommand<SetStateCommandArg, Boolean> {
@@ -35,8 +39,13 @@ public class SetStateCommand implements LocalCommand<SetStateCommandArg, Boolean
     }
 
     /** {@inheritDoc} */
-    @Override public Boolean execute(GridClient cli, SetStateCommandArg arg, Consumer<String> printer) throws Exception {
-        ClusterState clusterState = cli.state().state();
+    @Override public Boolean execute(
+        @Nullable GridClient cli,
+        @Nullable Ignite ignite,
+        SetStateCommandArg arg,
+        Consumer<String> printer
+    ) throws GridClientException {
+        ClusterState clusterState = cli != null ? cli.state().state() : ignite.cluster().state();
 
         if (clusterState == arg.state()) {
             printer.accept("Cluster state is already " + arg.state() + '.');
@@ -44,7 +53,10 @@ public class SetStateCommand implements LocalCommand<SetStateCommandArg, Boolean
             return false;
         }
 
-        cli.state().state(arg.state(), arg.force());
+        if (cli != null)
+            cli.state().state(arg.state(), arg.force());
+        else
+            ((IgniteClusterEx)ignite.cluster()).state(arg.state(), arg.force());
 
         printer.accept("Cluster state changed to " + arg.state() + '.');
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/StateCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/StateCommand.java
index 4386253affa..8b7ff823a86 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/StateCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/StateCommand.java
@@ -19,13 +19,17 @@ package org.apache.ignite.internal.management;
 
 import java.util.UUID;
 import java.util.function.Consumer;
+import org.apache.ignite.Ignite;
 import org.apache.ignite.cluster.ClusterState;
 import org.apache.ignite.internal.client.GridClient;
 import org.apache.ignite.internal.client.GridClientClusterState;
+import org.apache.ignite.internal.client.GridClientException;
 import org.apache.ignite.internal.management.api.LocalCommand;
 import org.apache.ignite.internal.management.api.NoArg;
 import org.apache.ignite.internal.util.lang.GridTuple3;
 import org.apache.ignite.internal.util.typedef.F;
+import org.jetbrains.annotations.Nullable;
+
 import static org.apache.ignite.internal.util.typedef.internal.U.DELIM;
 
 /** */
@@ -41,15 +45,35 @@ public class StateCommand implements LocalCommand<NoArg, GridTuple3<UUID, String
     }
 
     /** {@inheritDoc} */
-    @Override public GridTuple3<UUID, String, ClusterState> execute(GridClient cli, NoArg arg, Consumer<String> printer) throws Exception {
-        GridClientClusterState state = cli.state();
+    @Override public GridTuple3<UUID, String, ClusterState> execute(
+        @Nullable GridClient cli,
+        @Nullable Ignite ignite,
+        NoArg arg,
+        Consumer<String> printer
+    ) throws GridClientException {
+        ClusterState state;
+        UUID id;
+        String tag;
+
+        if (cli != null) {
+            GridClientClusterState state0 = cli.state();
+
+            state = state0.state();
+            id = state0.id();
+            tag = state0.tag();
+        }
+        else {
+            state = ignite.cluster().state();
+            id = ignite.cluster().id();
+            tag = ignite.cluster().tag();
+        }
 
-        printer.accept("Cluster  ID: " + state.id());
-        printer.accept("Cluster tag: " + state.tag());
+        printer.accept("Cluster  ID: " + id);
+        printer.accept("Cluster tag: " + tag);
 
         printer.accept(DELIM);
 
-        switch (state.state()) {
+        switch (state) {
             case ACTIVE:
                 printer.accept("Cluster is active");
 
@@ -66,9 +90,9 @@ public class StateCommand implements LocalCommand<NoArg, GridTuple3<UUID, String
                 break;
 
             default:
-                throw new IllegalStateException("Unknown state: " + state.state());
+                throw new IllegalStateException("Unknown state: " + state);
         }
 
-        return F.t(state.id(), state.tag(), state.state());
+        return F.t(id, tag, state);
     }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/SystemViewCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/SystemViewCommand.java
index f42ff0edd03..8f03ba7e8cc 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/SystemViewCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/SystemViewCommand.java
@@ -18,17 +18,14 @@
 package org.apache.ignite.internal.management;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.ListIterator;
-import java.util.Map;
-import java.util.UUID;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import org.apache.ignite.internal.client.GridClientNode;
+import org.apache.ignite.internal.management.api.CommandUtils;
 import org.apache.ignite.internal.management.api.ComputeCommand;
 import org.apache.ignite.internal.util.typedef.internal.U;
 
@@ -58,16 +55,13 @@ public class SystemViewCommand implements ComputeCommand<SystemViewCommandArg, S
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, SystemViewCommandArg arg) {
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, SystemViewCommandArg arg) {
         if (arg.allNodes())
-            return nodes.keySet();
+            return nodes;
 
-        if (arg.nodeIds() != null)
-            return Arrays.asList(arg.nodeIds());
-
-        return arg.nodeId() != null
-                ? Collections.singleton(arg.nodeId())
-                : null;
+        return arg.nodeIds() != null
+            ? CommandUtils.nodes(arg.nodeIds(), nodes)
+            : CommandUtils.nodeOrNull(arg.nodeId(), nodes);
     }
 
     /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/api/CommandInvoker.java b/modules/core/src/main/java/org/apache/ignite/internal/management/api/CommandInvoker.java
new file mode 100644
index 00000000000..0c500bca629
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/api/CommandInvoker.java
@@ -0,0 +1,110 @@
+/*
+ * 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.management.api;
+
+import java.util.Collection;
+import java.util.function.Consumer;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.client.GridClient;
+import org.apache.ignite.internal.client.GridClientException;
+import org.apache.ignite.internal.client.GridClientNode;
+import org.apache.ignite.internal.dto.IgniteDataTransferObject;
+import org.jetbrains.annotations.Nullable;
+
+import static java.util.Collections.singletonList;
+
+/**
+ * Command invoker.
+ */
+public class CommandInvoker<A extends IgniteDataTransferObject> {
+    /** */
+    private final @Nullable IgniteEx ignite;
+
+    /** Command to execute. */
+    protected final Command<A, ?> cmd;
+
+    /** Parsed argument. */
+    protected final A arg;
+
+    /**
+     * @param cmd Command to execute.
+     * @param arg Argument
+     * @param ignite Optional ignite instance.
+     */
+    public CommandInvoker(Command<A, ?> cmd, A arg, @Nullable IgniteEx ignite) {
+        this.cmd = cmd;
+        this.arg = arg;
+        this.ignite = ignite;
+    }
+
+    /**
+     * @param printer Result printer.
+     * @return {@code True} of command successfully prepared and can be invoked, {@code false} otherwise.
+     * @throws GridClientException In failed.
+     */
+    public boolean prepare(Consumer<String> printer) throws GridClientException {
+        if (!(cmd instanceof PreparableCommand))
+            return true;
+
+        return ((PreparableCommand<A, ?>)cmd).prepare(client(), ignite, arg, printer);
+    }
+
+    /**
+     * Actual command execution with verbose mode if required.
+     *
+     * @param printer Result printer.
+     * @param verbose Use verbose mode or not
+     * @return Result of operation (mostly usable for tests).
+     * @throws GridClientException In failed.
+     */
+    public <R> R invoke(Consumer<String> printer, boolean verbose) throws GridClientException {
+        R res;
+
+        if (cmd instanceof LocalCommand)
+            res = ((LocalCommand<A, R>)cmd).execute(client(), ignite, arg, printer);
+        else if (cmd instanceof ComputeCommand) {
+            ComputeCommand<A, R> cmd = (ComputeCommand<A, R>)this.cmd;
+
+            Collection<GridClientNode> cmdNodes = cmd.nodes(CommandUtils.nodes(client(), ignite), arg);
+
+            if (cmdNodes == null)
+                cmdNodes = singletonList(defaultNode());
+
+            res = CommandUtils.execute(client(), ignite, cmd.taskClass(), arg, cmdNodes);
+
+            cmd.printResult(arg, res, printer);
+        }
+        else
+            throw new IllegalArgumentException("Unknown command type: " + cmd);
+
+        return res;
+    }
+
+    /** @return Default node to execute commands. */
+    protected GridClientNode defaultNode() throws GridClientException {
+        return CommandUtils.clusterToClientNode(ignite.localNode());
+    }
+
+    /**
+     * @return Grid thin client instance which is already connected to cluster.
+     * @throws GridClientException If failed.
+     */
+    protected @Nullable GridClient client() throws GridClientException {
+        return null;
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/api/CommandMBean.java b/modules/core/src/main/java/org/apache/ignite/internal/management/api/CommandMBean.java
new file mode 100644
index 00000000000..9af32a2a4ad
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/api/CommandMBean.java
@@ -0,0 +1,239 @@
+/*
+ * 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.management.api;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.DynamicMBean;
+import javax.management.InvalidAttributeValueException;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+import javax.management.ReflectionException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.client.GridClientException;
+import org.apache.ignite.internal.dto.IgniteDataTransferObject;
+import org.apache.ignite.internal.util.typedef.F;
+
+import static javax.management.MBeanOperationInfo.ACTION;
+import static org.apache.ignite.internal.management.api.CommandUtils.visitCommandParams;
+
+/**
+ * Bean expose single mamagement command via JMX interface.
+ *
+ * @see Command
+ * @see CommandsRegistry
+ */
+public class CommandMBean<A extends IgniteDataTransferObject, R> implements DynamicMBean {
+    /** Name of the JMX method to invoke command. */
+    public static final String INVOKE = "invoke";
+
+    /**
+     * Used for tests.
+     * Name of the method to retrive last method result.
+     */
+    public static final String LAST_RES_METHOD = "lastResult";
+
+    /** Local ignite node. */
+    private final IgniteEx ignite;
+
+    /** Logger. */
+    private final IgniteLogger log;
+
+    /** Management command to expose. */
+    private final Command<A, ?> cmd;
+
+    /**
+     * Used for tests.
+     * Last invocation result.
+     */
+    private R res;
+
+    /**
+     * @param ignite Ignite node.
+     * @param cmd Management command to expose.
+     */
+    public CommandMBean(IgniteEx ignite, Command<A, R> cmd) {
+        this.ignite = ignite;
+        this.cmd = cmd;
+        this.log = ignite.log().getLogger(CommandMBean.class.getName() + '#' + cmd.getClass().getSimpleName());
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object getAttribute(
+        String attribute
+    ) throws AttributeNotFoundException, MBeanException, ReflectionException {
+        throw new UnsupportedOperationException("getAttribute");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setAttribute(
+        Attribute attribute
+    ) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException {
+        throw new UnsupportedOperationException("setAttribute");
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object invoke(
+        String actionName,
+        Object[] params,
+        String[] signature
+    ) throws MBeanException, ReflectionException {
+        // Default JMX invoker pass arguments in for params: Object[] = { "invoke", parameter_values_array, types_array}
+        // while JConsole pass params values directly in params array.
+        // This check supports both way of invocation.
+        if (params.length == 3
+            && (params[0].equals(INVOKE) || params[0].equals(LAST_RES_METHOD))
+            && params[1] instanceof Object[])
+            return invoke((String)params[0], (Object[])params[1], (String[])params[2]);
+
+        if (LAST_RES_METHOD.equals(actionName))
+            return res;
+
+        if (!INVOKE.equals(actionName))
+            throw new UnsupportedOperationException(actionName);
+
+        try {
+            StringBuilder resStr = new StringBuilder();
+
+            Consumer<String> printer = str -> resStr.append(str).append('\n');
+
+            CommandInvoker<A> invoker = new CommandInvoker<>(cmd, new ParamsToArgument(params).argument(), ignite);
+
+            if (invoker.prepare(printer))
+                res = invoker.invoke(printer, false);
+
+            return resStr.toString();
+        }
+        catch (GridClientException e) {
+            log.error("Invoke error:", e);
+
+            throw new RuntimeException(e);
+        }
+        catch (Exception e) {
+            log.error("Invoke error:", e);
+
+            throw e;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public MBeanInfo getMBeanInfo() {
+        List<MBeanParameterInfo> args = new ArrayList<>();
+
+        BiConsumer<ArgumentGroup, Field> fldCnsmr = (argGrp, fld) -> {
+            String descStr = "";
+
+            if ((argGrp != null && argGrp.optional()) || fld.getAnnotation(Argument.class).optional())
+                descStr += "Optional. ";
+
+            if (!fld.isAnnotationPresent(EnumDescription.class))
+                descStr += fld.getAnnotation(Argument.class).description();
+            else {
+                EnumDescription enumDesc = fld.getAnnotation(EnumDescription.class);
+
+                String[] names = enumDesc.names();
+                String[] descriptions = enumDesc.descriptions();
+
+                StringBuilder bldr = new StringBuilder();
+
+                for (int i = 0; i < names.length; i++) {
+                    if (i != 0)
+                        bldr.append('\n');
+                    bldr.append(names[i]).append(" - ").append(descriptions[i]);
+                }
+
+                descStr += bldr.toString();
+            }
+
+            args.add(new MBeanParameterInfo(fld.getName(), String.class.getName(), descStr));
+        };
+
+        visitCommandParams(
+            cmd.argClass(),
+            fld -> fldCnsmr.accept(null, fld),
+            fld -> fldCnsmr.accept(null, fld),
+            (grp, flds) -> flds.forEach(fld -> fldCnsmr.accept(grp, fld))
+        );
+
+        return new MBeanInfo(
+            CommandMBean.class.getName(),
+            cmd.getClass().getSimpleName(),
+            null,
+            null,
+            new MBeanOperationInfo[]{
+                new MBeanOperationInfo(
+                    INVOKE,
+                    cmd.description(),
+                    args.toArray(new MBeanParameterInfo[0]),
+                    String.class.getName(),
+                    ACTION
+                )
+            },
+            null
+        );
+    }
+
+    /** {@inheritDoc} */
+    @Override public AttributeList getAttributes(String[] attributes) {
+        throw new UnsupportedOperationException("getAttributes");
+    }
+
+    /** {@inheritDoc} */
+    @Override public AttributeList setAttributes(AttributeList attributes) {
+        throw new UnsupportedOperationException("setAttributes");
+    }
+
+    /** */
+    private class ParamsToArgument implements Function<Field, Object> {
+        /** */
+        private int cntr;
+
+        /** */
+        private final Object[] vals;
+
+        /** */
+        private ParamsToArgument(Object[] vals) {
+            this.vals = vals;
+        }
+
+        /** */
+        public A argument() {
+            // This will map vals to argument fields.
+            return CommandUtils.argument(cmd.argClass(), (fld, pos) -> apply(fld), this);
+        }
+
+        /** {@inheritDoc} */
+        @Override public Object apply(Field field) {
+            String val = (String)vals[cntr];
+
+            cntr++;
+
+            return !F.isEmpty(val) ? CommandUtils.parseVal(val, field.getType()) : null;
+        }
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/api/CommandRegistryImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/management/api/CommandRegistryImpl.java
index 546fb9c438a..6bf87e949a2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/api/CommandRegistryImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/api/CommandRegistryImpl.java
@@ -23,6 +23,8 @@ import java.util.Map;
 import org.apache.ignite.internal.dto.IgniteDataTransferObject;
 import org.apache.ignite.internal.management.IgniteCommandRegistry;
 
+import static org.apache.ignite.internal.management.api.CommandUtils.cmdKey;
+
 /**
  * All commands class names stored in registry must ends with {@link Command#CMD_NAME_POSTFIX}.
  *
@@ -47,33 +49,15 @@ public abstract class CommandRegistryImpl<A extends IgniteDataTransferObject, R>
         boolean hasParent = CommandsRegistry.class.isAssignableFrom(getClass())
             && getClass() != IgniteCommandRegistry.class;
 
-        Class<? extends CommandsRegistry<?, ?>> parent = hasParent ?
-            (Class<? extends CommandsRegistry<?, ?>>)getClass()
-            : null;
-
-        String name = cmd.getClass().getSimpleName();
-
-        if (parent != null) {
-            String parentName = parent.getSimpleName();
-            parentName = parentName.substring(0, parentName.length() - CMD_NAME_POSTFIX.length());
-
-            if (!name.startsWith(parentName)) {
-                throw new IllegalArgumentException(
-                    "Command class name must starts with parent name [parent=" + parentName + ']');
-            }
-
-            name = name.substring(parentName.length());
-        }
-
-        if (!name.endsWith(CMD_NAME_POSTFIX))
-            throw new IllegalArgumentException("Command class name must ends with 'Command'");
-
-        name = name.substring(0, name.length() - CMD_NAME_POSTFIX.length());
+        String key = cmdKey(
+            cmd.getClass(),
+            hasParent ? (Class<? extends CommandsRegistry<?, ?>>)getClass() : null
+        );
 
-        if (commands.containsKey(name))
+        if (commands.containsKey(key))
             throw new IllegalArgumentException("Command already registered");
 
-        commands.put(name, cmd);
+        commands.put(key, cmd);
     }
 
     /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/api/CommandUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/management/api/CommandUtils.java
index 0c42c174667..8611d1bb8fc 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/api/CommandUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/api/CommandUtils.java
@@ -19,6 +19,8 @@ package org.apache.ignite.internal.management.api;
 
 import java.lang.reflect.Array;
 import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.net.InetSocketAddress;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -27,19 +29,32 @@ import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
 import java.util.function.Consumer;
+import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
+import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteException;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.compute.ComputeTask;
+import org.apache.ignite.internal.client.GridClient;
+import org.apache.ignite.internal.client.GridClientCacheMode;
+import org.apache.ignite.internal.client.GridClientCompute;
+import org.apache.ignite.internal.client.GridClientException;
 import org.apache.ignite.internal.client.GridClientNode;
+import org.apache.ignite.internal.client.GridClientNodeMetrics;
+import org.apache.ignite.internal.client.GridClientProtocol;
 import org.apache.ignite.internal.dto.IgniteDataTransferObject;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.SB;
 import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.internal.visor.VisorTaskArgument;
 import org.apache.ignite.lang.IgniteExperimental;
 import org.apache.ignite.lang.IgniteUuid;
 import org.jetbrains.annotations.Nullable;
@@ -75,6 +90,33 @@ public class CommandUtils {
         return NAME_PREFIX + toFormattedCommandName(cmd.getClass(), CMD_WORDS_DELIM);
     }
 
+    /**
+     * @param cmdCls Command class.
+     * @param parent Parent registry.
+     * @return Command key without parent prefix.
+     */
+    public static String cmdKey(Class<?> cmdCls, Class<? extends CommandsRegistry<?, ?>> parent) {
+        String name = cmdCls.getSimpleName();
+
+        if (parent != null) {
+            String parentName = parent.getSimpleName();
+
+            parentName = parentName.substring(0, parentName.length() - CMD_NAME_POSTFIX.length());
+
+            if (!name.startsWith(parentName)) {
+                throw new IllegalArgumentException(
+                    "Command class name must starts with parent name [parent=" + parentName + ']');
+            }
+
+            name = name.substring(parentName.length());
+        }
+
+        if (!name.endsWith(CMD_NAME_POSTFIX))
+            throw new IllegalArgumentException("Command class name must ends with 'Command'");
+
+        return name.substring(0, name.length() - CMD_NAME_POSTFIX.length());
+    }
+
     /**
      * Example: {@code "SystemView" -> "system-view"}.
      *
@@ -387,27 +429,76 @@ public class CommandUtils {
      * @param nodes Nodes.
      * @return Coordinator ID or null is {@code nodes} are empty.
      */
-    public static @Nullable Collection<UUID> coordinatorOrNull(Map<UUID, GridClientNode> nodes) {
-        return nodes.entrySet().stream()
-            .filter(e -> !e.getValue().isClient())
-            .min(Comparator.comparingLong(e -> e.getValue().order()))
-            .map(e -> Collections.singleton(e.getKey()))
+    public static @Nullable Collection<GridClientNode> coordinatorOrNull(Collection<GridClientNode> nodes) {
+        return nodes.stream()
+            .filter(n -> !n.isClient())
+            .min(Comparator.comparingLong(GridClientNode::order))
+            .map(Collections::singleton)
             .orElse(null);
     }
 
     /** */
-    public static Collection<UUID> nodeOrNull(@Nullable UUID nodeId) {
-        return nodeId == null ? null : Collections.singleton(nodeId);
+    public static Collection<GridClientNode> nodeOrNull(@Nullable UUID nodeId, Collection<GridClientNode> nodes) {
+        return nodeId == null ? null : node(nodeId, nodes);
+    }
+
+    /** */
+    public static Collection<GridClientNode> nodeOrAll(@Nullable UUID nodeId, Collection<GridClientNode> nodes) {
+        return nodeId == null ? nodes : node(nodeId, nodes);
+    }
+
+    /** */
+    public static Collection<GridClientNode> nodes(UUID[] nodeIds, Collection<GridClientNode> nodes) {
+        List<GridClientNode> res = new ArrayList<>();
+
+        for (UUID nodeId : nodeIds)
+            res.addAll(node(nodeId, nodes));
+
+        return res;
+    }
+
+    /** */
+    public static List<GridClientNode> node(UUID nodeId, Collection<GridClientNode> nodes) {
+        for (GridClientNode node : nodes) {
+            if (node.nodeId().equals(nodeId))
+                return Collections.singletonList(node);
+        }
+
+        throw new IllegalArgumentException("Node with id=" + nodeId + " not found.");
     }
 
     /**
      * @param nodes Nodes.
      * @return Server nodes.
      */
-    public static Collection<UUID> servers(Map<UUID, GridClientNode> nodes) {
-        return nodes.entrySet().stream()
-            .filter(e -> !e.getValue().isClient())
-            .map(Map.Entry::getKey)
+    public static Collection<GridClientNode> servers(Collection<GridClientNode> nodes) {
+        return nodes.stream()
+            .filter(e -> !e.isClient())
+            .collect(Collectors.toList());
+    }
+
+    /**
+     * @param cmd Command.
+     * @return {@code True} if command can be executed, {@code false} otherwise.
+     */
+    public static boolean executable(Command<?, ?> cmd) {
+        return cmd instanceof LocalCommand
+            || cmd instanceof ComputeCommand
+            || cmd instanceof HelpCommand
+            || cmd instanceof BeforeNodeStartCommand;
+    }
+
+    /**
+     * @param cli Grid client.
+     * @param ignite Ignite node.
+     * @return Collection of cluster nodes.
+     */
+    public static Collection<GridClientNode> nodes(@Nullable GridClient cli, @Nullable Ignite ignite) throws GridClientException {
+        if (cli != null)
+            return cli.compute().nodes();
+
+        return ignite.cluster().nodes().stream()
+            .map(CommandUtils::clusterToClientNode)
             .collect(Collectors.toList());
     }
 
@@ -484,7 +575,7 @@ public class CommandUtils {
      */
     private static <T> T parseSingleVal(String val, Class<T> type) {
         if (isBoolean(type))
-            return (T)Boolean.TRUE;
+            return (T)(Boolean)Boolean.parseBoolean(val);
         if (type == String.class)
             return (T)val;
         else if (type == Integer.class || type == int.class) {
@@ -555,4 +646,238 @@ public class CommandUtils {
             throw new NumberFormatException("Can't parse number '" + val + "', expected type: " + expectedType.getName());
         }
     }
+
+    /**
+     * Fill and vaildate command argument.
+     *
+     * @param argCls Argument class.
+     * @param positionalParamProvider Provider of positional parameters.
+     * @param paramProvider Provider of named parameters.
+     * @return Argument filled with parameters.
+     * @param <A> Argument type.
+     */
+    public static <A extends IgniteDataTransferObject> A argument(
+        Class<A> argCls,
+        BiFunction<Field, Integer, Object> positionalParamProvider,
+        Function<Field, Object> paramProvider
+    ) {
+        try {
+            ArgumentState<A> arg = new ArgumentState<>(argCls);
+
+            visitCommandParams(
+                argCls,
+                fld -> arg.accept(fld, positionalParamProvider.apply(fld, arg.nextIdx())),
+                fld -> arg.accept(fld, paramProvider.apply(fld)),
+                (argGrp, flds) -> flds.forEach(fld -> {
+                    if (fld.isAnnotationPresent(Positional.class))
+                        arg.accept(fld, positionalParamProvider.apply(fld, arg.nextIdx()));
+                    else
+                        arg.accept(fld, paramProvider.apply(fld));
+                })
+            );
+
+            if (arg.argGrp != null && (!arg.grpOptional() && !arg.grpFldExists))
+                throw new IllegalArgumentException("One of " + toFormattedNames(argCls, arg.grpdFlds) + " required");
+
+            return arg.res;
+        }
+        catch (InstantiationException | IllegalAccessException e) {
+            throw new IgniteException(e);
+        }
+    }
+
+    /** */
+    public static <A, R> R execute(
+        @Nullable GridClient cli,
+        @Nullable Ignite ignite,
+        Class<? extends ComputeTask<VisorTaskArgument<A>, R>> taskCls,
+        A arg,
+        Collection<GridClientNode> nodes
+    ) throws GridClientException {
+        Collection<UUID> nodesIds = nodes.stream()
+            .map(GridClientNode::nodeId)
+            .collect(Collectors.toList());
+
+        if (cli != null) {
+            GridClientCompute compute = cli.compute();
+
+            Collection<GridClientNode> connectable = compute.nodes().stream()
+                .filter(nodes::contains)
+                .filter(GridClientNode::connectable)
+                .collect(Collectors.toList());
+
+            if (!connectable.isEmpty())
+                compute = compute.projection(nodes);
+
+            return compute.execute(
+                taskCls.getName(),
+                new VisorTaskArgument<>(nodesIds, arg, false)
+            );
+        }
+
+        return ignite
+            .compute(ignite.cluster())
+            .execute(taskCls, new VisorTaskArgument<>(nodesIds, arg, false));
+    }
+
+    /** */
+    private static class ArgumentState<A extends IgniteDataTransferObject> implements BiConsumer<Field, Object> {
+        /** */
+        final A res;
+
+        /** */
+        final ArgumentGroup argGrp;
+
+        /** */
+        boolean grpFldExists;
+
+        /** */
+        int idx;
+
+        /** */
+        final Set<String> grpdFlds;
+
+        /** */
+        public ArgumentState(Class<A> argCls) throws InstantiationException, IllegalAccessException {
+            res = argCls.newInstance();
+            argGrp = argCls.getAnnotation(ArgumentGroup.class);
+            grpdFlds = argGrp == null
+                ? Collections.emptySet()
+                : new HashSet<>(Arrays.asList(argGrp.value()));
+        }
+
+        /** */
+        public boolean grpOptional() {
+            return argGrp == null || argGrp.optional();
+        }
+
+        /** */
+        private int nextIdx() {
+            int idx0 = idx;
+
+            idx++;
+
+            return idx0;
+        }
+
+        /** {@inheritDoc} */
+        @Override public void accept(Field fld, Object val) {
+            boolean grpdFld = grpdFlds.contains(fld.getName());
+
+            if (val == null) {
+                if (grpdFld || fld.getAnnotation(Argument.class).optional())
+                    return;
+
+                String name = fld.isAnnotationPresent(Positional.class)
+                    ? parameterExample(fld, false)
+                    : toFormattedFieldName(fld);
+
+                throw new IllegalArgumentException("Argument " + name + " required.");
+            }
+
+            if (Objects.equals(val, get(fld)))
+                return;
+
+            if (grpdFld) {
+                if (grpFldExists && (argGrp != null && argGrp.onlyOneOf())) {
+                    throw new IllegalArgumentException(
+                        "Only one of " + toFormattedNames(res.getClass(), grpdFlds) + " allowed"
+                    );
+                }
+
+                grpFldExists = true;
+            }
+
+            set(fld, val);
+        }
+
+        /** */
+        private Object get(Field fld) {
+            try {
+                return res.getClass().getMethod(fld.getName()).invoke(res);
+            }
+            catch (NoSuchMethodException | IllegalAccessException e) {
+                throw new IgniteException(e);
+            }
+            catch (InvocationTargetException e) {
+                if (e.getTargetException() != null && e.getTargetException() instanceof RuntimeException)
+                    throw (RuntimeException)e.getTargetException();
+
+                throw new IgniteException(e);
+            }
+        }
+
+        /** */
+        private void set(Field fld, Object val) {
+            try {
+                res.getClass().getMethod(fld.getName(), fld.getType()).invoke(res, val);
+            }
+            catch (NoSuchMethodException | IllegalAccessException e) {
+                throw new IgniteException(e);
+            }
+            catch (InvocationTargetException e) {
+                if (e.getTargetException() != null && e.getTargetException() instanceof RuntimeException)
+                    throw (RuntimeException)e.getTargetException();
+
+                throw new IgniteException(e);
+            }
+        }
+    }
+
+    /** */
+    public static GridClientNode clusterToClientNode(ClusterNode n) {
+        return new GridClientNode() {
+            @Override public UUID nodeId() {
+                return n.id();
+            }
+
+            @Override public Object consistentId() {
+                return n.consistentId();
+            }
+
+            @Override public boolean connectable() {
+                return true;
+            }
+
+            @Override public long order() {
+                return n.order();
+            }
+
+            @Override public boolean isClient() {
+                return n.isClient();
+            }
+
+            @Override public List<String> tcpAddresses() {
+                return U.arrayList(n.addresses());
+            }
+
+            @Override public List<String> tcpHostNames() {
+                return U.arrayList(n.hostNames());
+            }
+
+            @Override public int tcpPort() {
+                return -1;
+            }
+
+            @Override public Map<String, Object> attributes() {
+                return n.attributes();
+            }
+
+            @Override public <T> @Nullable T attribute(String name) {
+                return n.attribute(name);
+            }
+
+            @Override public GridClientNodeMetrics metrics() {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override public Map<String, GridClientCacheMode> caches() {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override public Collection<InetSocketAddress> availableAddresses(GridClientProtocol proto, boolean filterResolved) {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/api/ComputeCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/api/ComputeCommand.java
index d40e5d9a549..26da81496a8 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/api/ComputeCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/api/ComputeCommand.java
@@ -18,8 +18,6 @@
 package org.apache.ignite.internal.management.api;
 
 import java.util.Collection;
-import java.util.Map;
-import java.util.UUID;
 import java.util.function.Consumer;
 import org.apache.ignite.compute.ComputeTask;
 import org.apache.ignite.internal.client.GridClientNode;
@@ -49,7 +47,7 @@ public interface ComputeCommand<A extends IgniteDataTransferObject, R> extends C
      * @param arg Argument.
      * @return nodes to execute command on, {@code null} means default node must be used.
      */
-    public default @Nullable Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, A arg) {
+    public default @Nullable Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, A arg) {
         return null;
     }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/api/LocalCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/api/LocalCommand.java
index fc32e932479..d8f3976660e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/api/LocalCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/api/LocalCommand.java
@@ -18,18 +18,31 @@
 package org.apache.ignite.internal.management.api;
 
 import java.util.function.Consumer;
+import org.apache.ignite.Ignite;
 import org.apache.ignite.internal.client.GridClient;
+import org.apache.ignite.internal.client.GridClientException;
 import org.apache.ignite.internal.dto.IgniteDataTransferObject;
+import org.jetbrains.annotations.Nullable;
 
 /**
- * Command that must be executed directly using {@link GridClient} instance.
+ * Command that must be executed directly using {@link GridClient} or {@link Ignite} instance.
  */
 public interface LocalCommand<A extends IgniteDataTransferObject, R> extends Command<A, R> {
     /**
-     * @param cli Grid client instance.
+     * Executes command logic.
+     * Commands invoked in two different environments:
+     * <ul>
+     *     <li>CLI utility using {@link GridClient} connection to interact with cluster.</li>
+     *     <li>Inside {@link Ignite} node therefore local node can be used to interact with cluster.</li>
+     * </ul>
+     *
+     * Please, keep in mind that {@link GridClient} protocol is obsolete and can be removed in future releases.
+     *
+     * @param cli Optional grid client instance.
+     * @param ignite Optional ignite node.
      * @param arg Command argument.
      * @param printer Results printer.
      * @return Command result.
      */
-    public R execute(GridClient cli, A arg, Consumer<String> printer) throws Exception;
+    public R execute(@Nullable GridClient cli, @Nullable Ignite ignite, A arg, Consumer<String> printer) throws GridClientException;
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/api/PreparableCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/api/PreparableCommand.java
index ae59d8e6dec..14ff4d0e8c4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/api/PreparableCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/api/PreparableCommand.java
@@ -18,9 +18,11 @@
 package org.apache.ignite.internal.management.api;
 
 import java.util.function.Consumer;
+import org.apache.ignite.Ignite;
 import org.apache.ignite.internal.client.GridClient;
 import org.apache.ignite.internal.client.GridClientException;
 import org.apache.ignite.internal.dto.IgniteDataTransferObject;
+import org.jetbrains.annotations.Nullable;
 
 /** */
 public interface PreparableCommand<A extends IgniteDataTransferObject, R> extends Command<A, R> {
@@ -31,7 +33,6 @@ public interface PreparableCommand<A extends IgniteDataTransferObject, R> extend
      * @param arg Command argument.
      * @param printer Implementation specific printer.
      * @return {@code True} if command must be executed, {@code false} otherwise.
-     * @throws GridClientException If failed.
      */
-    public boolean prepare(GridClient cli, A arg, Consumer<String> printer) throws GridClientException;
+    public boolean prepare(@Nullable GridClient cli, @Nullable Ignite ignite, A arg, Consumer<String> printer) throws GridClientException;
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/baseline/AbstractBaselineCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/baseline/AbstractBaselineCommand.java
index 1c90f4e9911..227873d23df 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/baseline/AbstractBaselineCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/baseline/AbstractBaselineCommand.java
@@ -24,7 +24,6 @@ import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
-import java.util.UUID;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.stream.Collectors;
@@ -48,7 +47,7 @@ public abstract class AbstractBaselineCommand implements ComputeCommand<Baseline
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, BaselineTaskArg arg) {
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, BaselineTaskArg arg) {
         return coordinatorOrNull(nodes);
     }
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/baseline/BaselineCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/baseline/BaselineCommand.java
index b01382507a0..d145ccaa3a2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/baseline/BaselineCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/baseline/BaselineCommand.java
@@ -21,8 +21,6 @@ import java.io.IOException;
 import java.io.ObjectInput;
 import java.io.ObjectOutput;
 import java.util.Collection;
-import java.util.Map;
-import java.util.UUID;
 import java.util.function.Consumer;
 import org.apache.ignite.internal.client.GridClientNode;
 import org.apache.ignite.internal.dto.IgniteDataTransferObject;
@@ -63,7 +61,7 @@ public class BaselineCommand extends CommandRegistryImpl<BaselineCommand.Baselin
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, BaselineTaskArg arg) {
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, BaselineTaskArg arg) {
         return coordinatorOrNull(nodes);
     }
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheCheckIndexInlineSizesCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheCheckIndexInlineSizesCommand.java
index 8bb8a83b91c..600c1db8251 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheCheckIndexInlineSizesCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheCheckIndexInlineSizesCommand.java
@@ -60,7 +60,7 @@ public class CacheCheckIndexInlineSizesCommand
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, NoArg arg) {
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, NoArg arg) {
         return servers(nodes);
     }
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheContentionCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheContentionCommand.java
index 92c92de1e4e..a25bc4830ac 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheContentionCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheContentionCommand.java
@@ -18,9 +18,6 @@
 package org.apache.ignite.internal.management.cache;
 
 import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.UUID;
 import java.util.function.Consumer;
 import org.apache.ignite.internal.client.GridClientNode;
 import org.apache.ignite.internal.management.api.CommandUtils;
@@ -44,8 +41,8 @@ public class CacheContentionCommand implements ComputeCommand<CacheContentionCom
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, CacheContentionCommandArg arg) {
-        return arg.nodeId() == null ? nodes.keySet() : Collections.singleton(arg.nodeId());
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, CacheContentionCommandArg arg) {
+        return CommandUtils.nodeOrAll(arg.nodeId(), nodes);
     }
 
     /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheDestroyCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheDestroyCommand.java
index 65acae9a306..6853adc80a9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheDestroyCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheDestroyCommand.java
@@ -21,6 +21,7 @@ import java.util.Arrays;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.function.Consumer;
+import org.apache.ignite.Ignite;
 import org.apache.ignite.internal.client.GridClient;
 import org.apache.ignite.internal.client.GridClientException;
 import org.apache.ignite.internal.client.GridClientNode;
@@ -29,6 +30,7 @@ import org.apache.ignite.internal.management.api.PreparableCommand;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
+import org.jetbrains.annotations.Nullable;
 
 /** Destroy caches. */
 public class CacheDestroyCommand
@@ -49,12 +51,21 @@ public class CacheDestroyCommand
     }
 
     /** {@inheritDoc} */
-    @Override public boolean prepare(GridClient cli, CacheDestroyCommandArg arg, Consumer<String> printer) throws GridClientException {
+    @Override public boolean prepare(
+        @Nullable GridClient cli,
+        @Nullable Ignite ignite,
+        CacheDestroyCommandArg arg,
+        Consumer<String> printer
+    ) throws GridClientException {
         if (arg.destroyAllCaches()) {
             Set<String> caches = new TreeSet<>();
 
-            for (GridClientNode node : cli.compute().nodes(GridClientNode::connectable))
-                caches.addAll(node.caches().keySet());
+            if (cli != null) {
+                for (GridClientNode node : cli.compute().nodes(GridClientNode::connectable))
+                    caches.addAll(node.caches().keySet());
+            }
+            else
+                caches.addAll(ignite.cacheNames());
 
             arg.caches(caches.toArray(U.EMPTY_STRS));
         }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheDistributionCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheDistributionCommand.java
index 2da416fd3cd..c64ce00ecab 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheDistributionCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheDistributionCommand.java
@@ -18,9 +18,6 @@
 package org.apache.ignite.internal.management.cache;
 
 import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.UUID;
 import java.util.function.Consumer;
 import org.apache.ignite.internal.client.GridClientNode;
 import org.apache.ignite.internal.commandline.cache.distribution.CacheDistributionTask;
@@ -47,10 +44,8 @@ public class CacheDistributionCommand
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, CacheDistributionCommandArg arg) {
-        return arg.nodeId() != null
-            ? Collections.singleton(arg.nodeId())
-            : nodes.keySet();
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, CacheDistributionCommandArg arg) {
+        return CommandUtils.nodeOrAll(arg.nodeId(), nodes);
     }
 
     /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheIndexesForceRebuildCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheIndexesForceRebuildCommand.java
index a504f9e3009..575e1e0d085 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheIndexesForceRebuildCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheIndexesForceRebuildCommand.java
@@ -18,11 +18,10 @@
 package org.apache.ignite.internal.management.cache;
 
 import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.UUID;
+import java.util.List;
 import java.util.function.Consumer;
 import org.apache.ignite.internal.client.GridClientNode;
+import org.apache.ignite.internal.management.api.CommandUtils;
 import org.apache.ignite.internal.management.api.ComputeCommand;
 import org.apache.ignite.internal.util.typedef.F;
 
@@ -46,14 +45,14 @@ public class CacheIndexesForceRebuildCommand implements ComputeCommand<CacheInde
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, CacheIndexesForceRebuildCommandArg arg) {
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, CacheIndexesForceRebuildCommandArg arg) {
         if (arg.nodeId() != null) {
-            GridClientNode node = nodes.get(arg.nodeId());
+            List<GridClientNode> node = CommandUtils.node(arg.nodeId(), nodes);
 
-            if (node != null && node.isClient())
+            if (node.get(0) != null && node.get(0).isClient())
                 throw new IllegalArgumentException("Please, specify server node id");
 
-            return Collections.singleton(arg.nodeId());
+            return node;
         }
 
         return null;
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheIndexesListCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheIndexesListCommand.java
index 2f02ee4f1b7..097b5c914e5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheIndexesListCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheIndexesListCommand.java
@@ -19,13 +19,11 @@ package org.apache.ignite.internal.management.cache;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
-import java.util.UUID;
 import java.util.function.Consumer;
 import org.apache.ignite.internal.client.GridClientNode;
+import org.apache.ignite.internal.management.api.CommandUtils;
 import org.apache.ignite.internal.management.api.ComputeCommand;
 
 /** Index list. */
@@ -46,8 +44,8 @@ public class CacheIndexesListCommand implements ComputeCommand<CacheIndexesListC
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, CacheIndexesListCommandArg arg) {
-        return arg.nodeId() != null ? Collections.singleton(arg.nodeId()) : null;
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, CacheIndexesListCommandArg arg) {
+        return CommandUtils.nodeOrNull(arg.nodeId(), nodes);
     }
 
     /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheIndexesRebuildStatusCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheIndexesRebuildStatusCommand.java
index b2fcb835727..a130fa5e660 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheIndexesRebuildStatusCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheIndexesRebuildStatusCommand.java
@@ -18,12 +18,12 @@
 package org.apache.ignite.internal.management.cache;
 
 import java.util.Collection;
-import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
 import java.util.function.Consumer;
 import org.apache.ignite.internal.client.GridClientNode;
+import org.apache.ignite.internal.management.api.CommandUtils;
 import org.apache.ignite.internal.management.api.ComputeCommand;
 
 /** Index rebuild status. */
@@ -45,10 +45,8 @@ public class CacheIndexesRebuildStatusCommand
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, CacheIndexesRebuildStatusCommandArg arg) {
-        return arg.nodeId() != null
-            ? Collections.singleton(arg.nodeId())
-            : nodes.keySet();
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, CacheIndexesRebuildStatusCommandArg arg) {
+        return CommandUtils.nodeOrAll(arg.nodeId(), nodes);
     }
 
     /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheListCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheListCommand.java
index 6826c179c6e..9b8f692b794 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheListCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheListCommand.java
@@ -18,7 +18,6 @@
 package org.apache.ignite.internal.management.cache;
 
 import java.util.Collection;
-import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Objects;
@@ -27,13 +26,17 @@ import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
+import org.apache.ignite.Ignite;
 import org.apache.ignite.internal.client.GridClient;
 import org.apache.ignite.internal.client.GridClientException;
 import org.apache.ignite.internal.client.GridClientNode;
+import org.apache.ignite.internal.management.api.CommandUtils;
 import org.apache.ignite.internal.management.api.LocalCommand;
 import org.apache.ignite.internal.util.typedef.internal.SB;
-import org.apache.ignite.internal.visor.VisorTaskArgument;
+import org.jetbrains.annotations.Nullable;
 
+import static java.util.Collections.singletonList;
+import static org.apache.ignite.internal.management.api.CommandUtils.nodes;
 import static org.apache.ignite.internal.management.cache.ViewCacheCmd.CACHES;
 import static org.apache.ignite.internal.management.cache.ViewCacheCmd.GROUPS;
 import static org.apache.ignite.internal.management.cache.ViewCacheCmd.SEQ;
@@ -57,15 +60,16 @@ public class CacheListCommand implements LocalCommand<CacheListCommandArg, ViewC
 
     /** {@inheritDoc} */
     @Override public ViewCacheTaskResult execute(
-        GridClient cli,
+        @Nullable GridClient cli,
+        @Nullable Ignite ignite,
         CacheListCommandArg arg,
         Consumer<String> printer
-    ) throws Exception {
+    ) throws GridClientException {
         ViewCacheCmd cmd = arg.groups()
             ? GROUPS
             : (arg.seq() ? SEQ : CACHES);
 
-        Optional<GridClientNode> node = cli.compute().nodes()
+        Optional<GridClientNode> node = nodes(cli, ignite)
             .stream()
             .filter(FILTER.apply(arg))
             .findFirst();
@@ -73,13 +77,10 @@ public class CacheListCommand implements LocalCommand<CacheListCommandArg, ViewC
         if (!node.isPresent())
             throw new IllegalArgumentException("Node not found: id=" + arg.nodeId());
 
-        ViewCacheTaskResult res = cli.compute().projection(node.get()).execute(
-            ViewCacheTask.class.getName(),
-            new VisorTaskArgument<>(Collections.singleton(node.get().nodeId()), arg, false)
-        );
+        ViewCacheTaskResult res = CommandUtils.execute(cli, ignite, ViewCacheTask.class, arg, singletonList(node.get()));
 
         if (arg.config() && cmd == CACHES)
-            cachesConfig(cli, arg, res, printer);
+            cachesConfig(cli, ignite, arg, res, printer);
         else
             printCacheInfos(res.cacheInfos(), cmd, printer);
 
@@ -93,22 +94,22 @@ public class CacheListCommand implements LocalCommand<CacheListCommandArg, ViewC
      */
     private void cachesConfig(
         GridClient cli,
+        Ignite ignite,
         CacheListCommandArg arg,
         ViewCacheTaskResult viewRes,
         Consumer<String> printer
     ) throws GridClientException {
-        Collection<GridClientNode> nodes = cli.compute().nodes()
+        Collection<GridClientNode> nodes = nodes(cli, ignite)
             .stream()
             .filter(FILTER.apply(arg))
             .collect(Collectors.toSet());
 
-        Map<String, CacheConfiguration> res = cli.compute().projection(nodes).execute(
-            CacheConfigurationCollectorTask.class.getName(),
-            new VisorTaskArgument<>(
-                nodes.stream().map(GridClientNode::nodeId).collect(Collectors.toSet()),
-                new CacheConfigurationCollectorTaskArg(arg.regex()),
-                false
-            )
+        Map<String, CacheConfiguration> res = CommandUtils.execute(
+            cli,
+            ignite,
+            CacheConfigurationCollectorTask.class,
+            new CacheConfigurationCollectorTaskArg(arg.regex()),
+            nodes
         );
 
         Map<String, Integer> cacheToMapped =
@@ -163,10 +164,10 @@ public class CacheListCommand implements LocalCommand<CacheListCommandArg, ViewC
 
                     params.put("Mapped", cacheToMapped.get(cacheName));
 
-                    printer.accept(String.format("[cache = '%s']%n", cacheName));
+                    printer.accept(String.format("[cache = '%s']", cacheName));
 
                     for (Map.Entry<String, Object> innerEntry : params.entrySet())
-                        printer.accept(String.format("%s: %s%n", innerEntry.getKey(), innerEntry.getValue()));
+                        printer.accept(String.format("%s: %s", innerEntry.getKey(), innerEntry.getValue()));
 
                     printer.accept("");
 
@@ -175,7 +176,7 @@ public class CacheListCommand implements LocalCommand<CacheListCommandArg, ViewC
                 default:
                     int mapped = cacheToMapped.get(cacheName);
 
-                    printer.accept(String.format("%s: %s %s=%s%n", entry.getKey(), toString(entry.getValue()), "mapped", mapped));
+                    printer.accept(String.format("%s: %s %s=%s", entry.getKey(), toString(entry.getValue()), "mapped", mapped));
 
                     break;
             }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheScheduleIndexesRebuildCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheScheduleIndexesRebuildCommand.java
index 9912087b39b..ccf845786f8 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheScheduleIndexesRebuildCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheScheduleIndexesRebuildCommand.java
@@ -18,16 +18,15 @@
 package org.apache.ignite.internal.management.cache;
 
 import java.util.Collection;
-import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
-import java.util.UUID;
 import java.util.function.Consumer;
 import org.apache.ignite.internal.client.GridClientNode;
 import org.apache.ignite.internal.management.api.ComputeCommand;
 import org.apache.ignite.internal.util.typedef.F;
 
 import static org.apache.ignite.internal.management.api.CommandUtils.INDENT;
+import static org.apache.ignite.internal.management.api.CommandUtils.nodeOrAll;
 
 /** Index rebuild via the maintenance mode. */
 public class CacheScheduleIndexesRebuildCommand
@@ -49,13 +48,8 @@ public class CacheScheduleIndexesRebuildCommand
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(
-        Map<UUID, GridClientNode> nodes,
-        CacheScheduleIndexesRebuildCommandArg arg
-    ) {
-        return arg.nodeId() != null
-            ? Collections.singleton(arg.nodeId())
-            : nodes.keySet();
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, CacheScheduleIndexesRebuildCommandArg arg) {
+        return nodeOrAll(arg.nodeId(), nodes);
     }
 
     /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheValidateIndexesCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheValidateIndexesCommand.java
index d953f69969e..525ddb244f2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheValidateIndexesCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheValidateIndexesCommand.java
@@ -17,7 +17,6 @@
 
 package org.apache.ignite.internal.management.cache;
 
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Map;
 import java.util.UUID;
@@ -25,6 +24,7 @@ import java.util.function.Consumer;
 import org.apache.ignite.internal.client.GridClientNode;
 import org.apache.ignite.internal.management.api.CommandUtils;
 import org.apache.ignite.internal.management.api.ComputeCommand;
+import org.apache.ignite.internal.util.typedef.F;
 
 import static org.apache.ignite.internal.management.api.CommandUtils.DOUBLE_INDENT;
 import static org.apache.ignite.internal.management.api.CommandUtils.INDENT;
@@ -51,8 +51,11 @@ public class CacheValidateIndexesCommand
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, CacheValidateIndexesCommandArg arg) {
-        return arg.nodeIds() != null ? Arrays.asList(arg.nodeIds()) : null;
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, CacheValidateIndexesCommandArg arg) {
+        if (F.isEmpty(arg.nodeIds()))
+            return null;
+
+        return CommandUtils.nodes(arg.nodeIds(), nodes);
     }
 
     /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheValidateIndexesCommandArg.java b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheValidateIndexesCommandArg.java
index b84f5b5e8f3..971878a5ca7 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheValidateIndexesCommandArg.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheValidateIndexesCommandArg.java
@@ -159,6 +159,9 @@ public class CacheValidateIndexesCommandArg extends IgniteDataTransferObject {
 
     /** */
     public void checkFirst(int checkFirst) {
+        if (this.checkFirst == checkFirst)
+            return;
+
         ensurePositive(checkFirst, "--check-first");
 
         this.checkFirst = checkFirst;
@@ -171,6 +174,9 @@ public class CacheValidateIndexesCommandArg extends IgniteDataTransferObject {
 
     /** */
     public void checkThrough(int checkThrough) {
+        if (this.checkThrough == checkThrough)
+            return;
+
         ensurePositive(checkThrough, "--check-through");
 
         this.checkThrough = checkThrough;
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/cdc/CdcDeleteLostSegmentLinksCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/cdc/CdcDeleteLostSegmentLinksCommand.java
index 9da6a086363..247a4418632 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/cdc/CdcDeleteLostSegmentLinksCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/cdc/CdcDeleteLostSegmentLinksCommand.java
@@ -18,15 +18,13 @@
 package org.apache.ignite.internal.management.cdc;
 
 import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.UUID;
 import java.util.function.Consumer;
 import org.apache.ignite.internal.client.GridClientNode;
 import org.apache.ignite.internal.management.api.ComputeCommand;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgniteExperimental;
 
+import static org.apache.ignite.internal.management.api.CommandUtils.node;
 import static org.apache.ignite.internal.management.api.CommandUtils.servers;
 
 /**
@@ -50,9 +48,9 @@ public class CdcDeleteLostSegmentLinksCommand implements ComputeCommand<CdcDelet
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, CdcDeleteLostSegmentLinksCommandArg arg) {
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, CdcDeleteLostSegmentLinksCommandArg arg) {
         return arg.nodeId() != null
-            ? Collections.singleton(arg.nodeId())
+            ? node(arg.nodeId(), nodes)
             : servers(nodes);
     }
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/consistency/ConsistencyRepairCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/consistency/ConsistencyRepairCommand.java
index 02ea7a2f1b7..a6bdac934b7 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/consistency/ConsistencyRepairCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/consistency/ConsistencyRepairCommand.java
@@ -21,16 +21,18 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.Set;
 import java.util.function.Consumer;
-import java.util.stream.Collectors;
-import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteException;
 import org.apache.ignite.internal.client.GridClient;
 import org.apache.ignite.internal.client.GridClientException;
 import org.apache.ignite.internal.client.GridClientNode;
+import org.apache.ignite.internal.management.api.CommandUtils;
 import org.apache.ignite.internal.management.api.LocalCommand;
-import org.apache.ignite.internal.visor.VisorTaskArgument;
 import org.apache.ignite.lang.IgniteExperimental;
+import org.jetbrains.annotations.Nullable;
 
 import static java.util.stream.Collectors.toSet;
+import static org.apache.ignite.internal.management.api.CommandUtils.nodes;
 
 /** */
 @IgniteExperimental
@@ -47,22 +49,23 @@ public class ConsistencyRepairCommand implements LocalCommand<ConsistencyRepairC
 
     /** {@inheritDoc} */
     @Override public String execute(
-        GridClient cli,
+        @Nullable GridClient cli,
+        @Nullable Ignite ignite,
         ConsistencyRepairCommandArg arg,
         Consumer<String> printer
-    ) throws Exception {
+    ) throws GridClientException, IgniteException {
         StringBuilder sb = new StringBuilder();
         boolean failed = false;
 
         if (arg.parallel())
-            failed = execute(cli, arg, cli.compute().nodes(GridClientNode::connectable), sb);
+            failed = execute(cli, ignite, arg, nodes(cli, ignite), sb);
         else {
-            Set<GridClientNode> nodes = cli.compute().nodes().stream()
+            Set<GridClientNode> nodes = nodes(cli, ignite).stream()
                 .filter(node -> !node.isClient())
                 .collect(toSet());
 
             for (GridClientNode node : nodes) {
-                failed = execute(cli, arg, Collections.singleton(node), sb);
+                failed = execute(cli, ignite, arg, Collections.singleton(node), sb);
 
                 if (failed)
                     break;
@@ -72,7 +75,7 @@ public class ConsistencyRepairCommand implements LocalCommand<ConsistencyRepairC
         String res = sb.toString();
 
         if (failed)
-            throw new IgniteCheckedException(res);
+            throw new IgniteException(res);
 
         printer.accept(res);
 
@@ -81,16 +84,20 @@ public class ConsistencyRepairCommand implements LocalCommand<ConsistencyRepairC
 
     /** */
     private boolean execute(
-        GridClient cli,
+        @Nullable GridClient cli,
+        @Nullable Ignite ignite,
         ConsistencyRepairCommandArg arg,
         Collection<GridClientNode> nodes,
         StringBuilder sb
     ) throws GridClientException {
         boolean failed = false;
 
-        ConsistencyTaskResult res = cli.compute().projection(nodes).execute(
-            ConsistencyRepairTask.class.getName(),
-            new VisorTaskArgument<>(nodes.stream().map(GridClientNode::nodeId).collect(Collectors.toList()), arg, false)
+        ConsistencyTaskResult res = CommandUtils.execute(
+            cli,
+            ignite,
+            ConsistencyRepairTask.class,
+            arg,
+            nodes
         );
 
         if (res.cancelled()) {
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/consistency/ConsistencyStatusCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/consistency/ConsistencyStatusCommand.java
index a8834c016f1..d205335c126 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/consistency/ConsistencyStatusCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/consistency/ConsistencyStatusCommand.java
@@ -18,8 +18,6 @@
 package org.apache.ignite.internal.management.consistency;
 
 import java.util.Collection;
-import java.util.Map;
-import java.util.UUID;
 import java.util.function.Consumer;
 import org.apache.ignite.internal.client.GridClientNode;
 import org.apache.ignite.internal.management.api.ComputeCommand;
@@ -47,7 +45,7 @@ public class ConsistencyStatusCommand implements ComputeCommand<NoArg, Consisten
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, NoArg arg) {
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, NoArg arg) {
         return servers(nodes);
     }
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/defragmentation/DefragmentationScheduleCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/defragmentation/DefragmentationScheduleCommand.java
index 2b4614f544d..a587e5e42b5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/defragmentation/DefragmentationScheduleCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/defragmentation/DefragmentationScheduleCommand.java
@@ -20,10 +20,8 @@ package org.apache.ignite.internal.management.defragmentation;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
-import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
-import java.util.UUID;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import org.apache.ignite.internal.client.GridClientNode;
@@ -59,7 +57,7 @@ public class DefragmentationScheduleCommand
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, DefragmentationStatusCommandArg arg0) {
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, DefragmentationStatusCommandArg arg0) {
         DefragmentationScheduleCommandArg arg = (DefragmentationScheduleCommandArg)arg0;
 
         if (F.isEmpty(arg.nodes()))
@@ -67,9 +65,8 @@ public class DefragmentationScheduleCommand
 
         Set<String> nodesArg = new HashSet<>(Arrays.asList(arg.nodes()));
 
-        return nodes.entrySet().stream()
-            .filter(e -> nodesArg.contains(Objects.toString(e.getValue().consistentId())))
-            .map(Map.Entry::getKey)
+        return nodes.stream()
+            .filter(n -> nodesArg.contains(Objects.toString(n.consistentId())))
             .collect(Collectors.toList());
     }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/diagnostic/DiagnosticConnectivityCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/diagnostic/DiagnosticConnectivityCommand.java
index d6f1ecf5713..159fe151dcc 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/diagnostic/DiagnosticConnectivityCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/diagnostic/DiagnosticConnectivityCommand.java
@@ -23,7 +23,6 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.UUID;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import org.apache.ignite.cluster.ClusterNode;
@@ -74,9 +73,9 @@ public class DiagnosticConnectivityCommand
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, DiagnosticConnectivityCommandArg arg) {
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, DiagnosticConnectivityCommandArg arg) {
         // Task runs on default node but maps to all nodes in cluster.
-        arg.nodes(nodes.keySet().toArray(EMPTY_UUIDS));
+        arg.nodes(nodes.stream().map(GridClientNode::nodeId).collect(Collectors.toList()).toArray(EMPTY_UUIDS));
 
         return null;
     }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/diagnostic/DiagnosticPagelocksCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/diagnostic/DiagnosticPagelocksCommand.java
index 0907b0ef361..c580ac74f8f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/diagnostic/DiagnosticPagelocksCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/diagnostic/DiagnosticPagelocksCommand.java
@@ -22,7 +22,6 @@ import java.util.Collection;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
-import java.util.UUID;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import org.apache.ignite.cluster.ClusterNode;
@@ -48,19 +47,18 @@ public class DiagnosticPagelocksCommand implements ComputeCommand<DiagnosticPage
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, DiagnosticPagelocksCommandArg arg) {
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, DiagnosticPagelocksCommandArg arg) {
         if (arg.all())
-            return nodes.keySet();
+            return nodes;
 
         if (F.isEmpty(arg.nodes()))
             return null;
 
         Set<String> argNodes = new HashSet<>(Arrays.asList(arg.nodes()));
 
-        return nodes.entrySet().stream()
-            .filter(entry -> argNodes.contains(entry.getKey().toString())
-                || argNodes.contains(String.valueOf(entry.getValue().consistentId())))
-            .map(Map.Entry::getKey)
+        return nodes.stream()
+            .filter(entry -> argNodes.contains(entry.nodeId().toString())
+                || argNodes.contains(String.valueOf(entry.consistentId())))
             .collect(Collectors.toList());
     }
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/encryption/CacheGroupEncryptionCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/encryption/CacheGroupEncryptionCommand.java
index 3584ff08c1f..fa7de2686eb 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/encryption/CacheGroupEncryptionCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/encryption/CacheGroupEncryptionCommand.java
@@ -56,8 +56,8 @@ abstract class CacheGroupEncryptionCommand<T>
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, EncryptionCacheGroupArg arg) {
-        return nodes.keySet();
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, EncryptionCacheGroupArg arg) {
+        return nodes;
     }
 
     /**
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/encryption/EncryptionReencryptionRateLimitCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/encryption/EncryptionReencryptionRateLimitCommand.java
index 771774c365a..924686303c1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/encryption/EncryptionReencryptionRateLimitCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/encryption/EncryptionReencryptionRateLimitCommand.java
@@ -47,8 +47,8 @@ public class EncryptionReencryptionRateLimitCommand
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, EncryptionReencryptionRateLimitCommandArg arg) {
-        return nodes.keySet();
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, EncryptionReencryptionRateLimitCommandArg arg) {
+        return nodes;
     }
 
     /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/kill/KillClientCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/kill/KillClientCommand.java
index 7802e54c672..58aadb1cab5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/kill/KillClientCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/kill/KillClientCommand.java
@@ -18,10 +18,8 @@
 package org.apache.ignite.internal.management.kill;
 
 import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.UUID;
 import org.apache.ignite.internal.client.GridClientNode;
+import org.apache.ignite.internal.management.api.CommandUtils;
 import org.apache.ignite.internal.management.api.ComputeCommand;
 
 /** */
@@ -42,10 +40,7 @@ public class KillClientCommand implements ComputeCommand<KillClientCommandArg, V
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, KillClientCommandArg arg) {
-        if (arg.nodeId() == null)
-            return nodes.keySet();
-
-        return Collections.singleton(arg.nodeId());
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, KillClientCommandArg arg) {
+        return CommandUtils.nodeOrAll(arg.nodeId(), nodes);
     }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/kill/KillConsistencyCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/kill/KillConsistencyCommand.java
index 5c69caa642f..f8c84c78597 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/kill/KillConsistencyCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/kill/KillConsistencyCommand.java
@@ -18,8 +18,6 @@
 package org.apache.ignite.internal.management.kill;
 
 import java.util.Collection;
-import java.util.Map;
-import java.util.UUID;
 import org.apache.ignite.internal.client.GridClientNode;
 import org.apache.ignite.internal.management.api.ComputeCommand;
 import org.apache.ignite.internal.management.api.NoArg;
@@ -42,7 +40,7 @@ public class KillConsistencyCommand implements ComputeCommand<NoArg, Void> {
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, NoArg arg) {
-        return nodes.keySet();
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, NoArg arg) {
+        return nodes;
     }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/kill/KillTransactionCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/kill/KillTransactionCommand.java
index bcd9df9f639..474f6820289 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/kill/KillTransactionCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/kill/KillTransactionCommand.java
@@ -19,7 +19,6 @@ package org.apache.ignite.internal.management.kill;
 
 import java.util.Collection;
 import java.util.Map;
-import java.util.UUID;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.internal.client.GridClientNode;
 import org.apache.ignite.internal.management.api.ComputeCommand;
@@ -44,7 +43,7 @@ public class KillTransactionCommand
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, KillTransactionCommandArg arg) {
-        return nodes.keySet();
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, KillTransactionCommandArg arg) {
+        return nodes;
     }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricCommand.java
index 76a921337ae..bbdebca900c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricCommand.java
@@ -21,7 +21,6 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
-import java.util.UUID;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import org.apache.ignite.internal.client.GridClientNode;
@@ -62,8 +61,8 @@ public class MetricCommand extends CommandRegistryImpl<MetricCommandArg, Map<Str
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, MetricCommandArg arg) {
-        return nodeOrNull(arg.nodeId());
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, MetricCommandArg arg) {
+        return nodeOrNull(arg.nodeId(), nodes);
     }
 
     /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricConfigureHistogramCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricConfigureHistogramCommand.java
index d5cc8aa6d91..64edd68ea9a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricConfigureHistogramCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricConfigureHistogramCommand.java
@@ -19,7 +19,6 @@ package org.apache.ignite.internal.management.metric;
 
 import java.util.Collection;
 import java.util.Map;
-import java.util.UUID;
 import org.apache.ignite.internal.client.GridClientNode;
 import org.apache.ignite.internal.management.api.ComputeCommand;
 
@@ -43,7 +42,7 @@ public class MetricConfigureHistogramCommand implements ComputeCommand<MetricCom
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, MetricCommandArg arg) {
-        return nodeOrNull(arg.nodeId());
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, MetricCommandArg arg) {
+        return nodeOrNull(arg.nodeId(), nodes);
     }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricConfigureHitrateCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricConfigureHitrateCommand.java
index d97e7b34528..cd1e717a9db 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricConfigureHitrateCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricConfigureHitrateCommand.java
@@ -19,7 +19,6 @@ package org.apache.ignite.internal.management.metric;
 
 import java.util.Collection;
 import java.util.Map;
-import java.util.UUID;
 import org.apache.ignite.internal.client.GridClientNode;
 import org.apache.ignite.internal.management.api.ComputeCommand;
 
@@ -43,7 +42,7 @@ public class MetricConfigureHitrateCommand implements ComputeCommand<MetricComma
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, MetricCommandArg arg) {
-        return nodeOrNull(arg.nodeId());
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, MetricCommandArg arg) {
+        return nodeOrNull(arg.nodeId(), nodes);
     }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/performancestatistics/PerformanceStatisticsStatusCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/performancestatistics/PerformanceStatisticsStatusCommand.java
index 4e383b34fc5..5ce6e30b758 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/performancestatistics/PerformanceStatisticsStatusCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/performancestatistics/PerformanceStatisticsStatusCommand.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.management.performancestatistics;
 
+import java.util.function.Consumer;
 import org.apache.ignite.internal.dto.IgniteDataTransferObject;
 import org.apache.ignite.internal.management.api.ComputeCommand;
 import org.apache.ignite.internal.management.performancestatistics.PerformanceStatisticsCommand.PerformanceStatisticsStatusCommandArg;
@@ -37,4 +38,9 @@ public class PerformanceStatisticsStatusCommand implements ComputeCommand<Ignite
     @Override public Class<PerformanceStatisticsTask> taskClass() {
         return PerformanceStatisticsTask.class;
     }
+
+    /** {@inheritDoc} */
+    @Override public void printResult(IgniteDataTransferObject arg, String res, Consumer<String> printer) {
+        printer.accept(res);
+    }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/tracing/AbstractTracingConfigurationCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/tracing/AbstractTracingConfigurationCommand.java
index 7d9a1f1061f..a3af482cfd9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/tracing/AbstractTracingConfigurationCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/tracing/AbstractTracingConfigurationCommand.java
@@ -18,8 +18,6 @@
 package org.apache.ignite.internal.management.tracing;
 
 import java.util.Collection;
-import java.util.Map;
-import java.util.UUID;
 import java.util.function.Consumer;
 import org.apache.ignite.internal.client.GridClientNode;
 import org.apache.ignite.internal.management.api.ComputeCommand;
@@ -31,7 +29,7 @@ import static org.apache.ignite.internal.management.api.CommandUtils.coordinator
 public abstract class AbstractTracingConfigurationCommand implements
     ComputeCommand<TracingConfigurationCommandArg, TracingConfigurationTaskResult> {
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, TracingConfigurationCommandArg arg) {
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, TracingConfigurationCommandArg arg) {
         return coordinatorOrNull(nodes);
     }
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/tracing/TracingConfigurationCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/tracing/TracingConfigurationCommand.java
index 7f7e0e1c4db..b4ddc688482 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/tracing/TracingConfigurationCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/tracing/TracingConfigurationCommand.java
@@ -21,8 +21,6 @@ import java.io.IOException;
 import java.io.ObjectInput;
 import java.io.ObjectOutput;
 import java.util.Collection;
-import java.util.Map;
-import java.util.UUID;
 import java.util.function.Consumer;
 import org.apache.ignite.internal.client.GridClientNode;
 import org.apache.ignite.internal.dto.IgniteDataTransferObject;
@@ -64,7 +62,7 @@ public class TracingConfigurationCommand extends CommandRegistryImpl<TracingConf
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<UUID> nodes(Map<UUID, GridClientNode> nodes, TracingConfigurationCommandArg arg) {
+    @Override public Collection<GridClientNode> nodes(Collection<GridClientNode> nodes, TracingConfigurationCommandArg arg) {
         return coordinatorOrNull(nodes);
     }
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/tx/TxInfoCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/tx/TxInfoCommand.java
index 0aa33cf738d..2326cb70b7f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/management/tx/TxInfoCommand.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/management/tx/TxInfoCommand.java
@@ -17,21 +17,25 @@
 
 package org.apache.ignite.internal.management.tx;
 
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.Consumer;
+import org.apache.ignite.Ignite;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.internal.client.GridClient;
+import org.apache.ignite.internal.client.GridClientException;
 import org.apache.ignite.internal.client.GridClientNode;
+import org.apache.ignite.internal.management.api.CommandUtils;
 import org.apache.ignite.internal.management.api.LocalCommand;
 import org.apache.ignite.internal.management.tx.TxCommand.AbstractTxCommandArg;
 import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
 import org.apache.ignite.internal.util.typedef.F;
-import org.apache.ignite.internal.visor.VisorTaskArgument;
 import org.apache.ignite.transactions.TransactionState;
+import org.jetbrains.annotations.Nullable;
 
 import static org.apache.ignite.internal.management.api.CommandUtils.DOUBLE_INDENT;
 import static org.apache.ignite.internal.management.tx.TxCommand.nodeDescription;
@@ -50,13 +54,14 @@ public class TxInfoCommand implements LocalCommand<AbstractTxCommandArg, Map<Clu
 
     /** {@inheritDoc} */
     @Override public Map<ClusterNode, TxTaskResult> execute(
-        GridClient cli,
+        @Nullable GridClient cli,
+        @Nullable Ignite ignite,
         AbstractTxCommandArg arg0,
         Consumer<String> printer
-    ) throws Exception {
+    ) throws GridClientException {
         TxInfoCommandArg arg = (TxInfoCommandArg)arg0;
 
-        Optional<GridClientNode> node = cli.compute().nodes().stream()
+        Optional<GridClientNode> node = CommandUtils.nodes(cli, ignite).stream()
             .filter(n -> !n.isClient())
             .filter(GridClientNode::connectable)
             .findFirst();
@@ -64,8 +69,13 @@ public class TxInfoCommand implements LocalCommand<AbstractTxCommandArg, Map<Clu
         if (!node.isPresent())
             throw new IllegalStateException("No nodes to connect");
 
-        GridCacheVersion nearXidVer = cli.compute().projection(node.get())
-            .execute(FetchNearXidVersionTask.class.getName(), new VisorTaskArgument<>(node.get().nodeId(), arg, false));
+        GridCacheVersion nearXidVer = CommandUtils.execute(
+            cli,
+            ignite,
+            FetchNearXidVersionTask.class,
+            arg,
+            Collections.singleton(node.get())
+        );
 
         boolean histMode = false;
 
@@ -92,8 +102,13 @@ public class TxInfoCommand implements LocalCommand<AbstractTxCommandArg, Map<Clu
             }
         }
 
-        Map<ClusterNode, TxTaskResult> res = cli.compute().projection(node.get())
-            .execute(TxTask.class.getName(), new VisorTaskArgument<>(node.get().nodeId(), arg, false));
+        Map<ClusterNode, TxTaskResult> res = CommandUtils.execute(
+            cli,
+            ignite,
+            TxTask.class,
+            arg,
+            Collections.singleton(node.get())
+        );
 
         if (histMode)
             printTxInfoHistoricalResult(res, printer);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/IgniteMBeansManager.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/IgniteMBeansManager.java
index 34f99dc1d78..e2a37f47a2c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/IgniteMBeansManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/IgniteMBeansManager.java
@@ -18,10 +18,13 @@
 package org.apache.ignite.internal.managers;
 
 import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Set;
 import javax.management.JMException;
 import javax.management.ObjectName;
 import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.internal.ClusterLocalNodeMetricsMXBeanImpl;
 import org.apache.ignite.internal.ClusterMetricsMXBeanImpl;
@@ -33,6 +36,9 @@ import org.apache.ignite.internal.QueryMXBeanImpl;
 import org.apache.ignite.internal.ServiceMXBeanImpl;
 import org.apache.ignite.internal.TransactionMetricsMxBeanImpl;
 import org.apache.ignite.internal.TransactionsMXBeanImpl;
+import org.apache.ignite.internal.management.api.Command;
+import org.apache.ignite.internal.management.api.CommandMBean;
+import org.apache.ignite.internal.management.api.CommandsRegistry;
 import org.apache.ignite.internal.managers.encryption.EncryptionMXBeanImpl;
 import org.apache.ignite.internal.processors.cache.persistence.DataStorageMXBeanImpl;
 import org.apache.ignite.internal.processors.cache.persistence.defragmentation.DefragmentationMXBeanImpl;
@@ -64,6 +70,9 @@ import org.apache.ignite.mxbean.TransactionsMXBean;
 import org.apache.ignite.mxbean.WarmUpMXBean;
 import org.apache.ignite.mxbean.WorkersControlMXBean;
 
+import static org.apache.ignite.internal.management.api.CommandUtils.executable;
+import static org.apache.ignite.internal.util.IgniteUtils.makeMBeanName;
+
 /**
  * Class that registers and unregisters MBeans for kernal.
  */
@@ -175,6 +184,8 @@ public class IgniteMBeansManager {
         PerformanceStatisticsMBeanImpl performanceStatMbean = new PerformanceStatisticsMBeanImpl(ctx);
         registerMBean("PerformanceStatistics", performanceStatMbean.getClass().getSimpleName(), performanceStatMbean,
             PerformanceStatisticsMBean.class);
+
+        registerManagementBeans();
     }
 
     /**
@@ -223,6 +234,43 @@ public class IgniteMBeansManager {
         }
     }
 
+    /** Registers all management API beans. */
+    private void registerManagementBeans() {
+        ctx.grid().commandsRegistry().commands().forEachRemaining(cmd -> register(cmd.getKey(), new LinkedList<>(), cmd.getValue()));
+    }
+
+    /** Recursively register management commands. */
+    public void register(String name, List<String> parents, Command<?, ?> cmd) {
+        if (cmd instanceof CommandsRegistry) {
+            parents.add(name);
+
+            ((CommandsRegistry<?, ?>)cmd).commands()
+                .forEachRemaining(cmd0 -> register(cmd0.getKey(), parents, cmd0.getValue()));
+
+            parents.remove(parents.size() - 1);
+
+            if (!executable(cmd))
+                return;
+        }
+
+        try {
+            ObjectName objName = U.registerMBean(
+                ctx.config().getMBeanServer(),
+                makeMBeanName(ctx.config().getIgniteInstanceName(), "management", parents, name),
+                new CommandMBean<>(ctx.grid(), cmd),
+                CommandMBean.class
+            );
+
+            mBeanNames.add(objName);
+
+            if (log.isDebugEnabled())
+                log.debug("Registered MBean: " + objName);
+        }
+        catch (JMException e) {
+            throw new IgniteException("Failed to register MBean " + cmd.getClass().getSimpleName(), e);
+        }
+    }
+
     /**
      * Unregisters all previously registered MBeans.
      *
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/SystemViewMBean.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/SystemViewMBean.java
index 97e3b1426a6..ae28c2ed88b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/SystemViewMBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/SystemViewMBean.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.managers.systemview;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.net.InetSocketAddress;
+import java.util.AbstractMap;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
@@ -289,7 +290,7 @@ public class SystemViewMBean<R> extends ReadOnlyDynamicMBean {
             else if (clazz.isAssignableFrom(Class.class))
                 data.put(name, ((Class<?>)val).getName());
             else if (clazz.isAssignableFrom(IgniteUuid.class) || clazz.isAssignableFrom(UUID.class) ||
-                clazz.isAssignableFrom(InetSocketAddress.class))
+                clazz.isAssignableFrom(InetSocketAddress.class) || clazz.isAssignableFrom(AbstractMap.class))
                 data.put(name, String.valueOf(val));
             else
                 data.put(name, val);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/walker/ServiceViewWalker.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/walker/ServiceViewWalker.java
index 030396cb0a1..6dfdfaa8821 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/walker/ServiceViewWalker.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/walker/ServiceViewWalker.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.managers.systemview.walker;
 
+import java.util.Map;
 import java.util.UUID;
 import org.apache.ignite.lang.IgniteUuid;
 import org.apache.ignite.spi.systemview.view.ServiceView;
@@ -41,6 +42,7 @@ public class ServiceViewWalker implements SystemViewRowAttributeWalker<ServiceVi
         v.accept(7, "affinityKey", String.class);
         v.accept(8, "nodeFilter", Class.class);
         v.accept(9, "staticallyConfigured", boolean.class);
+        v.accept(10, "topologySnapshot", Map.class);
     }
 
     /** {@inheritDoc} */
@@ -55,10 +57,11 @@ public class ServiceViewWalker implements SystemViewRowAttributeWalker<ServiceVi
         v.accept(7, "affinityKey", String.class, row.affinityKey());
         v.accept(8, "nodeFilter", Class.class, row.nodeFilter());
         v.acceptBoolean(9, "staticallyConfigured", row.staticallyConfigured());
+        v.accept(10, "topologySnapshot", Map.class, row.topologySnapshot());
     }
 
     /** {@inheritDoc} */
     @Override public int count() {
-        return 10;
+        return 11;
     }
 }
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParserMetricsHolder.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryParserMetricsHolder.java
similarity index 94%
rename from modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParserMetricsHolder.java
rename to modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryParserMetricsHolder.java
index 73efd43d618..157620c3af7 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParserMetricsHolder.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryParserMetricsHolder.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.processors.query.h2;
+package org.apache.ignite.internal.processors.query;
 
 import org.apache.ignite.internal.processors.metric.GridMetricManager;
 import org.apache.ignite.internal.processors.metric.MetricRegistry;
@@ -26,7 +26,7 @@ import org.apache.ignite.internal.processors.metric.impl.LongAdderMetric;
  */
 public class QueryParserMetricsHolder {
     /** Query parser metric group name. */
-    static final String QUERY_PARSER_METRIC_GROUP_NAME = "sql.parser.cache";
+    public static final String QUERY_PARSER_METRIC_GROUP_NAME = "sql.parser.cache";
 
     /** Query cache hits counter. */
     private final LongAdderMetric qryCacheHits;
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceInfo.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceInfo.java
index 6a502c7ef55..3d6d60965ff 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceInfo.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/ServiceInfo.java
@@ -180,7 +180,6 @@ public class ServiceInfo implements ServiceDescriptor {
     }
 
     /** {@inheritDoc} */
-    @SuppressWarnings("unchecked")
     @Nullable @Override public <K> K affinityKey() {
         return (K)cfg.getAffinityKey();
     }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
index 25a9b9acd12..8482a6e88b9 100755
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
@@ -5053,6 +5053,26 @@ public abstract class IgniteUtils {
      */
     public static ObjectName makeMBeanName(@Nullable String igniteInstanceName, @Nullable String grp, String name)
         throws MalformedObjectNameException {
+        return makeMBeanName(igniteInstanceName, grp, Collections.emptyList(), name);
+    }
+
+    /**
+     * Constructs JMX object name with given properties.
+     * Map with ordered {@code groups} used for proper object name construction.
+     *
+     * @param igniteInstanceName Ignite instance name.
+     * @param grp Name of the group.
+     * @param grps Names of extended groups.
+     * @param name Name of mbean.
+     * @return JMX object name.
+     * @throws MalformedObjectNameException Thrown in case of any errors.
+     */
+    public static ObjectName makeMBeanName(
+        @Nullable String igniteInstanceName,
+        @Nullable String grp,
+        List<String> grps,
+        String name
+    ) throws MalformedObjectNameException {
         SB sb = new SB(JMX_DOMAIN + ':');
 
         appendClassLoaderHash(sb);
@@ -5065,6 +5085,9 @@ public abstract class IgniteUtils {
         if (grp != null)
             sb.a("group=").a(escapeObjectNameValue(grp)).a(',');
 
+        for (int i = 0; i < grps.size(); i++)
+            sb.a("group").a(i).a("=").a(grps.get(i)).a(',');
+
         sb.a("name=").a(escapeObjectNameValue(name));
 
         return new ObjectName(sb.toString());
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/BaselineAutoAdjustMXBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/BaselineAutoAdjustMXBean.java
index 11bdff62542..723345639ee 100644
--- a/modules/core/src/main/java/org/apache/ignite/mxbean/BaselineAutoAdjustMXBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/BaselineAutoAdjustMXBean.java
@@ -23,7 +23,12 @@ import org.apache.ignite.internal.cluster.DistributedBaselineConfiguration;
  * This interface defines JMX view on {@link DistributedBaselineConfiguration}.
  */
 public interface BaselineAutoAdjustMXBean {
-    /** @return Whether baseline autoadjustment is enabled ot not. */
+    /**
+     * @return Whether baseline autoadjustment is enabled ot not.
+     * @see org.apache.ignite.internal.management.api.CommandMBean
+     * @deprecated Use managements API beans, instead.
+     */
+    @Deprecated
     @MXBeanDescription("Whether baseline autoadjustment is enabled ot not.")
     boolean isAutoAdjustmentEnabled();
 
@@ -31,7 +36,11 @@ public interface BaselineAutoAdjustMXBean {
     @MXBeanDescription("Baseline autoadjustment timeout value.")
     long getAutoAdjustmentTimeout();
 
-    /** @return Time until baseline will be adjusted automatically. */
+    /**
+     * @return Time until baseline will be adjusted automatically.
+     * @see org.apache.ignite.internal.management.api.CommandMBean
+     * @deprecated Use managements API beans, instead.
+     */
     @MXBeanDescription("Time until baseline will be adjusted automatically.")
     long getTimeUntilAutoAdjust();
 
@@ -39,13 +48,23 @@ public interface BaselineAutoAdjustMXBean {
     @MXBeanDescription("State of task of auto-adjust(IN_PROGRESS, SCHEDULED, NOT_SCHEDULED).")
     String getTaskState();
 
-    /** @param enabled Enable/disable baseline autoadjustment flag. */
+    /**
+     * @param enabled Enable/disable baseline autoadjustment flag.
+     * @see org.apache.ignite.internal.management.api.CommandMBean
+     * @deprecated Use managements API beans, instead.
+     */
+    @Deprecated
     @MXBeanDescription("Enable/disable baseline autoadjustment feature.")
     public void setAutoAdjustmentEnabled(
         @MXBeanParameter(name = "enabled", description = "Enable/disable flag.") boolean enabled
     );
 
-    /** @param timeout Timeout value. */
+    /**
+     * @param timeout Timeout value.
+     * @see org.apache.ignite.internal.management.api.CommandMBean
+     * @deprecated Use managements API beans, instead.
+     */
+    @Deprecated
     @MXBeanDescription("Set baseline autoadjustment timeout value.")
     public void setAutoAdjustmentTimeout(
         @MXBeanParameter(name = "timeout", description = "Timeout value.") long timeout
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/ClientProcessorMXBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/ClientProcessorMXBean.java
index 93375afb62d..7ae34355970 100644
--- a/modules/core/src/main/java/org/apache/ignite/mxbean/ClientProcessorMXBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/ClientProcessorMXBean.java
@@ -21,7 +21,11 @@ import java.util.List;
 
 /**
  * MXBean interface that provides access to ODBC\JDBC\Thin client connections.
+ *
+ * @see org.apache.ignite.internal.management.api.CommandMBean
+ * @deprecated Use managements API beans, instead.
  */
+@Deprecated
 @MXBeanDescription("MBean that provides information about client connections.")
 public interface ClientProcessorMXBean {
     /**
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/ComputeMXBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/ComputeMXBean.java
index 0f64e7b4fd7..78f22641460 100644
--- a/modules/core/src/main/java/org/apache/ignite/mxbean/ComputeMXBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/ComputeMXBean.java
@@ -22,7 +22,11 @@ import org.apache.ignite.spi.systemview.view.ComputeTaskView;
 
 /**
  * Compute MXBean interface.
+ *
+ * @see org.apache.ignite.internal.management.api.CommandMBean
+ * @deprecated Use managements API beans, instead.
  */
+@Deprecated
 public interface ComputeMXBean {
     /**
      * Kills compute task by the session idenitifier.
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/DefragmentationMXBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/DefragmentationMXBean.java
index 220f68410db..9fd5c8d7c30 100644
--- a/modules/core/src/main/java/org/apache/ignite/mxbean/DefragmentationMXBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/DefragmentationMXBean.java
@@ -19,7 +19,11 @@ package org.apache.ignite.mxbean;
 
 /**
  * JMX bean for defragmentation manager.
+ *
+ * @see org.apache.ignite.internal.management.api.CommandMBean
+ * @deprecated Use managements API beans, instead.
  */
+@Deprecated
 @MXBeanDescription("MBean that provides access for defragmentation features.")
 public interface DefragmentationMXBean {
     /**
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/EncryptionMXBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/EncryptionMXBean.java
index 5aaca05e47c..909888b3107 100644
--- a/modules/core/src/main/java/org/apache/ignite/mxbean/EncryptionMXBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/EncryptionMXBean.java
@@ -22,7 +22,11 @@ import org.apache.ignite.IgniteEncryption;
 
 /**
  * Encryption features MBean.
+ *
+ * @see org.apache.ignite.internal.management.api.CommandMBean
+ * @deprecated Use managements API beans, instead.
  */
+@Deprecated
 @MXBeanDescription("MBean that provides access to encryption features.")
 public interface EncryptionMXBean {
     /**
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/IgniteClusterMXBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/IgniteClusterMXBean.java
index 46856d162f6..549e27ee4b0 100644
--- a/modules/core/src/main/java/org/apache/ignite/mxbean/IgniteClusterMXBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/IgniteClusterMXBean.java
@@ -22,7 +22,11 @@ import javax.management.JMException;
 
 /**
  * MX Bean allows to access information about cluster ID and tag and change tag.
+ *
+ * @see org.apache.ignite.internal.management.api.CommandMBean
+ * @deprecated Use managements API beans, instead.
  */
+@Deprecated
 @MXBeanDescription("MBean that provides access to information about cluster ID and tag.")
 public interface IgniteClusterMXBean {
     /**
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/MetricsMxBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/MetricsMxBean.java
index 23f2aefe225..b9e0d94bd95 100644
--- a/modules/core/src/main/java/org/apache/ignite/mxbean/MetricsMxBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/MetricsMxBean.java
@@ -23,7 +23,11 @@ import org.apache.ignite.spi.metric.HistogramMetric;
 
 /**
  * Metrics MXBean interface.
+ *
+ * @see org.apache.ignite.internal.management.api.CommandMBean
+ * @deprecated Use managements API beans, instead.
  */
+@Deprecated
 @MXBeanDescription("MBean that provides access to Ignite metrics management methods.")
 public interface MetricsMxBean {
     /**
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/PerformanceStatisticsMBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/PerformanceStatisticsMBean.java
index 2c736d55e03..b0036b95213 100644
--- a/modules/core/src/main/java/org/apache/ignite/mxbean/PerformanceStatisticsMBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/PerformanceStatisticsMBean.java
@@ -22,7 +22,11 @@ import org.apache.ignite.lang.IgniteExperimental;
 
 /**
  * MBean that provides access to performance statistics management.
+ *
+ * @see org.apache.ignite.internal.management.api.CommandMBean
+ * @deprecated Use managements API beans, instead.
  */
+@Deprecated
 @IgniteExperimental
 @MXBeanDescription("MBean provide access to performance statistics management.")
 public interface PerformanceStatisticsMBean {
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/QueryMXBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/QueryMXBean.java
index 6334c4655bb..cedb7399e98 100644
--- a/modules/core/src/main/java/org/apache/ignite/mxbean/QueryMXBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/QueryMXBean.java
@@ -23,7 +23,11 @@ import org.apache.ignite.spi.systemview.view.SqlQueryView;
 
 /**
  * Query MXBean interface.
+ *
+ * @see org.apache.ignite.internal.management.api.CommandMBean
+ * @deprecated Use managements API beans, instead.
  */
+@Deprecated
 public interface QueryMXBean {
     /**
      * Kills continuous query by the identifier.
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/ServiceMXBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/ServiceMXBean.java
index 4897d216597..4484134d124 100644
--- a/modules/core/src/main/java/org/apache/ignite/mxbean/ServiceMXBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/ServiceMXBean.java
@@ -21,7 +21,11 @@ import org.apache.ignite.spi.systemview.view.ServiceView;
 
 /**
  * Service MXBean interface.
+ *
+ * @see org.apache.ignite.internal.management.api.CommandMBean
+ * @deprecated Use managements API beans, instead.
  */
+@Deprecated
 public interface ServiceMXBean {
     /**
      * @param name Service name.
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/SnapshotMXBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/SnapshotMXBean.java
index b677f55574c..dee7c59a42e 100644
--- a/modules/core/src/main/java/org/apache/ignite/mxbean/SnapshotMXBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/SnapshotMXBean.java
@@ -22,7 +22,11 @@ import org.apache.ignite.IgniteSnapshot;
 
 /**
  * Snapshot features MBean.
+ *
+ * @see org.apache.ignite.internal.management.api.CommandMBean
+ * @deprecated Use managements API beans, instead.
  */
+@Deprecated
 @MXBeanDescription("MBean that provides access for snapshot features.")
 public interface SnapshotMXBean {
     /**
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/TransactionsMXBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/TransactionsMXBean.java
index e9e65f44607..c6c10625ecf 100644
--- a/modules/core/src/main/java/org/apache/ignite/mxbean/TransactionsMXBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/TransactionsMXBean.java
@@ -37,7 +37,11 @@ public interface TransactionsMXBean {
      * @param detailed Detailed.
      * @param kill Kill.
      * @return If {@code detailed} flag is set - description of each transaction, else transactions count.
+     *
+     * @see org.apache.ignite.internal.management.api.CommandMBean
+     * @deprecated Use managements API beans, instead.
      */
+    @Deprecated
     @MXBeanDescription("Returns or kills transactions matching the filter conditions.")
     public String getActiveTransactions(
         @MXBeanParameter(name = "minDuration", description = "Minimum duration (seconds).")
@@ -67,7 +71,10 @@ public interface TransactionsMXBean {
      *
      * @param xid Transaction xid.
      * @see TransactionView#xid()
+     * @see org.apache.ignite.internal.management.api.CommandMBean
+     * @deprecated Use managements API beans, instead.
      */
+    @Deprecated
     @MXBeanDescription("Kills transactions by the xid.")
     public void cancel(
         @MXBeanParameter(name = "xid", description = "Transaction XID.") String xid
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/WarmUpMXBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/WarmUpMXBean.java
index ec821819ce8..42fa6584b8f 100644
--- a/modules/core/src/main/java/org/apache/ignite/mxbean/WarmUpMXBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/WarmUpMXBean.java
@@ -19,7 +19,11 @@ package org.apache.ignite.mxbean;
 
 /**
  * Warm-up MXBean interface.
+ *
+ * @see org.apache.ignite.internal.management.api.CommandMBean
+ * @deprecated Use managements API beans, instead.
  */
+@Deprecated
 @MXBeanDescription("MBean that provides access to warm-up.")
 public interface WarmUpMXBean {
     /**
diff --git a/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/ServiceView.java b/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/ServiceView.java
index 9e638ef9f48..ec303340749 100644
--- a/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/ServiceView.java
+++ b/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/ServiceView.java
@@ -17,15 +17,19 @@
 
 package org.apache.ignite.spi.systemview.view;
 
+import java.util.Map;
 import java.util.UUID;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.internal.managers.systemview.walker.Order;
 import org.apache.ignite.internal.processors.service.ServiceInfo;
 import org.apache.ignite.lang.IgnitePredicate;
 import org.apache.ignite.lang.IgniteUuid;
+import org.apache.ignite.services.ServiceDescriptor;
 
 /**
  * Service representation for a {@link SystemView}.
+ *
+ * @see ServiceDescriptor
  */
 public class ServiceView {
     /** Service descriptor. */
@@ -98,4 +102,9 @@ public class ServiceView {
     public UUID originNodeId() {
         return serviceInfo.originNodeId();
     }
+
+    /** @return Map of number of service instances per node ID. */
+    public Map<UUID, Integer> topologySnapshot() {
+        return serviceInfo.topologySnapshot();
+    }
 }
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/metric/JmxExporterSpiTest.java b/modules/core/src/test/java/org/apache/ignite/internal/metric/JmxExporterSpiTest.java
index 7bd0b0b421d..1145006a198 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/metric/JmxExporterSpiTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/metric/JmxExporterSpiTest.java
@@ -415,6 +415,7 @@ public class JmxExporterSpiTest extends AbstractExporterSpiTest {
         assertEquals(srvcCfg.getName(), sysView.get("name"));
         assertEquals(srvcCfg.getMaxPerNodeCount(), sysView.get("maxPerNodeCount"));
         assertEquals(DummyService.class.getName(), sysView.get("serviceClass"));
+        assertEquals(F.asMap(ignite.localNode().id(), 1).toString(), sysView.get("topologySnapshot"));
     }
 
     /** */
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/metric/SystemViewSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/metric/SystemViewSelfTest.java
index 6044030c016..dff7e9cdb1f 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/metric/SystemViewSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/metric/SystemViewSelfTest.java
@@ -281,7 +281,6 @@ public class SystemViewSelfTest extends GridCommonAbstractTest {
 
                 assertEquals(srvcCfg.getName(), sview.name());
                 assertNotNull(sview.serviceId());
-                assertEquals(srvcCfg.getMaxPerNodeCount(), sview.maxPerNodeCount());
                 assertEquals(DummyService.class, sview.serviceClass());
                 assertEquals(srvcCfg.getMaxPerNodeCount(), sview.maxPerNodeCount());
                 assertNull(sview.cacheName());
@@ -289,6 +288,7 @@ public class SystemViewSelfTest extends GridCommonAbstractTest {
                 assertEquals(TestNodeFilter.class, sview.nodeFilter());
                 assertFalse(sview.staticallyConfigured());
                 assertEquals(g.localNode().id(), sview.originNodeId());
+                assertEquals(F.asMap(g.localNode().id(), 1), sview.topologySnapshot());
             }
 
             {
@@ -314,7 +314,6 @@ public class SystemViewSelfTest extends GridCommonAbstractTest {
 
                 assertEquals(srvcCfg.getName(), sview[0].name());
                 assertNotNull(sview[0].serviceId());
-                assertEquals(srvcCfg.getMaxPerNodeCount(), sview[0].maxPerNodeCount());
                 assertEquals(DummyService.class, sview[0].serviceClass());
                 assertEquals(srvcCfg.getMaxPerNodeCount(), sview[0].maxPerNodeCount());
                 assertEquals("test-cache", sview[0].cacheName());
@@ -322,6 +321,7 @@ public class SystemViewSelfTest extends GridCommonAbstractTest {
                 assertEquals(TestNodeFilter.class, sview[0].nodeFilter());
                 assertFalse(sview[0].staticallyConfigured());
                 assertEquals(g.localNode().id(), sview[0].originNodeId());
+                assertEquals(F.asMap(g.localNode().id(), 2), sview[0].topologySnapshot());
             }
         }
     }
diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java
index d7a8f235e53..81e32da1858 100755
--- a/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java
@@ -3151,10 +3151,25 @@ public abstract class GridAbstractTest extends JUnitAssertAware {
      * @throws Exception If failed.
      */
     public static <T> T getMxBean(String igniteInstanceName, String grp, String name, Class<T> clazz) {
+        return getMxBean(igniteInstanceName, grp, Collections.emptyList(), name, clazz);
+    }
+
+    /**
+     * Return JMX bean.
+     *
+     * @param igniteInstanceName Ignite instance name.
+     * @param grp Name of the group.
+     * @param grps Extended groups.
+     * @param name Name of the bean.
+     * @param clazz Class of the mbean.
+     * @return MX bean.
+     * @throws Exception If failed.
+     */
+    public static <T> T getMxBean(String igniteInstanceName, String grp, List<String> grps, String name, Class<T> clazz) {
         ObjectName mbeanName = null;
 
         try {
-            mbeanName = U.makeMBeanName(igniteInstanceName, grp, name);
+            mbeanName = U.makeMBeanName(igniteInstanceName, grp, grps, name);
         }
         catch (MalformedObjectNameException e) {
             fail("Failed to register MBean.");
@@ -3163,7 +3178,7 @@ public abstract class GridAbstractTest extends JUnitAssertAware {
         MBeanServer mbeanSrv = ManagementFactory.getPlatformMBeanServer();
 
         if (!mbeanSrv.isRegistered(mbeanName))
-            throw new IgniteException("MBean not registered.");
+            throw new IgniteException("MBean not registered: " + mbeanName);
 
         return MBeanServerInvocationHandler.newProxyInstance(mbeanSrv, mbeanName, clazz, false);
     }
diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/multijvm/IgniteClusterProcessProxy.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/multijvm/IgniteClusterProcessProxy.java
index 97b1c884f04..8ffa254abba 100644
--- a/modules/core/src/test/java/org/apache/ignite/testframework/junits/multijvm/IgniteClusterProcessProxy.java
+++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/multijvm/IgniteClusterProcessProxy.java
@@ -402,6 +402,11 @@ public class IgniteClusterProcessProxy implements IgniteClusterEx {
         throw new UnsupportedOperationException("Operation is not supported yet.");
     }
 
+    /** {@inheritDoc} */
+    @Override public void state(ClusterState newState, boolean force) throws IgniteException {
+        throw new UnsupportedOperationException("Operation is not supported yet.");
+    }
+
     /** {@inheritDoc} */
     @Override public ShutdownPolicy shutdownPolicy() {
         throw new UnsupportedOperationException("Operation is not supported yet.");
diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/multijvm/IgniteProcessProxy.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/multijvm/IgniteProcessProxy.java
index 3b06a49b4b3..02c4577fdb0 100644
--- a/modules/core/src/test/java/org/apache/ignite/testframework/junits/multijvm/IgniteProcessProxy.java
+++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/multijvm/IgniteProcessProxy.java
@@ -76,6 +76,7 @@ import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.IgniteInterruptedCheckedException;
 import org.apache.ignite.internal.binary.BinaryMarshaller;
 import org.apache.ignite.internal.cluster.IgniteClusterEx;
+import org.apache.ignite.internal.management.IgniteCommandRegistry;
 import org.apache.ignite.internal.processors.cache.GridCacheUtilityKey;
 import org.apache.ignite.internal.processors.cache.IgniteInternalCache;
 import org.apache.ignite.internal.util.GridJavaProcess;
@@ -555,6 +556,11 @@ public class IgniteProcessProxy implements IgniteEx {
         throw new UnsupportedOperationException("Operation isn't supported yet.");
     }
 
+    /** {@inheritDoc} */
+    @Override public IgniteCommandRegistry commandsRegistry() {
+        throw new UnsupportedOperationException("Operation isn't supported yet.");
+    }
+
     /** {@inheritDoc} */
     @Override public IgniteCompute compute() {
         throw new UnsupportedOperationException("Operation isn't supported yet.");
diff --git a/modules/extdata/pluggable/src/test/java/org/apache/ignite/internal/commandline/CommandsProviderExtImpl.java b/modules/extdata/pluggable/src/test/java/org/apache/ignite/internal/commandline/CommandsProviderExtImpl.java
index 0e9af40bfd3..f47659b6e1a 100644
--- a/modules/extdata/pluggable/src/test/java/org/apache/ignite/internal/commandline/CommandsProviderExtImpl.java
+++ b/modules/extdata/pluggable/src/test/java/org/apache/ignite/internal/commandline/CommandsProviderExtImpl.java
@@ -23,6 +23,7 @@ import java.io.ObjectOutput;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.function.Consumer;
+import org.apache.ignite.Ignite;
 import org.apache.ignite.internal.client.GridClient;
 import org.apache.ignite.internal.dto.IgniteDataTransferObject;
 import org.apache.ignite.internal.management.api.Argument;
@@ -64,7 +65,7 @@ public class CommandsProviderExtImpl implements CommandsProvider {
         }
 
         /** {@inheritDoc} */
-        @Override public Void execute(GridClient cli, TestCommandCommandArg arg, Consumer<String> printer) {
+        @Override public Void execute(GridClient cli, Ignite ignite, TestCommandCommandArg arg, Consumer<String> printer) {
             printer.accept(TEST_COMMAND_OUTPUT + ": " + arg.testPrint);
 
             return null;
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParser.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParser.java
index 952e33c58bf..e53ea65b477 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParser.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/QueryParser.java
@@ -40,6 +40,7 @@ import org.apache.ignite.internal.processors.odbc.jdbc.JdbcParameterMeta;
 import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata;
 import org.apache.ignite.internal.processors.query.IgniteSQLException;
 import org.apache.ignite.internal.processors.query.NestedTxMode;
+import org.apache.ignite.internal.processors.query.QueryParserMetricsHolder;
 import org.apache.ignite.internal.processors.query.QueryUtils;
 import org.apache.ignite.internal.processors.query.h2.dml.DmlAstUtils;
 import org.apache.ignite.internal.processors.query.h2.dml.UpdatePlan;
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/metric/SqlViewExporterSpiTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/metric/SqlViewExporterSpiTest.java
index 74aa625cda0..d462f491cb8 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/metric/SqlViewExporterSpiTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/metric/SqlViewExporterSpiTest.java
@@ -299,7 +299,8 @@ public class SqlViewExporterSpiTest extends AbstractExporterSpiTest {
                 "  AFFINITY_KEY, " +
                 "  NODE_FILTER, " +
                 "  STATICALLY_CONFIGURED, " +
-                "  ORIGIN_NODE_ID " +
+                "  ORIGIN_NODE_ID, " +
+                "  TOPOLOGY_SNAPSHOT " +
                 "FROM SYS.SERVICES");
 
         assertEquals(ignite0.context().service().serviceDescriptors().size(), srvs.size());
@@ -309,6 +310,7 @@ public class SqlViewExporterSpiTest extends AbstractExporterSpiTest {
         assertEquals(srvcCfg.getName(), sysView.get(0));
         assertEquals(DummyService.class.getName(), sysView.get(2));
         assertEquals(srvcCfg.getMaxPerNodeCount(), sysView.get(4));
+        assertEquals(F.first(ignite0.services().serviceDescriptors()).topologySnapshot().toString(), sysView.get(10));
     }
 
     /** */
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/QueryParserMetricsHolderSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/QueryParserMetricsHolderSelfTest.java
index 3695cec0594..a6f53e686f8 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/QueryParserMetricsHolderSelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/QueryParserMetricsHolderSelfTest.java
@@ -21,12 +21,13 @@ import org.apache.ignite.IgniteCache;
 import org.apache.ignite.cache.query.SqlFieldsQuery;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.processors.query.QueryParserMetricsHolder;
 import org.apache.ignite.spi.metric.LongMetric;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
 import org.junit.Assert;
 import org.junit.Test;
 
-import static org.apache.ignite.internal.processors.query.h2.QueryParserMetricsHolder.QUERY_PARSER_METRIC_GROUP_NAME;
+import static org.apache.ignite.internal.processors.query.QueryParserMetricsHolder.QUERY_PARSER_METRIC_GROUP_NAME;
 
 /**
  * Test to check {@link QueryParserMetricsHolder}
diff --git a/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsMXBeanTest.java b/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsMXBeanTest.java
index 38bf9f5beb1..3b0d69e2533 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsMXBeanTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsMXBeanTest.java
@@ -20,6 +20,7 @@ package org.apache.ignite.util;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
+import java.util.function.Consumer;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.cache.CacheAtomicityMode;
 import org.apache.ignite.configuration.CacheConfiguration;
@@ -34,6 +35,7 @@ import org.apache.ignite.internal.TransactionsMXBeanImpl;
 import org.apache.ignite.internal.processors.cache.persistence.snapshot.SnapshotMXBeanImpl;
 import org.apache.ignite.internal.processors.odbc.ClientListenerProcessor;
 import org.apache.ignite.internal.util.typedef.G;
+import org.apache.ignite.internal.util.typedef.T3;
 import org.apache.ignite.lang.IgniteUuid;
 import org.apache.ignite.mxbean.ClientProcessorMXBean;
 import org.apache.ignite.mxbean.ComputeMXBean;
@@ -86,6 +88,10 @@ public class KillCommandsMXBeanTest extends GridCommonAbstractTest {
     /** Snapshot control JMX bean. */
     private static SnapshotMXBean snpMxBean;
 
+    /** */
+    private Consumer<T3<UUID, String, Long>> killScan = args ->
+        qryMBean.cancelScan(args.get1().toString(), args.get2(), args.get3());
+
     /** {@inheritDoc} */
     @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
         return super.getConfiguration(igniteInstanceName)
@@ -150,8 +156,7 @@ public class KillCommandsMXBeanTest extends GridCommonAbstractTest {
     /** @throws Exception If failed. */
     @Test
     public void testCancelScanQuery() throws Exception {
-        doTestScanQueryCancel(startCli, srvs, args ->
-            qryMBean.cancelScan(args.get1().toString(), args.get2(), args.get3()));
+        doTestScanQueryCancel(startCli, srvs, killScan);
     }
 
     /** @throws Exception If failed. */
@@ -182,7 +187,7 @@ public class KillCommandsMXBeanTest extends GridCommonAbstractTest {
     @Test
     public void testCancelContinuousQuery() throws Exception {
         doTestCancelContinuousQuery(startCli, srvs,
-            (nodeId, routineId) -> qryMBean.cancelContinuous(nodeId.toString(), routineId.toString()));
+            (nodeId, routineId) -> qryMBean.cancelContinuous(nodeId.toString(), routineId.toString()), killScan);
     }
 
     /** */
diff --git a/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsSQLTest.java b/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsSQLTest.java
index 1458565e372..9b8fd60dc11 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsSQLTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsSQLTest.java
@@ -20,6 +20,7 @@ package org.apache.ignite.util;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
+import java.util.function.Consumer;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.cache.CacheAtomicityMode;
@@ -27,6 +28,7 @@ import org.apache.ignite.cache.query.SqlFieldsQuery;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.util.typedef.G;
+import org.apache.ignite.internal.util.typedef.T3;
 import org.apache.ignite.lang.IgniteUuid;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
 import org.junit.Test;
@@ -87,6 +89,10 @@ public class KillCommandsSQLTest extends GridCommonAbstractTest {
     /** Client that kills tasks. */
     private static IgniteEx killCli;
 
+    /** */
+    private Consumer<T3<UUID, String, Long>> killScan =
+        args -> execute(killCli, KILL_SCAN_QRY + " '" + args.get1() + "' '" + args.get2() + "' " + args.get3());
+
     /** {@inheritDoc} */
     @Override protected void beforeTestsStarted() throws Exception {
         startGridsMultiThreaded(NODES_CNT);
@@ -117,8 +123,7 @@ public class KillCommandsSQLTest extends GridCommonAbstractTest {
     /** @throws Exception If failed. */
     @Test
     public void testCancelScanQuery() throws Exception {
-        doTestScanQueryCancel(startCli, srvs,
-            args -> execute(killCli, KILL_SCAN_QRY + " '" + args.get1() + "' '" + args.get2() + "' " + args.get3()));
+        doTestScanQueryCancel(startCli, srvs, killScan);
     }
 
     /** @throws Exception If failed. */
@@ -150,7 +155,7 @@ public class KillCommandsSQLTest extends GridCommonAbstractTest {
     @Test
     public void testCancelContinuousQuery() throws Exception {
         doTestCancelContinuousQuery(startCli, srvs, (nodeId, routineId) ->
-            execute(killCli, KILL_CQ_QRY + " '" + nodeId.toString() + "'" + " '" + routineId.toString() + "'"));
+            execute(killCli, KILL_CQ_QRY + " '" + nodeId.toString() + "'" + " '" + routineId.toString() + "'"), killScan);
     }
 
     /** */
diff --git a/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsTests.java b/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsTests.java
index 4149acb8b72..33aac557fec 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsTests.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/util/KillCommandsTests.java
@@ -423,9 +423,14 @@ class KillCommandsTests {
      * @param cli Client node.
      * @param srvs Server nodes.
      * @param qryCanceler Query cancel closure.
+     * @param scanCanceler Scan query cancel closure.
      */
-    public static void doTestCancelContinuousQuery(IgniteEx cli, List<IgniteEx> srvs,
-        BiConsumer<UUID, UUID> qryCanceler) throws Exception {
+    public static void doTestCancelContinuousQuery(
+        IgniteEx cli,
+        List<IgniteEx> srvs,
+        BiConsumer<UUID, UUID> qryCanceler,
+        Consumer<T3<UUID, String, Long>> scanCanceler
+    ) throws Exception {
         IgniteCache<Object, Object> cache = cli.cache(DEFAULT_CACHE_NAME);
 
         ContinuousQuery<Integer, Integer> cq = new ContinuousQuery<>();
@@ -477,6 +482,10 @@ class KillCommandsTests {
 
             assertTrue(srv.configuration().getIgniteInstanceName(), res);
         }
+
+        T3<UUID, String, Long> qryInfo = scanQuery(srvs.get(0));
+
+        scanCanceler.accept(qryInfo);
     }
 
     /**
diff --git a/parent/pom.xml b/parent/pom.xml
index cfa5b7bde72..57f38c71509 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -103,7 +103,7 @@
         <javax.cache.version>1.0.0</javax.cache.version>
         <jboss.rmi.version>1.0.6.Final</jboss.rmi.version>
         <jetbrains.annotations.version>16.0.3</jetbrains.annotations.version>
-        <jetty.version>9.4.49.v20220914</jetty.version>
+        <jetty.version>9.4.51.v20230217</jetty.version>
         <jmh.version>1.13</jmh.version>
         <jms.spec.version>1.1.1</jms.spec.version>
         <jna.version>4.5.2</jna.version>