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 2019/09/21 10:23:52 UTC

[ignite] branch master updated: IGNITE-12145: Monitoring list engine. (#6845)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new bb1b11f  IGNITE-12145: Monitoring list engine. (#6845)
bb1b11f is described below

commit bb1b11fd234235fb6cd1d5764a0e3ba2780d2141
Author: Nikolay <ni...@apache.org>
AuthorDate: Sat Sep 21 13:23:37 2019 +0300

    IGNITE-12145: Monitoring list engine. (#6845)
---
 .../client/router/ClientFailedInitSelfTest.java    |   4 +-
 .../internal/jdbc2/JdbcMetadataSelfTest.java       |  19 +-
 .../ignite/jdbc/thin/JdbcThinMetadataSelfTest.java |  70 +++-
 .../ignite/codegen/MessageCodeGenerator.java       |   4 +-
 .../SystemViewRowAttributeWalkerGenerator.java     | 269 ++++++++++++++
 .../ignite/configuration/IgniteConfiguration.java  |  31 +-
 .../apache/ignite/internal/GridKernalContext.java  |   8 +
 .../ignite/internal/GridKernalContextImpl.java     |  12 +
 .../ignite/internal/IgniteComponentType.java       |  10 +-
 .../org/apache/ignite/internal/IgniteKernal.java   |   2 +
 .../org/apache/ignite/internal/IgnitionEx.java     |  19 +
 .../managers/systemview/GridSystemViewManager.java | 147 ++++++++
 .../managers/systemview/SystemViewAdapter.java     | 115 ++++++
 .../systemview/walker/CacheGroupViewWalker.java    |  78 ++++
 .../systemview/walker/CacheViewWalker.java         | 170 +++++++++
 .../systemview/walker/ComputeTaskViewWalker.java   |  66 ++++
 .../managers/systemview/walker/Order.java}         |  33 +-
 .../systemview/walker/ServiceViewWalker.java       |  64 ++++
 .../processors/cache/ClusterCachesInfo.java        |  28 +-
 .../processors/cache/DynamicCacheDescriptor.java   |   4 +-
 .../wal/reader/StandaloneGridKernalContext.java    |  12 +
 .../processors/metric/GridMetricManager.java       |  23 +-
 .../internal/processors/query/QueryUtils.java      |   3 -
 .../processors/service/IgniteServiceProcessor.java |  12 +
 .../processors/task/GridTaskProcessor.java         |  12 +
 .../internal/processors/task/GridTaskWorker.java   |  14 +-
 .../apache/ignite/internal/util/IgniteUtils.java   |  62 ++++
 .../org/apache/ignite/mxbean/IgniteMXBean.java     |   1 -
 .../ignite/spi/metric/MetricExporterSpi.java       |   4 -
 .../ignite/spi/metric/ReadOnlyMetricRegistry.java  |   4 +-
 .../spi/metric/jmx/JmxMetricExporterSpi.java       |  73 +++-
 .../ignite/spi/metric/jmx/MetricRegistryMBean.java |  36 +-
 .../spi/metric/jmx/ReadOnlyDynamicMBean.java       |  73 ++++
 .../ReadOnlySystemViewRegistry.java}               |  25 +-
 .../SystemViewExporterSpi.java}                    |  37 +-
 .../systemview/jmx/JmxSystemViewExporterSpi.java   | 127 +++++++
 .../ignite/spi/systemview/jmx/SystemViewMBean.java | 261 ++++++++++++++
 .../jmx/package-info.java}                         |  25 +-
 .../package-info.java}                             |  24 +-
 .../ignite/spi/systemview/view/CacheGroupView.java | 153 ++++++++
 .../ignite/spi/systemview/view/CacheView.java      | 373 +++++++++++++++++++
 .../spi/systemview/view/ComputeTaskView.java       |  92 +++++
 .../ignite/spi/systemview/view/ServiceView.java    | 101 ++++++
 .../view/SystemView.java}                          |  36 +-
 .../view/SystemViewRowAttributeWalker.java         | 140 ++++++++
 .../view/package-info.java}                        |  24 +-
 ...xporterSpiTest.java => JmxExporterSpiTest.java} | 180 +++++++++-
 .../ignite/internal/metric/SystemViewSelfTest.java | 398 +++++++++++++++++++++
 .../ServiceReassignmentFunctionSelfTest.java       |   3 +
 .../testframework/junits/GridAbstractTest.java     |   2 +-
 .../ignite/testsuites/IgniteCacheTestSuite9.java   |   6 +-
 .../processors/query/h2/SchemaManager.java         |   4 -
 .../h2/sys/view/SqlSystemViewCacheGroups.java      | 115 ------
 .../query/h2/sys/view/SqlSystemViewCaches.java     | 200 -----------
 ...iew.java => MetricRegistryLocalSystemView.java} |   6 +-
 ...orterSpi.java => SqlViewMetricExporterSpi.java} |   7 +-
 .../sql => systemview}/SqlViewExporterSpi.java     |  63 ++--
 .../ignite/spi/systemview/SystemViewLocal.java     | 237 ++++++++++++
 .../ignite/spi/systemview/package-info.java}       |  24 +-
 .../cache/metric/SqlViewExporterSpiTest.java       | 137 ++++++-
 .../processors/query/SqlSystemViewsSelfTest.java   |  25 +-
 .../IgniteBinaryCacheQueryTestSuite.java           |   5 +-
 .../opencensus/OpenCensusMetricExporterSpi.java    |  12 +-
 63 files changed, 3673 insertions(+), 651 deletions(-)

diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/client/router/ClientFailedInitSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/client/router/ClientFailedInitSelfTest.java
index 1124271..96bcad5 100644
--- a/modules/clients/src/test/java/org/apache/ignite/internal/client/router/ClientFailedInitSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/client/router/ClientFailedInitSelfTest.java
@@ -21,7 +21,6 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.compute.ComputeJob;
 import org.apache.ignite.compute.ComputeJobAdapter;
@@ -29,6 +28,7 @@ import org.apache.ignite.compute.ComputeJobResult;
 import org.apache.ignite.compute.ComputeTaskSplitAdapter;
 import org.apache.ignite.configuration.ConnectorConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.client.GridClient;
 import org.apache.ignite.internal.client.GridClientConfiguration;
 import org.apache.ignite.internal.client.GridClientDisconnectedException;
