You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by ji...@apache.org on 2019/04/18 18:15:15 UTC

[geode] branch develop updated: GEODE-6612: add entry count for list Region and rework filtering by groups (#3465)

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

jinmeiliao pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/develop by this push:
     new 6a177ec  GEODE-6612: add entry count for list Region and rework filtering by groups (#3465)
6a177ec is described below

commit 6a177ecfebc443affda84bea33f3c6cfa26f7c00
Author: jinmeiliao <ji...@pivotal.io>
AuthorDate: Thu Apr 18 11:15:04 2019 -0700

    GEODE-6612: add entry count for list Region and rework filtering by groups (#3465)
    
    Co-authored-by: Owen Nichols <on...@pivotal.io>
    Co-authored-by: Jens Deppe jdeppe@pivotal.io
    
    * Create a RuntimeCacheElement interface to hold multiple groups
    * Using RegionConfig can only set/get one group
---
 .../rest/ListRegionManagementDunitTest.java        | 140 +++++++++++++++++----
 .../integrationTest/resources/assembly_content.txt |   2 +
 .../RegionConfigMutatorIntegrationTest.java        |   2 +-
 .../api/LocatorClusterManagementService.java       |  77 ++++++++----
 .../mutators/ConfigurationManager.java             |   3 +-
 .../mutators/RegionConfigManager.java              |  39 +++++-
 .../validators/CacheElementValidator.java          |  37 ++++++
 .../validators/RegionConfigValidator.java          |   5 -
 .../api/LocatorClusterManagementServiceTest.java   | 111 +++++++++++-----
 .../validators/CacheElementValidatorTest.java}     |  32 ++---
 .../validators/RegionConfigValidatorTest.java      |   9 --
 .../assertions/ClusterManagementResultAssert.java  |   4 +-
 .../geode/cache/configuration/CacheElement.java    |  40 ++++--
 .../geode/cache/configuration/RegionConfig.java    |  24 ++++
 .../management/api/ClusterManagementResult.java    |   8 +-
 .../management/configuration/MemberConfig.java     |   6 +-
 .../configuration/RuntimeCacheElement.java         |  36 ++++++
 .../configuration/RuntimeRegionConfig.java         |  45 +++++++
 .../configuration/CacheElementJsonMappingTest.java |  54 +++++++-
 .../cache/configuration/CacheElementTest.java      | 102 +++++++++++++++
 .../ClientClusterManagementServiceDUnitTest.java   |  15 +--
 .../rest/RegionManagementIntegrationTest.java      |  13 ++
 22 files changed, 656 insertions(+), 148 deletions(-)

diff --git a/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/rest/ListRegionManagementDunitTest.java b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/rest/ListRegionManagementDunitTest.java
index 6dd1135..b0bca71 100644
--- a/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/rest/ListRegionManagementDunitTest.java
+++ b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/rest/ListRegionManagementDunitTest.java
@@ -15,6 +15,7 @@
 
 package org.apache.geode.management.internal.rest;
 
+import static org.apache.geode.test.awaitility.GeodeAwaitility.await;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import java.util.List;
@@ -25,10 +26,14 @@ import org.junit.BeforeClass;
 import org.junit.ClassRule;
 import org.junit.Test;
 
+import org.apache.geode.cache.Region;
 import org.apache.geode.cache.configuration.CacheElement;
 import org.apache.geode.cache.configuration.RegionConfig;
+import org.apache.geode.cache.configuration.RegionType;
 import org.apache.geode.management.api.ClusterManagementService;
 import org.apache.geode.management.client.ClusterManagementServiceProvider;
+import org.apache.geode.management.configuration.RuntimeCacheElement;
+import org.apache.geode.management.configuration.RuntimeRegionConfig;
 import org.apache.geode.test.dunit.rules.ClusterStartupRule;
 import org.apache.geode.test.dunit.rules.MemberVM;
 import org.apache.geode.test.junit.rules.GfshCommandRule;
@@ -61,15 +66,34 @@ public class ListRegionManagementDunitTest {
     regionConfig.setName("customers1");
     regionConfig.setGroup("group1");
     client.create(regionConfig);
+    locator.waitUntilRegionIsReadyOnExactlyThisManyServers("/customers1", 1);
+
+    // create a region that has different type on different group
+    regionConfig = new RegionConfig();
+    regionConfig.setName("customers2");
+    regionConfig.setGroup("group1");
+    regionConfig.setType(RegionType.PARTITION_PROXY);
+    client.create(regionConfig);
 
     regionConfig = new RegionConfig();
     regionConfig.setName("customers2");
     regionConfig.setGroup("group2");
     client.create(regionConfig);
+    locator.waitUntilRegionIsReadyOnExactlyThisManyServers("/customers2", 2);
 
     regionConfig = new RegionConfig();
     regionConfig.setName("customers");
     client.create(regionConfig);
+    locator.waitUntilRegionIsReadyOnExactlyThisManyServers("/customers", 2);
+
+    // create a region that belongs to multiple groups
+    regionConfig = new RegionConfig();
+    regionConfig.setName("customers3");
+    regionConfig.setGroup("group1");
+    client.create(regionConfig);
+    regionConfig.setGroup("group2");
+    client.create(regionConfig);
+    locator.waitUntilRegionIsReadyOnExactlyThisManyServers("/customers3", 2);
   }
 
   @Before
@@ -80,69 +104,141 @@ public class ListRegionManagementDunitTest {
   @Test
   public void listAll() throws Exception {
     // list all
-    List<CacheElement> regions = client.list(filter).getResult();
-    assertThat(regions.stream().map(CacheElement::getId).collect(Collectors.toList()))
-        .containsExactlyInAnyOrder("customers", "customers1", "customers2");
-    assertThat(regions.stream().map(CacheElement::getConfigGroup).collect(Collectors.toList()))
-        .containsExactlyInAnyOrder("cluster", "group1", "group2");
+    List<RuntimeCacheElement> regions = client.list(filter).getResult();
+    assertThat(regions).hasSize(5);
+    RuntimeCacheElement element = CacheElement.findElement(regions, "customers");
+    assertThat(element.getGroup()).isNull();
+
+    element = CacheElement.findElement(regions, "customers1");
+    assertThat(element.getGroup()).isEqualTo("group1");
+
+    RegionConfig region = (RegionConfig) CacheElement.findElement(regions, "customers2");
+    assertThat(region.getGroup()).isIn("group1", "group2");
+    assertThat(region.getType()).isIn("PARTITION", "PARTITION_PROXY");
+
+    element = CacheElement.findElement(regions, "customers3");
+    assertThat(element.getGroups()).containsExactlyInAnyOrder("group1", "group2");
   }
 
   @Test
   public void listClusterLevel() throws Exception {
     // list cluster level only
     filter.setGroup("cluster");
-    List<CacheElement> regions = client.list(filter).getResult();
+    List<RuntimeCacheElement> regions = client.list(filter).getResult();
     assertThat(regions).hasSize(1);
     assertThat(regions.get(0).getId()).isEqualTo("customers");
-    assertThat(regions.get(0).getConfigGroup()).isEqualTo("cluster");
     assertThat(regions.get(0).getGroup()).isNull();
   }
 
   @Test
+  public void testEntryCount() throws Exception {
+    server1.invoke(() -> {
+      Region region = ClusterStartupRule.getCache().getRegion("/customers");
+      region.put("k1", "v1");
+      region.put("k2", "v2");
+    });
+
+    // wait till entry size are correctly gathered by the mbean
+    locator.invoke(() -> {
+      await().untilAsserted(
+          () -> assertThat(ClusterStartupRule.memberStarter.getRegionMBean("/customers")
+              .getSystemRegionEntryCount()).isEqualTo(2));
+    });
+
+    filter.setName("customers");
+    List<RuntimeCacheElement> regions = client.list(filter).getResult();
+    assertThat(regions).hasSize(1);
+    RuntimeRegionConfig regionConfig = (RuntimeRegionConfig) regions.get(0);
+    assertThat(regionConfig.getName()).isEqualTo("customers");
+    assertThat(regionConfig).isInstanceOf(RuntimeRegionConfig.class);
+    assertThat(regionConfig.getEntryCount()).isEqualTo(2);
+  }
+
+  @Test
   public void listGroup1() throws Exception {
     // list group1
     filter.setGroup("group1");
-    List<CacheElement> regions = client.list(filter).getResult();
-    assertThat(regions).hasSize(1);
-    assertThat(regions.get(0).getId()).isEqualTo("customers1");
-    assertThat(regions.get(0).getConfigGroup()).isEqualTo("group1");
-    assertThat(regions.get(0).getGroup()).isEqualTo("group1");
+    List<RuntimeCacheElement> regions = client.list(filter).getResult();
+    assertThat(regions).hasSize(3);
+    // when filtering by group, the returned list should not have group info
+    RuntimeCacheElement region =
+        (RuntimeCacheElement) CacheElement.findElement(regions, "customers1");
+    assertThat(region.getGroup()).isEqualTo("group1");
+
+    region = (RuntimeCacheElement) CacheElement.findElement(regions, "customers2");
+    assertThat(region.getGroup()).isEqualTo("group1");
+
+    region = (RuntimeCacheElement) CacheElement.findElement(regions, "customers3");
+    assertThat(region.getGroups()).containsExactlyInAnyOrder("group1", "group2");
   }
 
   @Test
   public void listGroup2() throws Exception {
     // list group1
     filter.setGroup("group2");
-    List<CacheElement> regions = client.list(filter).getResult();
-    assertThat(regions).hasSize(1);
-    assertThat(regions.get(0).getId()).isEqualTo("customers2");
-    assertThat(regions.get(0).getConfigGroup()).isEqualTo("group2");
-    assertThat(regions.get(0).getGroup()).isEqualTo("group2");
+    List<RuntimeCacheElement> regions = client.list(filter).getResult();
+    assertThat(regions).hasSize(2);
+
+    RuntimeCacheElement region = CacheElement.findElement(regions, "customers2");
+    assertThat(region.getGroup()).isEqualTo("group2");
+
+    region = CacheElement.findElement(regions, "customers3");
+    assertThat(region.getGroups()).containsExactlyInAnyOrder("group1", "group2");
   }
 
   @Test
   public void listNonExistentGroup() throws Exception {
     // list non-existent group
     filter.setGroup("group3");
-    List<CacheElement> regions = client.list(filter).getResult();
+    List<RuntimeCacheElement> regions = client.list(filter).getResult();
     assertThat(regions).hasSize(0);
   }
 
   @Test
   public void listRegionByName() throws Exception {
     filter.setName("customers");
-    List<CacheElement> regions = client.list(filter).getResult();
+    List<RuntimeCacheElement> regions = client.list(filter).getResult();
     assertThat(regions).hasSize(1);
     assertThat(regions.get(0).getId()).isEqualTo("customers");
-    assertThat(regions.get(0).getConfigGroup()).isEqualTo("cluster");
     assertThat(regions.get(0).getGroup()).isNull();
   }
 
   @Test
+  public void listRegionByName1() throws Exception {
+    filter.setName("customers1");
+    List<RuntimeCacheElement> regions = client.list(filter).getResult();
+    assertThat(regions).hasSize(1);
+    assertThat(regions.get(0).getId()).isEqualTo("customers1");
+    assertThat(regions.get(0).getGroup()).isEqualTo("group1");
+  }
+
+  @Test
+  public void listRegionByName2() throws Exception {
+    filter.setName("customers2");
+    List<RuntimeCacheElement> regions = client.list(filter).getResult();
+    assertThat(regions).hasSize(2);
+    assertThat(regions.stream().map(RuntimeCacheElement::getGroup).collect(Collectors.toList()))
+        .containsExactlyInAnyOrder("group1", "group2");
+    assertThat(regions.stream().map(RegionConfig.class::cast)
+        .map(RegionConfig::getType)
+        .collect(Collectors.toList()))
+            .containsExactlyInAnyOrder("PARTITION", "PARTITION_PROXY");
+  }
+
+  @Test
+  public void listRegionByName3() throws Exception {
+    filter.setName("customers3");
+    List<RuntimeCacheElement> regions = client.list(filter).getResult();
+    assertThat(regions).hasSize(1);
+    assertThat(regions.get(0).getId()).isEqualTo("customers3");
+    assertThat(regions.get(0).getGroups()).containsExactlyInAnyOrder("group1", "group2");
+  }
+
+  @Test
   public void listNonExistentRegion() throws Exception {
     // list non-existent region
-    filter.setName("customer3");
-    List<CacheElement> regions = client.list(filter).getResult();
+    filter.setName("customer4");
+    List<RuntimeCacheElement> regions = client.list(filter).getResult();
     assertThat(regions).hasSize(0);
   }
 }
diff --git a/geode-assembly/src/integrationTest/resources/assembly_content.txt b/geode-assembly/src/integrationTest/resources/assembly_content.txt
index bb3200b..5cc2a63 100644
--- a/geode-assembly/src/integrationTest/resources/assembly_content.txt
+++ b/geode-assembly/src/integrationTest/resources/assembly_content.txt
@@ -707,6 +707,8 @@ javadoc/org/apache/geode/management/client/package-frame.html
 javadoc/org/apache/geode/management/client/package-summary.html
 javadoc/org/apache/geode/management/client/package-tree.html
 javadoc/org/apache/geode/management/configuration/MemberConfig.html
+javadoc/org/apache/geode/management/configuration/RuntimeCacheElement.html
+javadoc/org/apache/geode/management/configuration/RuntimeRegionConfig.html
 javadoc/org/apache/geode/management/configuration/package-frame.html
 javadoc/org/apache/geode/management/configuration/package-summary.html
 javadoc/org/apache/geode/management/configuration/package-tree.html
diff --git a/geode-core/src/integrationTest/java/org/apache/geode/management/internal/configuration/mutators/RegionConfigMutatorIntegrationTest.java b/geode-core/src/integrationTest/java/org/apache/geode/management/internal/configuration/mutators/RegionConfigMutatorIntegrationTest.java
index 30d4140..c3b84aa 100644
--- a/geode-core/src/integrationTest/java/org/apache/geode/management/internal/configuration/mutators/RegionConfigMutatorIntegrationTest.java
+++ b/geode-core/src/integrationTest/java/org/apache/geode/management/internal/configuration/mutators/RegionConfigMutatorIntegrationTest.java
@@ -37,7 +37,7 @@ public class RegionConfigMutatorIntegrationTest {
   @Before
   public void before() throws Exception {
     config = new RegionConfig();
-    mutator = new RegionConfigManager();
+    mutator = new RegionConfigManager(locator.getCache());
   }
 
   @Test
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/api/LocatorClusterManagementService.java b/geode-core/src/main/java/org/apache/geode/management/internal/api/LocatorClusterManagementService.java
index 5d79e29..ffa9342 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/api/LocatorClusterManagementService.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/api/LocatorClusterManagementService.java
@@ -21,6 +21,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -42,12 +43,14 @@ import org.apache.geode.internal.logging.LogService;
 import org.apache.geode.management.api.ClusterManagementResult;
 import org.apache.geode.management.api.ClusterManagementService;
 import org.apache.geode.management.configuration.MemberConfig;
+import org.apache.geode.management.configuration.RuntimeCacheElement;
 import org.apache.geode.management.internal.cli.CliUtil;
 import org.apache.geode.management.internal.cli.functions.CliFunctionResult;
 import org.apache.geode.management.internal.cli.functions.UpdateCacheFunction;
 import org.apache.geode.management.internal.configuration.mutators.ConfigurationManager;
 import org.apache.geode.management.internal.configuration.mutators.MemberConfigManager;
 import org.apache.geode.management.internal.configuration.mutators.RegionConfigManager;
+import org.apache.geode.management.internal.configuration.validators.CacheElementValidator;
 import org.apache.geode.management.internal.configuration.validators.ConfigurationValidator;
 import org.apache.geode.management.internal.configuration.validators.RegionConfigValidator;
 import org.apache.geode.management.internal.exceptions.EntityExistsException;
@@ -56,23 +59,24 @@ public class LocatorClusterManagementService implements ClusterManagementService
   private static final Logger logger = LogService.getLogger();
   private InternalCache cache;
   private ConfigurationPersistenceService persistenceService;
-  private HashMap<Class, ConfigurationManager> managers;
-  private HashMap<Class, ConfigurationValidator> validators;
+  private Map<Class, ConfigurationManager> managers;
+  private Map<Class, ConfigurationValidator> validators;
 
   public LocatorClusterManagementService(InternalCache cache,
       ConfigurationPersistenceService persistenceService) {
     this(cache, persistenceService, new HashMap(), new HashMap());
     // initialize the list of managers
-    managers.put(RegionConfig.class, new RegionConfigManager());
+    managers.put(RegionConfig.class, new RegionConfigManager(cache));
     managers.put(MemberConfig.class, new MemberConfigManager(cache));
 
     // initialize the list of validators
+    validators.put(CacheElement.class, new CacheElementValidator());
     validators.put(RegionConfig.class, new RegionConfigValidator(cache));
   }
 
   @VisibleForTesting
   public LocatorClusterManagementService(InternalCache cache,
-      ConfigurationPersistenceService persistenceService, HashMap managers, HashMap validators) {
+      ConfigurationPersistenceService persistenceService, Map managers, Map validators) {
     this.cache = cache;
     this.persistenceService = persistenceService;
     this.managers = managers;
@@ -88,18 +92,24 @@ public class LocatorClusterManagementService implements ClusterManagementService
           "Cluster configuration service needs to be enabled");
     }
 
-    ClusterManagementResult result = new ClusterManagementResult();
-    ConfigurationManager configurationMutator = managers.get(config.getClass());
+    // first validate common attributes of all configuration object
+    validators.get(CacheElement.class).validate(config);
 
     ConfigurationValidator validator = validators.get(config.getClass());
     if (validator != null) {
       validator.validate(config);
+      // exit early if config element already exists in cache config
+      CacheConfig currentPersistedConfig = persistenceService.getCacheConfig(group, true);
+      if (validator.exists(config, currentPersistedConfig)) {
+        throw new EntityExistsException("cache element " + config.getId() + " already exists.");
+      }
     }
 
-    // exit early if config element already exists in cache config
-    CacheConfig currentPersistedConfig = persistenceService.getCacheConfig(group, true);
-    if (validator.exists(config, currentPersistedConfig)) {
-      throw new EntityExistsException("cache element " + config.getId() + " already exists.");
+    // validate that user used the correct config object type
+    ConfigurationManager configurationManager = managers.get(config.getClass());
+    if (configurationManager == null) {
+      throw new IllegalArgumentException(String.format("Configuration type %s is not supported.",
+          config.getClass().getSimpleName()));
     }
 
     // execute function on all members
@@ -110,6 +120,8 @@ public class LocatorClusterManagementService implements ClusterManagementService
           "no members found in " + group + " to create cache element");
     }
 
+    ClusterManagementResult result = new ClusterManagementResult();
+
     List<CliFunctionResult> functionResults = executeAndGetFunctionResult(
         new UpdateCacheFunction(),
         Arrays.asList(config, UpdateCacheFunction.CacheElementOperation.ADD),
@@ -129,7 +141,7 @@ public class LocatorClusterManagementService implements ClusterManagementService
     final String finalGroup = group; // the below lambda requires a reference that is final
     persistenceService.updateCacheConfig(finalGroup, cacheConfigForGroup -> {
       try {
-        configurationMutator.add(config, cacheConfigForGroup);
+        configurationManager.add(config, cacheConfigForGroup);
         result.setStatus(true,
             "successfully persisted config for " + finalGroup);
       } catch (Exception e) {
@@ -159,7 +171,7 @@ public class LocatorClusterManagementService implements ClusterManagementService
     ClusterManagementResult result = new ClusterManagementResult();
 
     if (filter instanceof MemberConfig) {
-      List<CacheElement> listResults = manager.list(filter, null);
+      List<RuntimeCacheElement> listResults = manager.list(filter, null);
       result.setResult(listResults);
       return result;
     }
@@ -169,23 +181,46 @@ public class LocatorClusterManagementService implements ClusterManagementService
           "Cluster configuration service needs to be enabled");
     }
 
-    List<CacheElement> elements = new ArrayList<>();
+    List<RuntimeCacheElement> resultList = new ArrayList<>();
+
+    // get a list of all the resultList from all groups that satisfy the filter criteria (all
+    // filters
+    // have been applied except the group)
     for (String group : persistenceService.getGroups()) {
-      if (StringUtils.isBlank(filter.getGroup()) || group.equals(filter.getConfigGroup())) {
-        CacheConfig currentPersistedConfig = persistenceService.getCacheConfig(group, true);
-        List<CacheElement> listInGroup = manager.list(filter, currentPersistedConfig);
-        // only set the group attribute when the config level is not in the cluster level
-        if (!group.equals("cluster")) {
-          listInGroup.stream().forEach(e -> e.setGroup(group));
+      CacheConfig currentPersistedConfig = persistenceService.getCacheConfig(group, true);
+      List<RuntimeCacheElement> listInGroup = manager.list(filter, currentPersistedConfig);
+      for (RuntimeCacheElement element : listInGroup) {
+        element.getGroups().add(group);
+        int index = resultList.indexOf(element);
+        if (index >= 0) {
+          RuntimeCacheElement exist = resultList.get(index);
+          exist.getGroups().add(group);
+        } else {
+          resultList.add(element);
         }
-        elements.addAll(listInGroup);
       }
     }
 
-    result.setResult(elements);
+    // filtering by group. Do this after iterating through all the groups because some region might
+    // belong to multiple groups and we want the "group" field to show that.
+    if (StringUtils.isNotBlank(filter.getGroup())) {
+      resultList =
+          resultList.stream().filter(e -> e.getGroups().contains(filter.getConfigGroup()))
+              .collect(Collectors.toList());
+    }
+
+    // if "cluster" is the only group of the element, remove it
+    for (RuntimeCacheElement element : resultList) {
+      if (element.getGroups().size() == 1 && "cluster".equals(element.getGroup())) {
+        element.getGroups().clear();
+      }
+    }
+
+    result.setResult(resultList);
     return result;
   }
 
+
   @Override
   public boolean isConnected() {
     return true;
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/mutators/ConfigurationManager.java b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/mutators/ConfigurationManager.java
index 9862cff..83f31f9 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/mutators/ConfigurationManager.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/mutators/ConfigurationManager.java
@@ -22,6 +22,7 @@ import java.util.List;
 import org.apache.geode.annotations.Experimental;
 import org.apache.geode.cache.configuration.CacheConfig;
 import org.apache.geode.cache.configuration.CacheElement;
+import org.apache.geode.management.configuration.RuntimeCacheElement;
 
 /**
  * Defines the behavior to mutate a configuration change into a pre-existing cache config from a
@@ -37,5 +38,5 @@ public interface ConfigurationManager<T extends CacheElement> {
 
   void delete(T config, CacheConfig existing);
 
-  List<? extends T> list(T filterConfig, CacheConfig existing);
+  List<? extends RuntimeCacheElement> list(T filterConfig, CacheConfig existing);
 }
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/mutators/RegionConfigManager.java b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/mutators/RegionConfigManager.java
index 87e239e..de44010 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/mutators/RegionConfigManager.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/mutators/RegionConfigManager.java
@@ -17,6 +17,7 @@
 
 package org.apache.geode.management.internal.configuration.mutators;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -25,10 +26,20 @@ import org.apache.commons.lang3.StringUtils;
 
 import org.apache.geode.cache.configuration.CacheConfig;
 import org.apache.geode.cache.configuration.RegionConfig;
+import org.apache.geode.internal.cache.InternalCache;
+import org.apache.geode.management.DistributedRegionMXBean;
+import org.apache.geode.management.ManagementService;
+import org.apache.geode.management.configuration.RuntimeRegionConfig;
 
-public class RegionConfigManager implements ConfigurationManager<RegionConfig> {
+public class RegionConfigManager
+    implements ConfigurationManager<RegionConfig> {
+  private InternalCache cache;
+  private ManagementService managementService;
 
-  public RegionConfigManager() {}
+  public RegionConfigManager(InternalCache cache) {
+    this.cache = cache;
+    this.managementService = ManagementService.getExistingManagementService(cache);
+  }
 
   @Override
   public void add(RegionConfig configElement, CacheConfig existingConfig) {
@@ -46,11 +57,27 @@ public class RegionConfigManager implements ConfigurationManager<RegionConfig> {
   }
 
   @Override
-  public List<RegionConfig> list(RegionConfig filter, CacheConfig existing) {
+  public List<RuntimeRegionConfig> list(RegionConfig filter, CacheConfig existing) {
+    List<RegionConfig> staticRegionConfigs;
     if (StringUtils.isBlank(filter.getName())) {
-      return existing.getRegions();
+      staticRegionConfigs = existing.getRegions();
+    } else {
+      staticRegionConfigs =
+          existing.getRegions().stream().filter(r -> filter.getName().equals(r.getName())).collect(
+              Collectors.toList());
+    }
+
+    List<RuntimeRegionConfig> results = new ArrayList<>();
+    for (RegionConfig config : staticRegionConfigs) {
+      DistributedRegionMXBean distributedRegionMXBean =
+          managementService.getDistributedRegionMXBean("/" + config.getName());
+      if (distributedRegionMXBean == null) {
+        throw new IllegalStateException("Can't get the region mbean info for " + config.getName());
+      }
+      RuntimeRegionConfig runtimeConfig = new RuntimeRegionConfig(config);
+      runtimeConfig.setEntryCount(distributedRegionMXBean.getSystemRegionEntryCount());
+      results.add(runtimeConfig);
     }
-    return existing.getRegions().stream().filter(r -> filter.getName().equals(r.getName())).collect(
-        Collectors.toList());
+    return results;
   }
 }
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/validators/CacheElementValidator.java b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/validators/CacheElementValidator.java
new file mode 100644
index 0000000..085fce3
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/validators/CacheElementValidator.java
@@ -0,0 +1,37 @@
+/*
+ * 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.geode.management.internal.configuration.validators;
+
+import org.apache.geode.cache.configuration.CacheConfig;
+import org.apache.geode.cache.configuration.CacheElement;
+
+/**
+ * this is used to validate all the common attributes of CacheElement, eg. group
+ */
+public class CacheElementValidator implements ConfigurationValidator<CacheElement> {
+  @Override
+  public void validate(CacheElement config) throws IllegalArgumentException {
+    if ("cluster".equalsIgnoreCase(config.getGroup())) {
+      throw new IllegalArgumentException(
+          "cluster is a reserved group name. Do not use it for member groups.");
+    }
+  }
+
+  @Override
+  public boolean exists(CacheElement config, CacheConfig persistedConfig) {
+    return false;
+  }
+}
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/validators/RegionConfigValidator.java b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/validators/RegionConfigValidator.java
index 79084fe..f794e4a 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/validators/RegionConfigValidator.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/validators/RegionConfigValidator.java
@@ -40,11 +40,6 @@ public class RegionConfigValidator implements ConfigurationValidator<RegionConfi
 
     RegionNameValidation.validate(config.getName());
 
-    if ("cluster".equalsIgnoreCase(config.getGroup())) {
-      throw new IllegalArgumentException(
-          "cluster is a reserved group name. Do not use it for member groups.");
-    }
-
     if (config.getType() == null) {
       RegionType defaultRegion = RegionType.PARTITION;
       config.setType(defaultRegion);
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/api/LocatorClusterManagementServiceTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/api/LocatorClusterManagementServiceTest.java
index ff5a79b..7bf79c8 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/api/LocatorClusterManagementServiceTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/api/LocatorClusterManagementServiceTest.java
@@ -15,29 +15,45 @@
 
 package org.apache.geode.management.internal.api;
 
+import static org.apache.geode.test.junit.assertions.ClusterManagementResultAssert.assertManagementResult;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
+import com.google.common.collect.Sets;
 import org.junit.Before;
 import org.junit.Test;
 
 import org.apache.geode.cache.configuration.CacheConfig;
+import org.apache.geode.cache.configuration.CacheElement;
 import org.apache.geode.cache.configuration.RegionConfig;
 import org.apache.geode.distributed.ConfigurationPersistenceService;
 import org.apache.geode.distributed.DistributedMember;
 import org.apache.geode.internal.cache.InternalCache;
 import org.apache.geode.management.api.ClusterManagementResult;
+import org.apache.geode.management.configuration.MemberConfig;
+import org.apache.geode.management.configuration.RuntimeCacheElement;
+import org.apache.geode.management.configuration.RuntimeRegionConfig;
 import org.apache.geode.management.internal.cli.functions.CliFunctionResult;
-import org.apache.geode.management.internal.exceptions.EntityExistsException;
+import org.apache.geode.management.internal.configuration.mutators.ConfigurationManager;
+import org.apache.geode.management.internal.configuration.validators.CacheElementValidator;
+import org.apache.geode.management.internal.configuration.validators.ConfigurationValidator;
+import org.apache.geode.management.internal.configuration.validators.RegionConfigValidator;
 
 public class LocatorClusterManagementServiceTest {
 
@@ -46,12 +62,25 @@ public class LocatorClusterManagementServiceTest {
   private ConfigurationPersistenceService persistenceService;
   private RegionConfig regionConfig;
   private ClusterManagementResult result;
+  private Map<Class, ConfigurationValidator> validators = new HashMap<>();
+  private Map<Class, ConfigurationManager> managers = new HashMap<>();
+  private ConfigurationValidator<RegionConfig> regionValidator;
+  private ConfigurationValidator<CacheElement> cacheElementValidator;
+  private ConfigurationManager<RegionConfig> regionManager;
 
   @Before
   public void before() throws Exception {
+    regionValidator = mock(RegionConfigValidator.class);
+    regionManager = mock(ConfigurationManager.class);
+    cacheElementValidator = mock(CacheElementValidator.class);
+    validators.put(RegionConfig.class, regionValidator);
+    validators.put(CacheElement.class, cacheElementValidator);
+    managers.put(RegionConfig.class, regionManager);
+
     cache = mock(InternalCache.class);
     persistenceService = mock(ConfigurationPersistenceService.class);
-    service = spy(new LocatorClusterManagementService(cache, persistenceService));
+    service =
+        spy(new LocatorClusterManagementService(cache, persistenceService, managers, validators));
     regionConfig = new RegionConfig();
   }
 
@@ -65,33 +94,14 @@ public class LocatorClusterManagementServiceTest {
   }
 
   @Test
-  public void elementAlreadyExist() throws Exception {
-    regionConfig.setName("test");
-    CacheConfig cacheConfig = new CacheConfig();
-    cacheConfig.getRegions().add(regionConfig);
-    when(persistenceService.getCacheConfig("cluster", true)).thenReturn(cacheConfig);
-
-    assertThatThrownBy(() -> service.create(regionConfig))
-        .isInstanceOf(EntityExistsException.class)
-        .hasMessageContaining("cache element test already exists");
-  }
-
-  @Test
-  public void validationFailed() throws Exception {
-    assertThatThrownBy(() -> service.create(regionConfig))
-        .isInstanceOf(IllegalArgumentException.class)
-        .hasMessageContaining("Name of the region has to be specified");
-  }
-
-  @Test
-  public void noMemberFound() throws Exception {
-    regionConfig.setName("test");
-    when(persistenceService.getCacheConfig("cluster", true)).thenReturn(new CacheConfig());
-    doReturn(Collections.emptySet()).when(service).findMembers(any());
-    result = service.create(regionConfig);
-    assertThat(result.isSuccessful()).isFalse();
-    assertThat(result.getStatusMessage())
-        .contains("no members found in cluster to create cache element");
+  public void validatorIsCalledCorrectly() throws Exception {
+    doReturn(Collections.emptySet()).when(service).findMembers(anyString());
+    assertManagementResult(service.create(regionConfig))
+        .failed().hasStatusCode(ClusterManagementResult.StatusCode.ERROR)
+        .containsStatusMessage("no members found");
+    verify(cacheElementValidator).validate(regionConfig);
+    verify(regionValidator).validate(regionConfig);
+    verify(regionValidator).exists(eq(regionConfig), any());
   }
 
   @Test
@@ -110,4 +120,47 @@ public class LocatorClusterManagementServiceTest {
     assertThat(result.getStatusMessage())
         .contains("Failed to apply the update on all members");
   }
+
+  @Test
+  public void non_supportedConfigObject() throws Exception {
+    MemberConfig config = new MemberConfig();
+    assertThatThrownBy(() -> service.create(config)).isInstanceOf(IllegalArgumentException.class)
+        .hasMessageContaining("Configuration type MemberConfig is not supported");
+  }
+
+  @Test
+  public void listOneGroup() throws Exception {
+    regionConfig.setGroup("cluster");
+    when(persistenceService.getGroups()).thenReturn(Sets.newHashSet("cluster", "group1"));
+
+    service.list(regionConfig);
+    // even we are listing regions in one group, we still need to go through all the groups
+    verify(persistenceService).getCacheConfig("cluster", true);
+    verify(persistenceService).getCacheConfig("group1", true);
+    verify(regionManager, times(2)).list(any(), any());
+  }
+
+  @Test
+  public void aRegionInClusterAndGroup1() throws Exception {
+    when(persistenceService.getGroups()).thenReturn(Sets.newHashSet("cluster", "group1"));
+    RuntimeRegionConfig region1 = new RuntimeRegionConfig();
+    region1.setName("region1");
+    region1.setType("REPLICATE");
+    RuntimeRegionConfig region2 = new RuntimeRegionConfig();
+    region2.setName("region1");
+    region2.setType("REPLICATE");
+
+    List clusterRegions = Arrays.asList(region1);
+    List group1Regions = Arrays.asList(region2);
+    when(regionManager.list(any(), any())).thenReturn(clusterRegions)
+        .thenReturn(group1Regions);
+
+    // this is to make sure when 'cluster" is in one of the group, it will show
+    // the cluster and the other group name
+    List<RuntimeCacheElement> results = service.list(new RegionConfig()).getResult();
+    assertThat(results).hasSize(1);
+    RuntimeRegionConfig result = (RuntimeRegionConfig) results.get(0);
+    assertThat(result.getName()).isEqualTo("region1");
+    assertThat(result.getGroups()).containsExactlyInAnyOrder("cluster", "group1");
+  }
 }
diff --git a/geode-core/src/integrationTest/java/org/apache/geode/management/internal/configuration/mutators/RegionConfigMutatorIntegrationTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/validators/CacheElementValidatorTest.java
similarity index 52%
copy from geode-core/src/integrationTest/java/org/apache/geode/management/internal/configuration/mutators/RegionConfigMutatorIntegrationTest.java
copy to geode-core/src/test/java/org/apache/geode/management/internal/configuration/validators/CacheElementValidatorTest.java
index 30d4140..1be5265 100644
--- a/geode-core/src/integrationTest/java/org/apache/geode/management/internal/configuration/mutators/RegionConfigMutatorIntegrationTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/validators/CacheElementValidatorTest.java
@@ -12,42 +12,34 @@
  * or implied. See the License for the specific language governing permissions and limitations under
  * the License.
  */
-package org.apache.geode.management.internal.configuration.mutators;
 
-import static org.assertj.core.api.Assertions.assertThat;
+package org.apache.geode.management.internal.configuration.validators;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
-import org.apache.geode.cache.configuration.CacheConfig;
-import org.apache.geode.cache.configuration.CacheElement;
 import org.apache.geode.cache.configuration.RegionConfig;
-import org.apache.geode.cache.configuration.RegionType;
-import org.apache.geode.test.junit.rules.LocatorStarterRule;
-
-public class RegionConfigMutatorIntegrationTest {
 
-  @Rule
-  public LocatorStarterRule locator = new LocatorStarterRule().withAutoStart();
+public class CacheElementValidatorTest {
 
-  private RegionConfigManager mutator;
+  private CacheElementValidator validator;
   private RegionConfig config;
 
   @Before
   public void before() throws Exception {
+    validator = new CacheElementValidator();
     config = new RegionConfig();
-    mutator = new RegionConfigManager();
   }
 
   @Test
-  public void sanity() throws Exception {
-    config.setType(RegionType.REPLICATE);
+  public void invalidGroup_cluster() throws Exception {
     config.setName("test");
-    CacheConfig cacheConfig =
-        locator.getLocator().getConfigurationPersistenceService().getCacheConfig("cluster", true);
-
-    mutator.add(config, cacheConfig);
-    assertThat(CacheElement.findElement(cacheConfig.getRegions(), config.getId())).isNotNull();
+    config.setGroup("cluster");
+    assertThatThrownBy(() -> validator.validate(config)).isInstanceOf(
+        IllegalArgumentException.class)
+        .hasMessageContaining(
+            "cluster is a reserved group name");
   }
 }
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/validators/RegionConfigValidatorTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/validators/RegionConfigValidatorTest.java
index 8c70c00..38dbf58 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/validators/RegionConfigValidatorTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/validators/RegionConfigValidatorTest.java
@@ -113,13 +113,4 @@ public class RegionConfigValidatorTest {
             "Region names may only be alphanumeric and may contain hyphens or underscores");
   }
 
-  @Test
-  public void invalidGroup() throws Exception {
-    config.setName("test");
-    config.setGroup("cluster");
-    assertThatThrownBy(() -> validator.validate(config)).isInstanceOf(
-        IllegalArgumentException.class)
-        .hasMessageContaining(
-            "cluster is a reserved group name");
-  }
 }
diff --git a/geode-junit/src/main/java/org/apache/geode/test/junit/assertions/ClusterManagementResultAssert.java b/geode-junit/src/main/java/org/apache/geode/test/junit/assertions/ClusterManagementResultAssert.java
index f66acbe..fa3a7a8 100644
--- a/geode-junit/src/main/java/org/apache/geode/test/junit/assertions/ClusterManagementResultAssert.java
+++ b/geode-junit/src/main/java/org/apache/geode/test/junit/assertions/ClusterManagementResultAssert.java
@@ -20,8 +20,8 @@ import static org.assertj.core.api.Assertions.assertThat;
 import org.assertj.core.api.AbstractAssert;
 import org.assertj.core.api.ListAssert;
 
-import org.apache.geode.cache.configuration.CacheElement;
 import org.apache.geode.management.api.ClusterManagementResult;
+import org.apache.geode.management.configuration.RuntimeCacheElement;
 
 public class ClusterManagementResultAssert
     extends AbstractAssert<ClusterManagementResultAssert, ClusterManagementResult> {
@@ -50,7 +50,7 @@ public class ClusterManagementResultAssert
     return this;
   }
 
-  public ListAssert<CacheElement> hasListResult() {
+  public ListAssert<RuntimeCacheElement> hasListResult() {
     return assertThat(actual.getResult());
   }
 
diff --git a/geode-management/src/main/java/org/apache/geode/cache/configuration/CacheElement.java b/geode-management/src/main/java/org/apache/geode/cache/configuration/CacheElement.java
index 4462f57..268ae1d 100644
--- a/geode-management/src/main/java/org/apache/geode/cache/configuration/CacheElement.java
+++ b/geode-management/src/main/java/org/apache/geode/cache/configuration/CacheElement.java
@@ -18,11 +18,13 @@
 package org.apache.geode.cache.configuration;
 
 import java.io.Serializable;
+import java.util.ArrayList;
 import java.util.List;
 
 import javax.xml.bind.annotation.XmlTransient;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonSetter;
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import org.apache.commons.lang3.StringUtils;
 
@@ -32,35 +34,53 @@ import org.apache.geode.lang.Identifiable;
 @Experimental
 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "class")
 public abstract class CacheElement implements Identifiable<String>, Serializable {
-  private String group;
+  protected List<String> groups = new ArrayList<>();
 
-  public static <T extends CacheElement> boolean exists(List<T> list, String id) {
+  public static <T extends Identifiable> boolean exists(List<T> list, String id) {
     return list.stream().anyMatch(o -> o.getId().equals(id));
   }
 
-  public static <T extends CacheElement> T findElement(List<T> list, String id) {
+  public static <T extends Identifiable> T findElement(List<T> list, String id) {
     return list.stream().filter(o -> o.getId().equals(id)).findFirst().orElse(null);
   }
 
-  public static <T extends CacheElement> void removeElement(List<T> list, String id) {
+  public static <T extends Identifiable> void removeElement(List<T> list, String id) {
     list.removeIf(t -> t.getId().equals(id));
   }
 
-  @XmlTransient
-  public String getGroup() {
-    return group;
-  }
-
+  /**
+   * this returns a non-null value
+   * for cluster level element, it will return "cluster" for sure.
+   */
   @XmlTransient
   @JsonIgnore
   public String getConfigGroup() {
+    String group = getGroup();
     if (StringUtils.isBlank(group)) {
       return "cluster";
     }
     return group;
   }
 
+  /**
+   * this returns the first group set by the user
+   * if no group is set, this returns null
+   */
+  @XmlTransient
+  public String getGroup() {
+    if (groups.size() == 0) {
+      return null;
+    }
+    return groups.get(0);
+  }
+
+  @JsonSetter
   public void setGroup(String group) {
-    this.group = group;
+    groups.clear();
+
+    if (StringUtils.isBlank(group)) {
+      return;
+    }
+    groups.add(group);
   }
 }
diff --git a/geode-management/src/main/java/org/apache/geode/cache/configuration/RegionConfig.java b/geode-management/src/main/java/org/apache/geode/cache/configuration/RegionConfig.java
index b3d43e4..610233e 100644
--- a/geode-management/src/main/java/org/apache/geode/cache/configuration/RegionConfig.java
+++ b/geode-management/src/main/java/org/apache/geode/cache/configuration/RegionConfig.java
@@ -30,6 +30,7 @@ import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlType;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.google.common.base.Objects;
 
 import org.apache.geode.annotations.Experimental;
 import org.apache.geode.management.api.RestfulEndpoint;
@@ -178,6 +179,17 @@ public class RegionConfig extends CacheElement implements RestfulEndpoint {
     this.type = refid;
   }
 
+  public RegionConfig(RegionConfig config) {
+    this.regionAttributes = config.getRegionAttributes();
+    this.type = config.getType();
+    this.entries = config.getEntries();
+    this.indexes = config.getIndexes();
+    this.name = config.getName();
+    this.regionElements = config.getCustomRegionElements();
+    this.regions = config.getRegions();
+    this.setGroup(config.getGroup());
+  }
+
   @Override
   public String getEndpoint() {
     return REGION_CONFIG_ENDPOINT;
@@ -467,6 +479,18 @@ public class RegionConfig extends CacheElement implements RestfulEndpoint {
     return getName();
   }
 
+  @Override
+  public boolean equals(Object that) {
+    if (this == that) {
+      return true;
+    }
+    if (that == null || getClass() != that.getClass()) {
+      return false;
+    }
+    RegionConfig config = (RegionConfig) that;
+    return Objects.equal(getName(), config.getName()) &&
+        Objects.equal(getType(), config.getType());
+  }
 
   /**
    * <p>
diff --git a/geode-management/src/main/java/org/apache/geode/management/api/ClusterManagementResult.java b/geode-management/src/main/java/org/apache/geode/management/api/ClusterManagementResult.java
index 0f825f2..17fc775 100644
--- a/geode-management/src/main/java/org/apache/geode/management/api/ClusterManagementResult.java
+++ b/geode-management/src/main/java/org/apache/geode/management/api/ClusterManagementResult.java
@@ -22,7 +22,7 @@ import java.util.Map;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 
 import org.apache.geode.annotations.Experimental;
-import org.apache.geode.cache.configuration.CacheElement;
+import org.apache.geode.management.configuration.RuntimeCacheElement;
 
 @Experimental
 public class ClusterManagementResult {
@@ -59,7 +59,7 @@ public class ClusterManagementResult {
   private StatusCode statusCode = StatusCode.OK;
   private String statusMessage;
 
-  private List<CacheElement> result = new ArrayList<>();
+  private List<RuntimeCacheElement> result = new ArrayList<>();
 
   public ClusterManagementResult() {}
 
@@ -109,11 +109,11 @@ public class ClusterManagementResult {
     return statusCode;
   }
 
-  public List<CacheElement> getResult() {
+  public List<RuntimeCacheElement> getResult() {
     return result;
   }
 
-  public void setResult(List<CacheElement> result) {
+  public void setResult(List<RuntimeCacheElement> result) {
     this.result = result;
   }
 }
diff --git a/geode-management/src/main/java/org/apache/geode/management/configuration/MemberConfig.java b/geode-management/src/main/java/org/apache/geode/management/configuration/MemberConfig.java
index 4f488db..f4697f5 100644
--- a/geode-management/src/main/java/org/apache/geode/management/configuration/MemberConfig.java
+++ b/geode-management/src/main/java/org/apache/geode/management/configuration/MemberConfig.java
@@ -22,7 +22,7 @@ import org.apache.geode.cache.configuration.CacheElement;
 import org.apache.geode.management.api.RestfulEndpoint;
 
 @Experimental
-public class MemberConfig extends CacheElement implements RestfulEndpoint {
+public class MemberConfig extends CacheElement implements RuntimeCacheElement, RestfulEndpoint {
 
   private static final long serialVersionUID = -6262538068604902018L;
 
@@ -92,4 +92,8 @@ public class MemberConfig extends CacheElement implements RestfulEndpoint {
   public String getId() {
     return id;
   }
+
+  public List<String> getGroups() {
+    return groups;
+  }
 }
diff --git a/geode-management/src/main/java/org/apache/geode/management/configuration/RuntimeCacheElement.java b/geode-management/src/main/java/org/apache/geode/management/configuration/RuntimeCacheElement.java
new file mode 100644
index 0000000..b15e199
--- /dev/null
+++ b/geode-management/src/main/java/org/apache/geode/management/configuration/RuntimeCacheElement.java
@@ -0,0 +1,36 @@
+/*
+ * 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.geode.management.configuration;
+
+import java.io.Serializable;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlTransient;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
+import org.apache.geode.lang.Identifiable;
+
+@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "class")
+public interface RuntimeCacheElement extends Identifiable<String>, Serializable {
+  @XmlTransient
+  List<String> getGroups();
+
+  @XmlTransient
+  @JsonIgnore
+  String getGroup();
+}
diff --git a/geode-management/src/main/java/org/apache/geode/management/configuration/RuntimeRegionConfig.java b/geode-management/src/main/java/org/apache/geode/management/configuration/RuntimeRegionConfig.java
new file mode 100644
index 0000000..da4367e
--- /dev/null
+++ b/geode-management/src/main/java/org/apache/geode/management/configuration/RuntimeRegionConfig.java
@@ -0,0 +1,45 @@
+/*
+ * 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.geode.management.configuration;
+
+
+import java.util.List;
+
+import org.apache.geode.annotations.Experimental;
+import org.apache.geode.cache.configuration.RegionConfig;
+
+@Experimental
+public class RuntimeRegionConfig extends RegionConfig implements RuntimeCacheElement {
+  private long entryCount;
+
+  public RuntimeRegionConfig() {}
+
+  public RuntimeRegionConfig(RegionConfig config) {
+    super(config);
+  }
+
+  public long getEntryCount() {
+    return entryCount;
+  }
+
+  public void setEntryCount(long entrySize) {
+    this.entryCount = entrySize;
+  }
+
+  public List<String> getGroups() {
+    return groups;
+  }
+}
diff --git a/geode-management/src/test/java/org/apache/geode/cache/configuration/CacheElementJsonMappingTest.java b/geode-management/src/test/java/org/apache/geode/cache/configuration/CacheElementJsonMappingTest.java
index c244e00..2d2be4f 100644
--- a/geode-management/src/test/java/org/apache/geode/cache/configuration/CacheElementJsonMappingTest.java
+++ b/geode-management/src/test/java/org/apache/geode/cache/configuration/CacheElementJsonMappingTest.java
@@ -26,13 +26,15 @@ import org.junit.Test;
 
 import org.apache.geode.management.api.ClusterManagementResult;
 import org.apache.geode.management.configuration.MemberConfig;
+import org.apache.geode.management.configuration.RuntimeCacheElement;
+import org.apache.geode.management.configuration.RuntimeRegionConfig;
 import org.apache.geode.util.internal.GeodeJsonMapper;
 
 public class CacheElementJsonMappingTest {
   private static ObjectMapper mapper = GeodeJsonMapper.getMapper();
 
   private static MemberConfig member;
-  private static RegionConfig region;
+  private static RuntimeRegionConfig region;
 
   @BeforeClass
   public static void beforeClass() throws Exception {
@@ -40,7 +42,7 @@ public class CacheElementJsonMappingTest {
     member.setId("server");
     member.setPid("123");
 
-    region = new RegionConfig();
+    region = new RuntimeRegionConfig();
     region.setName("test");
   }
 
@@ -79,7 +81,7 @@ public class CacheElementJsonMappingTest {
   @Test
   public void serializeResult() throws Exception {
     ClusterManagementResult result = new ClusterManagementResult();
-    List<CacheElement> elements = new ArrayList<>();
+    List<RuntimeCacheElement> elements = new ArrayList<>();
     elements.add(region);
     elements.add(member);
     result.setResult(elements);
@@ -108,4 +110,50 @@ public class CacheElementJsonMappingTest {
     String json = mapper.writeValueAsString(region);
     assertThat(json).doesNotContain("\"group\"");
   }
+
+  @Test
+  public void group() throws Exception {
+    String json = "{'name':'test','group':'group1'}";
+    RegionConfig regionConfig = mapper.readValue(json, RegionConfig.class);
+    assertThat(regionConfig.getGroup()).isEqualTo("group1");
+  }
+
+  @Test
+  public void groups() throws Exception {
+    String json = "{'name':'test','groups':['group1','group2']}";
+    RuntimeRegionConfig regionConfig = mapper.readValue(json, RuntimeRegionConfig.class);
+    assertThat(regionConfig.getGroups()).containsExactlyInAnyOrder("group1", "group2");
+  }
+
+  @Test
+  public void serializeGroup() throws Exception {
+    RegionConfig config = new RegionConfig();
+    config.setName("test");
+    config.setGroup("group1");
+    String json = mapper.writeValueAsString(config);
+    System.out.println(json);
+    assertThat(json)
+        .contains("\"group\":\"group1\"");
+  }
+
+  @Test
+  public void serializeMultipleGroup() throws Exception {
+    RuntimeRegionConfig config = new RuntimeRegionConfig();
+    config.setName("test");
+    config.getGroups().add("group1");
+    config.getGroups().add("group2");
+    String json = mapper.writeValueAsString(config);
+    System.out.println(json);
+    assertThat(json).contains("\"groups\":[\"group1\",\"group2\"]").doesNotContain("\"group\"");
+  }
+
+  @Test
+  public void serializeGroupCluster() throws Exception {
+    RegionConfig config = new RegionConfig();
+    config.setName("test");
+    config.setGroup("cluster");
+    String json = mapper.writeValueAsString(config);
+    System.out.println(json);
+    assertThat(json).contains("\"group\":\"cluster\"");
+  }
 }
diff --git a/geode-management/src/test/java/org/apache/geode/cache/configuration/CacheElementTest.java b/geode-management/src/test/java/org/apache/geode/cache/configuration/CacheElementTest.java
new file mode 100644
index 0000000..b3d075b
--- /dev/null
+++ b/geode-management/src/test/java/org/apache/geode/cache/configuration/CacheElementTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.geode.cache.configuration;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import org.apache.geode.management.configuration.RuntimeCacheElement;
+import org.apache.geode.management.configuration.RuntimeRegionConfig;
+import org.apache.geode.util.internal.GeodeJsonMapper;
+
+public class CacheElementTest {
+
+  private CacheElement element;
+  private RuntimeCacheElement runtime;
+
+  private static ObjectMapper mapper;
+  private String json;
+
+  @BeforeClass
+  public static void beforeClass() {
+    mapper = GeodeJsonMapper.getMapper();
+  }
+
+  @Before
+  public void before() throws Exception {
+    element = new RegionConfig();
+    runtime = new RuntimeRegionConfig();
+  }
+
+  @Test
+  public void plainRegionConfig() throws Exception {
+    assertThat(element.getGroup()).isNull();
+    assertThat(element.getConfigGroup()).isEqualTo("cluster");
+    json = mapper.writeValueAsString(element);
+    System.out.println(json);
+    assertThat(json).doesNotContain("group").doesNotContain("groups");
+  }
+
+  @Test
+  public void plainRuntimeRegionConfig() throws Exception {
+    assertThat(runtime.getGroup()).isNull();
+    assertThat(runtime.getGroups()).isNotNull().isEmpty();
+    json = mapper.writeValueAsString(runtime);
+    System.out.println(json);
+    assertThat(json).doesNotContain("group").doesNotContain("groups");
+  }
+
+
+  @Test
+  public void setterRegionConfigGroup() throws Exception {
+    element.setGroup("group1");
+    assertThat(element.getGroup()).isEqualTo("group1");
+    assertThat(element.getConfigGroup()).isEqualTo("group1");
+    json = mapper.writeValueAsString(element);
+    System.out.println(json);
+    assertThat(json).contains("\"group\":\"group1\"").doesNotContain("groups");
+  }
+
+  @Test
+  public void setterRuntimeRegionConfig() throws Exception {
+    runtime.getGroups().add("group1");
+    assertThat(runtime.getGroup()).isEqualTo("group1");
+    json = mapper.writeValueAsString(runtime);
+    System.out.println(json);
+    assertThat(json).contains("\"groups\":[\"group1\"]").doesNotContain("\"group\"");
+  }
+
+  @Test
+  public void copy() throws Exception {
+    RegionConfig config = new RegionConfig();
+    config.setName("test");
+    runtime = new RuntimeRegionConfig(config);
+    assertThat(runtime.getGroup()).isNull();
+    assertThat(runtime.getGroups()).hasSize(0);
+  }
+
+  @Test
+  public void setGroup() throws Exception {
+    element.setGroup("group1");
+    assertThat(element.getGroup()).isEqualTo("group1");
+    element.setGroup("");
+    assertThat(element.getGroup()).isNull();
+  }
+}
diff --git a/geode-web-management/src/distributedTest/java/org/apache/geode/management/client/ClientClusterManagementServiceDUnitTest.java b/geode-web-management/src/distributedTest/java/org/apache/geode/management/client/ClientClusterManagementServiceDUnitTest.java
index c98b445..2d14160 100644
--- a/geode-web-management/src/distributedTest/java/org/apache/geode/management/client/ClientClusterManagementServiceDUnitTest.java
+++ b/geode-web-management/src/distributedTest/java/org/apache/geode/management/client/ClientClusterManagementServiceDUnitTest.java
@@ -19,8 +19,6 @@ package org.apache.geode.management.client;
 import static org.apache.geode.test.junit.assertions.ClusterManagementResultAssert.assertManagementResult;
 import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
 
-import java.util.List;
-
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -32,7 +30,6 @@ import org.springframework.test.context.junit4.SpringRunner;
 import org.springframework.test.context.web.WebAppConfiguration;
 import org.springframework.web.context.WebApplicationContext;
 
-import org.apache.geode.cache.configuration.CacheElement;
 import org.apache.geode.cache.configuration.RegionConfig;
 import org.apache.geode.cache.configuration.RegionType;
 import org.apache.geode.management.api.ClusterManagementResult;
@@ -68,7 +65,7 @@ public class ClientClusterManagementServiceDUnitTest {
 
   @Test
   @WithMockUser
-  public void createAndListRegion() {
+  public void createRegion() {
     RegionConfig region = new RegionConfig();
     region.setName("customer");
     region.setType(RegionType.REPLICATE);
@@ -78,16 +75,6 @@ public class ClientClusterManagementServiceDUnitTest {
     // in StressNewTest, this will be run multiple times without restarting the locator
     assertManagementResult(result).hasStatusCode(ClusterManagementResult.StatusCode.OK,
         ClusterManagementResult.StatusCode.ENTITY_EXISTS);
-
-    // list region when regions are not created in a group
-    RegionConfig noFilter = new RegionConfig();
-    ClusterManagementResult list = client.list(noFilter);
-    List<CacheElement> regions = list.getResult();
-    assertThat(regions.size()).isEqualTo(1);
-    RegionConfig cacheElement = (RegionConfig) regions.get(0);
-    assertThat(cacheElement.getId()).isEqualTo("customer");
-    assertThat(cacheElement.getType()).isEqualTo("REPLICATE");
-    assertThat(cacheElement.getConfigGroup()).isEqualTo("cluster");
   }
 
 
diff --git a/geode-web-management/src/integrationTest/java/org/apache/geode/management/internal/rest/RegionManagementIntegrationTest.java b/geode-web-management/src/integrationTest/java/org/apache/geode/management/internal/rest/RegionManagementIntegrationTest.java
index 35c81d3..b193e1a 100644
--- a/geode-web-management/src/integrationTest/java/org/apache/geode/management/internal/rest/RegionManagementIntegrationTest.java
+++ b/geode-web-management/src/integrationTest/java/org/apache/geode/management/internal/rest/RegionManagementIntegrationTest.java
@@ -34,6 +34,7 @@ import org.apache.geode.cache.configuration.RegionType;
 import org.apache.geode.management.api.ClusterManagementResult;
 import org.apache.geode.management.api.ClusterManagementService;
 import org.apache.geode.management.client.ClusterManagementServiceProvider;
+import org.apache.geode.management.configuration.RuntimeRegionConfig;
 
 @RunWith(SpringRunner.class)
 @ContextConfiguration(locations = {"classpath*:WEB-INF/geode-management-servlet.xml"},
@@ -94,6 +95,18 @@ public class RegionManagementIntegrationTest {
   }
 
   @Test
+  public void invalidConfigObject() throws Exception {
+    RuntimeRegionConfig regionConfig = new RuntimeRegionConfig();
+    regionConfig.setName("customers");
+    regionConfig.setGroup("group1");
+
+    assertManagementResult(client.create(regionConfig))
+        .failed()
+        .hasStatusCode(ClusterManagementResult.StatusCode.ILLEGAL_ARGUMENT)
+        .containsStatusMessage("Configuration type RuntimeRegionConfig is not supported");
+  }
+
+  @Test
   @WithMockUser
   public void ping() throws Exception {
     context.perform(get("/v2/ping"))