@@ -215,7 +215,7 @@ public class ClientFailedInitSelfTest extends GridCommonAbstractTest {
      * @return Grid.
      * @throws Exception If failed.
      */
-    @Override protected Ignite startGrid() throws Exception {
+    @Override protected IgniteEx startGrid() throws Exception {
         System.setProperty(IGNITE_JETTY_PORT, Integer.toString(JETTY_PORT));
 
         try {
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcMetadataSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcMetadataSelfTest.java
index cf4b774..64ae7a1 100755
--- a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcMetadataSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcMetadataSelfTest.java
@@ -35,7 +35,6 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
-import java.util.List;
 import java.util.Set;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.cache.QueryEntity;
@@ -320,7 +319,7 @@ public class JdbcMetadataSelfTest extends GridCommonAbstractTest {
      */
     @Test
     public void testGetAllView() throws Exception {
-        List<String> expViews = Arrays.asList(
+        Set<String> expViews = new HashSet<>(Arrays.asList(
             "BASELINE_NODES",
             "CACHES",
             "CACHE_GROUPS",
@@ -332,27 +331,31 @@ public class JdbcMetadataSelfTest extends GridCommonAbstractTest {
             "NODE_ATTRIBUTES",
             "NODE_METRICS",
             "SCHEMAS",
-            "TABLES"
-        );
+            "TABLES",
+            "TASKS",
+            "SERVICES"
+        ));
+
+        Set<String> actViews = new HashSet<>();
 
         try (Connection conn = DriverManager.getConnection(BASE_URL)) {
             DatabaseMetaData meta = conn.getMetaData();
 
             ResultSet rs = meta.getTables(null, null, "%", new String[]{"VIEW"});
 
-            for (String viewName : expViews) {
-                assertTrue(rs.next());
-
+            while (rs.next()) {
                 assertEquals("VIEW", rs.getString("TABLE_TYPE"));
 
                 assertEquals(JdbcUtils.CATALOG_NAME, rs.getString("TABLE_CAT"));
 
                 assertEquals(QueryUtils.SCHEMA_SYS, rs.getString("TABLE_SCHEM"));
 
-                assertEquals(viewName, rs.getString("TABLE_NAME"));
+                actViews.add(rs.getString("TABLE_NAME"));
             }
 
             assertFalse(rs.next());
+
+            assertEquals(expViews, actViews);
         }
     }
 
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 322339d..45d1f2b 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
@@ -35,7 +35,6 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import org.apache.ignite.IgniteCache;
@@ -48,8 +47,8 @@ import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.IgniteVersionUtils;
 import org.apache.ignite.internal.jdbc2.JdbcUtils;
 import org.apache.ignite.internal.processors.query.QueryEntityEx;
-import org.apache.ignite.internal.processors.query.QueryUtils;
 import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.spi.metric.sql.SqlViewMetricExporterSpi;
 import org.junit.Assert;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.junit.Test;
@@ -61,6 +60,8 @@ import static java.sql.Types.OTHER;
 import static java.sql.Types.VARCHAR;
 import static org.apache.ignite.cache.CacheMode.PARTITIONED;
 import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+import static org.apache.ignite.internal.processors.query.QueryUtils.DFLT_SCHEMA;
+import static org.apache.ignite.internal.processors.query.QueryUtils.SCHEMA_SYS;
 
 /**
  * Metadata tests.
@@ -71,7 +72,9 @@ public class JdbcThinMetadataSelfTest extends JdbcThinAbstractSelfTest {
 
     /** {@inheritDoc} */
     @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
-        return super.getConfiguration(igniteInstanceName).setSqlSchemas("PREDEFINED_SCHEMAS_1", "PREDEFINED_SCHEMAS_2");
+        return super.getConfiguration(igniteInstanceName)
+            .setSqlSchemas("PREDEFINED_SCHEMAS_1", "PREDEFINED_SCHEMAS_2")
+            .setMetricExporterSpi(new SqlViewMetricExporterSpi());
     }
 
     /**
@@ -325,17 +328,20 @@ public class JdbcThinMetadataSelfTest extends JdbcThinAbstractSelfTest {
         testGetTables(
             new String[] {"VIEW"},
             new HashSet<>(Arrays.asList(
+                "SYS.METRICS",
+                "SYS.SERVICES",
+                "SYS.CACHE_GROUPS",
+                "SYS.CACHES",
+                "SYS.TASKS",
                 "SYS.LOCAL_SQL_QUERY_HISTORY",
                 "SYS.NODES",
                 "SYS.SCHEMAS",
-                "SYS.CACHE_GROUPS",
                 "SYS.NODE_METRICS",
                 "SYS.BASELINE_NODES",
                 "SYS.INDEXES",
                 "SYS.LOCAL_CACHE_GROUPS_IO",
                 "SYS.LOCAL_SQL_RUNNING_QUERIES",
                 "SYS.NODE_ATTRIBUTES",
-                "SYS.CACHES",
                 "SYS.TABLES"
             ))
         );
@@ -485,7 +491,7 @@ public class JdbcThinMetadataSelfTest extends JdbcThinAbstractSelfTest {
 
             ResultSet rs = meta.getColumns(null, null, null, null);
 
-            List<String> expectedCols = Arrays.asList(
+            Set<String> expectedCols = new HashSet<>(Arrays.asList(
                 "PUBLIC.Quoted.Id.null",
                 "PUBLIC.Quoted.Name.null.50",
                 "PUBLIC.TEST.ID.null",
@@ -506,11 +512,11 @@ public class JdbcThinMetadataSelfTest extends JdbcThinAbstractSelfTest {
                 "pers.PERSON.NAME.null",
                 "pers.PERSON.AGE.null",
                 "pers.PERSON.ORGID.null"
-            );
+            ));
 
-            List<String> actualUserCols = new ArrayList<>(expectedCols.size());
+            Set<String> actualUserCols = new HashSet<>(expectedCols.size());
 
-            List<String> actualSystemCols = new ArrayList<>();
+            Set<String> actualSystemCols = new HashSet<>();
 
             while(rs.next()) {
                 int precision = rs.getInt("COLUMN_SIZE");
@@ -526,7 +532,7 @@ public class JdbcThinMetadataSelfTest extends JdbcThinAbstractSelfTest {
                     + (precision == 0 ? "" : ("." + precision))
                     + (scale == 0 ? "" : ("." + scale));
 
-                if (!schemaName.equals(QueryUtils.SCHEMA_SYS))
+                if (!schemaName.equals(SCHEMA_SYS))
                     actualUserCols.add(colDefinition);
                 else
                     actualSystemCols.add(colDefinition);
@@ -534,7 +540,7 @@ public class JdbcThinMetadataSelfTest extends JdbcThinAbstractSelfTest {
 
             Assert.assertEquals(expectedCols, actualUserCols);
 
-            expectedCols = Arrays.asList(
+            expectedCols = new HashSet<>(Arrays.asList(
                 "SYS.BASELINE_NODES.CONSISTENT_ID.null.2147483647",
                 "SYS.BASELINE_NODES.ONLINE.null.1",
                 "SYS.CACHES.CACHE_GROUP_ID.null.10",
@@ -573,7 +579,7 @@ public class JdbcThinMetadataSelfTest extends JdbcThinAbstractSelfTest {
                 "SYS.CACHES.NEAR_CACHE_EVICTION_POLICY_FACTORY.null.2147483647",
                 "SYS.CACHES.NEAR_CACHE_START_SIZE.null.10",
                 "SYS.CACHES.DEFAULT_LOCK_TIMEOUT.null.19",
-                "SYS.CACHES.CACHE_INTERCEPTOR.null.2147483647",
+                "SYS.CACHES.INTERCEPTOR.null.2147483647",
                 "SYS.CACHES.CACHE_STORE_FACTORY.null.2147483647",
                 "SYS.CACHES.IS_STORE_KEEP_BINARY.null.1",
                 "SYS.CACHES.IS_READ_THROUGH.null.1",
@@ -583,17 +589,18 @@ public class JdbcThinMetadataSelfTest extends JdbcThinAbstractSelfTest {
                 "SYS.CACHES.WRITE_BEHIND_FLUSH_SIZE.null.10",
                 "SYS.CACHES.WRITE_BEHIND_FLUSH_FREQUENCY.null.19",
                 "SYS.CACHES.WRITE_BEHIND_FLUSH_THREAD_COUNT.null.10",
-                "SYS.CACHES.WRITE_BEHIND_FLUSH_BATCH_SIZE.null.10",
+                "SYS.CACHES.WRITE_BEHIND_BATCH_SIZE.null.10",
                 "SYS.CACHES.MAX_CONCURRENT_ASYNC_OPERATIONS.null.10",
                 "SYS.CACHES.CACHE_LOADER_FACTORY.null.2147483647",
                 "SYS.CACHES.CACHE_WRITER_FACTORY.null.2147483647",
                 "SYS.CACHES.EXPIRY_POLICY_FACTORY.null.2147483647",
                 "SYS.CACHES.IS_SQL_ESCAPE_ALL.null.1",
+                "SYS.CACHES.IS_ENCRYPTION_ENABLED.null.1",
                 "SYS.CACHES.SQL_SCHEMA.null.2147483647",
                 "SYS.CACHES.SQL_INDEX_MAX_INLINE_SIZE.null.10",
                 "SYS.CACHES.IS_SQL_ONHEAP_CACHE_ENABLED.null.1",
                 "SYS.CACHES.SQL_ONHEAP_CACHE_MAX_SIZE.null.10",
-                "SYS.CACHES.QUERY_DETAILS_METRICS_SIZE.null.10",
+                "SYS.CACHES.QUERY_DETAIL_METRICS_SIZE.null.10",
                 "SYS.CACHES.QUERY_PARALLELISM.null.10",
                 "SYS.CACHES.MAX_QUERY_ITERATORS_COUNT.null.10",
                 "SYS.CACHES.DATA_REGION_NAME.null.2147483647",
@@ -722,10 +729,35 @@ public class JdbcThinMetadataSelfTest extends JdbcThinAbstractSelfTest {
                 "SYS.TABLES.KEY_ALIAS.null.2147483647",
                 "SYS.TABLES.VALUE_ALIAS.null.2147483647",
                 "SYS.TABLES.KEY_TYPE_NAME.null.2147483647",
-                "SYS.TABLES.VALUE_TYPE_NAME.null.2147483647"
-            );
-            Assert.assertEquals(expectedCols, actualSystemCols);
+                "SYS.TABLES.VALUE_TYPE_NAME.null.2147483647",
+                "SYS.METRICS.NAME.null.2147483647",
+                "SYS.METRICS.VALUE.null.2147483647",
+                "SYS.METRICS.DESCRIPTION.null.2147483647",
+                "SYS.SERVICES.SERVICE_ID.null.2147483647",
+                "SYS.SERVICES.NAME.null.2147483647",
+                "SYS.SERVICES.SERVICE_CLASS.null.2147483647",
+                "SYS.SERVICES.CACHE_NAME.null.2147483647",
+                "SYS.SERVICES.ORIGIN_NODE_ID.null.2147483647",
+                "SYS.SERVICES.TOTAL_COUNT.null.10",
+                "SYS.SERVICES.MAX_PER_NODE_COUNT.null.10",
+                "SYS.SERVICES.AFFINITY_KEY.null.2147483647",
+                "SYS.SERVICES.NODE_FILTER.null.2147483647",
+                "SYS.SERVICES.STATICALLY_CONFIGURED.null.1",
+                "SYS.SERVICES.SERVICE_ID.null.2147483647",
+                "SYS.TASKS.AFFINITY_CACHE_NAME.null.2147483647",
+                "SYS.TASKS.INTERNAL.null.1",
+                "SYS.TASKS.END_TIME.null.19",
+                "SYS.TASKS.START_TIME.null.19",
+                "SYS.TASKS.USER_VERSION.null.2147483647",
+                "SYS.TASKS.TASK_NAME.null.2147483647",
+                "SYS.TASKS.TASK_NODE_ID.null.2147483647",
+                "SYS.TASKS.JOB_ID.null.2147483647",
+                "SYS.TASKS.AFFINITY_PARTITION_ID.null.10",
+                "SYS.TASKS.TASK_CLASS_NAME.null.2147483647",
+                "SYS.TASKS.EXEC_NAME.null.2147483647"
+            ));
 
+            Assert.assertEquals(expectedCols, actualSystemCols);
         }
     }
 
@@ -990,8 +1022,8 @@ public class JdbcThinMetadataSelfTest extends JdbcThinAbstractSelfTest {
         try (Connection conn = DriverManager.getConnection(URL)) {
             ResultSet rs = conn.getMetaData().getSchemas();
 
-            Set<String> expectedSchemas = new HashSet<>(Arrays.asList("SYS", "PUBLIC", "pers",
-                "org", "dep", "PREDEFINED_SCHEMAS_1", "PREDEFINED_SCHEMAS_2"));
+            Set<String> expectedSchemas = new HashSet<>(Arrays.asList(SCHEMA_SYS, DFLT_SCHEMA,
+                "pers", "org", "dep", "PREDEFINED_SCHEMAS_1", "PREDEFINED_SCHEMAS_2"));
 
             Set<String> schemas = new HashSet<>();
 
diff --git a/modules/codegen/src/main/java/org/apache/ignite/codegen/MessageCodeGenerator.java b/modules/codegen/src/main/java/org/apache/ignite/codegen/MessageCodeGenerator.java
index 186c87c..3bc7e75 100644
--- a/modules/codegen/src/main/java/org/apache/ignite/codegen/MessageCodeGenerator.java
+++ b/modules/codegen/src/main/java/org/apache/ignite/codegen/MessageCodeGenerator.java
@@ -69,7 +69,7 @@ public class MessageCodeGenerator {
     };
 
     /** */
-    private static final String DFLT_SRC_DIR = U.getIgniteHome() + "/modules/core/src/main/java";
+    public static final String DFLT_SRC_DIR = U.getIgniteHome() + "/modules/core/src/main/java";
 
     /** */
     private static final String INDEXING_SRC_DIR = U.getIgniteHome() + "/modules/indexing/src/main/java";
@@ -81,7 +81,7 @@ public class MessageCodeGenerator {
     private static final String EMPTY = "";
 
     /** */
-    private static final String TAB = "    ";
+    public static final String TAB = "    ";
 
     /** */
     private static final String BUF_VAR = "buf";
diff --git a/modules/codegen/src/main/java/org/apache/ignite/codegen/SystemViewRowAttributeWalkerGenerator.java b/modules/codegen/src/main/java/org/apache/ignite/codegen/SystemViewRowAttributeWalkerGenerator.java
new file mode 100644
index 0000000..1998285
--- /dev/null
+++ b/modules/codegen/src/main/java/org/apache/ignite/codegen/SystemViewRowAttributeWalkerGenerator.java
@@ -0,0 +1,269 @@
+/*
+ * 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.codegen;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.function.ObjIntConsumer;
+import org.apache.ignite.internal.managers.systemview.walker.Order;
+import org.apache.ignite.spi.systemview.jmx.SystemViewMBean;
+import org.apache.ignite.spi.systemview.view.SystemView;
+import org.apache.ignite.spi.systemview.view.SystemViewRowAttributeWalker;
+import org.apache.ignite.spi.systemview.view.CacheGroupView;
+import org.apache.ignite.spi.systemview.view.CacheView;
+import org.apache.ignite.spi.systemview.view.ComputeTaskView;
+import org.apache.ignite.spi.systemview.view.ServiceView;
+import org.apache.ignite.spi.systemview.SystemViewLocal;
+
+import static org.apache.ignite.codegen.MessageCodeGenerator.DFLT_SRC_DIR;
+
+/**
+ * Application for code generation of {@link SystemViewRowAttributeWalker}.
+ * Usage: simply run main method from Ignite source folder(using IDE or other way).
+ * Generated code used in {@link SystemView}.
+ *
+ * @see SystemViewMBean
+ * @see SystemViewLocal
+ */
+public class SystemViewRowAttributeWalkerGenerator {
+    /** Methods that should be excluded from specific {@link SystemViewRowAttributeWalker}. */
+    private static final Set<String> SYS_METHODS = new HashSet<>(Arrays.asList("equals", "hashCode", "toString",
+        "getClass"));
+
+    /** Package for {@link SystemViewRowAttributeWalker} implementations. */
+    public static final String WALKER_PACKAGE = "org.apache.ignite.internal.managers.systemview.walker";
+
+    /** */
+    public static final String TAB = "    ";
+
+    /**
+     * @throws Exception If generation failed.
+     */
+    public static void main(String[] args) throws Exception {
+        SystemViewRowAttributeWalkerGenerator gen = new SystemViewRowAttributeWalkerGenerator();
+
+        gen.generateAndWrite(CacheGroupView.class, DFLT_SRC_DIR);
+        gen.generateAndWrite(CacheView.class, DFLT_SRC_DIR);
+        gen.generateAndWrite(ServiceView.class, DFLT_SRC_DIR);
+        gen.generateAndWrite(ComputeTaskView.class, DFLT_SRC_DIR);
+    }
+
+    /**
+     * Generates {@link SystemViewRowAttributeWalker} implementation and write it to the file.
+     *
+     * @param clazz Class to geneare {@link SystemViewRowAttributeWalker} for.
+     * @param srcRoot Source root folder.
+     * @param <T> type of the row.
+     * @throws IOException If generation failed.
+     */
+    private <T> void generateAndWrite(Class<T> clazz, String srcRoot) throws IOException {
+        File walkerClass = new File(srcRoot + '/' + WALKER_PACKAGE.replaceAll("\\.", "/") + '/' +
+            clazz.getSimpleName() + "Walker.java");
+
+        Collection<String> code = generate(clazz);
+
+        walkerClass.createNewFile();
+
+        try (FileWriter writer = new FileWriter(walkerClass)) {
+            for (String line : code) {
+                writer.write(line);
+                writer.write('\n');
+            }
+        }
+    }
+
+    /**
+     * Generates {@link SystemViewRowAttributeWalker} implementation.
+     *
+     * @param clazz Class to geneare {@link SystemViewRowAttributeWalker} for.
+     * @param <T> type of the row.
+     * @return Java source code of the {@link SystemViewRowAttributeWalker} implementation.
+     */
+    private <T> Collection<String> generate(Class<T> clazz) {
+        final List<String> code = new ArrayList<>();
+        final Set<String> imports = new TreeSet<>();
+
+        imports.add("import " + SystemViewRowAttributeWalker.class.getName() + ';');
+        imports.add("import " + clazz.getName() + ';');
+
+        String simpleName = clazz.getSimpleName();
+
+        code.add("package " + WALKER_PACKAGE + ";");
+        code.add("");
+        code.add("");
+        code.add("/**");
+        code.add(" * Generated by {@code " + SystemViewRowAttributeWalkerGenerator.class.getName() + "}.");
+        code.add(" * {@link " + simpleName + "} attributes walker.");
+        code.add(" * ");
+        code.add(" * @see " + simpleName);
+        code.add(" */");
+        code.add("public class " + simpleName + "Walker implements SystemViewRowAttributeWalker<" + simpleName + "> {");
+        code.add(TAB + "/** {@inheritDoc} */");
+        code.add(TAB + "@Override public void visitAll(AttributeVisitor v) {");
+
+        forEachMethod(clazz, (m, i) -> {
+            String name = m.getName();
+
+            Class<?> retClazz = m.getReturnType();
+
+            String line = TAB + TAB;
+
+            if (!retClazz.isPrimitive() && !retClazz.getName().startsWith("java.lang"))
+                imports.add("import " + retClazz.getName() + ';');
+
+            line += "v.accept(" + i + ", \"" + name + "\", " + retClazz.getSimpleName() + ".class);";
+
+            code.add(line);
+        });
+
+        code.add(TAB + "}");
+        code.add("");
+        code.add(TAB + "/** {@inheritDoc} */");
+        code.add(TAB + "@Override public void visitAll(" + simpleName + " row, AttributeWithValueVisitor v) {");
+
+        forEachMethod(clazz, (m, i) -> {
+            String name = m.getName();
+
+            Class<?> retClazz = m.getReturnType();
+
+            String line = TAB + TAB;
+
+            if (!retClazz.isPrimitive()) {
+                line += "v.accept(" + i + ", \"" + name + "\", " + retClazz.getSimpleName() + ".class, row." + m.getName() + "());";
+            }
+            else if (retClazz == boolean.class)
+                line += "v.acceptBoolean(" + i + ", \"" + name + "\", row." + m.getName() + "());";
+            else if (retClazz == char.class)
+                line += "v.acceptChar(" + i + ", \"" + name + "\", row." + m.getName() + "());";
+            else if (retClazz == byte.class)
+                line += "v.acceptByte(" + i + ", \"" + name + "\", row." + m.getName() + "());";
+            else if (retClazz == short.class)
+                line += "v.acceptShort(" + i + ", \"" + name + "\", row." + m.getName() + "());";
+            else if (retClazz == int.class)
+                line += "v.acceptInt(" + i + ", \"" + name + "\", row." + m.getName() + "());";
+            else if (retClazz == long.class)
+                line += "v.acceptLong(" + i + ", \"" + name + "\", row." + m.getName() + "());";
+            else if (retClazz == float.class)
+                line += "v.acceptFloat(" + i + ", \"" + name + "\", row." + m.getName() + "());";
+            else if (retClazz == double.class)
+                line += "v.acceptDouble(" + i + ", \"" + name + "\", row." + m.getName() + "());";
+
+            code.add(line);
+        });
+
+        code.add(TAB + "}");
+        code.add("");
+
+        final int[] cnt = {0};
+        forEachMethod(clazz, (m, i) -> cnt[0]++);
+
+        code.add(TAB + "/** {@inheritDoc} */");
+        code.add(TAB + "@Override public int count() {");
+        code.add(TAB + TAB + "return " + cnt[0] + ';');
+        code.add(TAB + "}");
+        code.add("}");
+
+        code.addAll(2, imports);
+
+        addLicenseHeader(code);
+
+        return code;
+    }
+
+    /**
+     * Adds Apache License Header to the source code.
+     *
+     * @param code Source code.
+     */
+    private void addLicenseHeader(List<String> code) {
+        List<String> lic = new ArrayList<>();
+
+        lic.add("/*");
+        lic.add(" * Licensed to the Apache Software Foundation (ASF) under one or more");
+        lic.add(" * contributor license agreements.  See the NOTICE file distributed with");
+        lic.add(" * this work for additional information regarding copyright ownership.");
+        lic.add(" * The ASF licenses this file to You under the Apache License, Version 2.0");
+        lic.add(" * (the \"License\"); you may not use this file except in compliance with");
+        lic.add(" * the License.  You may obtain a copy of the License at");
+        lic.add(" *");
+        lic.add(" *      http://www.apache.org/licenses/LICENSE-2.0");
+        lic.add(" *");
+        lic.add(" * Unless required by applicable law or agreed to in writing, software");
+        lic.add(" * distributed under the License is distributed on an \"AS IS\" BASIS,");
+        lic.add(" * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.");
+        lic.add(" * See the License for the specific language governing permissions and");
+        lic.add(" * limitations under the License.");
+        lic.add(" */");
+        lic.add("");
+
+        code.addAll(0, lic);
+    }
+
+    /**
+     * Iterates each method of the {@code clazz} and call consume {@code c} for it.
+     *
+     * @param c Method consumer.
+     */
+    private void forEachMethod(Class<?> clazz, ObjIntConsumer<Method> c) {
+        Method[] methods = clazz.getMethods();
+
+        List<Method> notOrdered = new ArrayList<>();
+        List<Method> ordered = new ArrayList<>();
+
+        for (int i = 0; i < methods.length; i++) {
+            Method m = methods[i];
+
+            if (Modifier.isStatic(m.getModifiers()))
+                continue;
+
+            if (SYS_METHODS.contains(m.getName()))
+                continue;
+
+            Class<?> retClazz = m.getReturnType();
+
+            if (retClazz == void.class)
+                continue;
+
+            if (m.getAnnotation(Order.class) != null)
+                ordered.add(m);
+            else
+                notOrdered.add(m);
+        }
+
+        Collections.sort(ordered, Comparator.comparingInt(m -> m.getAnnotation(Order.class).value()));
+        Collections.sort(notOrdered, Comparator.comparing(Method::getName));
+
+        for (int i = 0; i < ordered.size(); i++)
+            c.accept(ordered.get(i), i);
+
+        for (int i = 0; i < notOrdered.size(); i++)
+            c.accept(notOrdered.get(i), i + ordered.size());
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/IgniteConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/IgniteConfiguration.java
index a1c18d9..664fd12 100644
--- a/modules/core/src/main/java/org/apache/ignite/configuration/IgniteConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/IgniteConfiguration.java
@@ -78,6 +78,7 @@ import org.apache.ignite.spi.indexing.IndexingSpi;
 import org.apache.ignite.spi.loadbalancing.LoadBalancingSpi;
 import org.apache.ignite.spi.loadbalancing.roundrobin.RoundRobinLoadBalancingSpi;
 import org.apache.ignite.spi.metric.MetricExporterSpi;
+import org.apache.ignite.spi.systemview.SystemViewExporterSpi;
 import org.apache.ignite.ssl.SslContextFactory;
 import org.jetbrains.annotations.Nullable;
 
@@ -400,6 +401,9 @@ public class IgniteConfiguration {
     /** Metric exporter SPI. */
     private MetricExporterSpi[] metricExporterSpi;
 
+    /** System view exporter SPI. */
+    private SystemViewExporterSpi[] sysViewExporterSpi;
+
     /** Cache configurations. */
     private CacheConfiguration[] cacheCfg;
 
@@ -590,6 +594,7 @@ public class IgniteConfiguration {
         indexingSpi = cfg.getIndexingSpi();
         encryptionSpi = cfg.getEncryptionSpi();
         metricExporterSpi = cfg.getMetricExporterSpi();
+        sysViewExporterSpi = cfg.getSystemViewExporterSpi();
 
         commFailureRslvr = cfg.getCommunicationFailureResolver();
 
@@ -2337,8 +2342,8 @@ public class IgniteConfiguration {
      * Sets fully configured instances of {@link MetricExporterSpi}.
      *
      * @param metricExporterSpi Fully configured instances of {@link MetricExporterSpi}.
-     * @see IgniteConfiguration#getMetricExporterSpi()
      * @return {@code this} for chaining.
+     * @see IgniteConfiguration#getMetricExporterSpi()
      */
     public IgniteConfiguration setMetricExporterSpi(MetricExporterSpi... metricExporterSpi) {
         this.metricExporterSpi = metricExporterSpi;
@@ -2347,7 +2352,20 @@ public class IgniteConfiguration {
     }
 
     /**
-     * Gets fully configured monitoring SPI implementations.
+     * Sets fully configured instances of {@link SystemViewExporterSpi}.
+     *
+     * @param sysViewExporterSpi Fully configured instances of {@link SystemViewExporterSpi}.
+     * @return {@code this} for chaining.
+     * @see IgniteConfiguration#getSystemViewExporterSpi()
+     */
+    public IgniteConfiguration setSystemViewExporterSpi(SystemViewExporterSpi... sysViewExporterSpi) {
+        this.sysViewExporterSpi = sysViewExporterSpi;
+
+        return this;
+    }
+
+    /**
+     * Gets fully configured metric SPI implementations.
      *
      * @return Metric exporter SPI implementations.
      */
@@ -2356,6 +2374,15 @@ public class IgniteConfiguration {
     }
 
     /**
+     * Gets fully configured system view SPI implementations.
+     *
+     * @return System view exporter SPI implementations.
+     */
+    public SystemViewExporterSpi[] getSystemViewExporterSpi() {
+        return sysViewExporterSpi;
+    }
+
+    /**
      * Gets address resolver for addresses mapping determination.
      *
      * @return Address resolver.
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContext.java b/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContext.java
index cae8e67..0e4549d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContext.java
@@ -33,6 +33,7 @@ import org.apache.ignite.internal.managers.eventstorage.GridEventStorageManager;
 import org.apache.ignite.internal.managers.failover.GridFailoverManager;
 import org.apache.ignite.internal.managers.indexing.GridIndexingManager;
 import org.apache.ignite.internal.managers.loadbalancer.GridLoadBalancerManager;
+import org.apache.ignite.internal.managers.systemview.GridSystemViewManager;
 import org.apache.ignite.internal.processors.affinity.GridAffinityProcessor;
 import org.apache.ignite.internal.processors.authentication.IgniteAuthenticationProcessor;
 import org.apache.ignite.internal.processors.cache.GridCacheProcessor;
@@ -202,6 +203,13 @@ public interface GridKernalContext extends Iterable<GridComponent> {
     public GridMetricManager metric();
 
     /**
+     * Gets system view manager.
+     *
+     * @return Monitoring manager.
+     */
+    public GridSystemViewManager systemView();
+
+    /**
      * Gets caches processor.
      *
      * @return Cache processor.
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java
index 2f8e3fd..eb56c5d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java
@@ -48,6 +48,7 @@ import org.apache.ignite.internal.managers.eventstorage.GridEventStorageManager;
 import org.apache.ignite.internal.managers.failover.GridFailoverManager;
 import org.apache.ignite.internal.managers.indexing.GridIndexingManager;
 import org.apache.ignite.internal.managers.loadbalancer.GridLoadBalancerManager;
+import org.apache.ignite.internal.managers.systemview.GridSystemViewManager;
 import org.apache.ignite.internal.processors.affinity.GridAffinityProcessor;
 import org.apache.ignite.internal.processors.authentication.IgniteAuthenticationProcessor;
 import org.apache.ignite.internal.processors.cache.CacheConflictResolutionManager;
@@ -211,6 +212,10 @@ public class GridKernalContextImpl implements GridKernalContext, Externalizable
 
     /** */
     @GridToStringInclude
+    private GridSystemViewManager sysViewMgr;
+
+    /** */
+    @GridToStringInclude
     private GridClosureProcessor closProc;
 
     /** */
@@ -631,6 +636,8 @@ public class GridKernalContextImpl implements GridKernalContext, Externalizable
             jobMetricsProc = (GridJobMetricsProcessor)comp;
         else if (comp instanceof GridMetricManager)
             metricMgr = (GridMetricManager)comp;
+        else if (comp instanceof GridSystemViewManager)
+            sysViewMgr = (GridSystemViewManager)comp;
         else if (comp instanceof GridCacheProcessor)
             cacheProc = (GridCacheProcessor)comp;
         else if (comp instanceof GridClusterStateProcessor)
@@ -788,6 +795,11 @@ public class GridKernalContextImpl implements GridKernalContext, Externalizable
     }
 
     /** {@inheritDoc} */
+    @Override public GridSystemViewManager systemView() {
+        return sysViewMgr;
+    }
+
+    /** {@inheritDoc} */
     @Override public GridCacheProcessor cache() {
         return cacheProc;
     }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/IgniteComponentType.java b/modules/core/src/main/java/org/apache/ignite/internal/IgniteComponentType.java
index 5415f8f..5e65876 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/IgniteComponentType.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/IgniteComponentType.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal;
 import java.lang.reflect.Constructor;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.internal.processors.compress.CompressionProcessor;
+import org.apache.ignite.internal.util.IgniteUtils;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.plugin.extensions.communication.MessageFactory;
 import org.jetbrains.annotations.Nullable;
@@ -156,14 +157,7 @@ public enum IgniteComponentType {
      * @return {@code True} if in classpath.
      */
     public boolean inClassPath() {
-        try {
-            Class.forName(clsName);
-
-            return true;
-        }
-        catch (ClassNotFoundException ignore) {
-            return false;
-        }
+        return IgniteUtils.inClassPath(clsName);
     }
 
     /**
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 b96a683..391dd9f 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
@@ -121,6 +121,7 @@ import org.apache.ignite.internal.managers.eventstorage.GridEventStorageManager;
 import org.apache.ignite.internal.managers.failover.GridFailoverManager;
 import org.apache.ignite.internal.managers.indexing.GridIndexingManager;
 import org.apache.ignite.internal.managers.loadbalancer.GridLoadBalancerManager;
+import org.apache.ignite.internal.managers.systemview.GridSystemViewManager;
 import org.apache.ignite.internal.marshaller.optimized.OptimizedMarshaller;
 import org.apache.ignite.internal.processors.GridProcessor;
 import org.apache.ignite.internal.processors.GridProcessorAdapter;
@@ -1168,6 +1169,7 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
             // Start SPI managers.
             // NOTE: that order matters as there are dependencies between managers.
             startManager(new GridMetricManager(ctx));
+            startManager(new GridSystemViewManager(ctx));
             startManager(new GridIoManager(ctx));
             startManager(new GridCheckpointManager(ctx));
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/IgnitionEx.java b/modules/core/src/main/java/org/apache/ignite/internal/IgnitionEx.java
index 89e0026..ea990db 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/IgnitionEx.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/IgnitionEx.java
@@ -123,6 +123,7 @@ import org.apache.ignite.spi.indexing.noop.NoopIndexingSpi;
 import org.apache.ignite.spi.loadbalancing.LoadBalancingSpi;
 import org.apache.ignite.spi.loadbalancing.roundrobin.RoundRobinLoadBalancingSpi;
 import org.apache.ignite.spi.metric.noop.NoopMetricExporterSpi;
+import org.apache.ignite.spi.systemview.jmx.JmxSystemViewExporterSpi;
 import org.apache.ignite.thread.IgniteStripedThreadPoolExecutor;
 import org.apache.ignite.thread.IgniteThread;
 import org.apache.ignite.thread.IgniteThreadPoolExecutor;
@@ -177,6 +178,9 @@ public class IgnitionEx {
     /** Default configuration path relative to Ignite home. */
     public static final String DFLT_CFG = "config/default-config.xml";
 
+    /** Class name for a SQL view exporter of system views. */
+    public static final String SYSTEM_VIEW_SQL_SPI = "org.apache.ignite.spi.systemview.SqlViewExporterSpi";
+
     /** Map of named Ignite instances. */
     private static final ConcurrentMap<Object, IgniteNamedInstance> grids = new ConcurrentHashMap<>();
 
@@ -2464,6 +2468,21 @@ public class IgnitionEx {
 
             if (F.isEmpty(cfg.getMetricExporterSpi()))
                 cfg.setMetricExporterSpi(new NoopMetricExporterSpi());
+
+            if (F.isEmpty(cfg.getSystemViewExporterSpi())) {
+                if (IgniteUtils.inClassPath(SYSTEM_VIEW_SQL_SPI)) {
+                    try {
+                        cfg.setSystemViewExporterSpi(new JmxSystemViewExporterSpi(),
+                            U.newInstance(SYSTEM_VIEW_SQL_SPI));
+                    }
+                    catch (IgniteCheckedException e) {
+                        throw new IgniteException(e);
+                    }
+
+                }
+                else
+                    cfg.setSystemViewExporterSpi(new JmxSystemViewExporterSpi());
+            }
         }
 
         /**
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/GridSystemViewManager.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/GridSystemViewManager.java
new file mode 100644
index 0000000..a9fcad3
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/GridSystemViewManager.java
@@ -0,0 +1,147 @@
+/*
+ * 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.managers.systemview;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.managers.GridManagerAdapter;
+import org.apache.ignite.internal.managers.systemview.walker.CacheGroupViewWalker;
+import org.apache.ignite.internal.managers.systemview.walker.CacheViewWalker;
+import org.apache.ignite.internal.managers.systemview.walker.ComputeTaskViewWalker;
+import org.apache.ignite.internal.managers.systemview.walker.ServiceViewWalker;
+import org.apache.ignite.spi.systemview.ReadOnlySystemViewRegistry;
+import org.apache.ignite.spi.systemview.SystemViewExporterSpi;
+import org.apache.ignite.spi.systemview.view.CacheGroupView;
+import org.apache.ignite.spi.systemview.view.CacheView;
+import org.apache.ignite.spi.systemview.view.ComputeTaskView;
+import org.apache.ignite.spi.systemview.view.ServiceView;
+import org.apache.ignite.spi.systemview.view.SystemView;
+import org.apache.ignite.spi.systemview.view.SystemViewRowAttributeWalker;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import static org.apache.ignite.internal.util.IgniteUtils.notifyListeners;
+
+/**
+ * This manager should provide {@link ReadOnlySystemViewRegistry} for each configured {@link SystemViewExporterSpi}.
+ *
+ * @see SystemView
+ * @see SystemViewAdapter
+ */
+public class GridSystemViewManager extends GridManagerAdapter<SystemViewExporterSpi>
+    implements ReadOnlySystemViewRegistry {
+    /** Registered system views. */
+    private final ConcurrentHashMap<String, SystemView<?>> systemViews = new ConcurrentHashMap<>();
+
+    /** System views creation listeners. */
+    private final List<Consumer<SystemView<?>>> viewCreationLsnrs = new CopyOnWriteArrayList<>();
+
+    /** Registered walkers for view row. */
+    private final Map<Class<?>, SystemViewRowAttributeWalker<?>> walkers = new HashMap<>();
+
+    /**
+     * @param ctx Kernal context.
+     */
+    public GridSystemViewManager(GridKernalContext ctx) {
+        super(ctx, ctx.config().getSystemViewExporterSpi());
+
+        registerWalker(CacheGroupView.class, new CacheGroupViewWalker());
+        registerWalker(CacheView.class, new CacheViewWalker());
+        registerWalker(ServiceView.class, new ServiceViewWalker());
+        registerWalker(ComputeTaskView.class, new ComputeTaskViewWalker());
+    }
+
+    /** {@inheritDoc} */
+    @Override public void start() throws IgniteCheckedException {
+        for (SystemViewExporterSpi spi : getSpis())
+            spi.setSystemViewRegistry(this);
+
+        startSpi();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void stop(boolean cancel) throws IgniteCheckedException {
+        stopSpi();
+    }
+
+    /**
+     * Registers view which exports {@link Collection} content.
+     *
+     * @param name Name.
+     * @param desc Description.
+     * @param rowCls Row class.
+     * @param data Data.
+     * @param rowFunc value to row function.
+     * @param <R> View row type.
+     * @param <D> Collection data type.
+     */
+    public <R, D> void registerView(String name, String desc, Class<R> rowCls, Collection<D> data,
+        Function<D, R> rowFunc) {
+
+        SystemView sysView = new SystemViewAdapter<>(name,
+            desc,
+            rowCls,
+            (SystemViewRowAttributeWalker<R>)walkers.get(rowCls),
+            data,
+            rowFunc);
+
+        SystemView<?> old = systemViews.putIfAbsent(name, sysView);
+
+        assert old == null;
+
+        notifyListeners(sysView, viewCreationLsnrs, log);
+    }
+
+    /**
+     * @param name Name of the view.
+     * @return List.
+     */
+    @Nullable public <R> SystemView<R> view(String name) {
+        return (SystemView<R>)systemViews.get(name);
+    }
+
+    /**
+     * Registers walker for specified class.
+     *
+     * @param rowClass Row class.
+     * @param walker Walker.
+     * @param <R> Row type.
+     */
+    public <R> void registerWalker(Class<R> rowClass, SystemViewRowAttributeWalker<R> walker) {
+        walkers.put(rowClass, walker);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void addSystemViewCreationListener(Consumer<SystemView<?>> lsnr) {
+        viewCreationLsnrs.add(lsnr);
+    }
+
+    /** {@inheritDoc} */
+    @NotNull @Override public Iterator<SystemView<?>> iterator() {
+        return systemViews.values().iterator();
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/SystemViewAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/SystemViewAdapter.java
new file mode 100644
index 0000000..dec8a97
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/SystemViewAdapter.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.managers.systemview;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.function.Function;
+import org.apache.ignite.internal.util.typedef.internal.A;
+import org.apache.ignite.spi.systemview.view.SystemView;
+import org.apache.ignite.spi.systemview.view.SystemViewRowAttributeWalker;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * System view backed by {@code data} {@link Collection}.
+ */
+public class SystemViewAdapter<R, D> implements SystemView<R> {
+    /** Data backed by this view. */
+    private final Collection<D> data;
+
+    /** Row function. */
+    private final Function<D, R> rowFunc;
+
+    /** Name of the view. */
+    private final String name;
+
+    /** Description of the view. */
+    private final String desc;
+
+    /** Class of the row */
+    private final Class<R> rowCls;
+
+    /**
+     * Row attribute walker.
+     *
+     * @see "org.apache.ignite.codegen.MonitoringRowAttributeWalkerGenerator"
+     */
+    private final SystemViewRowAttributeWalker<R> walker;
+
+    /**
+     * @param name Name.
+     * @param desc Description.
+     * @param rowCls Row class.
+     * @param walker Walker.
+     * @param data Data.
+     * @param rowFunc Row function.
+     */
+    public SystemViewAdapter(String name, String desc, Class<R> rowCls,
+        SystemViewRowAttributeWalker<R> walker, Collection<D> data, Function<D, R> rowFunc) {
+        A.notNull(rowCls, "rowCls");
+        A.notNull(walker, "walker");
+
+        this.name = name;
+        this.desc = desc;
+        this.rowCls = rowCls;
+        this.walker = walker;
+
+        this.data = data;
+        this.rowFunc = rowFunc;
+    }
+
+    /** {@inheritDoc} */
+    @NotNull @Override public Iterator<R> iterator() {
+        Iterator<D> data = this.data.iterator();
+
+        return new Iterator<R>() {
+            @Override public boolean hasNext() {
+                return data.hasNext();
+            }
+
+            @Override public R next() {
+                return rowFunc.apply(data.next());
+            }
+        };
+    }
+
+    /** {@inheritDoc} */
+    @Override public SystemViewRowAttributeWalker<R> walker() {
+        return walker;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Class<R> rowClass() {
+        return rowCls;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String name() {
+        return name;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String description() {
+        return desc;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int size() {
+        return data.size();
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/walker/CacheGroupViewWalker.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/walker/CacheGroupViewWalker.java
new file mode 100644
index 0000000..a4b6411
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/walker/CacheGroupViewWalker.java
@@ -0,0 +1,78 @@
+/*
+ * 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.managers.systemview.walker;
+
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.CacheRebalanceMode;
+import org.apache.ignite.cache.PartitionLossPolicy;
+import org.apache.ignite.spi.systemview.view.CacheGroupView;
+import org.apache.ignite.spi.systemview.view.SystemViewRowAttributeWalker;
+
+/**
+ * Generated by {@code org.apache.ignite.codegen.SystemViewRowAttributeWalkerGenerator}.
+ * {@link CacheGroupView} attributes walker.
+ * 
+ * @see CacheGroupView
+ */
+public class CacheGroupViewWalker implements SystemViewRowAttributeWalker<CacheGroupView> {
+    /** {@inheritDoc} */
+    @Override public void visitAll(AttributeVisitor v) {
+        v.accept(0, "cacheGroupName", String.class);
+        v.accept(1, "cacheCount", int.class);
+        v.accept(2, "dataRegionName", String.class);
+        v.accept(3, "cacheMode", CacheMode.class);
+        v.accept(4, "atomicityMode", CacheAtomicityMode.class);
+        v.accept(5, "affinity", String.class);
+        v.accept(6, "backups", int.class);
+        v.accept(7, "cacheGroupId", int.class);
+        v.accept(8, "isShared", boolean.class);
+        v.accept(9, "nodeFilter", String.class);
+        v.accept(10, "partitionLossPolicy", PartitionLossPolicy.class);
+        v.accept(11, "partitionsCount", int.class);
+        v.accept(12, "rebalanceDelay", long.class);
+        v.accept(13, "rebalanceMode", CacheRebalanceMode.class);
+        v.accept(14, "rebalanceOrder", int.class);
+        v.accept(15, "topologyValidator", String.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void visitAll(CacheGroupView row, AttributeWithValueVisitor v) {
+        v.accept(0, "cacheGroupName", String.class, row.cacheGroupName());
+        v.acceptInt(1, "cacheCount", row.cacheCount());
+        v.accept(2, "dataRegionName", String.class, row.dataRegionName());
+        v.accept(3, "cacheMode", CacheMode.class, row.cacheMode());
+        v.accept(4, "atomicityMode", CacheAtomicityMode.class, row.atomicityMode());
+        v.accept(5, "affinity", String.class, row.affinity());
+        v.acceptInt(6, "backups", row.backups());
+        v.acceptInt(7, "cacheGroupId", row.cacheGroupId());
+        v.acceptBoolean(8, "isShared", row.isShared());
+        v.accept(9, "nodeFilter", String.class, row.nodeFilter());
+        v.accept(10, "partitionLossPolicy", PartitionLossPolicy.class, row.partitionLossPolicy());
+        v.acceptInt(11, "partitionsCount", row.partitionsCount());
+        v.acceptLong(12, "rebalanceDelay", row.rebalanceDelay());
+        v.accept(13, "rebalanceMode", CacheRebalanceMode.class, row.rebalanceMode());
+        v.acceptInt(14, "rebalanceOrder", row.rebalanceOrder());
+        v.accept(15, "topologyValidator", String.class, row.topologyValidator());
+    }
+
+    /** {@inheritDoc} */
+    @Override public int count() {
+        return 16;
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/walker/CacheViewWalker.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/walker/CacheViewWalker.java
new file mode 100644
index 0000000..0b5f1c9
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/walker/CacheViewWalker.java
@@ -0,0 +1,170 @@
+/*
+ * 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.managers.systemview.walker;
+
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.CacheRebalanceMode;
+import org.apache.ignite.cache.CacheWriteSynchronizationMode;
+import org.apache.ignite.cache.PartitionLossPolicy;
+import org.apache.ignite.internal.processors.cache.CacheType;
+import org.apache.ignite.spi.systemview.view.CacheView;
+import org.apache.ignite.spi.systemview.view.SystemViewRowAttributeWalker;
+
+/**
+ * Generated by {@code org.apache.ignite.codegen.SystemViewRowAttributeWalkerGenerator}.
+ * {@link CacheView} attributes walker.
+ * 
+ * @see CacheView
+ */
+public class CacheViewWalker implements SystemViewRowAttributeWalker<CacheView> {
+    /** {@inheritDoc} */
+    @Override public void visitAll(AttributeVisitor v) {
+        v.accept(0, "cacheName", String.class);
+        v.accept(1, "cacheId", int.class);
+        v.accept(2, "cacheType", CacheType.class);
+        v.accept(3, "cacheMode", CacheMode.class);
+        v.accept(4, "atomicityMode", CacheAtomicityMode.class);
+        v.accept(5, "cacheGroupName", String.class);
+        v.accept(6, "affinity", String.class);
+        v.accept(7, "affinityMapper", String.class);
+        v.accept(8, "backups", int.class);
+        v.accept(9, "cacheGroupId", int.class);
+        v.accept(10, "cacheLoaderFactory", String.class);
+        v.accept(11, "cacheStoreFactory", String.class);
+        v.accept(12, "cacheWriterFactory", String.class);
+        v.accept(13, "dataRegionName", String.class);
+        v.accept(14, "defaultLockTimeout", long.class);
+        v.accept(15, "evictionFilter", String.class);
+        v.accept(16, "evictionPolicyFactory", String.class);
+        v.accept(17, "expiryPolicyFactory", String.class);
+        v.accept(18, "interceptor", String.class);
+        v.accept(19, "isCopyOnRead", boolean.class);
+        v.accept(20, "isEagerTtl", boolean.class);
+        v.accept(21, "isEncryptionEnabled", boolean.class);
+        v.accept(22, "isEventsDisabled", boolean.class);
+        v.accept(23, "isInvalidate", boolean.class);
+        v.accept(24, "isLoadPreviousValue", boolean.class);
+        v.accept(25, "isManagementEnabled", boolean.class);
+        v.accept(26, "isNearCacheEnabled", boolean.class);
+        v.accept(27, "isOnheapCacheEnabled", boolean.class);
+        v.accept(28, "isReadFromBackup", boolean.class);
+        v.accept(29, "isReadThrough", boolean.class);
+        v.accept(30, "isSqlEscapeAll", boolean.class);
+        v.accept(31, "isSqlOnheapCacheEnabled", boolean.class);
+        v.accept(32, "isStatisticsEnabled", boolean.class);
+        v.accept(33, "isStoreKeepBinary", boolean.class);
+        v.accept(34, "isWriteBehindEnabled", boolean.class);
+        v.accept(35, "isWriteThrough", boolean.class);
+        v.accept(36, "maxConcurrentAsyncOperations", int.class);
+        v.accept(37, "maxQueryIteratorsCount", int.class);
+        v.accept(38, "nearCacheEvictionPolicyFactory", String.class);
+        v.accept(39, "nearCacheStartSize", int.class);
+        v.accept(40, "nodeFilter", String.class);
+        v.accept(41, "partitionLossPolicy", PartitionLossPolicy.class);
+        v.accept(42, "queryDetailMetricsSize", int.class);
+        v.accept(43, "queryParallelism", int.class);
+        v.accept(44, "rebalanceBatchSize", int.class);
+        v.accept(45, "rebalanceBatchesPrefetchCount", long.class);
+        v.accept(46, "rebalanceDelay", long.class);
+        v.accept(47, "rebalanceMode", CacheRebalanceMode.class);
+        v.accept(48, "rebalanceOrder", int.class);
+        v.accept(49, "rebalanceThrottle", long.class);
+        v.accept(50, "rebalanceTimeout", long.class);
+        v.accept(51, "sqlIndexMaxInlineSize", int.class);
+        v.accept(52, "sqlOnheapCacheMaxSize", int.class);
+        v.accept(53, "sqlSchema", String.class);
+        v.accept(54, "topologyValidator", String.class);
+        v.accept(55, "writeBehindBatchSize", int.class);
+        v.accept(56, "writeBehindCoalescing", boolean.class);
+        v.accept(57, "writeBehindFlushFrequency", long.class);
+        v.accept(58, "writeBehindFlushSize", int.class);
+        v.accept(59, "writeBehindFlushThreadCount", int.class);
+        v.accept(60, "writeSynchronizationMode", CacheWriteSynchronizationMode.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void visitAll(CacheView row, AttributeWithValueVisitor v) {
+        v.accept(0, "cacheName", String.class, row.cacheName());
+        v.acceptInt(1, "cacheId", row.cacheId());
+        v.accept(2, "cacheType", CacheType.class, row.cacheType());
+        v.accept(3, "cacheMode", CacheMode.class, row.cacheMode());
+        v.accept(4, "atomicityMode", CacheAtomicityMode.class, row.atomicityMode());
+        v.accept(5, "cacheGroupName", String.class, row.cacheGroupName());
+        v.accept(6, "affinity", String.class, row.affinity());
+        v.accept(7, "affinityMapper", String.class, row.affinityMapper());
+        v.acceptInt(8, "backups", row.backups());
+        v.acceptInt(9, "cacheGroupId", row.cacheGroupId());
+        v.accept(10, "cacheLoaderFactory", String.class, row.cacheLoaderFactory());
+        v.accept(11, "cacheStoreFactory", String.class, row.cacheStoreFactory());
+        v.accept(12, "cacheWriterFactory", String.class, row.cacheWriterFactory());
+        v.accept(13, "dataRegionName", String.class, row.dataRegionName());
+        v.acceptLong(14, "defaultLockTimeout", row.defaultLockTimeout());
+        v.accept(15, "evictionFilter", String.class, row.evictionFilter());
+        v.accept(16, "evictionPolicyFactory", String.class, row.evictionPolicyFactory());
+        v.accept(17, "expiryPolicyFactory", String.class, row.expiryPolicyFactory());
+        v.accept(18, "interceptor", String.class, row.interceptor());
+        v.acceptBoolean(19, "isCopyOnRead", row.isCopyOnRead());
+        v.acceptBoolean(20, "isEagerTtl", row.isEagerTtl());
+        v.acceptBoolean(21, "isEncryptionEnabled", row.isEncryptionEnabled());
+        v.acceptBoolean(22, "isEventsDisabled", row.isEventsDisabled());
+        v.acceptBoolean(23, "isInvalidate", row.isInvalidate());
+        v.acceptBoolean(24, "isLoadPreviousValue", row.isLoadPreviousValue());
+        v.acceptBoolean(25, "isManagementEnabled", row.isManagementEnabled());
+        v.acceptBoolean(26, "isNearCacheEnabled", row.isNearCacheEnabled());
+        v.acceptBoolean(27, "isOnheapCacheEnabled", row.isOnheapCacheEnabled());
+        v.acceptBoolean(28, "isReadFromBackup", row.isReadFromBackup());
+        v.acceptBoolean(29, "isReadThrough", row.isReadThrough());
+        v.acceptBoolean(30, "isSqlEscapeAll", row.isSqlEscapeAll());
+        v.acceptBoolean(31, "isSqlOnheapCacheEnabled", row.isSqlOnheapCacheEnabled());
+        v.acceptBoolean(32, "isStatisticsEnabled", row.isStatisticsEnabled());
+        v.acceptBoolean(33, "isStoreKeepBinary", row.isStoreKeepBinary());
+        v.acceptBoolean(34, "isWriteBehindEnabled", row.isWriteBehindEnabled());
+        v.acceptBoolean(35, "isWriteThrough", row.isWriteThrough());
+        v.acceptInt(36, "maxConcurrentAsyncOperations", row.maxConcurrentAsyncOperations());
+        v.acceptInt(37, "maxQueryIteratorsCount", row.maxQueryIteratorsCount());
+        v.accept(38, "nearCacheEvictionPolicyFactory", String.class, row.nearCacheEvictionPolicyFactory());
+        v.acceptInt(39, "nearCacheStartSize", row.nearCacheStartSize());
+        v.accept(40, "nodeFilter", String.class, row.nodeFilter());
+        v.accept(41, "partitionLossPolicy", PartitionLossPolicy.class, row.partitionLossPolicy());
+        v.acceptInt(42, "queryDetailMetricsSize", row.queryDetailMetricsSize());
+        v.acceptInt(43, "queryParallelism", row.queryParallelism());
+        v.acceptInt(44, "rebalanceBatchSize", row.rebalanceBatchSize());
+        v.acceptLong(45, "rebalanceBatchesPrefetchCount", row.rebalanceBatchesPrefetchCount());
+        v.acceptLong(46, "rebalanceDelay", row.rebalanceDelay());
+        v.accept(47, "rebalanceMode", CacheRebalanceMode.class, row.rebalanceMode());
+        v.acceptInt(48, "rebalanceOrder", row.rebalanceOrder());
+        v.acceptLong(49, "rebalanceThrottle", row.rebalanceThrottle());
+        v.acceptLong(50, "rebalanceTimeout", row.rebalanceTimeout());
+        v.acceptInt(51, "sqlIndexMaxInlineSize", row.sqlIndexMaxInlineSize());
+        v.acceptInt(52, "sqlOnheapCacheMaxSize", row.sqlOnheapCacheMaxSize());
+        v.accept(53, "sqlSchema", String.class, row.sqlSchema());
+        v.accept(54, "topologyValidator", String.class, row.topologyValidator());
+        v.acceptInt(55, "writeBehindBatchSize", row.writeBehindBatchSize());
+        v.acceptBoolean(56, "writeBehindCoalescing", row.writeBehindCoalescing());
+        v.acceptLong(57, "writeBehindFlushFrequency", row.writeBehindFlushFrequency());
+        v.acceptInt(58, "writeBehindFlushSize", row.writeBehindFlushSize());
+        v.acceptInt(59, "writeBehindFlushThreadCount", row.writeBehindFlushThreadCount());
+        v.accept(60, "writeSynchronizationMode", CacheWriteSynchronizationMode.class, row.writeSynchronizationMode());
+    }
+
+    /** {@inheritDoc} */
+    @Override public int count() {
+        return 61;
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/walker/ComputeTaskViewWalker.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/walker/ComputeTaskViewWalker.java
new file mode 100644
index 0000000..bf1e0f0
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/walker/ComputeTaskViewWalker.java
@@ -0,0 +1,66 @@
+/*
+ * 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.managers.systemview.walker;
+
+import java.util.UUID;
+import org.apache.ignite.lang.IgniteUuid;
+import org.apache.ignite.spi.systemview.view.ComputeTaskView;
+import org.apache.ignite.spi.systemview.view.SystemViewRowAttributeWalker;
+
+/**
+ * Generated by {@code org.apache.ignite.codegen.SystemViewRowAttributeWalkerGenerator}.
+ * {@link ComputeTaskView} attributes walker.
+ * 
+ * @see ComputeTaskView
+ */
+public class ComputeTaskViewWalker implements SystemViewRowAttributeWalker<ComputeTaskView> {
+    /** {@inheritDoc} */
+    @Override public void visitAll(AttributeVisitor v) {
+        v.accept(0, "affinityCacheName", String.class);
+        v.accept(1, "affinityPartitionId", int.class);
+        v.accept(2, "endTime", long.class);
+        v.accept(3, "execName", String.class);
+        v.accept(4, "internal", boolean.class);
+        v.accept(5, "jobId", IgniteUuid.class);
+        v.accept(6, "startTime", long.class);
+        v.accept(7, "taskClassName", String.class);
+        v.accept(8, "taskName", String.class);
+        v.accept(9, "taskNodeId", UUID.class);
+        v.accept(10, "userVersion", String.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void visitAll(ComputeTaskView row, AttributeWithValueVisitor v) {
+        v.accept(0, "affinityCacheName", String.class, row.affinityCacheName());
+        v.acceptInt(1, "affinityPartitionId", row.affinityPartitionId());
+        v.acceptLong(2, "endTime", row.endTime());
+        v.accept(3, "execName", String.class, row.execName());
+        v.acceptBoolean(4, "internal", row.internal());
+        v.accept(5, "jobId", IgniteUuid.class, row.jobId());
+        v.acceptLong(6, "startTime", row.startTime());
+        v.accept(7, "taskClassName", String.class, row.taskClassName());
+        v.accept(8, "taskName", String.class, row.taskName());
+        v.accept(9, "taskNodeId", UUID.class, row.taskNodeId());
+        v.accept(10, "userVersion", String.class, row.userVersion());
+    }
+
+    /** {@inheritDoc} */
+    @Override public int count() {
+        return 11;
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/walker/Order.java
similarity index 56%
copy from modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java
copy to modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/walker/Order.java
index 07457cc..0575b0f 100644
--- a/modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/walker/Order.java
@@ -15,26 +15,23 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.spi.metric;
+package org.apache.ignite.internal.managers.systemview.walker;
 
-import java.util.function.Consumer;
-import org.apache.ignite.internal.processors.metric.MetricRegistry;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.ignite.spi.systemview.view.SystemViewRowAttributeWalker;
 
 /**
- * Read only metric registry.
+ * Annotation to set walk order for a {@link SystemViewRowAttributeWalker}.
+ * Walker traverse attributes from low to high value.
+ *
+ * @see SystemViewRowAttributeWalker
+ * @see CacheViewWalker
  */
-public interface ReadOnlyMetricRegistry extends Iterable<MetricRegistry> {
-    /**
-     * Adds listener of metrics group creation events.
-     *
-     * @param lsnr Listener.
-     */
-    public void addMetricRegistryCreationListener(Consumer<MetricRegistry> lsnr);
-
-    /**
-     * Adds listener of metrics group remove events.
-     *
-     * @param lsnr Listener.
-     */
-    public void addMetricRegistryRemoveListener(Consumer<MetricRegistry> lsnr);
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Order {
+    public int value() default 0;
 }
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
new file mode 100644
index 0000000..030396c
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/systemview/walker/ServiceViewWalker.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.managers.systemview.walker;
+
+import java.util.UUID;
+import org.apache.ignite.lang.IgniteUuid;
+import org.apache.ignite.spi.systemview.view.ServiceView;
+import org.apache.ignite.spi.systemview.view.SystemViewRowAttributeWalker;
+
+/**
+ * Generated by {@code org.apache.ignite.codegen.SystemViewRowAttributeWalkerGenerator}.
+ * {@link ServiceView} attributes walker.
+ * 
+ * @see ServiceView
+ */
+public class ServiceViewWalker implements SystemViewRowAttributeWalker<ServiceView> {
+    /** {@inheritDoc} */
+    @Override public void visitAll(AttributeVisitor v) {
+        v.accept(0, "serviceId", IgniteUuid.class);
+        v.accept(1, "name", String.class);
+        v.accept(2, "serviceClass", Class.class);
+        v.accept(3, "cacheName", String.class);
+        v.accept(4, "originNodeId", UUID.class);
+        v.accept(5, "totalCount", int.class);
+        v.accept(6, "maxPerNodeCount", int.class);
+        v.accept(7, "affinityKey", String.class);
+        v.accept(8, "nodeFilter", Class.class);
+        v.accept(9, "staticallyConfigured", boolean.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void visitAll(ServiceView row, AttributeWithValueVisitor v) {
+        v.accept(0, "serviceId", IgniteUuid.class, row.serviceId());
+        v.accept(1, "name", String.class, row.name());
+        v.accept(2, "serviceClass", Class.class, row.serviceClass());
+        v.accept(3, "cacheName", String.class, row.cacheName());
+        v.accept(4, "originNodeId", UUID.class, row.originNodeId());
+        v.acceptInt(5, "totalCount", row.totalCount());
+        v.acceptInt(6, "maxPerNodeCount", row.maxPerNodeCount());
+        v.accept(7, "affinityKey", String.class, row.affinityKey());
+        v.accept(8, "nodeFilter", Class.class, row.nodeFilter());
+        v.acceptBoolean(9, "staticallyConfigured", row.staticallyConfigured());
+    }
+
+    /** {@inheritDoc} */
+    @Override public int count() {
+        return 10;
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesInfo.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesInfo.java
index 13ef63c..7c3549d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesInfo.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesInfo.java
@@ -53,6 +53,8 @@ import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cluster.ChangeGlobalStateFinishMessage;
 import org.apache.ignite.internal.processors.cluster.ChangeGlobalStateMessage;
 import org.apache.ignite.internal.processors.cluster.DiscoveryDataClusterState;
+import org.apache.ignite.spi.systemview.view.CacheGroupView;
+import org.apache.ignite.spi.systemview.view.CacheView;
 import org.apache.ignite.internal.processors.query.QuerySchema;
 import org.apache.ignite.internal.processors.query.QuerySchemaPatch;
 import org.apache.ignite.internal.processors.query.QueryUtils;
@@ -81,7 +83,19 @@ import static org.apache.ignite.internal.GridComponent.DiscoveryDataExchangeType
 /**
  * Logic related to cache discovery data processing.
  */
-class ClusterCachesInfo {
+public class ClusterCachesInfo {
+    /** */
+    public static final String CACHES_VIEW = "caches";
+
+    /** */
+    public static final String CACHES_VIEW_DESC = "Caches";
+
+    /** */
+    public static final String CACHE_GRPS_VIEW = "cacheGroups";
+
+    /** */
+    public static final String CACHE_GRPS_VIEW_DESC = "Cache groups";
+
     /** Representation of null for restarting caches map */
     private static final IgniteUuid NULL_OBJECT = new IgniteUuid();
 
@@ -145,6 +159,16 @@ class ClusterCachesInfo {
     public ClusterCachesInfo(GridKernalContext ctx) {
         this.ctx = ctx;
 
+        ctx.systemView().registerView(CACHES_VIEW, CACHES_VIEW_DESC,
+            CacheView.class,
+            registeredCaches.values(),
+            CacheView::new);
+
+        ctx.systemView().registerView(CACHE_GRPS_VIEW, CACHE_GRPS_VIEW_DESC,
+            CacheGroupView.class,
+            registeredCacheGrps.values(),
+            CacheGroupView::new);
+
         log = ctx.log(getClass());
     }
 
@@ -2514,7 +2538,7 @@ class ClusterCachesInfo {
                 if (o1.cacheType().userCache() ^ o2.cacheType().userCache())
                     return o2.cacheType().userCache() ? -1 : 1;
 
-                return o1.cacheId().compareTo(o2.cacheId());
+                return Integer.compare(o1.cacheId(), o2.cacheId());
             }
         };
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/DynamicCacheDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/DynamicCacheDescriptor.java
index a1e1cf1..b9b494b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/DynamicCacheDescriptor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/DynamicCacheDescriptor.java
@@ -64,7 +64,7 @@ public class DynamicCacheDescriptor {
     private boolean updatesAllowed = true;
 
     /** */
-    private Integer cacheId;
+    private int cacheId;
 
     /** */
     private final UUID rcvdFrom;
@@ -176,7 +176,7 @@ public class DynamicCacheDescriptor {
     /**
      * @return Cache ID.
      */
-    public Integer cacheId() {
+    public int cacheId() {
         return cacheId;
     }
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/StandaloneGridKernalContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/StandaloneGridKernalContext.java
index 055472d..bc0673a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/StandaloneGridKernalContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/StandaloneGridKernalContext.java
@@ -47,6 +47,7 @@ import org.apache.ignite.internal.managers.eventstorage.GridEventStorageManager;
 import org.apache.ignite.internal.managers.failover.GridFailoverManager;
 import org.apache.ignite.internal.managers.indexing.GridIndexingManager;
 import org.apache.ignite.internal.managers.loadbalancer.GridLoadBalancerManager;
+import org.apache.ignite.internal.managers.systemview.GridSystemViewManager;
 import org.apache.ignite.internal.processors.affinity.GridAffinityProcessor;
 import org.apache.ignite.internal.processors.authentication.IgniteAuthenticationProcessor;
 import org.apache.ignite.internal.processors.cache.GridCacheProcessor;
@@ -99,6 +100,7 @@ import org.apache.ignite.marshaller.Marshaller;
 import org.apache.ignite.plugin.PluginNotFoundException;
 import org.apache.ignite.plugin.PluginProvider;
 import org.apache.ignite.spi.metric.noop.NoopMetricExporterSpi;
+import org.apache.ignite.spi.systemview.jmx.JmxSystemViewExporterSpi;
 import org.apache.ignite.thread.IgniteStripedThreadPoolExecutor;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -122,6 +124,9 @@ public class StandaloneGridKernalContext implements GridKernalContext {
     /** Metrics manager. */
     private final GridMetricManager metricMgr;
 
+    /** System view manager. */
+    private final GridSystemViewManager sysViewMgr;
+
     /**
      * Cache object processor. Used for converting cache objects and keys into binary objects. Null means there is no
      * convert is configured. All entries in this case will be lazy data entries.
@@ -157,6 +162,7 @@ public class StandaloneGridKernalContext implements GridKernalContext {
         this.marshallerCtx = new MarshallerContextImpl(null, null);
         this.cfg = prepareIgniteConfiguration();
         this.metricMgr = new GridMetricManager(this);
+        this.sysViewMgr = new GridSystemViewManager(this);
 
         // Fake folder provided to perform processor startup on empty folder.
         if (binaryMetadataFileStoreDir == null)
@@ -213,6 +219,7 @@ public class StandaloneGridKernalContext implements GridKernalContext {
         marshaller.setContext(marshallerCtx);
 
         cfg.setMetricExporterSpi(new NoopMetricExporterSpi());
+        cfg.setSystemViewExporterSpi(new JmxSystemViewExporterSpi());
 
         return cfg;
     }
@@ -307,6 +314,11 @@ public class StandaloneGridKernalContext implements GridKernalContext {
     }
 
     /** {@inheritDoc} */
+    @Override public GridSystemViewManager systemView() {
+        return sysViewMgr;
+    }
+
+    /** {@inheritDoc} */
     @Override public GridCacheProcessor cache() {
         return null;
     }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/GridMetricManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/GridMetricManager.java
index 6433bd1..88a7609 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/GridMetricManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/GridMetricManager.java
@@ -54,6 +54,7 @@ import org.jetbrains.annotations.Nullable;
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_PHY_RAM;
 import static org.apache.ignite.internal.processors.metric.impl.MetricUtils.metricName;
+import static org.apache.ignite.internal.util.IgniteUtils.notifyListeners;
 
 /**
  * This manager should provide {@link ReadOnlyMetricRegistry} for each configured {@link MetricExporterSpi}.
@@ -268,7 +269,7 @@ public class GridMetricManager extends GridManagerAdapter<MetricExporterSpi> imp
         return registries.computeIfAbsent(name, n -> {
             MetricRegistry mreg = new MetricRegistry(name, log);
 
-            notifyListeners(mreg, metricRegCreationLsnrs);
+            notifyListeners(mreg, metricRegCreationLsnrs, log);
 
             return mreg;
         });
@@ -298,23 +299,7 @@ public class GridMetricManager extends GridManagerAdapter<MetricExporterSpi> imp
         MetricRegistry mreg = registries.remove(regName);
 
         if (mreg != null)
-            notifyListeners(mreg, metricRegRemoveLsnrs);
-    }
-
-    /**
-     * @param t Consumed object.
-     * @param lsnrs Listeners.
-     * @param <T> Type of consumed object.
-     */
-    private <T> void notifyListeners(T t, List<Consumer<T>> lsnrs) {
-        for (Consumer<T> lsnr : lsnrs) {
-            try {
-                lsnr.accept(t);
-            }
-            catch (Exception e) {
-                U.warn(log, "Metric listener error", e);
-            }
-        }
+            notifyListeners(mreg, metricRegRemoveLsnrs, log);
     }
 
     /**
@@ -646,7 +631,7 @@ public class GridMetricManager extends GridManagerAdapter<MetricExporterSpi> imp
          * @param metricNamePrefix Metric name prefix.
          */
         public MemoryUsageMetrics(String group, String metricNamePrefix) {
-            MetricRegistry mreg = GridMetricManager.this.registry(group);
+            MetricRegistry mreg = registry(group);
 
             this.init = mreg.longMetric(metricName(metricNamePrefix, "init"), null);
             this.used = mreg.longMetric(metricName(metricNamePrefix, "used"), null);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
index 49421d3..cc7bc67 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
@@ -99,9 +99,6 @@ public class QueryUtils {
     /** Schema for system view. */
     public static final String SCHEMA_SYS = getBoolean(IGNITE_SQL_SYSTEM_SCHEMA_NAME_IGNITE, false) ? "IGNITE" : "SYS";
 
-    /** Schema for monitoring views. */
-    public static final String SCHEMA_MONITORING = "MONITORING";
-
     /** Schema for system view. */
     public static final String SCHEMA_INFORMATION = "INFORMATION_SCHEMA";
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java
index 3170090..52be83a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java
@@ -61,6 +61,7 @@ import org.apache.ignite.internal.processors.cache.DynamicCacheChangeRequest;
 import org.apache.ignite.internal.processors.cluster.ChangeGlobalStateMessage;
 import org.apache.ignite.internal.processors.cluster.DiscoveryDataClusterState;
 import org.apache.ignite.internal.processors.cluster.IgniteChangeGlobalStateSupport;
+import org.apache.ignite.spi.systemview.view.ServiceView;
 import org.apache.ignite.internal.util.future.GridCompoundFuture;
 import org.apache.ignite.internal.util.future.GridFinishedFuture;
 import org.apache.ignite.internal.util.future.GridFutureAdapter;
@@ -105,6 +106,12 @@ import static org.apache.ignite.internal.GridComponent.DiscoveryDataExchangeType
 @SkipDaemon
 @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
 public class IgniteServiceProcessor extends ServiceProcessorAdapter implements IgniteChangeGlobalStateSupport {
+    /** */
+    public static final String SVCS_VIEW = "services";
+
+    /** */
+    public static final String SVCS_VIEW_DESC = "Services";
+
     /** Local service instances. */
     private final ConcurrentMap<IgniteUuid, Collection<ServiceContextImpl>> locServices = new ConcurrentHashMap<>();
 
@@ -185,6 +192,11 @@ public class IgniteServiceProcessor extends ServiceProcessorAdapter implements I
      */
     public IgniteServiceProcessor(GridKernalContext ctx) {
         super(ctx);
+
+        ctx.systemView().registerView(SVCS_VIEW, SVCS_VIEW_DESC,
+            ServiceView.class,
+            registeredServices.values(),
+            ServiceView::new);
     }
 
     /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/task/GridTaskProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/task/GridTaskProcessor.java
index b5b45c8..30062cb 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/task/GridTaskProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/task/GridTaskProcessor.java
@@ -80,6 +80,7 @@ import org.apache.ignite.lang.IgnitePredicate;
 import org.apache.ignite.lang.IgniteUuid;
 import org.apache.ignite.marshaller.Marshaller;
 import org.apache.ignite.plugin.security.SecurityPermission;
+import org.apache.ignite.spi.systemview.view.ComputeTaskView;
 import org.jetbrains.annotations.Nullable;
 
 import static org.apache.ignite.events.EventType.EVT_MANAGEMENT_TASK_STARTED;
@@ -102,6 +103,12 @@ import static org.apache.ignite.internal.processors.task.GridTaskThreadContextKe
  * This class defines task processor.
  */
 public class GridTaskProcessor extends GridProcessorAdapter implements IgniteChangeGlobalStateSupport {
+    /** */
+    public static final String TASKS_VIEW = "tasks";
+
+    /** */
+    public static final String TASKS_VIEW_DESC = "Running compute tasks";
+
     /** Total executed tasks metric name. */
     public static final String TOTAL_EXEC_TASKS = "TotalExecutedTasks";
 
@@ -155,6 +162,11 @@ public class GridTaskProcessor extends GridProcessorAdapter implements IgniteCha
         MetricRegistry sysreg = ctx.metric().registry(SYS_METRICS);
 
         execTasks = sysreg.longAdderMetric(TOTAL_EXEC_TASKS, "Total executed tasks.");
+
+        ctx.systemView().registerView(TASKS_VIEW, TASKS_VIEW_DESC,
+            ComputeTaskView.class,
+            tasks.values(),
+            ComputeTaskView::new);
     }
 
     /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/task/GridTaskWorker.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/task/GridTaskWorker.java
index 2568b36..8ce33e8 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/task/GridTaskWorker.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/task/GridTaskWorker.java
@@ -111,7 +111,7 @@ import static org.apache.ignite.internal.processors.task.GridTaskThreadContextKe
  * @param <T> Task argument type.
  * @param <R> Task return value type.
  */
-class GridTaskWorker<T, R> extends GridWorker implements GridTimeoutObject {
+public class GridTaskWorker<T, R> extends GridWorker implements GridTimeoutObject {
     /** Split size threshold. */
     private static final int SPLIT_WARN_THRESHOLD = 1000;
 
@@ -371,7 +371,7 @@ class GridTaskWorker<T, R> extends GridWorker implements GridTimeoutObject {
     /**
      * @return Task session.
      */
-    GridTaskSessionImpl getSession() {
+    public GridTaskSessionImpl getSession() {
         return ses;
     }
 
@@ -1656,6 +1656,16 @@ class GridTaskWorker<T, R> extends GridWorker implements GridTimeoutObject {
         return ctx.discovery().node(uid) == null || !ctx.discovery().pingNode(uid);
     }
 
+    /** @return Affinity cache name for task. */
+    public String affCacheName() {
+        return affCacheName;
+    }
+
+    /** @return Affinity partition id. */
+    public int affPartId() {
+        return affPartId;
+    }
+
     /** {@inheritDoc} */
     @Override public boolean equals(Object obj) {
         if (this == obj)
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 aab15bd..68ab912 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
@@ -138,6 +138,7 @@ import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
+import java.util.function.Consumer;
 import java.util.jar.JarFile;
 import java.util.logging.ConsoleHandler;
 import java.util.logging.Handler;
@@ -1689,6 +1690,22 @@ public abstract class IgniteUtils {
     }
 
     /**
+     * Check whether class is in classpath.
+     *
+     * @return {@code True} if in classpath.
+     */
+    public static boolean inClassPath(String clsName) {
+        try {
+            Class.forName(clsName);
+
+            return true;
+        }
+        catch (ClassNotFoundException ignore) {
+            return false;
+        }
+    }
+
+    /**
      * Creates new instance of a class even if it does not have public constructor.
      *
      * @param cls Class to instantiate.
@@ -7034,6 +7051,30 @@ public abstract class IgniteUtils {
     }
 
     /**
+     * Get string representation of an object properly catching all exceptions.
+     *
+     * @param obj Object.
+     * @return Result or {@code null}.
+     */
+    @Nullable public static String toStringSafe(@Nullable Object obj) {
+        if (obj == null)
+            return null;
+        else {
+            try {
+                return obj.toString();
+            }
+            catch (Exception e) {
+                try {
+                    return "Failed to convert object to string: " + e.getMessage();
+                }
+                catch (Exception e0) {
+                    return "Failed to convert object to string (error message is not available)";
+                }
+            }
+        }
+    }
+
+    /**
      * Converts collection of integers into array.
      *
      * @param c Collection of integers.
@@ -11521,4 +11562,25 @@ public abstract class IgniteUtils {
     public static boolean isFlagSet(int flags, int flag) {
         return (flags & flag) == flag;
     }
+
+    /**
+     * Notifies provided {@code lsnrs} with the value {@code t}.
+     *
+     * @param t Consumed object.
+     * @param lsnrs Listeners.
+     * @param <T> Type of consumed object.
+     */
+    public static <T> void notifyListeners(T t, Collection<Consumer<T>> lsnrs, IgniteLogger log) {
+        if (lsnrs == null)
+            return;
+
+        for (Consumer<T> lsnr : lsnrs) {
+            try {
+                lsnr.accept(t);
+            }
+            catch (Exception e) {
+                U.warn(log, "Listener error", e);
+            }
+        }
+    }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/IgniteMXBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/IgniteMXBean.java
index e83b0c4..9b89c6d 100644
--- a/modules/core/src/main/java/org/apache/ignite/mxbean/IgniteMXBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/IgniteMXBean.java
@@ -506,7 +506,6 @@ public interface IgniteMXBean {
     @MXBeanParametersDescriptions("Metrics registry.")
     public void resetMetrics(String registry);
 
-
     /**
      * Gets cluster read-only mode status.
      *
diff --git a/modules/core/src/main/java/org/apache/ignite/spi/metric/MetricExporterSpi.java b/modules/core/src/main/java/org/apache/ignite/spi/metric/MetricExporterSpi.java
index 20aad78..1783984 100644
--- a/modules/core/src/main/java/org/apache/ignite/spi/metric/MetricExporterSpi.java
+++ b/modules/core/src/main/java/org/apache/ignite/spi/metric/MetricExporterSpi.java
@@ -17,7 +17,6 @@
 
 package org.apache.ignite.spi.metric;
 
-import java.util.function.Consumer;
 import java.util.function.Predicate;
 import org.apache.ignite.internal.processors.metric.MetricRegistry;
 import org.apache.ignite.spi.IgniteSpi;
@@ -45,9 +44,6 @@ public interface MetricExporterSpi extends IgniteSpi {
      * Sets metrics registry that SPI should export.
      * This method called before {@link #spiStart(String)}.
      *
-     * So all {@link MetricRegistry} that will be created by Ignite internal components can be obtained by
-     * listeners passed to {@link ReadOnlyMetricRegistry#addMetricRegistryCreationListener(Consumer)}.
-     *
      * @param registry Metric registry.
      */
     public void setMetricRegistry(ReadOnlyMetricRegistry registry);
diff --git a/modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java b/modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java
index 07457cc..6fffc72 100644
--- a/modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java
+++ b/modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java
@@ -25,14 +25,14 @@ import org.apache.ignite.internal.processors.metric.MetricRegistry;
  */
 public interface ReadOnlyMetricRegistry extends Iterable<MetricRegistry> {
     /**
-     * Adds listener of metrics group creation events.
+     * Adds listener of metrics registry creation events.
      *
      * @param lsnr Listener.
      */
     public void addMetricRegistryCreationListener(Consumer<MetricRegistry> lsnr);
 
     /**
-     * Adds listener of metrics group remove events.
+     * Adds listener of metrics registry remove events.
      *
      * @param lsnr Listener.
      */
diff --git a/modules/core/src/main/java/org/apache/ignite/spi/metric/jmx/JmxMetricExporterSpi.java b/modules/core/src/main/java/org/apache/ignite/spi/metric/jmx/JmxMetricExporterSpi.java
index 7f61ee6..2391f23 100644
--- a/modules/core/src/main/java/org/apache/ignite/spi/metric/jmx/JmxMetricExporterSpi.java
+++ b/modules/core/src/main/java/org/apache/ignite/spi/metric/jmx/JmxMetricExporterSpi.java
@@ -22,6 +22,7 @@ import java.util.List;
 import java.util.function.Predicate;
 import javax.management.JMException;
 import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
 import javax.management.ObjectName;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.internal.processors.metric.MetricRegistry;
@@ -34,12 +35,13 @@ import org.apache.ignite.spi.metric.ReadOnlyMetricRegistry;
 import org.jetbrains.annotations.Nullable;
 
 import static org.apache.ignite.internal.processors.metric.impl.MetricUtils.parse;
+import static org.apache.ignite.internal.util.IgniteUtils.makeMBeanName;
 
 /**
  * This SPI implementation exports metrics as JMX beans.
  */
 public class JmxMetricExporterSpi extends IgniteSpiAdapter implements MetricExporterSpi {
-    /** Monitoring registry. */
+    /** Metric registry. */
     private ReadOnlyMetricRegistry mreg;
 
     /** Metric filter. */
@@ -53,20 +55,28 @@ public class JmxMetricExporterSpi extends IgniteSpiAdapter implements MetricExpo
         mreg.forEach(this::register);
 
         mreg.addMetricRegistryCreationListener(this::register);
+        mreg.addMetricRegistryRemoveListener(this::unregister);
     }
 
-    /** @param grp Metric group to register as JMX bean. */
-    private void register(MetricRegistry grp) {
-        if (filter != null && !filter.test(grp))
-            return;
+    /**
+     * Registers JMX bean for specific metric registry.
+     *
+     * @param mreg Metric registry.
+     */
+    private void register(MetricRegistry mreg) {
+        if (filter != null && !filter.test(mreg)) {
+            if (log.isDebugEnabled())
+                U.debug(log, "Metric registry filtered and will not be registered.[registry=" + mreg.name() + ']');
 
-        if (log.isDebugEnabled())
-            log.debug("Found new metric registry [name=" + grp.name() + ']');
+            return;
+        }
+        else if (log.isDebugEnabled())
+            log.debug("Found new metric registry [name=" + mreg.name() + ']');
 
-        MetricUtils.MetricName n = parse(grp.name());
+        MetricUtils.MetricName n = parse(mreg.name());
 
         try {
-            MetricRegistryMBean mregBean = new MetricRegistryMBean(grp);
+            MetricRegistryMBean mregBean = new MetricRegistryMBean(mreg);
 
             ObjectName mbean = U.registerMBean(
                 ignite().configuration().getMBeanServer(),
@@ -82,7 +92,29 @@ public class JmxMetricExporterSpi extends IgniteSpiAdapter implements MetricExpo
                 log.debug("MetricGroup JMX bean created. " + mbean);
         }
         catch (JMException e) {
-            log.error("MBean for " + grp.name() + " can't be created.", e);
+            log.error("MBean for metric registry '" + mreg.name() + "' can't be created.", e);
+        }
+    }
+
+    /**
+     * Unregister JMX bean for specific metric registry.
+     *
+     * @param mreg Metric registry.
+     */
+    private void unregister(MetricRegistry mreg) {
+        MetricUtils.MetricName n = parse(mreg.name());
+
+        try {
+            ObjectName mbeanName = makeMBeanName(igniteInstanceName, n.root(), n.subName());
+
+            boolean rmv = mBeans.remove(mbeanName);
+
+            assert rmv;
+
+            unregBean(ignite, mbeanName);
+        }
+        catch (MalformedObjectNameException e) {
+            log.error("MBean for metric registry '" + n.root() + ',' + n.subName() + "' can't be unregistered.", e);
         }
     }
 
@@ -93,18 +125,21 @@ public class JmxMetricExporterSpi extends IgniteSpiAdapter implements MetricExpo
         if (ignite == null)
             return;
 
+        for (ObjectName bean : mBeans)
+            unregBean(ignite, bean);
+    }
+
+    private void unregBean(Ignite ignite, ObjectName bean) {
         MBeanServer jmx = ignite.configuration().getMBeanServer();
 
-        for (ObjectName bean : mBeans) {
-            try {
-                jmx.unregisterMBean(bean);
+        try {
+            jmx.unregisterMBean(bean);
 
-                if (log.isDebugEnabled())
-                    log.debug("Unregistered SPI MBean: " + bean);
-            }
-            catch (JMException e) {
-                log.error("Failed to unregister SPI MBean: " + bean, e);
-            }
+            if (log.isDebugEnabled())
+                log.debug("Unregistered SPI MBean: " + bean);
+        }
+        catch (JMException e) {
+            log.error("Failed to unregister SPI MBean: " + bean, e);
         }
     }
 
diff --git a/modules/core/src/main/java/org/apache/ignite/spi/metric/jmx/MetricRegistryMBean.java b/modules/core/src/main/java/org/apache/ignite/spi/metric/jmx/MetricRegistryMBean.java
index f4d563d..8c3cb12 100644
--- a/modules/core/src/main/java/org/apache/ignite/spi/metric/jmx/MetricRegistryMBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/spi/metric/jmx/MetricRegistryMBean.java
@@ -23,9 +23,6 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Scanner;
-import javax.management.Attribute;
-import javax.management.AttributeList;
-import javax.management.DynamicMBean;
 import javax.management.MBeanAttributeInfo;
 import javax.management.MBeanInfo;
 import org.apache.ignite.internal.processors.metric.MetricRegistry;
@@ -47,7 +44,7 @@ import static org.apache.ignite.internal.processors.metric.impl.MetricUtils.hist
 /**
  * MBean for exporting values of metric registry.
  */
-public class MetricRegistryMBean implements DynamicMBean {
+public class MetricRegistryMBean extends ReadOnlyDynamicMBean {
     /** Metric registry. */
     MetricRegistry mreg;
 
@@ -149,37 +146,6 @@ public class MetricRegistryMBean implements DynamicMBean {
         throw new IllegalArgumentException("Unknown metric class. " + metric.getClass());
     }
 
-    /** {@inheritDoc} */
-    @Override public AttributeList getAttributes(String[] attributes) {
-        AttributeList list = new AttributeList();
-
-        for (String attribute : attributes) {
-            Object val = getAttribute(attribute);
-
-            list.add(val);
-        }
-
-        return null;
-    }
-
-    /** {@inheritDoc} */
-    @Override public void setAttribute(Attribute attribute) {
-        throw new UnsupportedOperationException("setAttribute not supported.");
-    }
-
-    /** {@inheritDoc} */
-    @Override public AttributeList setAttributes(AttributeList attributes) {
-        throw new UnsupportedOperationException("setAttributes not supported.");
-    }
-
-    /** {@inheritDoc} */
-    @Override public Object invoke(String actionName, Object[] params, String[] signature) {
-        if (actionName.equals("getAttribute"))
-            return getAttribute((String)params[0]);
-
-        throw new UnsupportedOperationException("invoke not supported.");
-    }
-
     /**
      * Parse attribute name for a histogram and search it's value.
      *
diff --git a/modules/core/src/main/java/org/apache/ignite/spi/metric/jmx/ReadOnlyDynamicMBean.java b/modules/core/src/main/java/org/apache/ignite/spi/metric/jmx/ReadOnlyDynamicMBean.java
new file mode 100644
index 0000000..dc885a0
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/spi/metric/jmx/ReadOnlyDynamicMBean.java
@@ -0,0 +1,73 @@
+/*
+ * 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.spi.metric.jmx;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.DynamicMBean;
+import javax.management.MBeanException;
+import javax.management.ReflectionException;
+import org.apache.ignite.IgniteException;
+
+/**
+ * Base class for read only {@link DynamicMBean} implementations.
+ */
+public abstract class ReadOnlyDynamicMBean implements DynamicMBean {
+    /** {@inheritDoc} */
+    @Override public void setAttribute(Attribute attribute) {
+        throw new UnsupportedOperationException("setAttribute is not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public AttributeList setAttributes(AttributeList attributes) {
+        throw new UnsupportedOperationException("setAttributes is not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object invoke(String actionName, Object[] params, String[] signature)
+        throws MBeanException, ReflectionException {
+        try {
+            if ("getAttribute".equals(actionName))
+                return getAttribute((String)params[0]);
+        }
+        catch (AttributeNotFoundException e) {
+            throw new MBeanException(e);
+        }
+
+        throw new UnsupportedOperationException("invoke is not supported.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public AttributeList getAttributes(String[] attributes) {
+        AttributeList list = new AttributeList();
+
+        try {
+            for (String attribute : attributes) {
+                Object val = getAttribute(attribute);
+
+                list.add(val);
+            }
+
+            return list;
+        }
+        catch (MBeanException | ReflectionException | AttributeNotFoundException e) {
+            throw new IgniteException(e);
+        }
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java b/modules/core/src/main/java/org/apache/ignite/spi/systemview/ReadOnlySystemViewRegistry.java
similarity index 62%
copy from modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java
copy to modules/core/src/main/java/org/apache/ignite/spi/systemview/ReadOnlySystemViewRegistry.java
index 07457cc..5e06a36 100644
--- a/modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java
+++ b/modules/core/src/main/java/org/apache/ignite/spi/systemview/ReadOnlySystemViewRegistry.java
@@ -15,26 +15,25 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.spi.metric;
+package org.apache.ignite.spi.systemview;
 
 import java.util.function.Consumer;
-import org.apache.ignite.internal.processors.metric.MetricRegistry;
+import org.apache.ignite.internal.processors.metric.GridMetricManager;
+import org.apache.ignite.spi.metric.MetricExporterSpi;
+import org.apache.ignite.spi.systemview.view.SystemView;
 
 /**
- * Read only metric registry.
+ * Read only system view registry.
+ *
+ * @see GridMetricManager
+ * @see SystemView
+ * @see MetricExporterSpi
  */
-public interface ReadOnlyMetricRegistry extends Iterable<MetricRegistry> {
-    /**
-     * Adds listener of metrics group creation events.
-     *
-     * @param lsnr Listener.
-     */
-    public void addMetricRegistryCreationListener(Consumer<MetricRegistry> lsnr);
-
+public interface ReadOnlySystemViewRegistry extends Iterable<SystemView<?>> {
     /**
-     * Adds listener of metrics group remove events.
+     * Adds listener of view creation events.
      *
      * @param lsnr Listener.
      */
-    public void addMetricRegistryRemoveListener(Consumer<MetricRegistry> lsnr);
+    public void addSystemViewCreationListener(Consumer<SystemView<?>> lsnr);
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/spi/metric/MetricExporterSpi.java b/modules/core/src/main/java/org/apache/ignite/spi/systemview/SystemViewExporterSpi.java
similarity index 56%
copy from modules/core/src/main/java/org/apache/ignite/spi/metric/MetricExporterSpi.java
copy to modules/core/src/main/java/org/apache/ignite/spi/systemview/SystemViewExporterSpi.java
index 20aad78..e7916c6 100644
--- a/modules/core/src/main/java/org/apache/ignite/spi/metric/MetricExporterSpi.java
+++ b/modules/core/src/main/java/org/apache/ignite/spi/systemview/SystemViewExporterSpi.java
@@ -15,48 +15,41 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.spi.metric;
+package org.apache.ignite.spi.systemview;
 
-import java.util.function.Consumer;
 import java.util.function.Predicate;
-import org.apache.ignite.internal.processors.metric.MetricRegistry;
 import org.apache.ignite.spi.IgniteSpi;
-import org.apache.ignite.spi.metric.jmx.JmxMetricExporterSpi;
+import org.apache.ignite.spi.systemview.jmx.JmxSystemViewExporterSpi;
+import org.apache.ignite.spi.systemview.view.SystemView;
+import org.apache.ignite.spi.systemview.view.SystemViewRowAttributeWalker;
 
 /**
- * Exporter of metric information to the external recepient.
+ * Exporter of system view to the external recepient.
  * Expected, that each implementation would support some specific protocol.
  *
  * Implementation of this Spi should work by pull paradigm.
  * So after start SPI should respond to some incoming request.
  * HTTP servlet or JMX bean are good examples of expected implementations.
  *
- * @see ReadOnlyMetricRegistry
- * @see Metric
- * @see BooleanMetric
- * @see DoubleMetric
- * @see IntMetric
- * @see LongMetric
- * @see ObjectMetric
- * @see JmxMetricExporterSpi
+ * @see ReadOnlySystemViewRegistry
+ * @see SystemView
+ * @see SystemViewRowAttributeWalker
+ * @see JmxSystemViewExporterSpi
  */
-public interface MetricExporterSpi extends IgniteSpi {
+public interface SystemViewExporterSpi extends IgniteSpi {
     /**
-     * Sets metrics registry that SPI should export.
+     * Sets system view registry that SPI should export.
      * This method called before {@link #spiStart(String)}.
      *
-     * So all {@link MetricRegistry} that will be created by Ignite internal components can be obtained by
-     * listeners passed to {@link ReadOnlyMetricRegistry#addMetricRegistryCreationListener(Consumer)}.
-     *
-     * @param registry Metric registry.
+     * @param registry System view registry.
      */
-    public void setMetricRegistry(ReadOnlyMetricRegistry registry);
+    public void setSystemViewRegistry(ReadOnlySystemViewRegistry registry);
 
     /**
      * Sets export filter.
-     * Metric registry that not satisfy {@code filter} shouldn't be exported.
+     * System view that not satisfy {@code filter} shouldn't be exported.
      *
      * @param filter Filter.
      */
-    public void setExportFilter(Predicate<MetricRegistry> filter);
+    public void setExportFilter(Predicate<SystemView<?>> filter);
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/spi/systemview/jmx/JmxSystemViewExporterSpi.java b/modules/core/src/main/java/org/apache/ignite/spi/systemview/jmx/JmxSystemViewExporterSpi.java
new file mode 100644
index 0000000..7f1de78
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/spi/systemview/jmx/JmxSystemViewExporterSpi.java
@@ -0,0 +1,127 @@
+/*
+ * 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.spi.systemview.jmx;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+import javax.management.JMException;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.spi.IgniteSpiAdapter;
+import org.apache.ignite.spi.IgniteSpiException;
+import org.apache.ignite.spi.systemview.ReadOnlySystemViewRegistry;
+import org.apache.ignite.spi.systemview.SystemViewExporterSpi;
+import org.apache.ignite.spi.systemview.view.SystemView;
+import org.jetbrains.annotations.Nullable;
+
+import static org.apache.ignite.spi.systemview.jmx.SystemViewMBean.VIEWS;
+
+/**
+ * This SPI implementation exports system views as JMX beans.
+ */
+public class JmxSystemViewExporterSpi extends IgniteSpiAdapter implements SystemViewExporterSpi {
+    /** System view registry. */
+    private ReadOnlySystemViewRegistry sysViewReg;
+
+    /** System view filter. */
+    @Nullable private Predicate<SystemView<?>> filter;
+
+    /** Registered beans. */
+    private final List<ObjectName> mBeans = new ArrayList<>();
+
+    /** {@inheritDoc} */
+    @Override public void spiStart(@Nullable String igniteInstanceName) throws IgniteSpiException {
+        sysViewReg.forEach(this::register);
+
+        sysViewReg.addSystemViewCreationListener(this::register);
+    }
+
+    /**
+     * Registers JMX bean for specific system view.
+     *
+     * @param sysView System view.
+     */
+    private void register(SystemView<?> sysView) {
+        if (filter != null && !filter.test(sysView)) {
+            if (log.isDebugEnabled())
+                U.debug(log, "System view filtered and will not be registered.[name=" + sysView.name() + ']');
+
+            return;
+        }
+        else if (log.isDebugEnabled())
+            log.debug("Found new system view [name=" + sysView.name() + ']');
+
+        try {
+            SystemViewMBean mlBean = new SystemViewMBean<>(sysView);
+
+            ObjectName mbean = U.registerMBean(
+                ignite().configuration().getMBeanServer(),
+                igniteInstanceName,
+                VIEWS,
+                sysView.name(),
+                mlBean,
+                SystemViewMBean.class);
+
+            mBeans.add(mbean);
+
+            if (log.isDebugEnabled())
+                log.debug("MetricRegistry MBean created [mbean=" + mbean + ']');
+        }
+        catch (JMException e) {
+            log.error("MBean for system view '" + sysView.name() + "' can't be created.", e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void spiStop() throws IgniteSpiException {
+        Ignite ignite = ignite();
+
+        if (ignite == null)
+            return;
+
+        for (ObjectName bean : mBeans)
+            unregBean(ignite, bean);
+    }
+
+    private void unregBean(Ignite ignite, ObjectName bean) {
+        MBeanServer jmx = ignite.configuration().getMBeanServer();
+
+        try {
+            jmx.unregisterMBean(bean);
+
+            if (log.isDebugEnabled())
+                log.debug("Unregistered SPI MBean: " + bean);
+        }
+        catch (JMException e) {
+            log.error("Failed to unregister SPI MBean: " + bean, e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setSystemViewRegistry(ReadOnlySystemViewRegistry sysViewReg) {
+        this.sysViewReg = sysViewReg;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setExportFilter(Predicate<SystemView<?>> filter) {
+        this.filter = filter;
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/spi/systemview/jmx/SystemViewMBean.java b/modules/core/src/main/java/org/apache/ignite/spi/systemview/jmx/SystemViewMBean.java
new file mode 100644
index 0000000..af52dba
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/spi/systemview/jmx/SystemViewMBean.java
@@ -0,0 +1,261 @@
+/*
+ * 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.spi.systemview.jmx;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.InetSocketAddress;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import javax.management.MBeanInfo;
+import javax.management.ObjectName;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenMBeanAttributeInfo;
+import javax.management.openmbean.OpenMBeanAttributeInfoSupport;
+import javax.management.openmbean.OpenMBeanInfoSupport;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.managers.systemview.GridSystemViewManager;
+import org.apache.ignite.spi.metric.jmx.JmxMetricExporterSpi;
+import org.apache.ignite.spi.metric.jmx.ReadOnlyDynamicMBean;
+import org.apache.ignite.spi.systemview.view.SystemView;
+import org.apache.ignite.lang.IgniteUuid;
+import org.apache.ignite.spi.systemview.view.SystemViewRowAttributeWalker.AttributeVisitor;
+import org.apache.ignite.spi.systemview.view.SystemViewRowAttributeWalker.AttributeWithValueVisitor;
+
+/**
+ * JMX bean to expose specific {@link SystemView} data.
+ *
+ * @see JmxMetricExporterSpi
+ * @see GridSystemViewManager
+ */
+public class SystemViewMBean<R> extends ReadOnlyDynamicMBean {
+    /** View attribute. */
+    public static final String VIEWS = "views";
+
+    /** Row id attribute name. */
+    public static final String ID = "systemViewRowId";
+
+    /** System view to export. */
+    private final SystemView<R> sysView;
+
+    /** MBean info. */
+    private final MBeanInfo info;
+
+    /** Row type. */
+    private final CompositeType rowType;
+
+    /** System view type. */
+    private final TabularType sysViewType;
+
+    /**
+     * @param sysView System view to export.
+     */
+    public SystemViewMBean(SystemView<R> sysView) {
+        this.sysView = sysView;
+
+        int cnt = sysView.walker().count();
+
+        String[] fields = new String[cnt+1];
+        OpenType[] types = new OpenType[cnt+1];
+
+        sysView.walker().visitAll(new AttributeVisitor() {
+            @Override public <T> void accept(int idx, String name, Class<T> clazz) {
+                fields[idx] = name;
+
+                if (clazz.isAssignableFrom(String.class) || clazz.isEnum() ||
+                    clazz.isAssignableFrom(IgniteUuid.class) || clazz.isAssignableFrom(UUID.class) ||
+                    clazz.isAssignableFrom(Class.class) || clazz.isAssignableFrom(InetSocketAddress.class))
+                    types[idx] = SimpleType.STRING;
+                else if (clazz.isAssignableFrom(BigDecimal.class))
+                    types[idx] = SimpleType.BIGDECIMAL;
+                else if (clazz.isAssignableFrom(BigInteger.class))
+                    types[idx] = SimpleType.BIGINTEGER;
+                else if (clazz.isAssignableFrom(Date.class))
+                    types[idx] = SimpleType.DATE;
+                else if (clazz.isAssignableFrom(ObjectName.class))
+                    types[idx] = SimpleType.OBJECTNAME;
+                else if (clazz == boolean.class || clazz.isAssignableFrom(Boolean.class))
+                    types[idx] = SimpleType.BOOLEAN;
+                else if (clazz == byte.class || clazz.isAssignableFrom(Byte.class))
+                    types[idx] = SimpleType.BYTE;
+                else if (clazz == char.class || clazz.isAssignableFrom(Character.class))
+                    types[idx] = SimpleType.CHARACTER;
+                else if (clazz == short.class || clazz.isAssignableFrom(Short.class))
+                    types[idx] = SimpleType.SHORT;
+                else if (clazz == int.class || clazz.isAssignableFrom(Integer.class))
+                    types[idx] = SimpleType.INTEGER;
+                else if (clazz == long.class || clazz.isAssignableFrom(Long.class))
+                    types[idx] = SimpleType.LONG;
+                else if (clazz == float.class || clazz.isAssignableFrom(Float.class))
+                    types[idx] = SimpleType.FLOAT;
+                else if (clazz == double.class || clazz.isAssignableFrom(Double.class))
+                    types[idx] = SimpleType.DOUBLE;
+                else
+                    types[idx] = SimpleType.STRING;
+            }
+        });
+
+        fields[cnt] = ID;
+        types[cnt] = SimpleType.INTEGER;
+
+        try {
+            rowType = new CompositeType(sysView.rowClass().getName(),
+                sysView.description(),
+                fields,
+                fields,
+                types);
+
+            info = new OpenMBeanInfoSupport(
+                sysView.rowClass().getName(),
+                sysView.description(),
+                new OpenMBeanAttributeInfo[] {
+                    new OpenMBeanAttributeInfoSupport(VIEWS, VIEWS, rowType, true, false, false)
+                },
+                null,
+                null,
+                null
+            );
+
+            sysViewType = new TabularType(
+                sysView.rowClass().getName(),
+                sysView.description(),
+                rowType,
+                new String[] {ID}
+            );
+        }
+        catch (OpenDataException e) {
+            throw new IgniteException(e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object getAttribute(String attribute) {
+        if ("MBeanInfo".equals(attribute))
+            return getMBeanInfo();
+
+        if (attribute.equals(VIEWS)) {
+            TabularDataSupport rows = new TabularDataSupport(sysViewType);
+
+            AttributeToMapVisitor visitor = new AttributeToMapVisitor();
+
+            try {
+                int idx = 0;
+
+                for (R row : sysView) {
+                    Map<String, Object> data = new HashMap<>();
+
+                    visitor.data(data);
+
+                    sysView.walker().visitAll(row, visitor);
+
+                    data.put(ID, idx++);
+
+                    rows.put(new CompositeDataSupport(rowType, data));
+                }
+            }
+            catch (OpenDataException e) {
+                throw new IgniteException(e);
+            }
+
+            return rows;
+        }
+
+        throw new IllegalArgumentException("Unknown attribute " + attribute);
+    }
+
+    /** {@inheritDoc} */
+    @Override public MBeanInfo getMBeanInfo() {
+        return info;
+    }
+
+    /** Fullfill {@code data} Map for specific row. */
+    private static class AttributeToMapVisitor implements AttributeWithValueVisitor {
+        /** Map to store data. */
+        private Map<String, Object> data;
+
+        /**
+         * Sets map.
+         *
+         * @param data Map to fill.
+         */
+        public void data(Map<String, Object> data) {
+            this.data = data;
+        }
+
+        /** {@inheritDoc} */
+        @Override public <T> void accept(int idx, String name, Class<T> clazz, T val) {
+            if (clazz.isEnum())
+                data.put(name, ((Enum)val).name());
+            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))
+                data.put(name, String.valueOf(val));
+            else
+                data.put(name, val);
+        }
+
+        /** {@inheritDoc} */
+        @Override public void acceptBoolean(int idx, String name, boolean val) {
+            data.put(name, val);
+        }
+
+        /** {@inheritDoc} */
+        @Override public void acceptChar(int idx, String name, char val) {
+            data.put(name, val);
+        }
+
+        /** {@inheritDoc} */
+        @Override public void acceptByte(int idx, String name, byte val) {
+            data.put(name, val);
+        }
+
+        /** {@inheritDoc} */
+        @Override public void acceptShort(int idx, String name, short val) {
+            data.put(name, val);
+        }
+
+        /** {@inheritDoc} */
+        @Override public void acceptInt(int idx, String name, int val) {
+            data.put(name, val);
+        }
+
+        /** {@inheritDoc} */
+        @Override public void acceptLong(int idx, String name, long val) {
+            data.put(name, val);
+        }
+
+        /** {@inheritDoc} */
+        @Override public void acceptFloat(int idx, String name, float val) {
+            data.put(name, val);
+        }
+
+        /** {@inheritDoc} */
+        @Override public void acceptDouble(int idx, String name, double val) {
+            data.put(name, val);
+        }
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java b/modules/core/src/main/java/org/apache/ignite/spi/systemview/jmx/package-info.java
similarity index 56%
copy from modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java
copy to modules/core/src/main/java/org/apache/ignite/spi/systemview/jmx/package-info.java
index 07457cc..573b287 100644
--- a/modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java
+++ b/modules/core/src/main/java/org/apache/ignite/spi/systemview/jmx/package-info.java
@@ -15,26 +15,9 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.spi.metric;
-
-import java.util.function.Consumer;
-import org.apache.ignite.internal.processors.metric.MetricRegistry;
-
 /**
- * Read only metric registry.
+ * <!-- Package description. -->
+ * Contains implementations of the {@link org.apache.ignite.spi.systemview.SystemViewExporterSpi}
+ * that exports system vies as a JMX beans.
  */
-public interface ReadOnlyMetricRegistry extends Iterable<MetricRegistry> {
-    /**
-     * Adds listener of metrics group creation events.
-     *
-     * @param lsnr Listener.
-     */
-    public void addMetricRegistryCreationListener(Consumer<MetricRegistry> lsnr);
-
-    /**
-     * Adds listener of metrics group remove events.
-     *
-     * @param lsnr Listener.
-     */
-    public void addMetricRegistryRemoveListener(Consumer<MetricRegistry> lsnr);
-}
+package org.apache.ignite.spi.systemview.jmx;
diff --git a/modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java b/modules/core/src/main/java/org/apache/ignite/spi/systemview/package-info.java
similarity index 56%
copy from modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java
copy to modules/core/src/main/java/org/apache/ignite/spi/systemview/package-info.java
index 07457cc..c21c921 100644
--- a/modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java
+++ b/modules/core/src/main/java/org/apache/ignite/spi/systemview/package-info.java
@@ -15,26 +15,8 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.spi.metric;
-
-import java.util.function.Consumer;
-import org.apache.ignite.internal.processors.metric.MetricRegistry;
-
 /**
- * Read only metric registry.
+ * <!-- Package description. -->
+ * Contains common classes and interfaces for system view SPI implementations.
  */
-public interface ReadOnlyMetricRegistry extends Iterable<MetricRegistry> {
-    /**
-     * Adds listener of metrics group creation events.
-     *
-     * @param lsnr Listener.
-     */
-    public void addMetricRegistryCreationListener(Consumer<MetricRegistry> lsnr);
-
-    /**
-     * Adds listener of metrics group remove events.
-     *
-     * @param lsnr Listener.
-     */
-    public void addMetricRegistryRemoveListener(Consumer<MetricRegistry> lsnr);
-}
+package org.apache.ignite.spi.systemview;
diff --git a/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/CacheGroupView.java b/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/CacheGroupView.java
new file mode 100644
index 0000000..39f4e56
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/CacheGroupView.java
@@ -0,0 +1,153 @@
+/*
+ * 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.spi.systemview.view;
+
+import java.util.Map;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.CacheRebalanceMode;
+import org.apache.ignite.cache.PartitionLossPolicy;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.TopologyValidator;
+import org.apache.ignite.internal.processors.cache.CacheGroupDescriptor;
+import org.apache.ignite.internal.managers.systemview.walker.Order;
+import org.apache.ignite.lang.IgnitePredicate;
+
+import static org.apache.ignite.internal.util.IgniteUtils.toStringSafe;
+
+/**
+ * Cache group representation for the {@link SystemView}.
+ */
+public class CacheGroupView {
+    /** Cache group. */
+    private final CacheGroupDescriptor grp;
+
+    /** Cache configuration. */
+    private final CacheConfiguration<?, ?> ccfg;
+
+    /**
+     * @param grp Cache group.
+     */
+    public CacheGroupView(CacheGroupDescriptor grp) {
+        this.grp = grp;
+        this.ccfg = grp.config();
+    }
+
+    /** @return Cache group id. */
+    public int cacheGroupId() {
+        return grp.groupId();
+    }
+
+    /** @return Cache group name. */
+    @Order()
+    public String cacheGroupName() {
+        return grp.cacheOrGroupName();
+    }
+
+    /** @return {@code True} if group shared, {@code false} otherwise. */
+    public boolean isShared() {
+        return grp.sharedGroup();
+    }
+
+    /** @return Cache count. */
+    @Order(1)
+    public int cacheCount() {
+        Map<String, Integer> caches = grp.caches();
+
+        return caches == null ? 0 : caches.size();
+    }
+
+    /** @return Cache mode. */
+    @Order(3)
+    public CacheMode cacheMode() {
+        return ccfg.getCacheMode();
+    }
+
+    /** @return Atomicity mode. */
+    @Order(4)
+    public CacheAtomicityMode atomicityMode() {
+        return ccfg.getAtomicityMode();
+    }
+
+    /** @return Affinity string representation. */
+    public String affinity() {
+        return ccfg.getAffinity() != null ? toStringSafe(ccfg.getAffinity()) : null;
+    }
+
+    /** @return Partitions count. */
+    public int partitionsCount() {
+        return ccfg.getAffinity() != null ? ccfg.getAffinity().partitions() : -1;
+    }
+
+    /** @return Node filter string representation. */
+    public String nodeFilter() {
+        return nodeFilter(ccfg);
+    }
+
+    /**
+     * @param ccfg Cache configuration.
+     * @return Node filter string representation.
+     */
+    public static String nodeFilter(CacheConfiguration<?, ?> ccfg) {
+        IgnitePredicate<ClusterNode> nodeFilter = ccfg.getNodeFilter();
+
+        if (nodeFilter instanceof CacheConfiguration.IgniteAllNodesPredicate)
+            return null;
+
+        return toStringSafe(nodeFilter);
+    }
+
+    /** @return Data region name. */
+    @Order(2)
+    public String dataRegionName() {
+        return ccfg.getDataRegionName();
+    }
+
+    /** @return Topology validator. */
+    public String topologyValidator() {
+        TopologyValidator validator = ccfg.getTopologyValidator();
+
+        return validator == null ? null : toStringSafe(validator);
+    }
+
+    /** @return Partition loss policy. */
+    public PartitionLossPolicy partitionLossPolicy() {
+        return ccfg.getPartitionLossPolicy();
+    }
+
+    /** @return Cache rebalance mode. */
+    public CacheRebalanceMode rebalanceMode() {
+        return ccfg.getRebalanceMode();
+    }
+
+    /** @return Rebalance delay. */
+    public long rebalanceDelay() {
+        return ccfg.getRebalanceDelay();
+    }
+
+    /** @return Rebalance order. */
+    public int rebalanceOrder() {
+        return ccfg.getRebalanceOrder();
+    }
+
+    /** @return Backups count. */
+    public int backups() {
+        return ccfg.getBackups();
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/CacheView.java b/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/CacheView.java
new file mode 100644
index 0000000..6ce5e4b
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/CacheView.java
@@ -0,0 +1,373 @@
+/*
+ * 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.spi.systemview.view;
+
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.CacheRebalanceMode;
+import org.apache.ignite.cache.CacheWriteSynchronizationMode;
+import org.apache.ignite.cache.PartitionLossPolicy;
+import org.apache.ignite.cache.affinity.AffinityFunction;
+import org.apache.ignite.cache.affinity.AffinityKeyMapper;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.NearCacheConfiguration;
+import org.apache.ignite.configuration.TopologyValidator;
+import org.apache.ignite.internal.processors.cache.CacheGroupDescriptor;
+import org.apache.ignite.internal.processors.cache.CacheType;
+import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor;
+import org.apache.ignite.internal.managers.systemview.walker.Order;
+
+import static org.apache.ignite.internal.util.IgniteUtils.toStringSafe;
+
+/**
+ * Cache representation for a {@link SystemView}.
+ */
+public class CacheView {
+    /** Cache descriptor. */
+    private DynamicCacheDescriptor cache;
+
+    /**
+     * @param cache Cache descriptor.
+     */
+    public CacheView(DynamicCacheDescriptor cache) {
+        this.cache = cache;
+    }
+
+    /** @see DynamicCacheDescriptor#groupId() */
+    public int cacheGroupId() {
+        return cache.groupId();
+    }
+
+    /** @see CacheGroupDescriptor#cacheOrGroupName() */
+    @Order(5)
+    public String cacheGroupName() {
+        return cache.groupDescriptor().cacheOrGroupName();
+    }
+
+    /** @see DynamicCacheDescriptor#cacheId() */
+    @Order(1)
+    public int cacheId() {
+        return cache.cacheId();
+    }
+
+    /** @see CacheConfiguration#getName() */
+    @Order
+    public String cacheName() {
+        return cache.cacheConfiguration().getName();
+    }
+
+    /** @see DynamicCacheDescriptor#cacheType() */
+    @Order(2)
+    public CacheType cacheType() {
+        return cache.cacheType();
+    }
+
+    /** @see CacheConfiguration#getCacheMode() */
+    @Order(3)
+    public CacheMode cacheMode() {
+        return cache.cacheConfiguration().getCacheMode();
+    }
+
+    /** @see CacheConfiguration#getAtomicityMode() */
+    @Order(4)
+    public CacheAtomicityMode atomicityMode() {
+        return cache.cacheConfiguration().getAtomicityMode();
+    }
+
+    /** @see CacheConfiguration#isOnheapCacheEnabled() */
+    public boolean isOnheapCacheEnabled() {
+        return cache.cacheConfiguration().isOnheapCacheEnabled();
+    }
+
+    /** @see CacheConfiguration#isCopyOnRead() */
+    public boolean isCopyOnRead() {
+        return cache.cacheConfiguration().isCopyOnRead();
+    }
+
+    /** @see CacheConfiguration#isLoadPreviousValue() */
+    public boolean isLoadPreviousValue() {
+        return cache.cacheConfiguration().isLoadPreviousValue();
+    }
+
+    /** @see CacheConfiguration#isReadFromBackup() */
+    public boolean isReadFromBackup() {
+        return cache.cacheConfiguration().isReadFromBackup();
+    }
+
+    /** @see CacheConfiguration#getPartitionLossPolicy() */
+    public PartitionLossPolicy partitionLossPolicy() {
+        return cache.cacheConfiguration().getPartitionLossPolicy();
+    }
+
+    /** @see CacheConfiguration#getNodeFilter() */
+    public String nodeFilter() {
+        return CacheGroupView.nodeFilter(cache.cacheConfiguration());
+    }
+
+    /** @see CacheConfiguration#getTopologyValidator() */
+    public String topologyValidator() {
+        TopologyValidator validator = cache.cacheConfiguration().getTopologyValidator();
+
+        return validator == null ? null : toStringSafe(validator);
+    }
+
+    /** @see CacheConfiguration#isEagerTtl() */
+    public boolean isEagerTtl() {
+        return cache.cacheConfiguration().isEagerTtl();
+    }
+
+    /** @see CacheConfiguration#getWriteSynchronizationMode() */
+    public CacheWriteSynchronizationMode writeSynchronizationMode() {
+        return cache.cacheConfiguration().getWriteSynchronizationMode();
+    }
+
+    /** @see CacheConfiguration#isInvalidate() */
+    public boolean isInvalidate() {
+        return cache.cacheConfiguration().isInvalidate();
+    }
+
+    /** @see CacheConfiguration#isEventsDisabled() */
+    public boolean isEventsDisabled() {
+        return cache.cacheConfiguration().isEventsDisabled();
+    }
+
+    /** @see CacheConfiguration#isStatisticsEnabled() */
+    public boolean isStatisticsEnabled() {
+        return cache.cacheConfiguration().isStatisticsEnabled();
+    }
+
+    /** @see CacheConfiguration#isManagementEnabled() */
+    public boolean isManagementEnabled() {
+        return cache.cacheConfiguration().isManagementEnabled();
+    }
+
+    /** @see CacheConfiguration#isEncryptionEnabled()  */
+    public boolean isEncryptionEnabled() {
+        return cache.cacheConfiguration().isEncryptionEnabled();
+    }
+
+    /** @see CacheConfiguration#getBackups() */
+    public int backups() {
+        return cache.cacheConfiguration().getBackups();
+    }
+
+    /** @see CacheConfiguration#getAffinity() */
+    public String affinity() {
+        AffinityFunction aff = cache.cacheConfiguration().getAffinity();
+
+        return aff != null ? toStringSafe(aff) : null;
+    }
+
+    /** @see CacheConfiguration#getAffinityMapper() */
+    public String affinityMapper() {
+        AffinityKeyMapper affMap = cache.cacheConfiguration().getAffinityMapper();
+
+        return affMap != null ? toStringSafe(affMap) : null;
+    }
+
+    /** @see CacheConfiguration#getRebalanceMode() */
+    public CacheRebalanceMode rebalanceMode() {
+        return cache.cacheConfiguration().getRebalanceMode();
+    }
+
+    /** @see CacheConfiguration#getRebalanceBatchSize() */
+    public int rebalanceBatchSize() {
+        return cache.cacheConfiguration().getRebalanceBatchSize();
+    }
+
+    /** @see CacheConfiguration#getRebalanceTimeout() */
+    public long rebalanceTimeout() {
+        return cache.cacheConfiguration().getRebalanceTimeout();
+    }
+
+    /** @see CacheConfiguration#getRebalanceDelay() */
+    public long rebalanceDelay() {
+        return cache.cacheConfiguration().getRebalanceDelay();
+    }
+
+    /** @see CacheConfiguration#getRebalanceThrottle() */
+    public long rebalanceThrottle() {
+        return cache.cacheConfiguration().getRebalanceThrottle();
+    }
+
+    /** @see CacheConfiguration#getRebalanceBatchesPrefetchCount() */
+    public long rebalanceBatchesPrefetchCount() {
+        return cache.cacheConfiguration().getRebalanceBatchesPrefetchCount();
+    }
+
+    /** @see CacheConfiguration#getRebalanceOrder() */
+    public int rebalanceOrder() {
+        return cache.cacheConfiguration().getRebalanceOrder();
+    }
+
+    /** @see CacheConfiguration#getEvictionFilter() */
+    public String evictionFilter() {
+        return toStringSafe(cache.cacheConfiguration().getEvictionFilter());
+    }
+
+    /** @see CacheConfiguration#getEvictionPolicyFactory() */
+    public String evictionPolicyFactory() {
+        return toStringSafe(cache.cacheConfiguration().getEvictionPolicyFactory());
+    }
+
+    /** @see CacheConfiguration#getNearConfiguration() */
+    public boolean isNearCacheEnabled() {
+        return cache.cacheConfiguration().getNearConfiguration() != null;
+    }
+
+    /** @see NearCacheConfiguration#getNearEvictionPolicyFactory() */
+    public String nearCacheEvictionPolicyFactory() {
+        if (cache.cacheConfiguration().getNearConfiguration() == null)
+            return null;
+
+        return toStringSafe(cache.cacheConfiguration().getNearConfiguration().getNearEvictionPolicyFactory());
+    }
+
+    /** @see NearCacheConfiguration#getNearStartSize() */
+    public int nearCacheStartSize() {
+        if (cache.cacheConfiguration().getNearConfiguration() == null)
+            return 0;
+
+        return cache.cacheConfiguration().getNearConfiguration().getNearStartSize();
+    }
+
+    /** @see CacheConfiguration#getDefaultLockTimeout() */
+    public long defaultLockTimeout() {
+        return cache.cacheConfiguration().getDefaultLockTimeout();
+    }
+
+    /** @see CacheConfiguration#getInterceptor() */
+    public String interceptor() {
+        return toStringSafe(cache.cacheConfiguration().getInterceptor());
+    }
+
+    /** @see CacheConfiguration#getCacheStoreFactory() */
+    public String cacheStoreFactory() {
+        return toStringSafe(cache.cacheConfiguration().getCacheStoreFactory());
+    }
+
+    /** @see CacheConfiguration#isStoreKeepBinary() */
+    public boolean isStoreKeepBinary() {
+        return cache.cacheConfiguration().isStoreKeepBinary();
+    }
+
+    /** @see CacheConfiguration#isReadThrough() */
+    public boolean isReadThrough() {
+        return cache.cacheConfiguration().isReadThrough();
+    }
+
+    /** @see CacheConfiguration#isWriteThrough() */
+    public boolean isWriteThrough() {
+        return cache.cacheConfiguration().isWriteThrough();
+    }
+
+    /** @see CacheConfiguration#isWriteBehindEnabled() */
+    public boolean isWriteBehindEnabled() {
+        return cache.cacheConfiguration().isWriteBehindEnabled();
+    }
+
+    /** @see CacheConfiguration#getWriteBehindCoalescing()  */
+    public boolean writeBehindCoalescing() {
+        return cache.cacheConfiguration().getWriteBehindCoalescing();
+    }
+
+    /** @see CacheConfiguration#getWriteBehindFlushSize() */
+    public int writeBehindFlushSize() {
+        return cache.cacheConfiguration().getWriteBehindFlushSize();
+    }
+
+    /** @see CacheConfiguration#getWriteBehindFlushFrequency() */
+    public long writeBehindFlushFrequency() {
+        return cache.cacheConfiguration().getWriteBehindFlushFrequency();
+    }
+
+    /** @see CacheConfiguration#getWriteBehindFlushThreadCount() */
+    public int writeBehindFlushThreadCount() {
+        return cache.cacheConfiguration().getWriteBehindFlushThreadCount();
+    }
+
+    /** @see CacheConfiguration#getWriteBehindBatchSize() */
+    public int writeBehindBatchSize() {
+        return cache.cacheConfiguration().getWriteBehindBatchSize();
+    }
+
+    /** @see CacheConfiguration#getMaxConcurrentAsyncOperations() */
+    public int maxConcurrentAsyncOperations() {
+        return cache.cacheConfiguration().getMaxConcurrentAsyncOperations();
+    }
+
+    /** @see CacheConfiguration#getCacheLoaderFactory() */
+    public String cacheLoaderFactory() {
+        return toStringSafe(cache.cacheConfiguration().getCacheLoaderFactory());
+    }
+
+    /** @see CacheConfiguration#getCacheWriterFactory() */
+    public String cacheWriterFactory() {
+        return toStringSafe(cache.cacheConfiguration().getCacheWriterFactory());
+    }
+
+    /** @see CacheConfiguration#getExpiryPolicyFactory() */
+    public String expiryPolicyFactory() {
+        return toStringSafe(cache.cacheConfiguration().getExpiryPolicyFactory());
+    }
+
+    /** @see CacheConfiguration#isSqlEscapeAll() */
+    public boolean isSqlEscapeAll() {
+        return cache.cacheConfiguration().isSqlEscapeAll();
+    }
+
+    /** @see CacheConfiguration#getSqlSchema() */
+    public String sqlSchema() {
+        return cache.cacheConfiguration().getSqlSchema();
+    }
+
+    /** @see CacheConfiguration#getSqlIndexMaxInlineSize() */
+    public int sqlIndexMaxInlineSize() {
+        return cache.cacheConfiguration().getSqlIndexMaxInlineSize();
+    }
+
+    /** @see CacheConfiguration#isSqlOnheapCacheEnabled() */
+    public boolean isSqlOnheapCacheEnabled() {
+        return cache.cacheConfiguration().isSqlOnheapCacheEnabled();
+    }
+
+    /** @see CacheConfiguration#getSqlOnheapCacheMaxSize() */
+    public int sqlOnheapCacheMaxSize() {
+        return cache.cacheConfiguration().getSqlOnheapCacheMaxSize();
+    }
+
+    /** @see CacheConfiguration#getQueryDetailMetricsSize() */
+    public int queryDetailMetricsSize() {
+        return cache.cacheConfiguration().getQueryDetailMetricsSize();
+    }
+
+    /** @see CacheConfiguration#getQueryParallelism() */
+    public int queryParallelism() {
+        return cache.cacheConfiguration().getQueryParallelism();
+    }
+
+    /** @see CacheConfiguration#getMaxQueryIteratorsCount() */
+    public int maxQueryIteratorsCount() {
+        return cache.cacheConfiguration().getMaxQueryIteratorsCount();
+    }
+
+    /** @see CacheConfiguration#getDataRegionName() */
+    public String dataRegionName() {
+        return cache.cacheConfiguration().getDataRegionName();
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/ComputeTaskView.java b/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/ComputeTaskView.java
new file mode 100644
index 0000000..9c25fe9
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/ComputeTaskView.java
@@ -0,0 +1,92 @@
+/*
+ * 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.spi.systemview.view;
+
+import java.util.UUID;
+import org.apache.ignite.internal.processors.task.GridTaskWorker;
+import org.apache.ignite.lang.IgniteUuid;
+
+/**
+ * Compute task representation for a {@link SystemView}.
+ */
+public class ComputeTaskView {
+    /** Worker for task. */
+    private final GridTaskWorker worker;
+
+    /**
+     * @param worker Worker for task.
+     */
+    public ComputeTaskView(GridTaskWorker worker) {
+        this.worker = worker;
+    }
+
+    /** @return {@code True} if task is internal. */
+    public boolean internal() {
+        return worker.isInternal();
+    }
+
+    /** @return Task name. */
+    public String taskName() {
+        return worker.getSession().getTaskName();
+    }
+
+    /** @return Task class name. */
+    public String taskClassName() {
+        return worker.getSession().getTaskClassName();
+    }
+
+    /** @return Start time. */
+    public long startTime() {
+        return worker.getSession().getStartTime();
+    }
+
+    /** @return End time. */
+    public long endTime() {
+        return worker.getSession().getEndTime();
+    }
+
+    /** @return Task node id. */
+    public UUID taskNodeId() {
+        return worker.getSession().getTaskNodeId();
+    }
+
+    /** @return Executor name. */
+    public String execName() {
+        return worker.getSession().executorName();
+    }
+
+    /** @return Affinity cache name. */
+    public String affinityCacheName() {
+        return worker.affCacheName();
+    }
+
+    /** @return Affinity partition id. */
+    public int affinityPartitionId() {
+        return worker.affPartId();
+    }
+
+    /** @return Job id. */
+    public IgniteUuid jobId() {
+        return worker.getSession().getJobId();
+    }
+
+    /** @return User provided version. */
+    public String userVersion() {
+        return worker.getSession().getUserVersion();
+    }
+}
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
new file mode 100644
index 0000000..9e638ef
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/ServiceView.java
@@ -0,0 +1,101 @@
+/*
+ * 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.spi.systemview.view;
+
+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;
+
+/**
+ * Service representation for a {@link SystemView}.
+ */
+public class ServiceView {
+    /** Service descriptor. */
+    private final ServiceInfo serviceInfo;
+
+    /**
+     * @param serviceInfo Service descriptor.
+     */
+    public ServiceView(ServiceInfo serviceInfo) {
+        this.serviceInfo = serviceInfo;
+    }
+
+    /** @return Service name. */
+    @Order(1)
+    public String name() {
+        return serviceInfo.name();
+    }
+
+    /** @return Service id. */
+    @Order
+    public IgniteUuid serviceId() {
+        return serviceInfo.serviceId();
+    }
+
+    /** @return Service class. */
+    @Order(2)
+    public Class<?> serviceClass() {
+        return serviceInfo.serviceClass();
+    }
+
+    /** @return Total count of service instances. */
+    @Order(5)
+    public int totalCount() {
+        return serviceInfo.totalCount();
+    }
+
+    /** @return Maximum instance count per node. */
+    @Order(6)
+    public int maxPerNodeCount() {
+        return serviceInfo.maxPerNodeCount();
+    }
+
+    /** @return Cache name. */
+    @Order(3)
+    public String cacheName() {
+        return serviceInfo.cacheName();
+    }
+
+    /** @return Affininty key value. */
+    public String affinityKey() {
+        Object affKey = serviceInfo.configuration().getAffinityKey();
+
+        return affKey == null ? null : affKey.toString();
+    }
+
+    /** @return Node filter. */
+    public Class<?> nodeFilter() {
+        IgnitePredicate<ClusterNode> filter = serviceInfo.configuration().getNodeFilter();
+
+        return filter == null ? null : filter.getClass();
+    }
+
+    /** @return {@code True} if statically configured. */
+    public boolean staticallyConfigured() {
+        return serviceInfo.staticallyConfigured();
+    }
+
+    /** @return Origin node id. */
+    @Order(4)
+    public UUID originNodeId() {
+        return serviceInfo.originNodeId();
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java b/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/SystemView.java
similarity index 56%
copy from modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java
copy to modules/core/src/main/java/org/apache/ignite/spi/systemview/view/SystemView.java
index 07457cc..5de6908 100644
--- a/modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java
+++ b/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/SystemView.java
@@ -15,26 +15,26 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.spi.metric;
-
-import java.util.function.Consumer;
-import org.apache.ignite.internal.processors.metric.MetricRegistry;
+package org.apache.ignite.spi.systemview.view;
 
 /**
- * Read only metric registry.
+ * Implementation provides data for some internal Ignite objects.
+ *
+ * @param <R> Type of the row.
  */
-public interface ReadOnlyMetricRegistry extends Iterable<MetricRegistry> {
-    /**
-     * Adds listener of metrics group creation events.
-     *
-     * @param lsnr Listener.
-     */
-    public void addMetricRegistryCreationListener(Consumer<MetricRegistry> lsnr);
+public interface SystemView<R> extends Iterable<R> {
+    /** @return Helper for exporters. */
+    public SystemViewRowAttributeWalker<R> walker();
+
+    /** @return Class of the row. */
+    public Class<R> rowClass();
+
+    /** @return View name. */
+    public String name();
+
+    /** @return View description. */
+    public String description();
 
-    /**
-     * Adds listener of metrics group remove events.
-     *
-     * @param lsnr Listener.
-     */
-    public void addMetricRegistryRemoveListener(Consumer<MetricRegistry> lsnr);
+    /** @return Size of the view. */
+    public int size();
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/SystemViewRowAttributeWalker.java b/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/SystemViewRowAttributeWalker.java
new file mode 100644
index 0000000..0c26997
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/SystemViewRowAttributeWalker.java
@@ -0,0 +1,140 @@
+/*
+ * 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.spi.systemview.view;
+
+/**
+ * Utility class for quick iteration over row properties.
+ */
+public interface SystemViewRowAttributeWalker<R> {
+    /** @return Count of a row properties. */
+    public int count();
+
+    /**
+     * Calls visitor for each row attribute.
+     *
+     * @param visitor Attribute visitor.
+     */
+    public void visitAll(AttributeVisitor visitor);
+
+    /**
+     * Calls visitor for each row attribute.
+     * Value of the attribute also provided.
+     *
+     * @param row Row to iterate.
+     * @param visitor Attribute visitor.
+     */
+    public void visitAll(R row, AttributeWithValueVisitor visitor);
+
+    /** Attribute visitor. */
+    public interface AttributeVisitor {
+        /**
+         * Visit some object property.
+         * @param idx Index.
+         * @param name Name.
+         * @param clazz Value class.
+         * @param <T> Value type.
+         */
+        public <T> void accept(int idx, String name, Class<T> clazz);
+    }
+
+    /** Attribute visitor. */
+    public interface AttributeWithValueVisitor {
+        /**
+         * Visit attribute. Attribute value is object.
+         *
+         * @param idx Index.
+         * @param name Name.
+         * @param clazz Class.
+         * @param val Value.
+         * @param <T> Value type.
+         */
+        public <T> void accept(int idx, String name, Class<T> clazz, T val);
+
+        /**
+         * Visit attribute. Attribute value is {@code boolean} primitive.
+         *
+         * @param idx Index.
+         * @param name Name.
+         * @param val Value.
+         */
+        public void acceptBoolean(int idx, String name, boolean val);
+
+        /**
+         * Visit attribute. Attribute value is {@code char} primitive.
+         *
+         * @param idx Index.
+         * @param name Name.
+         * @param val Value.
+         */
+        public void acceptChar(int idx, String name, char val);
+
+        /**
+         * Visit attribute. Attribute value is {@code byte} primitive.
+         *
+         * @param idx Index.
+         * @param name Name.
+         * @param val Value.
+         */
+        public void acceptByte(int idx, String name, byte val);
+
+        /**
+         * Visit attribute. Attribute value is {@code short} primitive.
+         *
+         * @param idx Index.
+         * @param name Name.
+         * @param val Value.
+         */
+        public void acceptShort(int idx, String name, short val);
+
+        /**
+         * Visit attribute. Attribute value is {@code int} primitive.
+         *
+         * @param idx Index.
+         * @param name Name.
+         * @param val Value.
+         */
+        public void acceptInt(int idx, String name, int val);
+
+        /**
+         * Visit attribute. Attribute value is {@code long} primitive.
+         *
+         * @param idx Index.
+         * @param name Name.
+         * @param val Value.
+         */
+        public void acceptLong(int idx, String name, long val);
+
+        /**
+         * Visit attribute. Attribute value is {@code float} primitive.
+         *
+         * @param idx Index.
+         * @param name Name.
+         * @param val Value.
+         */
+        public void acceptFloat(int idx, String name, float val);
+
+        /**
+         * Visit attribute. Attribute value is {@code double} primitive.
+         *
+         * @param idx Index.
+         * @param name Name.
+         * @param val Value.
+         */
+        public void acceptDouble(int idx, String name, double val);
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java b/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/package-info.java
similarity index 56%
copy from modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java
copy to modules/core/src/main/java/org/apache/ignite/spi/systemview/view/package-info.java
index 07457cc..a3ec4b3 100644
--- a/modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java
+++ b/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/package-info.java
@@ -15,26 +15,8 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.spi.metric;
-
-import java.util.function.Consumer;
-import org.apache.ignite.internal.processors.metric.MetricRegistry;
-
 /**
- * Read only metric registry.
+ * <!-- Package description. -->
+ * Contains {@link org.apache.ignite.spi.systemview.view.SystemView} clases that exists in Ignite.
  */
-public interface ReadOnlyMetricRegistry extends Iterable<MetricRegistry> {
-    /**
-     * Adds listener of metrics group creation events.
-     *
-     * @param lsnr Listener.
-     */
-    public void addMetricRegistryCreationListener(Consumer<MetricRegistry> lsnr);
-
-    /**
-     * Adds listener of metrics group remove events.
-     *
-     * @param lsnr Listener.
-     */
-    public void addMetricRegistryRemoveListener(Consumer<MetricRegistry> lsnr);
-}
+package org.apache.ignite.spi.systemview.view;
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/metric/JmxMetricExporterSpiTest.java b/modules/core/src/test/java/org/apache/ignite/internal/metric/JmxExporterSpiTest.java
similarity index 56%
rename from modules/core/src/test/java/org/apache/ignite/internal/metric/JmxMetricExporterSpiTest.java
rename to modules/core/src/test/java/org/apache/ignite/internal/metric/JmxExporterSpiTest.java
index bb7d459..43a0ff3 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/metric/JmxMetricExporterSpiTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/metric/JmxExporterSpiTest.java
@@ -17,35 +17,59 @@
 
 package org.apache.ignite.internal.metric;
 
+import java.lang.management.ManagementFactory;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
 import javax.management.DynamicMBean;
 import javax.management.MBeanAttributeInfo;
 import javax.management.MBeanFeatureInfo;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerInvocationHandler;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.TabularDataSupport;
+import org.apache.ignite.IgniteCache;
 import org.apache.ignite.IgniteException;
+import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.DataRegionConfiguration;
 import org.apache.ignite.configuration.DataStorageConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.processors.metric.MetricRegistry;
 import org.apache.ignite.internal.processors.metric.impl.HistogramMetric;
+import org.apache.ignite.internal.processors.service.DummyService;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.services.ServiceConfiguration;
 import org.apache.ignite.spi.metric.jmx.JmxMetricExporterSpi;
 import org.apache.ignite.testframework.GridTestUtils.RunnableX;
 import org.junit.Test;
 
 import static java.util.Arrays.stream;
 import static java.util.stream.Collectors.toSet;
+import static org.apache.ignite.internal.processors.cache.CacheMetricsImpl.CACHE_METRICS;
+import static org.apache.ignite.internal.processors.cache.ClusterCachesInfo.CACHES_VIEW;
+import static org.apache.ignite.internal.processors.cache.ClusterCachesInfo.CACHE_GRPS_VIEW;
 import static org.apache.ignite.internal.processors.metric.GridMetricManager.CPU_LOAD;
 import static org.apache.ignite.internal.processors.metric.GridMetricManager.CPU_LOAD_DESCRIPTION;
 import static org.apache.ignite.internal.processors.metric.GridMetricManager.GC_CPU_LOAD;
 import static org.apache.ignite.internal.processors.metric.GridMetricManager.GC_CPU_LOAD_DESCRIPTION;
 import static org.apache.ignite.internal.processors.metric.GridMetricManager.SYS_METRICS;
 import static org.apache.ignite.internal.processors.metric.impl.MetricUtils.metricName;
+import static org.apache.ignite.internal.processors.service.IgniteServiceProcessor.SVCS_VIEW;
+import static org.apache.ignite.internal.processors.task.GridTaskProcessor.TASKS_VIEW;
 import static org.apache.ignite.spi.metric.jmx.MetricRegistryMBean.searchHistogram;
+import static org.apache.ignite.spi.systemview.jmx.SystemViewMBean.VIEWS;
 import static org.apache.ignite.testframework.GridTestUtils.assertThrowsWithCause;
 
 /** */
-public class JmxMetricExporterSpiTest extends AbstractExporterSpiTest {
+public class JmxExporterSpiTest extends AbstractExporterSpiTest {
     /** */
     private static IgniteEx ignite;
 
@@ -72,6 +96,16 @@ public class JmxMetricExporterSpiTest extends AbstractExporterSpiTest {
         cleanPersistenceDir();
 
         ignite = startGrid(0);
+
+        ignite.cluster().active(true);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        Collection<String> caches = ignite.cacheNames();
+
+        for (String cache : caches)
+            ignite.destroyCache(cache);
     }
 
     /** {@inheritDoc} */
@@ -129,6 +163,22 @@ public class JmxMetricExporterSpiTest extends AbstractExporterSpiTest {
 
     /** */
     @Test
+    public void testUnregisterRemovedRegistry() throws Exception {
+        String n = "cache-for-remove";
+
+        IgniteCache c = ignite.createCache(n);
+
+        DynamicMBean cacheBean = mbean(CACHE_METRICS, n);
+
+        assertNotNull(cacheBean);
+
+        ignite.destroyCache(n);
+
+        assertThrowsWithCause(() -> mbean(CACHE_METRICS, n), IgniteException.class);
+    }
+
+    /** */
+    @Test
     public void testFilterAndExport() throws Exception {
         createAdditionalMetrics(ignite);
 
@@ -150,6 +200,134 @@ public class JmxMetricExporterSpiTest extends AbstractExporterSpiTest {
 
     /** */
     @Test
+    public void testCachesView() throws Exception {
+        Set<String> cacheNames = new HashSet<>(Arrays.asList("cache-1", "cache-2"));
+
+        for (String name : cacheNames)
+            ignite.createCache(name);
+
+        TabularDataSupport data = systemView(CACHES_VIEW);
+
+        assertEquals(ignite.context().cache().cacheDescriptors().size(), data.size());
+
+        for (int i = 0; i < data.size(); i++) {
+            CompositeData row = data.get(new Object[] {i});
+
+            cacheNames.remove(row.get("cacheName"));
+        }
+
+        assertTrue(cacheNames.toString(), cacheNames.isEmpty());
+    }
+
+    /** */
+    @Test
+    public void testCacheGroupsView() throws Exception {
+        Set<String> grpNames = new HashSet<>(Arrays.asList("grp-1", "grp-2"));
+
+        for (String grpName : grpNames)
+            ignite.createCache(new CacheConfiguration<>("cache-" + grpName).setGroupName(grpName));
+
+        TabularDataSupport grps = systemView(CACHE_GRPS_VIEW);
+
+        assertEquals(ignite.context().cache().cacheGroupDescriptors().size(), grps.size());
+
+        for (Map.Entry entry : grps.entrySet()) {
+            CompositeData row = (CompositeData)entry.getValue();
+
+            grpNames.remove(row.get("cacheGroupName"));
+        }
+
+        assertTrue(grpNames.toString(), grpNames.isEmpty());
+    }
+
+    /** */
+    @Test
+    public void testServices() throws Exception {
+        ServiceConfiguration srvcCfg = new ServiceConfiguration();
+
+        srvcCfg.setName("service");
+        srvcCfg.setMaxPerNodeCount(1);
+        srvcCfg.setService(new DummyService());
+
+        ignite.services().deploy(srvcCfg);
+
+        TabularDataSupport srvs = systemView(SVCS_VIEW);
+
+        assertEquals(ignite.context().service().serviceDescriptors().size(), srvs.size());
+
+        CompositeData sysView = srvs.get(new Object[] {0});
+
+        assertEquals(srvcCfg.getName(), sysView.get("name"));
+        assertEquals(srvcCfg.getMaxPerNodeCount(), sysView.get("maxPerNodeCount"));
+        assertEquals(DummyService.class.getName(), sysView.get("serviceClass"));
+    }
+
+    /** */
+    @Test
+    public void testComputeBroadcast() throws Exception {
+        CyclicBarrier barrier = new CyclicBarrier(6);
+
+        for (int i = 0; i < 5; i++) {
+            ignite.compute().broadcastAsync(() -> {
+                try {
+                    barrier.await();
+                    barrier.await();
+                }
+                catch (InterruptedException | BrokenBarrierException e) {
+                    throw new RuntimeException(e);
+                }
+            });
+        }
+
+        barrier.await();
+
+        TabularDataSupport tasks = systemView(TASKS_VIEW);
+
+        assertEquals(5, tasks.size());
+
+        CompositeData t = tasks.get(new Object[] {0});
+
+        assertFalse((Boolean)t.get("internal"));
+        assertNull(t.get("affinityCacheName"));
+        assertEquals(-1, t.get("affinityPartitionId"));
+        assertTrue(t.get("taskClassName").toString().startsWith(getClass().getName()));
+        assertTrue(t.get("taskName").toString().startsWith(getClass().getName()));
+        assertEquals(ignite.localNode().id().toString(), t.get("taskNodeId"));
+        assertEquals("0", t.get("userVersion"));
+
+        barrier.await();
+    }
+
+    /** */
+    public TabularDataSupport systemView(String name) {
+        try {
+            DynamicMBean caches = mbean(VIEWS, name);
+
+            MBeanAttributeInfo[] attrs = caches.getMBeanInfo().getAttributes();
+
+            assertEquals(1, attrs.length);
+
+            return (TabularDataSupport)caches.getAttribute(VIEWS);
+        }
+        catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /** */
+    public DynamicMBean mbean(String grp, String name) throws MalformedObjectNameException {
+        ObjectName mbeanName = U.makeMBeanName(ignite.name(), grp, name);
+
+        MBeanServer mbeanSrv = ManagementFactory.getPlatformMBeanServer();
+
+        if (!mbeanSrv.isRegistered(mbeanName))
+            throw new IgniteException("MBean not registered.");
+
+        return MBeanServerInvocationHandler.newProxyInstance(mbeanSrv, mbeanName, DynamicMBean.class, false);
+    }
+
+    /** */
+    @Test
     public void testHistogramSearchByName() throws Exception {
         MetricRegistry mreg = new MetricRegistry("test", null);
 
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
new file mode 100644
index 0000000..f462861
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/metric/SystemViewSelfTest.java
@@ -0,0 +1,398 @@
+/*
+ * 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.metric;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteCompute;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.compute.ComputeJob;
+import org.apache.ignite.compute.ComputeJobResult;
+import org.apache.ignite.compute.ComputeJobResultPolicy;
+import org.apache.ignite.compute.ComputeTask;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.lang.IgniteCallable;
+import org.apache.ignite.lang.IgniteClosure;
+import org.apache.ignite.lang.IgnitePredicate;
+import org.apache.ignite.lang.IgniteRunnable;
+import org.apache.ignite.spi.systemview.view.SystemView;
+import org.apache.ignite.spi.systemview.view.CacheGroupView;
+import org.apache.ignite.spi.systemview.view.CacheView;
+import org.apache.ignite.spi.systemview.view.ComputeTaskView;
+import org.apache.ignite.spi.systemview.view.ServiceView;
+import org.apache.ignite.internal.processors.service.DummyService;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.services.ServiceConfiguration;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.junit.Test;
+
+import static org.apache.ignite.internal.processors.cache.ClusterCachesInfo.CACHES_VIEW;
+import static org.apache.ignite.internal.processors.cache.ClusterCachesInfo.CACHE_GRPS_VIEW;
+import static org.apache.ignite.internal.processors.service.IgniteServiceProcessor.SVCS_VIEW;
+import static org.apache.ignite.internal.processors.task.GridTaskProcessor.TASKS_VIEW;
+
+/** Tests for {@link SystemView}. */
+public class SystemViewSelfTest extends GridCommonAbstractTest {
+    /** Tests work of {@link SystemView} for caches. */
+    @Test
+    public void testCachesView() throws Exception {
+        try (IgniteEx g = startGrid()) {
+            Set<String> cacheNames = new HashSet<>(Arrays.asList("cache-1", "cache-2"));
+
+            for (String name : cacheNames)
+                g.createCache(name);
+
+            SystemView<CacheView> caches = g.context().systemView().view(CACHES_VIEW);
+
+            assertEquals(g.context().cache().cacheDescriptors().size(), F.size(caches.iterator()));
+
+            for (CacheView row : caches)
+                cacheNames.remove(row.cacheName());
+
+            assertTrue(cacheNames.toString(), cacheNames.isEmpty());
+        }
+    }
+
+    /** Tests work of {@link SystemView} for cache groups. */
+    @Test
+    public void testCacheGroupsView() throws Exception {
+        try (IgniteEx g = startGrid()) {
+            Set<String> grpNames = new HashSet<>(Arrays.asList("grp-1", "grp-2"));
+
+            for (String grpName : grpNames)
+                g.createCache(new CacheConfiguration<>("cache-" + grpName).setGroupName(grpName));
+
+            SystemView<CacheGroupView> grps = g.context().systemView().view(CACHE_GRPS_VIEW);
+
+            assertEquals(g.context().cache().cacheGroupDescriptors().size(), F.size(grps.iterator()));
+
+            for (CacheGroupView row : grps)
+                grpNames.remove(row.cacheGroupName());
+
+            assertTrue(grpNames.toString(), grpNames.isEmpty());
+        }
+    }
+
+    /** Tests work of {@link SystemView} for services. */
+    @Test
+    public void testServices() throws Exception {
+        try (IgniteEx g = startGrid()) {
+            {
+                ServiceConfiguration srvcCfg = new ServiceConfiguration();
+
+                srvcCfg.setName("service");
+                srvcCfg.setMaxPerNodeCount(1);
+                srvcCfg.setService(new DummyService());
+                srvcCfg.setNodeFilter(new TestNodeFilter());
+
+                g.services().deploy(srvcCfg);
+
+                SystemView<ServiceView> srvs = g.context().systemView().view(SVCS_VIEW);
+
+                assertEquals(g.context().service().serviceDescriptors().size(), F.size(srvs.iterator()));
+
+                ServiceView sview = srvs.iterator().next();
+
+                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());
+                assertNull(sview.affinityKey());
+                assertEquals(TestNodeFilter.class, sview.nodeFilter());
+                assertFalse(sview.staticallyConfigured());
+                assertEquals(g.localNode().id(), sview.originNodeId());
+            }
+
+            {
+                g.createCache("test-cache");
+
+                ServiceConfiguration srvcCfg = new ServiceConfiguration();
+
+                srvcCfg.setName("service-2");
+                srvcCfg.setMaxPerNodeCount(2);
+                srvcCfg.setService(new DummyService());
+                srvcCfg.setNodeFilter(new TestNodeFilter());
+                srvcCfg.setCacheName("test-cache");
+                srvcCfg.setAffinityKey(1L);
+
+                g.services().deploy(srvcCfg);
+
+                final ServiceView[] sview = {null};
+
+                g.context().systemView().<ServiceView>view(SVCS_VIEW).forEach(sv -> {
+                    if (sv.name().equals(srvcCfg.getName()))
+                        sview[0] = sv;
+                });
+
+                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());
+                assertEquals("1", sview[0].affinityKey());
+                assertEquals(TestNodeFilter.class, sview[0].nodeFilter());
+                assertFalse(sview[0].staticallyConfigured());
+                assertEquals(g.localNode().id(), sview[0].originNodeId());
+            }
+        }
+    }
+
+    /** Tests work of {@link SystemView} for compute grid {@link IgniteCompute#broadcastAsync(IgniteRunnable)} call. */
+    @Test
+    public void testComputeBroadcast() throws Exception {
+        CyclicBarrier barrier = new CyclicBarrier(6);
+
+        try (IgniteEx g1 = startGrid(0)) {
+            SystemView<ComputeTaskView> tasks = g1.context().systemView().view(TASKS_VIEW);
+
+            for (int i = 0; i < 5; i++) {
+                g1.compute().broadcastAsync(() -> {
+                    try {
+                        barrier.await();
+                        barrier.await();
+                    }
+                    catch (InterruptedException | BrokenBarrierException e) {
+                        throw new RuntimeException(e);
+                    }
+                });
+            }
+
+            barrier.await();
+
+            assertEquals(5, tasks.size());
+
+            ComputeTaskView t = tasks.iterator().next();
+
+            assertFalse(t.internal());
+            assertNull(t.affinityCacheName());
+            assertEquals(-1, t.affinityPartitionId());
+            assertTrue(t.taskClassName().startsWith(getClass().getName()));
+            assertTrue(t.taskName().startsWith(getClass().getName()));
+            assertEquals(g1.localNode().id(), t.taskNodeId());
+            assertEquals("0", t.userVersion());
+
+            barrier.await();
+        }
+    }
+
+    /** Tests work of {@link SystemView} for compute grid {@link IgniteCompute#runAsync(IgniteRunnable)} call. */
+    @Test
+    public void testComputeRunnable() throws Exception {
+        CyclicBarrier barrier = new CyclicBarrier(2);
+
+        try (IgniteEx g1 = startGrid(0)) {
+            SystemView<ComputeTaskView> tasks = g1.context().systemView().view(TASKS_VIEW);
+
+            g1.compute().runAsync(() -> {
+                try {
+                    barrier.await();
+                    barrier.await();
+                }
+                catch (InterruptedException | BrokenBarrierException e) {
+                    throw new RuntimeException(e);
+                }
+            });
+
+            barrier.await();
+
+            assertEquals(1, tasks.size());
+
+            ComputeTaskView t = tasks.iterator().next();
+
+            assertFalse(t.internal());
+            assertNull(t.affinityCacheName());
+            assertEquals(-1, t.affinityPartitionId());
+            assertTrue(t.taskClassName().startsWith(getClass().getName()));
+            assertTrue(t.taskName().startsWith(getClass().getName()));
+            assertEquals(g1.localNode().id(), t.taskNodeId());
+            assertEquals("0", t.userVersion());
+
+            barrier.await();
+        }
+    }
+
+    /** Tests work of {@link SystemView} for compute grid {@link IgniteCompute#apply(IgniteClosure, Object)} call. */
+    @Test
+    public void testComputeApply() throws Exception {
+        CyclicBarrier barrier = new CyclicBarrier(2);
+
+        try (IgniteEx g1 = startGrid(0)) {
+            SystemView<ComputeTaskView> tasks = g1.context().systemView().view(TASKS_VIEW);
+
+            GridTestUtils.runAsync(() -> {
+                g1.compute().apply(x -> {
+                    try {
+                        barrier.await();
+                        barrier.await();
+                    }
+                    catch (InterruptedException | BrokenBarrierException e) {
+                        throw new RuntimeException(e);
+                    }
+
+                    return 0;
+                }, 1);
+            });
+
+            barrier.await();
+
+            assertEquals(1, tasks.size());
+
+            ComputeTaskView t = tasks.iterator().next();
+
+            assertFalse(t.internal());
+            assertNull(t.affinityCacheName());
+            assertEquals(-1, t.affinityPartitionId());
+            assertTrue(t.taskClassName().startsWith(getClass().getName()));
+            assertTrue(t.taskName().startsWith(getClass().getName()));
+            assertEquals(g1.localNode().id(), t.taskNodeId());
+            assertEquals("0", t.userVersion());
+
+            barrier.await();
+        }
+    }
+
+    /**
+     * Tests work of {@link SystemView} for compute grid
+     * {@link IgniteCompute#affinityCallAsync(String, Object, IgniteCallable)} call.
+     */
+    @Test
+    public void testComputeAffinityCall() throws Exception {
+        CyclicBarrier barrier = new CyclicBarrier(2);
+
+        try (IgniteEx g1 = startGrid(0)) {
+            SystemView<ComputeTaskView> tasks = g1.context().systemView().view(TASKS_VIEW);
+
+            IgniteCache<Integer, Integer> cache = g1.createCache("test-cache");
+
+            cache.put(1, 1);
+
+            g1.compute().affinityCallAsync("test-cache", 1, () -> {
+                try {
+                    barrier.await();
+                    barrier.await();
+                }
+                catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+
+                return 0;
+            });
+
+            barrier.await();
+
+            assertEquals(1, tasks.size());
+
+            ComputeTaskView t = tasks.iterator().next();
+
+            assertFalse(t.internal());
+            assertEquals("test-cache", t.affinityCacheName());
+            assertEquals(1, t.affinityPartitionId());
+            assertTrue(t.taskClassName().startsWith(getClass().getName()));
+            assertTrue(t.taskName().startsWith(getClass().getName()));
+            assertEquals(g1.localNode().id(), t.taskNodeId());
+            assertEquals("0", t.userVersion());
+
+            barrier.await();
+        }
+    }
+
+    /** */
+    @Test
+    public void testComputeTask() throws Exception {
+        CyclicBarrier barrier = new CyclicBarrier(2);
+
+        try (IgniteEx g1 = startGrid(0)) {
+            SystemView<ComputeTaskView> tasks = g1.context().systemView().view(TASKS_VIEW);
+
+            IgniteCache<Integer, Integer> cache = g1.createCache("test-cache");
+
+            cache.put(1, 1);
+
+            g1.compute().executeAsync(new ComputeTask<Object, Object>() {
+                @Override public @NotNull Map<? extends ComputeJob, ClusterNode> map(List<ClusterNode> subgrid,
+                    @Nullable Object arg) throws IgniteException {
+                    return Collections.singletonMap(new ComputeJob() {
+                        @Override public void cancel() {
+                            // No-op.
+                        }
+
+                        @Override public Object execute() throws IgniteException {
+                            return 1;
+                        }
+                    }, subgrid.get(0));
+                }
+
+                @Override public ComputeJobResultPolicy result(ComputeJobResult res,
+                    List<ComputeJobResult> rcvd) throws IgniteException {
+                    try {
+                        barrier.await();
+                        barrier.await();
+                    }
+                    catch (InterruptedException | BrokenBarrierException e) {
+                        throw new RuntimeException(e);
+                    }
+
+                    return null;
+                }
+
+                @Nullable @Override public Object reduce(List<ComputeJobResult> results) throws IgniteException {
+                    return 1;
+                }
+            }, 1);
+
+            barrier.await();
+
+            assertEquals(1, tasks.size());
+
+            ComputeTaskView t = tasks.iterator().next();
+
+            assertFalse(t.internal());
+            assertNull(t.affinityCacheName());
+            assertEquals(-1, t.affinityPartitionId());
+            assertTrue(t.taskClassName().startsWith(getClass().getName()));
+            assertTrue(t.taskName().startsWith(getClass().getName()));
+            assertEquals(g1.localNode().id(), t.taskNodeId());
+            assertEquals("0", t.userVersion());
+
+            barrier.await();
+        }
+    }
+
+    /** Test node filter. */
+    public static class TestNodeFilter implements IgnitePredicate<ClusterNode> {
+        /** {@inheritDoc} */
+        @Override public boolean apply(ClusterNode node) {
+            return true;
+        }
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/service/ServiceReassignmentFunctionSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/service/ServiceReassignmentFunctionSelfTest.java
index 83eba02..fe63266 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/service/ServiceReassignmentFunctionSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/service/ServiceReassignmentFunctionSelfTest.java
@@ -29,6 +29,7 @@ import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.internal.managers.communication.GridIoManager;
 import org.apache.ignite.internal.managers.discovery.GridDiscoveryManager;
 import org.apache.ignite.internal.managers.eventstorage.GridEventStorageManager;
+import org.apache.ignite.internal.managers.systemview.GridSystemViewManager;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.lang.IgniteUuid;
 import org.apache.ignite.services.ServiceConfiguration;
@@ -103,9 +104,11 @@ public class ServiceReassignmentFunctionSelfTest {
 
         GridEventStorageManager mockEvt = mock(GridEventStorageManager.class);
         GridIoManager mockIo = mock(GridIoManager.class);
+        GridSystemViewManager sysViewMgr = mock(GridSystemViewManager.class);
 
         when(spyCtx.event()).thenReturn(mockEvt);
         when(spyCtx.io()).thenReturn(mockIo);
+        when(spyCtx.systemView()).thenReturn(sysViewMgr);
 
         GridDiscoveryManager mockDisco = mock(GridDiscoveryManager.class);
 
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 e14dbf5..a5e5dcd 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
@@ -739,7 +739,7 @@ public abstract class GridAbstractTest extends JUnitAssertAware {
      * @return Started grid.
      * @throws Exception If anything failed.
      */
-    protected Ignite startGrid() throws Exception {
+    protected IgniteEx startGrid() throws Exception {
         return startGrid(getTestIgniteInstanceName());
     }
 
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite9.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite9.java
index 096dcba..3b23b91 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite9.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite9.java
@@ -25,10 +25,11 @@ import org.apache.ignite.internal.metric.IoStatisticsCachePersistenceSelfTest;
 import org.apache.ignite.internal.metric.IoStatisticsCacheSelfTest;
 import org.apache.ignite.internal.metric.IoStatisticsMetricsLocalMXBeanImplSelfTest;
 import org.apache.ignite.internal.metric.IoStatisticsSelfTest;
-import org.apache.ignite.internal.metric.JmxMetricExporterSpiTest;
+import org.apache.ignite.internal.metric.JmxExporterSpiTest;
 import org.apache.ignite.internal.metric.LogExporterSpiTest;
 import org.apache.ignite.internal.metric.MetricsSelfTest;
 import org.apache.ignite.internal.metric.ReadMetricsOnNodeStartupTest;
+import org.apache.ignite.internal.metric.SystemViewSelfTest;
 import org.apache.ignite.internal.processors.cache.CachePutIfAbsentTest;
 import org.apache.ignite.internal.processors.cache.GridCacheLongRunningTransactionDiagnosticsTest;
 import org.apache.ignite.internal.processors.cache.IgniteCacheGetCustomCollectionsSelfTest;
@@ -111,8 +112,9 @@ public class IgniteCacheTestSuite9 {
         GridTestUtils.addTestIfNeeded(suite, IoStatisticsSelfTest.class, ignoredTests);
         GridTestUtils.addTestIfNeeded(suite, IoStatisticsMetricsLocalMXBeanImplSelfTest.class, ignoredTests);
         GridTestUtils.addTestIfNeeded(suite, MetricsSelfTest.class, ignoredTests);
+        GridTestUtils.addTestIfNeeded(suite, SystemViewSelfTest.class, ignoredTests);
         GridTestUtils.addTestIfNeeded(suite, CacheMetricsAddRemoveTest.class, ignoredTests);
-        GridTestUtils.addTestIfNeeded(suite, JmxMetricExporterSpiTest.class, ignoredTests);
+        GridTestUtils.addTestIfNeeded(suite, JmxExporterSpiTest.class, ignoredTests);
         GridTestUtils.addTestIfNeeded(suite, LogExporterSpiTest.class, ignoredTests);
         GridTestUtils.addTestIfNeeded(suite, ReadMetricsOnNodeStartupTest.class, ignoredTests);
 
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/SchemaManager.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/SchemaManager.java
index 4e102b3..af01bad 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/SchemaManager.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/SchemaManager.java
@@ -52,9 +52,7 @@ import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
 import org.apache.ignite.internal.processors.query.h2.sys.SqlSystemTableEngine;
 import org.apache.ignite.internal.processors.query.h2.sys.view.SqlSystemView;
 import org.apache.ignite.internal.processors.query.h2.sys.view.SqlSystemViewBaselineNodes;
-import org.apache.ignite.internal.processors.query.h2.sys.view.SqlSystemViewCacheGroups;
 import org.apache.ignite.internal.processors.query.h2.sys.view.SqlSystemViewCacheGroupsIOStatistics;
-import org.apache.ignite.internal.processors.query.h2.sys.view.SqlSystemViewCaches;
 import org.apache.ignite.internal.processors.query.h2.sys.view.SqlSystemViewIndexes;
 import org.apache.ignite.internal.processors.query.h2.sys.view.SqlSystemViewNodeAttributes;
 import org.apache.ignite.internal.processors.query.h2.sys.view.SqlSystemViewNodeMetrics;
@@ -187,8 +185,6 @@ public class SchemaManager {
         views.add(new SqlSystemViewNodeAttributes(ctx));
         views.add(new SqlSystemViewBaselineNodes(ctx));
         views.add(new SqlSystemViewNodeMetrics(ctx));
-        views.add(new SqlSystemViewCaches(ctx));
-        views.add(new SqlSystemViewCacheGroups(ctx));
         views.add(new SqlSystemViewCacheGroupsIOStatistics(ctx));
         views.add(new SqlSystemViewRunningQueries(ctx));
         views.add(new SqlSystemViewQueryHistoryMetrics(ctx));
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlSystemViewCacheGroups.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlSystemViewCacheGroups.java
deleted file mode 100644
index 0db6b65..0000000
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlSystemViewCacheGroups.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.internal.processors.query.h2.sys.view;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import org.apache.ignite.cache.CacheMode;
-import org.apache.ignite.configuration.CacheConfiguration;
-import org.apache.ignite.internal.GridKernalContext;
-import org.apache.ignite.internal.processors.cache.CacheGroupDescriptor;
-import org.apache.ignite.internal.util.typedef.F;
-import org.h2.engine.Session;
-import org.h2.result.Row;
-import org.h2.result.SearchRow;
-import org.h2.value.Value;
-
-/**
- * System view: cache groups.
- */
-public class SqlSystemViewCacheGroups extends SqlAbstractLocalSystemView {
-    /**
-     * @param ctx Grid context.
-     */
-    public SqlSystemViewCacheGroups(GridKernalContext ctx) {
-        super("CACHE_GROUPS", "Cache groups", ctx, "CACHE_GROUP_ID",
-            newColumn("CACHE_GROUP_ID", Value.INT),
-            newColumn("CACHE_GROUP_NAME"),
-            newColumn("IS_SHARED", Value.BOOLEAN),
-            newColumn("CACHE_COUNT", Value.INT),
-            newColumn("CACHE_MODE"),
-            newColumn("ATOMICITY_MODE"),
-            newColumn("AFFINITY"),
-            newColumn("PARTITIONS_COUNT", Value.INT),
-            newColumn("NODE_FILTER"),
-            newColumn("DATA_REGION_NAME"),
-            newColumn("TOPOLOGY_VALIDATOR"),
-            newColumn("PARTITION_LOSS_POLICY"),
-            newColumn("REBALANCE_MODE"),
-            newColumn("REBALANCE_DELAY", Value.LONG),
-            newColumn("REBALANCE_ORDER", Value.INT),
-            newColumn("BACKUPS", Value.INT)
-        );
-    }
-
-    /** {@inheritDoc} */
-    @Override public Iterator<Row> getRows(Session ses, SearchRow first, SearchRow last) {
-        SqlSystemViewColumnCondition idCond = conditionForColumn("ID", first, last);
-
-        Collection<CacheGroupDescriptor> cacheGroups;
-
-        if (idCond.isEquality()) {
-            try {
-                CacheGroupDescriptor cacheGrp = ctx.cache().cacheGroupDescriptors().get(idCond.valueForEquality().getInt());
-
-                cacheGroups = cacheGrp == null ? Collections.emptySet() : Collections.singleton(cacheGrp);
-            }
-            catch (Exception ignore) {
-                cacheGroups = Collections.emptySet();
-            }
-        }
-        else
-            cacheGroups = ctx.cache().cacheGroupDescriptors().values();
-
-        return F.iterator(cacheGroups,
-            grp -> {
-                CacheConfiguration<?, ?> ccfg = grp.config();
-
-                return createRow(
-                    ses,
-                    grp.groupId(),
-                    grp.cacheOrGroupName(),
-                    grp.sharedGroup(),
-                    grp.caches() == null ? 0 : grp.caches().size(),
-                    ccfg.getCacheMode(),
-                    ccfg.getAtomicityMode(),
-                    toStringSafe(ccfg.getAffinity()),
-                    ccfg.getAffinity() != null ? ccfg.getAffinity().partitions() : null,
-                    nodeFilter(ccfg),
-                    ccfg.getDataRegionName(),
-                    toStringSafe(ccfg.getTopologyValidator()),
-                    ccfg.getPartitionLossPolicy(),
-                    ccfg.getRebalanceMode(),
-                    ccfg.getRebalanceDelay(),
-                    ccfg.getRebalanceOrder(),
-                    ccfg.getCacheMode() == CacheMode.REPLICATED ? null : ccfg.getBackups()
-                );
-            }, true);
-    }
-
-    /** {@inheritDoc} */
-    @Override public boolean canGetRowCount() {
-        return true;
-    }
-
-    /** {@inheritDoc} */
-    @Override public long getRowCount() {
-        return ctx.cache().cacheGroupDescriptors().size();
-    }
-}
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlSystemViewCaches.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlSystemViewCaches.java
deleted file mode 100644
index 974d72b..0000000
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sys/view/SqlSystemViewCaches.java
+++ /dev/null
@@ -1,200 +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.h2.sys.view;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import org.apache.ignite.cache.CacheMode;
-import org.apache.ignite.configuration.CacheConfiguration;
-import org.apache.ignite.internal.GridKernalContext;
-import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor;
-import org.apache.ignite.internal.util.typedef.F;
-import org.h2.engine.Session;
-import org.h2.result.Row;
-import org.h2.result.SearchRow;
-import org.h2.value.Value;
-
-/**
- * System view: caches.
- */
-public class SqlSystemViewCaches extends SqlAbstractLocalSystemView {
-    /**
-     * @param ctx Grid context.
-     */
-    public SqlSystemViewCaches(GridKernalContext ctx) {
-        super("CACHES", "Ignite caches", ctx, "CACHE_NAME",
-            newColumn("CACHE_GROUP_ID", Value.INT),
-            newColumn("CACHE_GROUP_NAME"),
-            newColumn("CACHE_ID", Value.INT),
-            newColumn("CACHE_NAME"),
-            newColumn("CACHE_TYPE"),
-            newColumn("CACHE_MODE"),
-            newColumn("ATOMICITY_MODE"),
-            newColumn("IS_ONHEAP_CACHE_ENABLED", Value.BOOLEAN),
-            newColumn("IS_COPY_ON_READ", Value.BOOLEAN),
-            newColumn("IS_LOAD_PREVIOUS_VALUE", Value.BOOLEAN),
-            newColumn("IS_READ_FROM_BACKUP", Value.BOOLEAN),
-            newColumn("PARTITION_LOSS_POLICY"),
-            newColumn("NODE_FILTER"),
-            newColumn("TOPOLOGY_VALIDATOR"),
-            newColumn("IS_EAGER_TTL", Value.BOOLEAN),
-            newColumn("WRITE_SYNCHRONIZATION_MODE"),
-            newColumn("IS_INVALIDATE", Value.BOOLEAN),
-            newColumn("IS_EVENTS_DISABLED", Value.BOOLEAN),
-            newColumn("IS_STATISTICS_ENABLED", Value.BOOLEAN),
-            newColumn("IS_MANAGEMENT_ENABLED", Value.BOOLEAN),
-            newColumn("BACKUPS", Value.INT),
-            newColumn("AFFINITY"),
-            newColumn("AFFINITY_MAPPER"),
-            newColumn("REBALANCE_MODE"),
-            newColumn("REBALANCE_BATCH_SIZE", Value.INT),
-            newColumn("REBALANCE_TIMEOUT", Value.LONG),
-            newColumn("REBALANCE_DELAY", Value.LONG),
-            newColumn("REBALANCE_THROTTLE", Value.LONG),
-            newColumn("REBALANCE_BATCHES_PREFETCH_COUNT", Value.LONG),
-            newColumn("REBALANCE_ORDER", Value.INT),
-            newColumn("EVICTION_FILTER"),
-            newColumn("EVICTION_POLICY_FACTORY"),
-            newColumn("IS_NEAR_CACHE_ENABLED", Value.BOOLEAN),
-            newColumn("NEAR_CACHE_EVICTION_POLICY_FACTORY"),
-            newColumn("NEAR_CACHE_START_SIZE", Value.INT),
-            newColumn("DEFAULT_LOCK_TIMEOUT", Value.LONG),
-            newColumn("CACHE_INTERCEPTOR"),
-            newColumn("CACHE_STORE_FACTORY"),
-            newColumn("IS_STORE_KEEP_BINARY", Value.BOOLEAN),
-            newColumn("IS_READ_THROUGH", Value.BOOLEAN),
-            newColumn("IS_WRITE_THROUGH", Value.BOOLEAN),
-            newColumn("IS_WRITE_BEHIND_ENABLED", Value.BOOLEAN),
-            newColumn("WRITE_BEHIND_COALESCING", Value.BOOLEAN),
-            newColumn("WRITE_BEHIND_FLUSH_SIZE", Value.INT),
-            newColumn("WRITE_BEHIND_FLUSH_FREQUENCY", Value.LONG),
-            newColumn("WRITE_BEHIND_FLUSH_THREAD_COUNT", Value.INT),
-            newColumn("WRITE_BEHIND_FLUSH_BATCH_SIZE", Value.INT),
-            newColumn("MAX_CONCURRENT_ASYNC_OPERATIONS", Value.INT),
-            newColumn("CACHE_LOADER_FACTORY"),
-            newColumn("CACHE_WRITER_FACTORY"),
-            newColumn("EXPIRY_POLICY_FACTORY"),
-            newColumn("IS_SQL_ESCAPE_ALL", Value.BOOLEAN),
-            newColumn("SQL_SCHEMA"),
-            newColumn("SQL_INDEX_MAX_INLINE_SIZE", Value.INT),
-            newColumn("IS_SQL_ONHEAP_CACHE_ENABLED", Value.BOOLEAN),
-            newColumn("SQL_ONHEAP_CACHE_MAX_SIZE", Value.INT),
-            newColumn("QUERY_DETAILS_METRICS_SIZE", Value.INT),
-            newColumn("QUERY_PARALLELISM", Value.INT),
-            newColumn("MAX_QUERY_ITERATORS_COUNT", Value.INT),
-            newColumn("DATA_REGION_NAME")
-        );
-    }
-
-    /** {@inheritDoc} */
-    @Override public Iterator<Row> getRows(Session ses, SearchRow first, SearchRow last) {
-        SqlSystemViewColumnCondition nameCond = conditionForColumn("CACHE_NAME", first, last);
-
-        Collection<DynamicCacheDescriptor> caches;
-
-        if (nameCond.isEquality()) {
-            DynamicCacheDescriptor cache = ctx.cache().cacheDescriptor(nameCond.valueForEquality().getString());
-
-            caches = cache == null ? Collections.emptySet() : Collections.singleton(cache);
-        }
-        else
-            caches = ctx.cache().cacheDescriptors().values();
-
-        return F.iterator(caches,
-            cache -> {
-                CacheConfiguration ccfg = cache.cacheConfiguration();
-
-                return createRow(
-                    ses,
-                    cache.groupId(),
-                    cache.groupDescriptor().cacheOrGroupName(),
-                    cache.cacheId(),
-                    cache.cacheName(),
-                    cache.cacheType(),
-                    ccfg.getCacheMode(),
-                    ccfg.getAtomicityMode(),
-                    ccfg.isOnheapCacheEnabled(),
-                    ccfg.isCopyOnRead(),
-                    ccfg.isLoadPreviousValue(),
-                    ccfg.isReadFromBackup(),
-                    ccfg.getPartitionLossPolicy(),
-                    nodeFilter(ccfg),
-                    toStringSafe(ccfg.getTopologyValidator()),
-                    ccfg.isEagerTtl(),
-                    ccfg.getWriteSynchronizationMode(),
-                    ccfg.isInvalidate(),
-                    ccfg.isEventsDisabled(),
-                    ccfg.isStatisticsEnabled(),
-                    ccfg.isManagementEnabled(),
-                    ccfg.getCacheMode() == CacheMode.REPLICATED ? null : ccfg.getBackups(),
-                    toStringSafe(ccfg.getAffinity()),
-                    toStringSafe(ccfg.getAffinityMapper()),
-                    ccfg.getRebalanceMode(),
-                    ccfg.getRebalanceBatchSize(),
-                    ccfg.getRebalanceTimeout(),
-                    ccfg.getRebalanceDelay(),
-                    ccfg.getRebalanceThrottle(),
-                    ccfg.getRebalanceBatchesPrefetchCount(),
-                    ccfg.getRebalanceOrder(),
-                    toStringSafe(ccfg.getEvictionFilter()),
-                    toStringSafe(ccfg.getEvictionPolicyFactory()),
-                    ccfg.getNearConfiguration() != null,
-                    ccfg.getNearConfiguration() != null ?
-                        toStringSafe(ccfg.getNearConfiguration().getNearEvictionPolicyFactory()) : null,
-                    ccfg.getNearConfiguration() != null ?
-                        ccfg.getNearConfiguration().getNearStartSize() : null,
-                    ccfg.getDefaultLockTimeout(),
-                    toStringSafe(ccfg.getInterceptor()),
-                    toStringSafe(ccfg.getCacheStoreFactory()),
-                    ccfg.isStoreKeepBinary(),
-                    ccfg.isReadThrough(),
-                    ccfg.isWriteThrough(),
-                    ccfg.isWriteBehindEnabled(),
-                    ccfg.getWriteBehindCoalescing(),
-                    ccfg.getWriteBehindFlushSize(),
-                    ccfg.getWriteBehindFlushFrequency(),
-                    ccfg.getWriteBehindFlushThreadCount(),
-                    ccfg.getWriteBehindBatchSize(),
-                    ccfg.getMaxConcurrentAsyncOperations(),
-                    toStringSafe(ccfg.getCacheLoaderFactory()),
-                    toStringSafe(ccfg.getCacheWriterFactory()),
-                    toStringSafe(ccfg.getExpiryPolicyFactory()),
-                    ccfg.isSqlEscapeAll(),
-                    ccfg.getSqlSchema(),
-                    ccfg.getSqlIndexMaxInlineSize(),
-                    ccfg.isSqlOnheapCacheEnabled(),
-                    ccfg.getSqlOnheapCacheMaxSize(),
-                    ccfg.getQueryDetailMetricsSize(),
-                    ccfg.getQueryParallelism(),
-                    ccfg.getMaxQueryIteratorsCount(),
-                    ccfg.getDataRegionName()
-                );
-            }, true);
-    }
-
-    /** {@inheritDoc} */
-    @Override public boolean canGetRowCount() {
-        return true;
-    }
-
-    /** {@inheritDoc} */
-    @Override public long getRowCount() {
-        return ctx.cache().cacheDescriptors().size();
-    }
-}
diff --git a/modules/indexing/src/main/java/org/apache/ignite/spi/metric/sql/MetricSetLocalSystemView.java b/modules/indexing/src/main/java/org/apache/ignite/spi/metric/sql/MetricRegistryLocalSystemView.java
similarity index 92%
rename from modules/indexing/src/main/java/org/apache/ignite/spi/metric/sql/MetricSetLocalSystemView.java
rename to modules/indexing/src/main/java/org/apache/ignite/spi/metric/sql/MetricRegistryLocalSystemView.java
index 21f690c..5a7066b 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/spi/metric/sql/MetricSetLocalSystemView.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/spi/metric/sql/MetricRegistryLocalSystemView.java
@@ -34,7 +34,7 @@ import org.jetbrains.annotations.Nullable;
 /**
  * Sql view for exporting metrics.
  */
-public class MetricSetLocalSystemView extends SqlAbstractLocalSystemView {
+public class MetricRegistryLocalSystemView extends SqlAbstractLocalSystemView {
     /** Metric registry. */
     private ReadOnlyMetricRegistry mreg;
 
@@ -46,9 +46,9 @@ public class MetricSetLocalSystemView extends SqlAbstractLocalSystemView {
      * @param mreg Metric registry.
      * @param filter Metric registry filter.
      */
-    public MetricSetLocalSystemView(GridKernalContext ctx, ReadOnlyMetricRegistry mreg,
+    public MetricRegistryLocalSystemView(GridKernalContext ctx, ReadOnlyMetricRegistry mreg,
         @Nullable Predicate<MetricRegistry> filter) {
-        super(SqlViewExporterSpi.SYS_VIEW_NAME, "Ignite metrics",
+        super(SqlViewMetricExporterSpi.SYS_VIEW_NAME, "Ignite metrics",
             ctx,
             newColumn("NAME", Value.STRING),
             newColumn("VALUE", Value.STRING),
diff --git a/modules/indexing/src/main/java/org/apache/ignite/spi/metric/sql/SqlViewExporterSpi.java b/modules/indexing/src/main/java/org/apache/ignite/spi/metric/sql/SqlViewMetricExporterSpi.java
similarity index 90%
copy from modules/indexing/src/main/java/org/apache/ignite/spi/metric/sql/SqlViewExporterSpi.java
copy to modules/indexing/src/main/java/org/apache/ignite/spi/metric/sql/SqlViewMetricExporterSpi.java
index 7960798..a0014e2 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/spi/metric/sql/SqlViewExporterSpi.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/spi/metric/sql/SqlViewMetricExporterSpi.java
@@ -21,7 +21,6 @@ import java.util.function.Predicate;
 import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.processors.metric.MetricRegistry;
-import org.apache.ignite.internal.processors.query.QueryUtils;
 import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
 import org.apache.ignite.internal.processors.query.h2.SchemaManager;
 import org.apache.ignite.spi.IgniteSpiAdapter;
@@ -31,10 +30,12 @@ import org.apache.ignite.spi.metric.MetricExporterSpi;
 import org.apache.ignite.spi.metric.ReadOnlyMetricRegistry;
 import org.jetbrains.annotations.Nullable;
 
+import static org.apache.ignite.internal.processors.query.QueryUtils.SCHEMA_SYS;
+
 /**
  * This SPI implementation exports metrics as SQL views.
  */
-public class SqlViewExporterSpi extends IgniteSpiAdapter implements MetricExporterSpi {
+public class SqlViewMetricExporterSpi extends IgniteSpiAdapter implements MetricExporterSpi {
     /** System view name. */
     public static final String SYS_VIEW_NAME = "METRICS";
 
@@ -50,7 +51,7 @@ public class SqlViewExporterSpi extends IgniteSpiAdapter implements MetricExport
 
         SchemaManager mgr = ((IgniteH2Indexing)ctx.query().getIndexing()).schemaManager();
 
-        mgr.createSystemView(QueryUtils.SCHEMA_MONITORING, new MetricSetLocalSystemView(ctx, mreg, filter));
+        mgr.createSystemView(SCHEMA_SYS, new MetricRegistryLocalSystemView(ctx, mreg, filter));
 
         if (log.isDebugEnabled())
             log.debug(SYS_VIEW_NAME + " SQL view for metrics created.");
diff --git a/modules/indexing/src/main/java/org/apache/ignite/spi/metric/sql/SqlViewExporterSpi.java b/modules/indexing/src/main/java/org/apache/ignite/spi/systemview/SqlViewExporterSpi.java
similarity index 53%
rename from modules/indexing/src/main/java/org/apache/ignite/spi/metric/sql/SqlViewExporterSpi.java
rename to modules/indexing/src/main/java/org/apache/ignite/spi/systemview/SqlViewExporterSpi.java
index 7960798..00a8130 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/spi/metric/sql/SqlViewExporterSpi.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/spi/systemview/SqlViewExporterSpi.java
@@ -15,45 +15,69 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.spi.metric.sql;
+package org.apache.ignite.spi.systemview;
 
 import java.util.function.Predicate;
 import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.IgniteEx;
-import org.apache.ignite.internal.processors.metric.MetricRegistry;
-import org.apache.ignite.internal.processors.query.QueryUtils;
+import org.apache.ignite.internal.IgnitionEx;
 import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
 import org.apache.ignite.internal.processors.query.h2.SchemaManager;
+import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.spi.IgniteSpiAdapter;
 import org.apache.ignite.spi.IgniteSpiContext;
 import org.apache.ignite.spi.IgniteSpiException;
-import org.apache.ignite.spi.metric.MetricExporterSpi;
-import org.apache.ignite.spi.metric.ReadOnlyMetricRegistry;
+import org.apache.ignite.spi.systemview.view.SystemView;
 import org.jetbrains.annotations.Nullable;
 
+import static org.apache.ignite.internal.processors.query.QueryUtils.SCHEMA_SYS;
+
 /**
  * This SPI implementation exports metrics as SQL views.
+ *
+ * Note, instance of this class created with reflection.
+ * @see IgnitionEx#SYSTEM_VIEW_SQL_SPI
  */
-public class SqlViewExporterSpi extends IgniteSpiAdapter implements MetricExporterSpi {
-    /** System view name. */
-    public static final String SYS_VIEW_NAME = "METRICS";
+public class SqlViewExporterSpi extends IgniteSpiAdapter implements SystemViewExporterSpi {
+    /** System view filter. */
+    @Nullable private Predicate<SystemView<?>> filter;
 
-    /** Metric filter. */
-    private @Nullable Predicate<MetricRegistry> filter;
+    /** System view registry. */
+    private ReadOnlySystemViewRegistry sysViewReg;
 
-    /** Metric Registry. */
-    private ReadOnlyMetricRegistry mreg;
+    /** Schema manager. */
+    private SchemaManager mgr;
 
     /** {@inheritDoc} */
     @Override protected void onContextInitialized0(IgniteSpiContext spiCtx) throws IgniteSpiException {
         GridKernalContext ctx = ((IgniteEx)ignite()).context();
 
-        SchemaManager mgr = ((IgniteH2Indexing)ctx.query().getIndexing()).schemaManager();
+        this.mgr = ((IgniteH2Indexing)ctx.query().getIndexing()).schemaManager();
+
+        sysViewReg.forEach(this::register);
+        sysViewReg.addSystemViewCreationListener(this::register);
+    }
+
+    /**
+     * Registers system view as SQL View.
+     *
+     * @param sysView System view.
+     */
+    private void register(SystemView<?> sysView) {
+        if (filter != null && !filter.test(sysView)) {
+            if (log.isDebugEnabled())
+                U.debug(log, "System view filtered and will not be registered.[name=" + sysView.name() + ']');
 
-        mgr.createSystemView(QueryUtils.SCHEMA_MONITORING, new MetricSetLocalSystemView(ctx, mreg, filter));
+            return;
+        }
+        else if (log.isDebugEnabled())
+            log.debug("Found new system view [name=" + sysView.name() + ']');
 
-        if (log.isDebugEnabled())
-            log.debug(SYS_VIEW_NAME + " SQL view for metrics created.");
+        GridKernalContext ctx = ((IgniteEx)ignite()).context();
+
+        SystemViewLocal<?> view = new SystemViewLocal<>(ctx, sysView);
+
+        mgr.createSystemView(SCHEMA_SYS, view);
     }
 
     /** {@inheritDoc} */
@@ -67,13 +91,12 @@ public class SqlViewExporterSpi extends IgniteSpiAdapter implements MetricExport
     }
 
     /** {@inheritDoc} */
-    @Override public void setMetricRegistry(ReadOnlyMetricRegistry mreg) {
-        this.mreg = mreg;
+    @Override public void setSystemViewRegistry(ReadOnlySystemViewRegistry mlreg) {
+        this.sysViewReg = mlreg;
     }
 
     /** {@inheritDoc} */
-    @Override public void setExportFilter(Predicate<MetricRegistry> filter) {
+    @Override public void setExportFilter(Predicate<SystemView<?>> filter) {
         this.filter = filter;
     }
-
 }
diff --git a/modules/indexing/src/main/java/org/apache/ignite/spi/systemview/SystemViewLocal.java b/modules/indexing/src/main/java/org/apache/ignite/spi/systemview/SystemViewLocal.java
new file mode 100644
index 0000000..291cb68
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/spi/systemview/SystemViewLocal.java
@@ -0,0 +1,237 @@
+/*
+ * 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.spi.systemview;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.InetSocketAddress;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.UUID;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.processors.metric.impl.MetricUtils;
+import org.apache.ignite.spi.systemview.view.SystemView;
+import org.apache.ignite.internal.processors.query.h2.sys.view.SqlAbstractLocalSystemView;
+import org.apache.ignite.lang.IgniteUuid;
+import org.apache.ignite.spi.systemview.view.SystemViewRowAttributeWalker.AttributeVisitor;
+import org.apache.ignite.spi.systemview.view.SystemViewRowAttributeWalker.AttributeWithValueVisitor;
+import org.h2.engine.Session;
+import org.h2.result.Row;
+import org.h2.result.SearchRow;
+import org.h2.table.Column;
+import org.h2.value.Value;
+import org.h2.value.ValueBoolean;
+import org.h2.value.ValueByte;
+import org.h2.value.ValueDecimal;
+import org.h2.value.ValueDouble;
+import org.h2.value.ValueFloat;
+import org.h2.value.ValueInt;
+import org.h2.value.ValueLong;
+import org.h2.value.ValueNull;
+import org.h2.value.ValueShort;
+import org.h2.value.ValueString;
+import org.h2.value.ValueTimestamp;
+import org.h2.value.ValueUuid;
+
+/**
+ * SQL system view to export {@link SystemView} data.
+ */
+public class SystemViewLocal<R> extends SqlAbstractLocalSystemView {
+    /** System view for export. */
+    private final SystemView<R> sysView;
+
+    /**
+     * @param ctx Kernal context.
+     * @param sysView View to export.
+     */
+    public SystemViewLocal(GridKernalContext ctx, SystemView<R> sysView) {
+        super(sqlName(sysView.name()), sysView.description(), ctx, columnsList(sysView));
+
+        this.sysView = sysView;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Iterator<Row> getRows(Session ses, SearchRow first, SearchRow last) {
+        Iterator<R> rows = sysView.iterator();
+
+        return new Iterator<Row>() {
+            @Override public boolean hasNext() {
+                return rows.hasNext();
+            }
+
+            @Override public Row next() {
+                R row = rows.next();
+
+                Value[] data = new Value[sysView.walker().count()];
+
+                sysView.walker().visitAll(row, new AttributeWithValueVisitor() {
+                    @Override public <T> void accept(int idx, String name, Class<T> clazz, T val) {
+                        if (val == null)
+                            data[idx] = ValueNull.INSTANCE;
+                        else if (clazz.isAssignableFrom(Class.class))
+                            data[idx] = ValueString.get(((Class)val).getName());
+                        else if (clazz.isAssignableFrom(String.class) || clazz.isEnum() ||
+                            clazz.isAssignableFrom(IgniteUuid.class) ||
+                            clazz.isAssignableFrom(InetSocketAddress.class))
+                            data[idx] = ValueString.get(Objects.toString(val));
+                        else if (clazz.isAssignableFrom(UUID.class))
+                            data[idx] = ValueUuid.get((UUID)val);
+                        else if (clazz.isAssignableFrom(BigDecimal.class))
+                            data[idx] = ValueDecimal.get((BigDecimal)val);
+                        else if (clazz.isAssignableFrom(BigInteger.class))
+                            data[idx] = ValueDecimal.get(new BigDecimal((BigInteger)val));
+                        else if (clazz.isAssignableFrom(Date.class))
+                            data[idx] = ValueTimestamp.fromMillis(((Date)val).getTime());
+                        else if (clazz.isAssignableFrom(Boolean.class))
+                            data[idx] = ValueBoolean.get((Boolean)val);
+                        else if (clazz.isAssignableFrom(Byte.class))
+                            data[idx] = ValueByte.get((Byte)val);
+                        else if (clazz.isAssignableFrom(Character.class))
+                            data[idx] = ValueString.get(Objects.toString(val));
+                        else if (clazz.isAssignableFrom(Short.class))
+                            data[idx] = ValueShort.get((Short)val);
+                        else if (clazz.isAssignableFrom(Integer.class))
+                            data[idx] = ValueInt.get((Integer)val);
+                        else if (clazz.isAssignableFrom(Long.class))
+                            data[idx] = ValueLong.get((Long)val);
+                        else if (clazz.isAssignableFrom(Float.class))
+                            data[idx] = ValueFloat.get((Float)val);
+                        else if (clazz.isAssignableFrom(Double.class))
+                            data[idx] = ValueDouble.get((Double)val);
+                        else
+                            data[idx] = ValueString.get(val.toString());
+                    }
+
+                    @Override public void acceptBoolean(int idx, String name, boolean val) {
+                        data[idx] = ValueBoolean.get(val);
+                    }
+
+                    @Override public void acceptChar(int idx, String name, char val) {
+                        data[idx] = ValueString.get(Character.toString(val));
+                    }
+
+                    @Override public void acceptByte(int idx, String name, byte val) {
+                        data[idx] = ValueByte.get(val);
+                    }
+
+                    @Override public void acceptShort(int idx, String name, short val) {
+                        data[idx] = ValueShort.get(val);
+                    }
+
+                    @Override public void acceptInt(int idx, String name, int val) {
+                        data[idx] = ValueInt.get(val);
+                    }
+
+                    @Override public void acceptLong(int idx, String name, long val) {
+                        data[idx] = ValueLong.get(val);
+                    }
+
+                    @Override public void acceptFloat(int idx, String name, float val) {
+                        data[idx] = ValueFloat.get(val);
+                    }
+
+                    @Override public void acceptDouble(int idx, String name, double val) {
+                        data[idx] = ValueDouble.get(val);
+                    }
+                });
+
+                return createRow(ses, data);
+            }
+        };
+    }
+
+    /**
+     * Extract column array for specific {@link SystemView}.
+     *
+     * @param sysView System view.
+     * @param <R> Row type.
+     * @return SQL column array for {@code rowClass}.
+     * @see SystemView#rowClass()
+     */
+    private static <R> Column[] columnsList(SystemView<R> sysView) {
+        Column[] cols = new Column[sysView.walker().count()];
+
+        sysView.walker().visitAll(new AttributeVisitor() {
+            @Override public <T> void accept(int idx, String name, Class<T> clazz) {
+                int type;
+
+                if (clazz.isAssignableFrom(String.class) || clazz.isEnum() ||
+                    clazz.isAssignableFrom(IgniteUuid.class) ||
+                    clazz.isAssignableFrom(Class.class) || clazz.isAssignableFrom(InetSocketAddress.class))
+                    type = Value.STRING;
+                else if (clazz.isAssignableFrom(UUID.class))
+                    type = Value.UUID;
+                else if (clazz.isAssignableFrom(BigDecimal.class))
+                    type = Value.DECIMAL;
+                else if (clazz.isAssignableFrom(BigInteger.class))
+                    type = Value.DECIMAL;
+                else if (clazz.isAssignableFrom(Date.class))
+                    type = Value.TIMESTAMP;
+                else if (clazz == boolean.class || clazz.isAssignableFrom(Boolean.class))
+                    type = Value.BOOLEAN;
+                else if (clazz == byte.class || clazz.isAssignableFrom(Byte.class))
+                    type = Value.BYTE;
+                else if (clazz == char.class || clazz.isAssignableFrom(Character.class))
+                    type = Value.STRING;
+                else if (clazz == short.class || clazz.isAssignableFrom(Short.class))
+                    type = Value.SHORT;
+                else if (clazz == int.class || clazz.isAssignableFrom(Integer.class))
+                    type = Value.INT;
+                else if (clazz == long.class || clazz.isAssignableFrom(Long.class))
+                    type = Value.LONG;
+                else if (clazz == float.class || clazz.isAssignableFrom(Float.class))
+                    type = Value.FLOAT;
+                else if (clazz == double.class || clazz.isAssignableFrom(Double.class))
+                    type = Value.DOUBLE;
+                else
+                    type = Value.STRING;
+
+                cols[idx] = newColumn(sqlName(name), type);
+            }
+        });
+
+        return cols;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getRowCount() {
+        return sysView.size();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean canGetRowCount() {
+        return true;
+    }
+
+    /**
+     * Build SQL-like name from Java code style name.
+     * Some examples:
+     *
+     * cacheName -> CACHE_NAME.
+     * affinitiKeyName -> AFFINITY_KEY_NAME.
+     *
+     * @param name Name to convert.
+     * @return SQL compatible name.
+     */
+    public static String sqlName(String name) {
+        return name
+            .replaceAll("([A-Z])", "_$1")
+            .replaceAll('\\' + MetricUtils.SEPARATOR, "_").toUpperCase();
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java b/modules/indexing/src/main/java/org/apache/ignite/spi/systemview/package-info.java
similarity index 56%
copy from modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java
copy to modules/indexing/src/main/java/org/apache/ignite/spi/systemview/package-info.java
index 07457cc..e5f8662 100644
--- a/modules/core/src/main/java/org/apache/ignite/spi/metric/ReadOnlyMetricRegistry.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/spi/systemview/package-info.java
@@ -15,26 +15,8 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.spi.metric;
-
-import java.util.function.Consumer;
-import org.apache.ignite.internal.processors.metric.MetricRegistry;
-
 /**
- * Read only metric registry.
+ * <!-- Package description. -->
+ * Contains SQL view exporter implementation for {@link org.apache.ignite.spi.systemview.view.SystemView}.
  */
-public interface ReadOnlyMetricRegistry extends Iterable<MetricRegistry> {
-    /**
-     * Adds listener of metrics group creation events.
-     *
-     * @param lsnr Listener.
-     */
-    public void addMetricRegistryCreationListener(Consumer<MetricRegistry> lsnr);
-
-    /**
-     * Adds listener of metrics group remove events.
-     *
-     * @param lsnr Listener.
-     */
-    public void addMetricRegistryRemoveListener(Consumer<MetricRegistry> lsnr);
-}
+package org.apache.ignite.spi.systemview;
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 ce804b0..23b9a11 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
@@ -18,18 +18,24 @@
 package org.apache.ignite.internal.processors.cache.metric;
 
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.DataRegionConfiguration;
 import org.apache.ignite.configuration.DataStorageConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.metric.AbstractExporterSpiTest;
+import org.apache.ignite.internal.processors.service.DummyService;
 import org.apache.ignite.lang.IgniteBiTuple;
-import org.apache.ignite.spi.metric.sql.SqlViewExporterSpi;
+import org.apache.ignite.services.ServiceConfiguration;
+import org.apache.ignite.spi.metric.sql.SqlViewMetricExporterSpi;
 import org.junit.Test;
 
 import static org.apache.ignite.internal.processors.cache.index.AbstractSchemaSelfTest.queryProcessor;
@@ -49,7 +55,7 @@ public class SqlViewExporterSpiTest extends AbstractExporterSpiTest {
                 new DataRegionConfiguration()
                     .setPersistenceEnabled(true)));
 
-        SqlViewExporterSpi sqlSpi = new SqlViewExporterSpi();
+        SqlViewMetricExporterSpi sqlSpi = new SqlViewMetricExporterSpi();
 
         sqlSpi.setExportFilter(mgrp -> !mgrp.name().startsWith(FILTERED_PREFIX));
 
@@ -68,6 +74,14 @@ public class SqlViewExporterSpiTest extends AbstractExporterSpiTest {
     }
 
     /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        Collection<String> caches = ignite.cacheNames();
+
+        for (String cache : caches)
+            ignite.destroyCache(cache);
+    }
+
+    /** {@inheritDoc} */
     @Override protected void afterTestsStopped() throws Exception {
         stopAllGrids(true);
 
@@ -78,7 +92,7 @@ public class SqlViewExporterSpiTest extends AbstractExporterSpiTest {
     @Test
     public void testDataRegionJmxMetrics() throws Exception {
         List<List<?>> res = execute(ignite,
-            "SELECT REPLACE(name, 'io.dataregion.default.'), value, description FROM MONITORING.METRICS");
+            "SELECT REPLACE(name, 'io.dataregion.default.'), value, description FROM SYS.METRICS");
 
         Set<String> names = new HashSet<>();
 
@@ -98,7 +112,7 @@ public class SqlViewExporterSpiTest extends AbstractExporterSpiTest {
         createAdditionalMetrics(ignite);
 
         List<List<?>> res = execute(ignite,
-            "SELECT name, value, description FROM MONITORING.METRICS WHERE name LIKE 'other.prefix%'");
+            "SELECT name, value, description FROM SYS.METRICS WHERE name LIKE 'other.prefix%'");
 
         Set<IgniteBiTuple<String, String>> expVals = new HashSet<>(Arrays.asList(
             t("other.prefix.test", "42"),
@@ -114,6 +128,121 @@ public class SqlViewExporterSpiTest extends AbstractExporterSpiTest {
         assertEquals(expVals, vals);
     }
 
+    /** */
+    @Test
+    public void testCachesView() throws Exception {
+        Set<String> cacheNames = new HashSet<>(Arrays.asList("cache-1", "cache-2"));
+
+        for (String name : cacheNames)
+            ignite.createCache(name);
+
+        List<List<?>> caches = execute(ignite, "SELECT CACHE_NAME FROM SYS.CACHES");
+
+        assertEquals(ignite.context().cache().cacheDescriptors().size(), caches.size());
+
+        for (List<?> row : caches)
+            cacheNames.remove(row.get(0));
+
+        assertTrue(cacheNames.toString(), cacheNames.isEmpty());
+    }
+
+    /** */
+    @Test
+    public void testCacheGroupsView() throws Exception {
+        Set<String> grpNames = new HashSet<>(Arrays.asList("grp-1", "grp-2"));
+
+        for (String grpName : grpNames)
+            ignite.createCache(new CacheConfiguration<>("cache-" + grpName).setGroupName(grpName));
+
+        List<List<?>> grps = execute(ignite, "SELECT CACHE_GROUP_NAME FROM SYS.CACHE_GROUPS");
+
+        assertEquals(ignite.context().cache().cacheGroupDescriptors().size(), grps.size());
+
+        for (List<?> row : grps)
+            grpNames.remove(row.get(0));
+
+        assertTrue(grpNames.toString(), grpNames.isEmpty());
+    }
+
+    /** */
+    @Test
+    public void testComputeBroadcast() throws Exception {
+        CyclicBarrier barrier = new CyclicBarrier(6);
+
+        for (int i = 0; i < 5; i++) {
+            ignite.compute().broadcastAsync(() -> {
+                try {
+                    barrier.await();
+                    barrier.await();
+                }
+                catch (InterruptedException | BrokenBarrierException e) {
+                    throw new RuntimeException(e);
+                }
+            });
+        }
+
+        barrier.await();
+
+        List<List<?>> tasks = execute(ignite,
+            "SELECT " +
+            "  INTERNAL, " +
+            "  AFFINITY_CACHE_NAME, " +
+            "  AFFINITY_PARTITION_ID, " +
+            "  TASK_CLASS_NAME, " +
+            "  TASK_NAME, " +
+            "  TASK_NODE_ID, " +
+            "  USER_VERSION " +
+            "FROM SYS.TASKS");
+
+        assertEquals(5, tasks.size());
+
+        List<?> t = tasks.get(0);
+
+        assertFalse((Boolean)t.get(0));
+        assertNull(t.get(1));
+        assertEquals(-1, t.get(2));
+        assertTrue(t.get(3).toString().startsWith(getClass().getName()));
+        assertTrue(t.get(4).toString().startsWith(getClass().getName()));
+        assertEquals(ignite.localNode().id(), t.get(5));
+        assertEquals("0", t.get(6));
+
+        barrier.await();
+    }
+
+    /** */
+    @Test
+    public void testServices() throws Exception {
+        ServiceConfiguration srvcCfg = new ServiceConfiguration();
+
+        srvcCfg.setName("service");
+        srvcCfg.setMaxPerNodeCount(1);
+        srvcCfg.setService(new DummyService());
+
+        ignite.services().deploy(srvcCfg);
+
+        List<List<?>> srvs = execute(ignite,
+            "SELECT " +
+                "  NAME, " +
+                "  SERVICE_ID, " +
+                "  SERVICE_CLASS, " +
+                "  TOTAL_COUNT, " +
+                "  MAX_PER_NODE_COUNT, " +
+                "  CACHE_NAME, " +
+                "  AFFINITY_KEY, " +
+                "  NODE_FILTER, " +
+                "  STATICALLY_CONFIGURED, " +
+                "  ORIGIN_NODE_ID " +
+                "FROM SYS.SERVICES");
+
+        assertEquals(ignite.context().service().serviceDescriptors().size(), srvs.size());
+
+        List<?> sysView = srvs.iterator().next();
+
+        assertEquals(srvcCfg.getName(), sysView.get(0));
+        assertEquals(DummyService.class.getName(), sysView.get(2));
+        assertEquals(srvcCfg.getMaxPerNodeCount(), sysView.get(4));
+    }
+
     /**
      * Execute query on given node.
      *
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/SqlSystemViewsSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/SqlSystemViewsSelfTest.java
index c278792..25e7249 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/SqlSystemViewsSelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/SqlSystemViewsSelfTest.java
@@ -73,6 +73,7 @@ import org.apache.ignite.lang.IgniteFuture;
 import org.apache.ignite.lang.IgnitePredicate;
 import org.apache.ignite.lang.IgniteRunnable;
 import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNode;
+import org.apache.ignite.spi.metric.sql.SqlViewMetricExporterSpi;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.junit.Assert;
 import org.junit.Test;
@@ -102,9 +103,7 @@ public class SqlSystemViewsSelfTest extends AbstractIndexingCommonTest {
         cleanPersistenceDir();
     }
 
-    /**
-     * @return System schema name.
-     */
+    /** @return System schema name. */
     protected String systemSchemaName() {
         return "SYS";
     }
@@ -875,8 +874,18 @@ public class SqlSystemViewsSelfTest extends AbstractIndexingCommonTest {
     }
 
     /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        return super.getConfiguration(igniteInstanceName)
+            .setMetricExporterSpi(new SqlViewMetricExporterSpi());
+    }
+
+    /** {@inheritDoc} */
     @Override protected IgniteConfiguration getConfiguration() throws Exception {
-        return super.getConfiguration().setCacheConfiguration(new CacheConfiguration().setName(DEFAULT_CACHE_NAME));
+        IgniteConfiguration cfg = super.getConfiguration()
+            .setCacheConfiguration(new CacheConfiguration().setName(DEFAULT_CACHE_NAME))
+            .setMetricExporterSpi(new SqlViewMetricExporterSpi());
+
+        return cfg;
     }
 
     /**
@@ -1241,13 +1250,13 @@ public class SqlSystemViewsSelfTest extends AbstractIndexingCommonTest {
                 "REBALANCE_BATCHES_PREFETCH_COUNT, REBALANCE_ORDER, " +
                 "EVICTION_FILTER, EVICTION_POLICY_FACTORY, " +
                 "IS_NEAR_CACHE_ENABLED, NEAR_CACHE_EVICTION_POLICY_FACTORY, NEAR_CACHE_START_SIZE, " +
-                "DEFAULT_LOCK_TIMEOUT, CACHE_INTERCEPTOR, CACHE_STORE_FACTORY, " +
+                "DEFAULT_LOCK_TIMEOUT, INTERCEPTOR, CACHE_STORE_FACTORY, " +
                 "IS_STORE_KEEP_BINARY, IS_READ_THROUGH, IS_WRITE_THROUGH, " +
                 "IS_WRITE_BEHIND_ENABLED, WRITE_BEHIND_COALESCING, WRITE_BEHIND_FLUSH_SIZE, " +
-                "WRITE_BEHIND_FLUSH_FREQUENCY, WRITE_BEHIND_FLUSH_THREAD_COUNT, WRITE_BEHIND_FLUSH_BATCH_SIZE, " +
+                "WRITE_BEHIND_FLUSH_FREQUENCY, WRITE_BEHIND_FLUSH_THREAD_COUNT, WRITE_BEHIND_BATCH_SIZE, " +
                 "MAX_CONCURRENT_ASYNC_OPERATIONS, CACHE_LOADER_FACTORY, CACHE_WRITER_FACTORY, EXPIRY_POLICY_FACTORY, " +
                 "IS_SQL_ESCAPE_ALL, SQL_SCHEMA, SQL_INDEX_MAX_INLINE_SIZE, IS_SQL_ONHEAP_CACHE_ENABLED, " +
-                "SQL_ONHEAP_CACHE_MAX_SIZE, QUERY_DETAILS_METRICS_SIZE, QUERY_PARALLELISM, MAX_QUERY_ITERATORS_COUNT, " +
+                "SQL_ONHEAP_CACHE_MAX_SIZE, QUERY_DETAIL_METRICS_SIZE, QUERY_PARALLELISM, MAX_QUERY_ITERATORS_COUNT, " +
             "DATA_REGION_NAME FROM " + systemSchemaName() + ".CACHES");
 
         assertColumnTypes(resAll.get(0),
@@ -1293,7 +1302,7 @@ public class SqlSystemViewsSelfTest extends AbstractIndexingCommonTest {
         assertEquals(0L, execSql("SELECT COUNT(*) FROM " + systemSchemaName() + ".CACHES WHERE CACHE_NAME = 'no_such_cache'").get(0)
             .get(0));
 
-        assertEquals(0L, execSql("SELECT COUNT(*) FROM " + systemSchemaName() + ".CACHES WHERE CACHE_NAME = 1").get(0).get(0));
+        assertEquals(0L, execSql("SELECT COUNT(*) FROM " + systemSchemaName() + ".CACHES WHERE CACHE_NAME = '1'").get(0).get(0));
 
         assertEquals("TestNodeFilter", execSql("SELECT NODE_FILTER FROM " + systemSchemaName() + ".CACHES WHERE CACHE_NAME = " +
             "'cache_atomic_part'").get(0).get(0));
diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite.java
index c689c98..f60b2a2 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.testsuites;
 
+import org.apache.ignite.internal.metric.SystemViewSelfTest;
 import org.apache.ignite.internal.processors.cache.AffinityKeyNameAndValueFieldNameConflictTest;
 import org.apache.ignite.internal.processors.cache.BigEntryQueryTest;
 import org.apache.ignite.internal.processors.cache.BinaryMetadataConcurrentUpdateWithIndexesTest;
@@ -579,8 +580,8 @@ import org.junit.runners.Suite;
     RowCountTableStatisticsUsageTest.class,
     RowCountTableStatisticsSurvivesNodeRestartTest.class,
 
-    SqlViewExporterSpiTest.class
-
+    SqlViewExporterSpiTest.class,
+    SystemViewSelfTest.class
 })
 public class IgniteBinaryCacheQueryTestSuite {
 }
diff --git a/modules/opencensus/src/main/java/org/apache/ignite/spi/metric/opencensus/OpenCensusMetricExporterSpi.java b/modules/opencensus/src/main/java/org/apache/ignite/spi/metric/opencensus/OpenCensusMetricExporterSpi.java
index ca8a537..e3d1c35 100644
--- a/modules/opencensus/src/main/java/org/apache/ignite/spi/metric/opencensus/OpenCensusMetricExporterSpi.java
+++ b/modules/opencensus/src/main/java/org/apache/ignite/spi/metric/opencensus/OpenCensusMetricExporterSpi.java
@@ -29,6 +29,7 @@ import io.opencensus.stats.View;
 import io.opencensus.stats.View.Name;
 import io.opencensus.tags.TagContextBuilder;
 import io.opencensus.tags.TagKey;
+import io.opencensus.tags.TagMetadata;
 import io.opencensus.tags.TagValue;
 import io.opencensus.tags.Tags;
 import java.time.OffsetDateTime;
@@ -55,6 +56,8 @@ import org.apache.ignite.spi.metric.ObjectMetric;
 import org.apache.ignite.spi.metric.ReadOnlyMetricRegistry;
 import org.jetbrains.annotations.Nullable;
 
+import static io.opencensus.tags.TagMetadata.TagTtl.UNLIMITED_PROPAGATION;
+
 import static org.apache.ignite.internal.processors.metric.impl.MetricUtils.histogramBucketNames;
 
 /**
@@ -99,6 +102,9 @@ public class OpenCensusMetricExporterSpi extends PushMetricsExporterAdapter {
     /** Ignite node consistent id. */
     public static final TagKey CONSISTENT_ID_TAG = TagKey.create("inci");
 
+    /** Tags metadata. */
+    public static final TagMetadata METADATA = TagMetadata.create(UNLIMITED_PROPAGATION);
+
     /** Ignite instance name in the form of {@link TagValue}. */
     private TagValue instanceNameValue;
 
@@ -221,13 +227,13 @@ public class OpenCensusMetricExporterSpi extends PushMetricsExporterAdapter {
         TagContextBuilder builder = Tags.getTagger().currentBuilder();
 
         if (sendInstanceName)
-            builder.put(INSTANCE_NAME_TAG, instanceNameValue);
+            builder.put(INSTANCE_NAME_TAG, instanceNameValue, METADATA);
 
         if (sendNodeId)
-            builder.put(NODE_ID_TAG, nodeIdValue);
+            builder.put(NODE_ID_TAG, nodeIdValue, METADATA);
 
         if (sendConsistentId)
-            builder.put(CONSISTENT_ID_TAG, consistenIdValue);
+            builder.put(CONSISTENT_ID_TAG, consistenIdValue, METADATA);
 
         return builder.buildScoped();
     }