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 2018/12/06 19:13:34 UTC

[geode] 01/03: GEODE-5971: Refactor CreateRegionCommand to extend SingleGfshCommand

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

commit e9f5cd35b64ffa63ca6ebddee8aba3ef91a76d4b
Author: Aditya Anchuri <aa...@pivotal.io>
AuthorDate: Thu Nov 8 10:06:36 2018 -0800

    GEODE-5971: Refactor CreateRegionCommand to extend SingleGfshCommand
    
    - Store config via generating a RegionConfig object rather than an XML
    entity
    
    Signed-off-by: Peter Tran <pt...@pivotal.io>
    Signed-off-by: Aditya Anchuri <aa...@pivotal.io>
    Signed-off-by: Jens Deppe <jd...@pivotal.io>
---
 .gitignore                                         |   1 +
 .../cli/commands/ConfigureEvictionThroughGfsh.java |   6 +-
 .../integrationTest/resources/assembly_content.txt |   2 +-
 geode-core/build.gradle                            |   1 -
 .../CreateDefinedIndexesCommandDUnitTest.java      |  13 +-
 .../cli/commands/CreateRegionCommandDUnitTest.java | 120 +++-
 ...egionCommandPersistsConfigurationDUnitTest.java | 789 +++++++++++++++++++++
 .../ImportOldClusterConfigDUnitTest.java           |   2 +-
 .../java/org/apache/geode/cache/DataPolicy.java    |   7 +
 .../org/apache/geode/cache/EvictionAttributes.java |  39 +
 .../org/apache/geode/cache/ExpirationAction.java   |  15 +
 .../apache/geode/cache/ExpirationAttributes.java   |  16 +-
 .../apache/geode/cache/PartitionAttributes.java    |  19 +
 .../cache/configuration/RegionAttributesType.java  |  11 +
 .../InternalConfigurationPersistenceService.java   |  23 -
 .../apache/geode/internal/config/JAXBService.java  |   4 +-
 .../internal/cli/commands/AlterRegionCommand.java  |   3 +-
 .../internal/cli/commands/CreateRegionCommand.java | 123 +++-
 .../cli/domain/RegionAttributeGetFunction.java     |  22 +
 .../cli/domain/RegionAttributeSetFunction.java     |  22 +
 .../internal/cli/domain/RegionConfigFactory.java   | 331 +++++++++
 .../cli/functions/RegionAlterFunction.java         |  12 +-
 .../cli/functions/RegionCreateFunction.java        | 157 ++--
 .../internal/cli/functions/RegionFunctionArgs.java |  86 ++-
 .../management/internal/cli/i18n/CliStrings.java   |   2 +
 .../management/internal/cli/util/RegionPath.java   |  16 +
 .../geode/redis/internal/RegionProvider.java       |  20 +-
 .../cli/commands/CreateRegionCommandTest.java      |  26 +-
 .../cli/domain/RegionConfigFactoryTest.java        | 273 +++++++
 .../cli/functions/RegionFunctionArgsTest.java      |  45 +-
 .../cli/commands/CreateRegionCommandDUnitTest.java |  55 +-
 31 files changed, 2061 insertions(+), 200 deletions(-)

diff --git a/.gitignore b/.gitignore
index 47d635b..1525f4a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,6 +28,7 @@ out/
 *.orig
 geode-pulse/screenshots/
 /jpf.properties
+geode-assembly/assembly_content.txt
 
 .git-together
 bin/
diff --git a/geode-assembly/src/acceptanceTest/java/org/apache/geode/management/internal/cli/commands/ConfigureEvictionThroughGfsh.java b/geode-assembly/src/acceptanceTest/java/org/apache/geode/management/internal/cli/commands/ConfigureEvictionThroughGfsh.java
index 125d327..29ea9a5 100644
--- a/geode-assembly/src/acceptanceTest/java/org/apache/geode/management/internal/cli/commands/ConfigureEvictionThroughGfsh.java
+++ b/geode-assembly/src/acceptanceTest/java/org/apache/geode/management/internal/cli/commands/ConfigureEvictionThroughGfsh.java
@@ -57,7 +57,7 @@ public class ConfigureEvictionThroughGfsh {
             "create region --name=region6 --eviction-action=local-destroy --eviction-entry-count=1000 --type=REPLICATE_PERSISTENT")
         .expectFailure().execute(gfsh);
     assertThat(execution.getOutputText()).contains(
-        "ERROR: An Eviction Controller with local destroy eviction action is incompatible with");
+        "An Eviction Controller with local destroy eviction action is incompatible with");
 
     execution = GfshScript
         .of("connect --locator=localhost[10334]", "describe region --name=region1").execute(gfsh);
@@ -112,7 +112,7 @@ public class ConfigureEvictionThroughGfsh {
             "create region --name=region6 --eviction-action=local-destroy --eviction-max-memory=1000 --type=REPLICATE_PERSISTENT")
         .expectFailure().execute(gfsh);
     assertThat(execution.getOutputText()).contains(
-        "ERROR: An Eviction Controller with local destroy eviction action is incompatible with");
+        "An Eviction Controller with local destroy eviction action is incompatible with");
 
     execution = GfshScript
         .of("connect --locator=localhost[10334]", "describe region --name=region1").execute(gfsh);
@@ -181,7 +181,7 @@ public class ConfigureEvictionThroughGfsh {
             "create region --name=region6 --eviction-action=local-destroy --eviction-max-memory=1000 --eviction-object-sizer=MySizer --type=REPLICATE_PERSISTENT")
         .expectFailure().execute(gfsh);
     assertThat(execution.getOutputText()).contains(
-        "ERROR: An Eviction Controller with local destroy eviction action is incompatible with");
+        "An Eviction Controller with local destroy eviction action is incompatible with");
 
     execution = GfshScript
         .of("connect --locator=localhost[10334]", "describe region --name=region1").execute(gfsh);
diff --git a/geode-assembly/src/integrationTest/resources/assembly_content.txt b/geode-assembly/src/integrationTest/resources/assembly_content.txt
index 9cd4c19..f216ab3 100644
--- a/geode-assembly/src/integrationTest/resources/assembly_content.txt
+++ b/geode-assembly/src/integrationTest/resources/assembly_content.txt
@@ -957,4 +957,4 @@ tools/Modules/Apache_Geode_Modules-0.0.0-AppServer.zip
 tools/Modules/Apache_Geode_Modules-0.0.0-Tomcat.zip
 tools/Modules/Apache_Geode_Modules-0.0.0-tcServer.zip
 tools/Modules/Apache_Geode_Modules-0.0.0-tcServer30.zip
-tools/Pulse/geode-pulse-0.0.0.war
+tools/Pulse/geode-pulse-0.0.0.war
\ No newline at end of file
diff --git a/geode-core/build.gradle b/geode-core/build.gradle
index 3f89a6f..668b2f1 100755
--- a/geode-core/build.gradle
+++ b/geode-core/build.gradle
@@ -15,7 +15,6 @@
  * limitations under the License.
  */
 
-
 apply plugin: 'antlr'
 apply plugin: 'me.champeau.gradle.jmh'
 
diff --git a/geode-core/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/CreateDefinedIndexesCommandDUnitTest.java b/geode-core/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/CreateDefinedIndexesCommandDUnitTest.java
index 093a536..9b2df1b 100644
--- a/geode-core/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/CreateDefinedIndexesCommandDUnitTest.java
+++ b/geode-core/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/CreateDefinedIndexesCommandDUnitTest.java
@@ -15,6 +15,7 @@
 
 package org.apache.geode.management.internal.cli.commands;
 
+import static org.apache.geode.management.internal.cli.result.model.ResultModel.MEMBER_STATUS_SECTION;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import java.util.List;
@@ -188,13 +189,15 @@ public class CreateDefinedIndexesCommandDUnitTest {
     CommandResult result = gfsh
         .executeCommand("create region --name=" + region1Name + " --type=REPLICATE --group=group1");
     assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
-    assertThat(result.getMapFromTableContent("0", "0").get("Member")).contains("server-1",
+    assertThat(result.getMapFromTableContent(MEMBER_STATUS_SECTION).get("Member")).contains(
+        "server-1",
         "server-2");
 
     result = gfsh
         .executeCommand("create region --name=" + region2Name + " --type=REPLICATE --group=group1");
     assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
-    assertThat(result.getMapFromTableContent("0", "0").get("Member")).contains("server-1",
+    assertThat(result.getMapFromTableContent(MEMBER_STATUS_SECTION).get("Member")).contains(
+        "server-1",
         "server-2");
 
     VMProvider.invokeInEveryMember(() -> {
@@ -251,13 +254,15 @@ public class CreateDefinedIndexesCommandDUnitTest {
     CommandResult result = gfsh
         .executeCommand("create region --name=" + region1Name + " --type=REPLICATE --group=group1");
     assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
-    assertThat(result.getMapFromTableContent("0", "0").get("Member")).contains("server-1",
+    assertThat(result.getMapFromTableContent(MEMBER_STATUS_SECTION).get("Member")).contains(
+        "server-1",
         "server-2");
 
     result = gfsh
         .executeCommand("create region --name=" + region2Name + " --type=REPLICATE --group=group2");
     assertThat(result.getStatus()).isEqualTo(Result.Status.OK);
-    assertThat(result.getMapFromTableContent("0", "0").get("Member")).contains("server-3");
+    assertThat(result.getMapFromTableContent(MEMBER_STATUS_SECTION).get("Member"))
+        .contains("server-3");
 
     gfsh.executeAndAssertThat(
         "define index --name=" + index1Name + " --expression=value1 --region=" + region1Name)
diff --git a/geode-core/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/CreateRegionCommandDUnitTest.java b/geode-core/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/CreateRegionCommandDUnitTest.java
index 8d8dc19..0c50623 100644
--- a/geode-core/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/CreateRegionCommandDUnitTest.java
+++ b/geode-core/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/CreateRegionCommandDUnitTest.java
@@ -20,10 +20,12 @@ import static org.assertj.core.api.Assertions.assertThat;
 import java.io.File;
 import java.io.Serializable;
 import java.util.Arrays;
+import java.util.List;
 import java.util.stream.Collectors;
 
 import org.junit.BeforeClass;
 import org.junit.ClassRule;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
@@ -31,13 +33,19 @@ import org.junit.rules.TemporaryFolder;
 import org.junit.rules.TestName;
 
 import org.apache.geode.cache.Cache;
+import org.apache.geode.cache.Declarable;
+import org.apache.geode.cache.EntryOperation;
 import org.apache.geode.cache.PartitionResolver;
 import org.apache.geode.cache.Region;
+import org.apache.geode.cache.asyncqueue.AsyncEvent;
+import org.apache.geode.cache.asyncqueue.AsyncEventListener;
 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.util.CacheListenerAdapter;
+import org.apache.geode.compression.Compressor;
 import org.apache.geode.compression.SnappyCompressor;
+import org.apache.geode.internal.cache.InternalRegion;
 import org.apache.geode.internal.cache.PartitionedRegion;
 import org.apache.geode.internal.cache.RegionEntryContext;
 import org.apache.geode.test.compiler.JarBuilder;
@@ -59,6 +67,37 @@ public class CreateRegionCommandDUnitTest {
       implements Serializable {
   }
 
+  public static class DummyAEQListener implements AsyncEventListener, Declarable {
+    @Override
+    public boolean processEvents(List<AsyncEvent> events) {
+      return false;
+    }
+  }
+
+  public static class DummyPartitionResolver implements PartitionResolver, Declarable {
+    @Override
+    public Object getRoutingObject(EntryOperation opDetails) {
+      return null;
+    }
+
+    @Override
+    public String getName() {
+      return "dummy";
+    }
+  }
+
+  public static class DummyCompressor implements Compressor, Declarable {
+    @Override
+    public byte[] compress(byte[] input) {
+      return new byte[0];
+    }
+
+    @Override
+    public byte[] decompress(byte[] input) {
+      return new byte[0];
+    }
+  }
+
   @ClassRule
   public static ClusterStartupRule lsRule = new ClusterStartupRule();
 
@@ -230,6 +269,7 @@ public class CreateRegionCommandDUnitTest {
     gfsh.executeAndAssertThat("destroy region --name=/TEMPLATE").statusIsSuccess();
   }
 
+
   @Test
   public void cannotSetRegionExpirationForPartitionedTemplate() {
     gfsh.executeAndAssertThat("create region --name=/TEMPLATE --type=PARTITION")
@@ -431,11 +471,12 @@ public class CreateRegionCommandDUnitTest {
   }
 
   @Test
-  public void startWithReplicateProxyThenPartitionRegion() {
+  public void startWithReplicateProxyThenPartitionRegion() throws Exception {
     String regionName = testName.getMethodName();
     gfsh.executeAndAssertThat(
         "create region --type=REPLICATE_PROXY --group=group1 --name=" + regionName)
         .statusIsSuccess().tableHasRowWithValues("Member", "server-1");
+
     gfsh.executeAndAssertThat("create region --type=PARTITION --group=group2 --name=" + regionName)
         .statusIsError().containsOutput("The existing region is not a partitioned region");
     gfsh.executeAndAssertThat(
@@ -560,6 +601,83 @@ public class CreateRegionCommandDUnitTest {
         .containsOutput("Region /startWithLocalRegion already exists on the cluster");
   }
 
+  @Test
+  public void cannotDisableCloningWhenCompressorIsSet() {
+    String regionName = testName.getMethodName();
+    gfsh.executeAndAssertThat("create region"
+        + " --name=" + regionName
+        + " --type=REPLICATE"
+        + " --compressor=" + DummyCompressor.class.getName()
+        + " --enable-cloning=false").statusIsError();
+  }
+
+  /**
+   * Ignored this test until we refactor the FetchRegionAttributesFunction to not use
+   * AttributesFactory, and instead use RegionConfig, which we will do as part of implementing
+   * GEODE-6103
+   */
+  @Ignore
+  @Test
+  public void testCreateRegionFromTemplateWithAsyncEventListeners() {
+    String queueId = "queue1";
+    gfsh.executeAndAssertThat(
+        "create async-event-queue --id=" + queueId
+            + " --listener=" + CreateRegionCommandDUnitTest.DummyAEQListener.class.getName())
+        .statusIsSuccess();
+
+    String regionName = testName.getMethodName();
+    gfsh.executeAndAssertThat(
+        "create region --name=" + regionName
+            + " --type=REPLICATE"
+            + " --async-event-queue-id=" + queueId)
+        .statusIsSuccess();
+
+    gfsh.executeAndAssertThat(
+        "create region --name=" + regionName + "-from-template"
+            + " --template-region=" + regionName)
+        .statusIsSuccess();
+
+    server1.invoke(() -> {
+      Region regionFromTemplate = ClusterStartupRule.getCache()
+          .getRegion(regionName + "-from-template");
+      assertThat(regionFromTemplate).isNotNull();
+      assertThat(((InternalRegion) regionFromTemplate).getAsyncEventQueueIds())
+          .contains(queueId);
+    });
+  }
+
+  /**
+   * Ignored this test until we refactor the FetchRegionAttributesFunction to not use
+   * AttributesFactory, and instead use RegionConfig, which we will do as part of implementing
+   * GEODE-6103
+   */
+  @Ignore
+  @Test
+  public void testCreateRegionFromTemplateWithPartitionResolver() {
+    String regionName = testName.getMethodName();
+    String regionFromTemplateName = regionName + "-from-template";
+
+    gfsh.executeAndAssertThat("create region"
+        + " --name=" + regionName
+        + " --type=PARTITION"
+        + " --partition-resolver=" + DummyPartitionResolver.class.getName()).statusIsSuccess();
+    gfsh.executeAndAssertThat("create region"
+        + " --name=" + regionFromTemplateName
+        + " --template-region=" + regionName).statusIsSuccess();
+
+    server1.invoke(() -> {
+      Region regionFromTemplate = ClusterStartupRule.getCache()
+          .getRegion(regionName + "-from-template");
+      assertThat(regionFromTemplate).isNotNull();
+      assertThat(((InternalRegion) regionFromTemplate).getPartitionAttributes()
+          .getPartitionResolver())
+              .isNotNull();
+      assertThat(((InternalRegion) regionFromTemplate).getPartitionAttributes()
+          .getPartitionResolver().getName())
+              .isEqualTo(DummyPartitionResolver.class.getName());
+    });
+  }
+
   private String getUniversalClassCode(String classname) {
     String code = "package io.pivotal;" + "import org.apache.geode.cache.CacheLoader;"
         + "import org.apache.geode.cache.CacheLoaderException;"
diff --git a/geode-core/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/CreateRegionCommandPersistsConfigurationDUnitTest.java b/geode-core/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/CreateRegionCommandPersistsConfigurationDUnitTest.java
new file mode 100644
index 0000000..275fac9
--- /dev/null
+++ b/geode-core/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/CreateRegionCommandPersistsConfigurationDUnitTest.java
@@ -0,0 +1,789 @@
+/*
+ * 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.cli.commands;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.TestName;
+
+import org.apache.geode.cache.CacheLoader;
+import org.apache.geode.cache.CacheLoaderException;
+import org.apache.geode.cache.CustomExpiry;
+import org.apache.geode.cache.Declarable;
+import org.apache.geode.cache.EntryOperation;
+import org.apache.geode.cache.ExpirationAttributes;
+import org.apache.geode.cache.LoaderHelper;
+import org.apache.geode.cache.PartitionResolver;
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.configuration.CacheConfig;
+import org.apache.geode.cache.configuration.CacheElement;
+import org.apache.geode.cache.configuration.ExpirationAttributesType;
+import org.apache.geode.cache.configuration.RegionAttributesDataPolicy;
+import org.apache.geode.cache.configuration.RegionAttributesType;
+import org.apache.geode.cache.configuration.RegionConfig;
+import org.apache.geode.cache.util.CacheListenerAdapter;
+import org.apache.geode.cache.util.CacheWriterAdapter;
+import org.apache.geode.compression.Compressor;
+import org.apache.geode.distributed.internal.InternalConfigurationPersistenceService;
+import org.apache.geode.test.dunit.rules.ClusterStartupRule;
+import org.apache.geode.test.dunit.rules.MemberVM;
+import org.apache.geode.test.junit.categories.RegionsTest;
+import org.apache.geode.test.junit.rules.GfshCommandRule;
+import org.apache.geode.test.junit.rules.serializable.SerializableTestName;
+
+@Category({RegionsTest.class})
+public class CreateRegionCommandPersistsConfigurationDUnitTest {
+
+  private MemberVM locator, server1;
+
+  @Rule
+  public ClusterStartupRule clusterRule = new ClusterStartupRule();
+
+  @Rule
+  public GfshCommandRule gfsh = new GfshCommandRule();
+
+  @Rule
+  public TestName testName = new SerializableTestName();
+
+  public static class DummyCacheListener extends CacheListenerAdapter {
+  }
+
+  public static class DummyCustomExpiry implements CustomExpiry, Declarable {
+    @Override
+    public ExpirationAttributes getExpiry(Region.Entry entry) {
+      return null;
+    }
+  }
+
+  public static class DummyPartitionResolver implements PartitionResolver, Declarable {
+    @Override
+    public Object getRoutingObject(EntryOperation opDetails) {
+      return null;
+    }
+
+    @Override
+    public String getName() {
+      return "dummy";
+    }
+  }
+
+  public static class DummyCompressor implements Compressor, Declarable {
+    @Override
+    public byte[] compress(byte[] input) {
+      return new byte[0];
+    }
+
+    @Override
+    public byte[] decompress(byte[] input) {
+      return new byte[0];
+    }
+  }
+
+  public static class DummyCacheLoader implements CacheLoader, Declarable {
+    @Override
+    public Object load(LoaderHelper helper) throws CacheLoaderException {
+      return null;
+    }
+  }
+
+  public static class DummyCacheWriter extends CacheWriterAdapter {
+  }
+
+  @Before
+  public void before() throws Exception {
+    locator = clusterRule.startLocatorVM(0);
+    server1 = clusterRule.startServerVM(1, locator.getPort());
+
+    gfsh.connectAndVerify(locator);
+  }
+
+  @Test
+  public void createRegionPersistsEmptyConfig() {
+    String regionName = testName.getMethodName();
+    gfsh.executeAndAssertThat("create region --name=" + regionName + " --type=REPLICATE")
+        .statusIsSuccess();
+
+    server1.stop();
+    server1 = clusterRule.startServerVM(1, "group1", locator.getPort());
+
+    gfsh.executeAndAssertThat("list regions")
+        .statusIsSuccess().containsOutput(regionName);
+
+    locator.invoke(() -> {
+      InternalConfigurationPersistenceService cc =
+          ClusterStartupRule.getLocator().getConfigurationPersistenceService();
+      CacheConfig config = cc.getCacheConfig("cluster");
+
+      List<RegionConfig> regions = config.getRegions();
+      assertThat(regions).isNotEmpty();
+      RegionConfig regionConfig = regions.get(0);
+      assertThat(regionConfig).isNotNull();
+      assertThat(regionConfig.getName()).isEqualTo(regionName);
+      assertThat(regionConfig.getIndexes()).isEmpty();
+      assertThat(regionConfig.getRegions()).isEmpty();
+      assertThat(regionConfig.getEntries()).isEmpty();
+      assertThat(regionConfig.getCustomRegionElements()).isEmpty();
+    });
+  }
+
+  @Test
+  public void createRegionPersistsConfigParams() {
+    String regionName = testName.getMethodName();
+    gfsh.executeAndAssertThat("create region --name=" + regionName + " --type=PARTITION"
+        + " --enable-statistics=true" + " --enable-async-conflation=true"
+        + " --entry-idle-time-expiration=100").statusIsSuccess();
+
+    server1.stop();
+    server1 = clusterRule.startServerVM(1, "group1", locator.getPort());
+
+    gfsh.executeAndAssertThat("list regions")
+        .statusIsSuccess().containsOutput(regionName);
+
+    locator.invoke(() -> {
+      InternalConfigurationPersistenceService cc =
+          ClusterStartupRule.getLocator().getConfigurationPersistenceService();
+      CacheConfig config = cc.getCacheConfig("cluster");
+
+      List<RegionConfig> regions = config.getRegions();
+      assertThat(regions).isNotEmpty();
+      RegionConfig regionConfig = regions.get(0);
+      assertThat(regionConfig).isNotNull();
+      assertThat(regionConfig.getName()).isEqualTo(regionName);
+      assertThat(regionConfig.getRegionAttributes()).hasSize(1);
+
+      RegionAttributesType attr = regionConfig.getRegionAttributes().get(0);
+      assertThat(attr.isStatisticsEnabled()).isTrue();
+      assertThat(attr.isEnableAsyncConflation()).isTrue();
+
+      ExpirationAttributesType entryIdleTimeExp = attr.getEntryIdleTime().getExpirationAttributes();
+      assertThat(entryIdleTimeExp.getTimeout()).isEqualTo("100");
+    });
+
+    server1.invoke(() -> {
+      Region<?, ?> region = ClusterStartupRule.getCache().getRegion(regionName);
+      assertThat(region.getAttributes().getStatisticsEnabled())
+          .describedAs("Expecting statistics to be enabled")
+          .isTrue();
+      assertThat(region.getAttributes().getEnableAsyncConflation())
+          .describedAs("Expecting async conflation to be enabled")
+          .isTrue();
+      assertThat(region.getAttributes().getEntryIdleTimeout().getTimeout())
+          .describedAs("Expecting entry idle time exp timeout to be 100")
+          .isEqualTo(100);
+    });
+  }
+
+  @Test
+  public void createRegionFromTemplateCreatesCorrectConfig() {
+    String regionName = testName.getMethodName();
+    String templateRegionName = regionName + "_template";
+    gfsh.executeAndAssertThat("create region"
+        + " --name=" + templateRegionName
+        + " --type=PARTITION"
+        + " --cache-listener=" + DummyCacheListener.class.getName()
+        + " --enable-statistics=true"
+        + " --enable-async-conflation=true"
+        + " --entry-idle-time-expiration=100").statusIsSuccess();
+
+    gfsh.executeAndAssertThat(
+        "create region --name=" + regionName + " --template-region=" + templateRegionName);
+
+    locator.invoke(() -> {
+      InternalConfigurationPersistenceService cc =
+          ClusterStartupRule.getLocator().getConfigurationPersistenceService();
+      CacheConfig config = cc.getCacheConfig("cluster");
+
+      List<RegionConfig> regions = config.getRegions();
+      assertThat(regions).isNotEmpty();
+      RegionConfig regionConfig = CacheElement.findElement(config.getRegions(), regionName);
+
+      assertThat(regionConfig).isNotNull();
+      assertThat(regionConfig.getName()).isEqualTo(regionName);
+      assertThat(regionConfig.getRegionAttributes()).hasSize(1);
+
+      RegionAttributesType attr = regionConfig.getRegionAttributes().get(0);
+      assertThat(attr.isStatisticsEnabled()).isTrue();
+      assertThat(attr.isEnableAsyncConflation()).isTrue();
+
+      ExpirationAttributesType entryIdleTimeExp = attr.getEntryIdleTime().getExpirationAttributes();
+      assertThat(entryIdleTimeExp.getTimeout()).isEqualTo("100");
+    });
+  }
+
+  @Test
+  public void createRegionAndValidateAllConfigIsPersistedForReplicatedRegion() {
+    String regionName = testName.getMethodName();
+    gfsh.executeAndAssertThat("create region"
+        + " --name=" + regionName
+        + " --type=REPLICATE"
+        + " --cache-listener=" + DummyCacheListener.class.getName()
+        + " --cache-loader=" + DummyCacheLoader.class.getName()
+        + " --cache-writer=" + DummyCacheWriter.class.getName()
+        + " --compressor=" + DummyCompressor.class.getName()
+        + " --enable-async-conflation=false"
+        + " --enable-concurrency-checks=false"
+        + " --enable-multicast=false"
+        + " --concurrency-level=1"
+        + " --enable-statistics=true"
+        + " --enable-subscription-conflation=true"
+        + " --entry-idle-time-expiration=100"
+        + " --entry-idle-time-expiration-action=local-destroy"
+        + " --entry-time-to-live-expiration=200"
+        + " --entry-time-to-live-expiration-action=local-destroy"
+        + " --eviction-action=local-destroy"
+        + " --key-constraint=" + Object.class.getName()
+        + " --off-heap=false"
+        + " --region-idle-time-expiration=100"
+        + " --region-idle-time-expiration-action=local-destroy"
+        + " --region-time-to-live-expiration=200"
+        + " --region-time-to-live-expiration-action=local-destroy"
+        + " --value-constraint=" + Object.class.getName()).statusIsSuccess();
+
+    String regionNameFromTemplate = regionName + "-from-template";
+    gfsh.executeAndAssertThat("create region --name=" + regionNameFromTemplate
+        + " --template-region=" + regionName)
+        .statusIsSuccess();
+
+    locator.invoke(() -> {
+      InternalConfigurationPersistenceService cc =
+          ClusterStartupRule.getLocator().getConfigurationPersistenceService();
+      CacheConfig config = cc.getCacheConfig("cluster");
+
+      List<RegionConfig> regions = config.getRegions();
+      assertThat(regions).isNotEmpty();
+      assertThat(regions).hasSize(2);
+
+      List<String> regionNames = Arrays.asList(regionName, regionNameFromTemplate);
+      regionNames.forEach(name -> {
+        RegionConfig regionConfig = CacheElement.findElement(config.getRegions(), name);
+        assertThat(regionConfig).isNotNull();
+        assertThat(regionConfig.getName()).isEqualTo(name);
+        assertThat(regionConfig.getRegionAttributes())
+            .describedAs("Expecting region attributes to exist")
+            .hasSize(1);
+
+        RegionAttributesType attr = regionConfig.getRegionAttributes().get(0);
+        assertThat(attr.getCacheListeners().get(0).toString())
+            .describedAs("Expecting one cache listener for region " + name)
+            .isEqualTo(DummyCacheListener.class.getName());
+        assertThat(attr.getCacheLoader().toString())
+            .describedAs("Expecting a DummyCacheLoader for region " + name)
+            .isEqualTo(DummyCacheLoader.class.getName());
+        assertThat(attr.getCacheWriter().toString())
+            .describedAs("Expecting a DummyCacheWriter for region " + name)
+            .isEqualTo(DummyCacheWriter.class.getName());
+        assertThat(attr.getCompressor().toString())
+            .describedAs("Expecting a DummyCompressor for region " + name)
+            .isEqualTo(DummyCompressor.class.getName());
+        assertThat(attr.isEnableAsyncConflation())
+            .describedAs("Expecting async conflation to not be enabled for region "
+                + name)
+            .isFalse();
+        assertThat(attr.isConcurrencyChecksEnabled())
+            .describedAs("Expecting concurrency checks not to be enabled for region "
+                + name)
+            .isFalse();
+        assertThat(attr.isMulticastEnabled())
+            .describedAs("Expecting multicast is not enabled for region " + name)
+            .isFalse();
+        assertThat(attr.getConcurrencyLevel())
+            .describedAs("Expecting concurrency level to be 1 for region " + name)
+            .isEqualTo("1");
+        assertThat(attr.isStatisticsEnabled())
+            .describedAs("Expecting statistics to be enabled for region " + name)
+            .isTrue();
+        assertThat(attr.isCloningEnabled())
+            .describedAs("Expecting cloning to be enabled for region " + name
+                + " since compressor is provided")
+            .isTrue();
+        assertThat(attr.isEnableSubscriptionConflation())
+            .describedAs("Expecting subscription conflation to be enabled for region "
+                + name)
+            .isTrue();
+        assertThat(attr.getEntryIdleTime().getExpirationAttributes().getTimeout())
+            .describedAs("Entry idle time timeout should be 100 for region " + name)
+            .isEqualTo("100");
+        assertThat(attr.getEntryIdleTime().getExpirationAttributes().getAction())
+            .describedAs("Entry idle time expiration action should be local-destroy for region "
+                + name)
+            .isEqualTo("local-destroy");
+        assertThat(attr.getEntryTimeToLive().getExpirationAttributes().getTimeout())
+            .describedAs("Expecting entry time to live expiration to be 200 for region "
+                + name)
+            .isEqualTo("200");
+        assertThat(attr.getEntryTimeToLive().getExpirationAttributes().getAction())
+            .describedAs("Entry time to live expiration action should be local-destroy "
+                + "for region " + name)
+            .isEqualTo("local-destroy");
+        assertThat(attr.getEvictionAttributes().getLruHeapPercentage().getAction().value())
+            .describedAs("Eviction action should be local-destroy for region " + name)
+            .isEqualTo("local-destroy");
+        assertThat(attr.getKeyConstraint())
+            .describedAs("Expected key constraint to be " + Object.class.getName() +
+                " for region " + name)
+            .isEqualTo(Object.class.getName());
+        assertThat(attr.isOffHeap())
+            .describedAs("Expected off heap to be false for region " + name)
+            .isFalse();
+        assertThat(attr.getRegionIdleTime().getExpirationAttributes().getTimeout())
+            .describedAs("Expecting region idle time expiration to be 100 for region "
+                + name)
+            .isEqualTo("100");
+        assertThat(attr.getRegionIdleTime().getExpirationAttributes().getAction())
+            .describedAs("Expecting region idle time expiration action to be "
+                + "local-destroy for region " + name)
+            .isEqualTo("local-destroy");
+        assertThat(attr.getRegionTimeToLive().getExpirationAttributes().getTimeout())
+            .describedAs("Expecting region idle time timeout to be 200 for "
+                + "region " + name)
+            .isEqualTo("200");
+        assertThat(attr.getRegionTimeToLive().getExpirationAttributes().getAction())
+            .describedAs("Expecting region ttl action to be local-destroy for "
+                + "region " + name)
+            .isEqualTo("local-destroy");
+        assertThat(attr.getValueConstraint())
+            .describedAs("Expecting value constraint to be Object.class for "
+                + "region " + name)
+            .isEqualTo(Object.class.getName());
+      });
+    });
+  }
+
+  @Test
+  public void createRegionDoesNotPersistEmptyOrDefaultEvictionAttributes() {
+    String regionName = testName.getMethodName();
+    gfsh.executeAndAssertThat("create region"
+        + " --name=" + regionName
+        + " --type=REPLICATE").statusIsSuccess();
+
+    locator.invoke(() -> {
+      InternalConfigurationPersistenceService cc =
+          ClusterStartupRule.getLocator().getConfigurationPersistenceService();
+      CacheConfig config = cc.getCacheConfig("cluster");
+
+      List<RegionConfig> regions = config.getRegions();
+      assertThat(regions).isNotEmpty();
+      assertThat(regions).hasSize(1);
+
+      List<String> regionNames = Arrays.asList(regionName);
+      regionNames.forEach(name -> {
+        RegionConfig regionConfig = CacheElement.findElement(config.getRegions(), name);
+        assertThat(regionConfig).isNotNull();
+        assertThat(regionConfig.getName()).isEqualTo(name);
+        assertThat(regionConfig.getRegionAttributes())
+            .describedAs("Expecting region attributes to exist")
+            .hasSize(1);
+
+        RegionAttributesType attr = regionConfig.getRegionAttributes().get(0);
+        assertThat(attr.getEvictionAttributes())
+            .describedAs("Eviction attributes should be null for " + name)
+            .isNull();
+      });
+    });
+  }
+
+  @Test
+  public void createRegionPersistsAEQConfig() {
+    String queueId = "queue1";
+    gfsh.executeAndAssertThat(
+        "create async-event-queue --id=" + queueId
+            + " --listener=" + CreateRegionCommandDUnitTest.DummyAEQListener.class.getName())
+        .statusIsSuccess();
+
+    String regionName = testName.getMethodName();
+    gfsh.executeAndAssertThat(
+        "create region --name=" + regionName
+            + " --type=REPLICATE"
+            + " --async-event-queue-id=" + queueId)
+        .statusIsSuccess();
+
+    locator.invoke(() -> {
+      InternalConfigurationPersistenceService cc =
+          ClusterStartupRule.getLocator().getConfigurationPersistenceService();
+      CacheConfig config = cc.getCacheConfig("cluster");
+
+      List<RegionConfig> regions = config.getRegions();
+      assertThat(regions).isNotEmpty();
+      assertThat(regions).hasSize(1);
+      RegionConfig regionConfig = CacheElement.findElement(regions, regionName);
+      assertThat(regionConfig.getRegionAttributes().get(0).getAsyncEventQueueIds())
+          .contains(queueId);
+    });
+  }
+
+  @Test
+  public void createRegionWithColocation() {
+    String regionName = testName.getMethodName();
+    String colocatedRegionName = regionName + "-colocated";
+    String colocatedRegionFromTemplateName = colocatedRegionName + "-from-template";
+
+    gfsh.executeAndAssertThat("create region"
+        + " --name=" + regionName
+        + " --type=PARTITION");
+    gfsh.executeAndAssertThat("create region"
+        + " --name=" + colocatedRegionName
+        + " --colocated-with=" + regionName
+        + " --type=PARTITION");
+
+    gfsh.executeAndAssertThat("create region"
+        + " --name=" + colocatedRegionFromTemplateName
+        + " --template-region=" + colocatedRegionName);
+
+    locator.invoke(() -> {
+      InternalConfigurationPersistenceService cc =
+          ClusterStartupRule.getLocator().getConfigurationPersistenceService();
+      CacheConfig config = cc.getCacheConfig("cluster");
+
+      List<RegionConfig> regions = config.getRegions();
+      assertThat(regions).isNotEmpty();
+      assertThat(regions).hasSize(3);
+
+      RegionConfig colocatedConfig = CacheElement.findElement(regions, colocatedRegionName);
+      assertThat(
+          colocatedConfig.getRegionAttributes().get(0).getPartitionAttributes().getColocatedWith())
+              .isEqualTo("/" + regionName);
+
+      RegionConfig colocatedConfigFromTemplate = CacheElement.findElement(regions,
+          colocatedRegionFromTemplateName);
+      assertThat(
+          colocatedConfigFromTemplate.getRegionAttributes().get(0).getPartitionAttributes()
+              .getColocatedWith())
+                  .isEqualTo("/" + regionName);
+    });
+  }
+
+  @Test
+  public void createRegionPersistsDiskstores() throws Exception {
+    String regionName = testName.getMethodName();
+    String store = "Store1";
+    gfsh.executeAndAssertThat("create disk-store"
+        + " --name=" + store
+        + " --dir=/tmp/foo").statusIsSuccess();
+
+    // Give disk store time to get created
+    Thread.sleep(2000);
+
+    gfsh.executeAndAssertThat("create region"
+        + " --name=" + regionName
+        + " --type=REPLICATE_PERSISTENT"
+        + " --disk-store=" + store
+        + " --enable-synchronous-disk=true").statusIsSuccess();
+
+    String regionNameFromTemplate = regionName + "-from-template";
+    gfsh.executeAndAssertThat("create region --name=" + regionNameFromTemplate
+        + " --template-region=" + regionName)
+        .statusIsSuccess();
+
+    locator.invoke(() -> {
+      InternalConfigurationPersistenceService cc =
+          ClusterStartupRule.getLocator().getConfigurationPersistenceService();
+      CacheConfig config = cc.getCacheConfig("cluster");
+
+      List<RegionConfig> regions = config.getRegions();
+      assertThat(regions).isNotEmpty();
+      assertThat(regions).hasSize(2);
+
+      List<String> regionNames = Arrays.asList(regionName, regionNameFromTemplate);
+      regionNames.forEach(name -> {
+        RegionConfig regionConfig = CacheElement.findElement(config.getRegions(), name);
+        assertThat(regionConfig).isNotNull();
+        assertThat(regionConfig.getName()).isEqualTo(name);
+
+        RegionAttributesType regionAttributes = regionConfig.getRegionAttributes().get(0);
+        assertThat(regionAttributes.getDiskStoreName())
+            .isEqualTo(store);
+        assertThat(regionAttributes.isDiskSynchronous())
+            .isTrue();
+      });
+    });
+  }
+
+  @Test
+  public void createRegionPersistsPartitionAttributes() {
+    String regionName = testName.getMethodName();
+    String regionFromTemplateName = regionName + "-from-template";
+
+    gfsh.executeAndAssertThat("create region"
+        + " --name=" + regionName
+        + " --type=PARTITION"
+        + " --recovery-delay=1"
+        + " --local-max-memory=1000"
+        + " --redundant-copies=1"
+        + " --startup-recovery-delay=1"
+        + " --total-max-memory=100"
+        + " --total-num-buckets=1").statusIsSuccess();
+    gfsh.executeAndAssertThat("create region"
+        + " --name=" + regionFromTemplateName
+        + " --template-region=" + regionName);
+
+    locator.invoke(() -> {
+      InternalConfigurationPersistenceService cc =
+          ClusterStartupRule.getLocator().getConfigurationPersistenceService();
+      CacheConfig config = cc.getCacheConfig("cluster");
+
+      List<RegionConfig> regions = config.getRegions();
+      assertThat(regions).isNotEmpty();
+      assertThat(regions).hasSize(2);
+
+      List<String> regionNames = Arrays.asList(regionName, regionFromTemplateName);
+      regionNames.forEach(name -> {
+        RegionConfig regionConfig = CacheElement.findElement(config.getRegions(), name);
+        assertThat(regionConfig).isNotNull();
+        assertThat(regionConfig.getName()).isEqualTo(name);
+
+        RegionAttributesType regionAttributes = regionConfig.getRegionAttributes().get(0);
+        RegionAttributesType.PartitionAttributes partitionAttributes =
+            regionAttributes.getPartitionAttributes();
+
+        assertThat(partitionAttributes.getRecoveryDelay())
+            .describedAs("Recovery delay should be 1 for region " + name)
+            .isEqualTo("1");
+        assertThat(partitionAttributes.getLocalMaxMemory())
+            .describedAs("Local max memory should be 1000 for region " + name)
+            .isEqualTo("1000");
+        assertThat(partitionAttributes.getRedundantCopies())
+            .describedAs("Redundant copies should be 1 for region " + name)
+            .isEqualTo("1");
+        assertThat(partitionAttributes.getStartupRecoveryDelay())
+            .describedAs("Startup recovery delay should be 1 for region " + name)
+            .isEqualTo("1");
+        assertThat(partitionAttributes.getTotalMaxMemory())
+            .describedAs("Total max memory should be 100 for region " + name)
+            .isEqualTo("100");
+        assertThat(partitionAttributes.getTotalNumBuckets())
+            .describedAs("Total num buckets should be 1 for region " + name)
+            .isEqualTo("1");
+      });
+    });
+  }
+
+  @Test
+  public void createRegionPersistsPartitionResolver() {
+    String regionName = testName.getMethodName();
+
+    gfsh.executeAndAssertThat("create region"
+        + " --name=" + regionName
+        + " --type=PARTITION"
+        + " --partition-resolver=" + DummyPartitionResolver.class.getName()).statusIsSuccess();
+
+    locator.invoke(() -> {
+      InternalConfigurationPersistenceService cc =
+          ClusterStartupRule.getLocator().getConfigurationPersistenceService();
+      CacheConfig config = cc.getCacheConfig("cluster");
+
+      List<RegionConfig> regions = config.getRegions();
+      assertThat(regions).isNotEmpty();
+      assertThat(regions).hasSize(1);
+
+      RegionConfig regionConfig = CacheElement.findElement(config.getRegions(), regionName);
+      assertThat(regionConfig).isNotNull();
+      assertThat(regionConfig.getName()).isEqualTo(regionName);
+
+      RegionAttributesType regionAttributes = regionConfig.getRegionAttributes().get(0);
+      RegionAttributesType.PartitionAttributes partitionAttributes =
+          regionAttributes.getPartitionAttributes();
+
+      assertThat(partitionAttributes.getPartitionResolver().getClassName())
+          .isEqualTo(DummyPartitionResolver.class.getName());
+    });
+  }
+
+  @Test
+  public void createRegionDoesNotPersistEmptyOrDefaultPartitionAttributes() {
+    String regionName = testName.getMethodName();
+    String regionFromTemplateName = regionName + "-from-template";
+
+    gfsh.executeAndAssertThat("create region"
+        + " --name=" + regionName
+        + " --type=REPLICATE").statusIsSuccess();
+
+    gfsh.executeAndAssertThat("create region"
+        + " --name=" + regionFromTemplateName
+        + " --template-region=" + regionName);
+
+    locator.invoke(() -> {
+      InternalConfigurationPersistenceService cc =
+          ClusterStartupRule.getLocator().getConfigurationPersistenceService();
+      CacheConfig config = cc.getCacheConfig("cluster");
+
+      List<RegionConfig> regions = config.getRegions();
+      assertThat(regions).isNotEmpty();
+      assertThat(regions).hasSize(2);
+
+      RegionConfig regionConfig =
+          CacheElement.findElement(config.getRegions(), regionFromTemplateName);
+      assertThat(regionConfig).isNotNull();
+      assertThat(regionConfig.getName()).isEqualTo(regionFromTemplateName);
+      assertThat(regionConfig.getRegionAttributes())
+          .describedAs("Expecting region attributes to exist")
+          .hasSize(1);
+
+      RegionAttributesType attr = regionConfig.getRegionAttributes().get(0);
+      assertThat(attr.getPartitionAttributes())
+          .describedAs("Partition attributes should be null for " + regionFromTemplateName)
+          .isNull();
+    });
+  }
+
+  @Test
+  public void createRegionDoestNotPersistEmptyOrDefaultExpirationAttributes() {
+    String regionName = testName.getMethodName();
+    gfsh.executeAndAssertThat("create region"
+        + " --name=" + regionName
+        + " --type=REPLICATE").statusIsSuccess();
+
+    locator.invoke(() -> {
+      InternalConfigurationPersistenceService cc =
+          ClusterStartupRule.getLocator().getConfigurationPersistenceService();
+      CacheConfig config = cc.getCacheConfig("cluster");
+
+      List<RegionConfig> regions = config.getRegions();
+      assertThat(regions).isNotEmpty();
+      assertThat(regions).hasSize(1);
+
+      RegionConfig regionConfig =
+          CacheElement.findElement(config.getRegions(), regionName);
+      assertThat(regionConfig).isNotNull();
+      assertThat(regionConfig.getName()).isEqualTo(regionName);
+      assertThat(regionConfig.getRegionAttributes())
+          .describedAs("Expecting region attributes to exist")
+          .hasSize(1);
+
+      RegionAttributesType attr = regionConfig.getRegionAttributes().get(0);
+      assertThat(attr.getRegionTimeToLive())
+          .describedAs("Expiration attributes should be null for " + regionName)
+          .isNull();
+      assertThat(attr.getRegionIdleTime())
+          .describedAs("Expiration attributes should be null for " + regionName)
+          .isNull();
+      assertThat(attr.getEntryIdleTime())
+          .describedAs("Expiration attributes should be null for " + regionName)
+          .isNull();
+      assertThat(attr.getEntryTimeToLive())
+          .describedAs("Expiration attributes should be null for " + regionName)
+          .isNull();
+    });
+  }
+
+  @Test
+  public void createRegionPersistsDisableCloningSetting() {
+    String regionName = testName.getMethodName();
+    String regionFromTemplateName = regionName + "-from-template";
+
+    gfsh.executeAndAssertThat("create region"
+        + " --name=" + regionName
+        + " --type=REPLICATE"
+        + " --enable-cloning=false").statusIsSuccess();
+
+    gfsh.executeAndAssertThat("create region"
+        + " --name=" + regionFromTemplateName
+        + " --template-region=" + regionName);
+
+    locator.invoke(() -> {
+      InternalConfigurationPersistenceService cc =
+          ClusterStartupRule.getLocator().getConfigurationPersistenceService();
+      CacheConfig config = cc.getCacheConfig("cluster");
+
+      List<RegionConfig> regions = config.getRegions();
+      assertThat(regions).isNotEmpty();
+      assertThat(regions).hasSize(2);
+
+      List<String> regionNames = Arrays.asList(regionName, regionFromTemplateName);
+      regionNames.forEach(name -> {
+        RegionConfig regionConfig = CacheElement.findElement(config.getRegions(), name);
+        assertThat(regionConfig).isNotNull();
+        assertThat(regionConfig.getName()).isEqualTo(name);
+        assertThat(regionConfig.getRegionAttributes())
+            .describedAs("Expecting region attributes to exist for " + name)
+            .hasSize(1);
+
+        RegionAttributesType attr = regionConfig.getRegionAttributes().get(0);
+        assertThat(attr.isCloningEnabled())
+            .describedAs("Cloning should be disabled for " + name)
+            .isFalse();
+      });
+    });
+  }
+
+  @Test
+  public void createRegionPersistsCustomExpiryClass() {
+    String regionName = testName.getMethodName();
+    gfsh.executeAndAssertThat("create region"
+        + " --name=" + regionName
+        + " --type=REPLICATE"
+        + " --enable-statistics=true"
+        + " --entry-idle-time-custom-expiry=" + DummyCustomExpiry.class.getName())
+        .statusIsSuccess();
+
+    locator.invoke(() -> {
+      InternalConfigurationPersistenceService cc =
+          ClusterStartupRule.getLocator().getConfigurationPersistenceService();
+      CacheConfig config = cc.getCacheConfig("cluster");
+
+      List<RegionConfig> regions = config.getRegions();
+      assertThat(regions).isNotEmpty();
+      assertThat(regions).hasSize(1);
+
+      RegionConfig regionConfig = CacheElement.findElement(config.getRegions(), regionName);
+      assertThat(regionConfig).isNotNull();
+      assertThat(regionConfig.getName()).isEqualTo(regionName);
+      assertThat(regionConfig.getRegionAttributes())
+          .describedAs("Expecting region attributes to exist for " + regionName)
+          .hasSize(1);
+      RegionAttributesType attr = regionConfig.getRegionAttributes().get(0);
+      assertThat(attr.getEntryIdleTime().getExpirationAttributes().getCustomExpiry().toString())
+          .describedAs("Entry expiration custom expiration should be DummyCustomExpiry")
+          .isEqualTo(DummyCustomExpiry.class.getName());
+    });
+  }
+
+  @Test
+  public void createRegionPersistsImplicitTemplateAttributes() {
+    String regionName = testName.getMethodName();
+    gfsh.executeAndAssertThat("create region"
+        + " --name=" + regionName
+        + " --type=PARTITION")
+        .statusIsSuccess();
+
+    locator.invoke(() -> {
+      InternalConfigurationPersistenceService cc =
+          ClusterStartupRule.getLocator().getConfigurationPersistenceService();
+      CacheConfig config = cc.getCacheConfig("cluster");
+
+      List<RegionConfig> regions = config.getRegions();
+      assertThat(regions).isNotEmpty();
+      assertThat(regions).hasSize(1);
+
+      RegionConfig regionConfig = CacheElement.findElement(config.getRegions(), regionName);
+      assertThat(regionConfig).isNotNull();
+      assertThat(regionConfig.getName()).isEqualTo(regionName);
+      assertThat(regionConfig.getRegionAttributes())
+          .describedAs("Expecting region attributes to exist for " + regionName)
+          .hasSize(1);
+
+      RegionAttributesType attr = regionConfig.getRegionAttributes().get(0);
+      assertThat(attr.getDataPolicy())
+          .describedAs("Data policy for partitioned region should be persisted correctly")
+          .isEqualTo(RegionAttributesDataPolicy.PARTITION);
+    });
+  }
+}
diff --git a/geode-core/src/distributedTest/java/org/apache/geode/management/internal/configuration/ImportOldClusterConfigDUnitTest.java b/geode-core/src/distributedTest/java/org/apache/geode/management/internal/configuration/ImportOldClusterConfigDUnitTest.java
index 287ed83..e6974a2 100644
--- a/geode-core/src/distributedTest/java/org/apache/geode/management/internal/configuration/ImportOldClusterConfigDUnitTest.java
+++ b/geode-core/src/distributedTest/java/org/apache/geode/management/internal/configuration/ImportOldClusterConfigDUnitTest.java
@@ -62,7 +62,7 @@ public class ImportOldClusterConfigDUnitTest {
   }
 
   @Test
-  public void importOldConfigThenCreateRegionCorruptsCachXml() throws Exception {
+  public void importOldConfigThenCreateRegionCorruptsCacheXml() throws Exception {
     locator = lsRule.startLocatorVM(0);
 
     gfsh.connectAndVerify(locator);
diff --git a/geode-core/src/main/java/org/apache/geode/cache/DataPolicy.java b/geode-core/src/main/java/org/apache/geode/cache/DataPolicy.java
index 5ba6e53..06221fd 100644
--- a/geode-core/src/main/java/org/apache/geode/cache/DataPolicy.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/DataPolicy.java
@@ -18,6 +18,8 @@ package org.apache.geode.cache;
 
 import java.io.ObjectStreamException;
 
+import org.apache.geode.cache.configuration.RegionAttributesDataPolicy;
+
 
 /**
  * Enumerated type for region data policy. The data policy specifies how this local cache will
@@ -289,4 +291,9 @@ public class DataPolicy implements java.io.Serializable {
   public String toString() {
     return this.name;
   }
+
+  public RegionAttributesDataPolicy toConfigType() {
+    String configName = this.name.toLowerCase().replace("_", "-");
+    return RegionAttributesDataPolicy.fromValue(configName);
+  }
 }
diff --git a/geode-core/src/main/java/org/apache/geode/cache/EvictionAttributes.java b/geode-core/src/main/java/org/apache/geode/cache/EvictionAttributes.java
index 66a1bb7..b333b2e 100644
--- a/geode-core/src/main/java/org/apache/geode/cache/EvictionAttributes.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/EvictionAttributes.java
@@ -16,6 +16,8 @@
 package org.apache.geode.cache;
 
 import org.apache.geode.DataSerializable;
+import org.apache.geode.cache.configuration.EnumActionDestroyOverflow;
+import org.apache.geode.cache.configuration.RegionAttributesType;
 import org.apache.geode.cache.control.ResourceManager;
 import org.apache.geode.cache.util.ObjectSizer;
 import org.apache.geode.internal.cache.EvictionAttributesImpl;
@@ -505,4 +507,41 @@ public abstract class EvictionAttributes implements DataSerializable {
         .setAction(evictionAction).setMaximum(maximumMegabytes).setObjectSizer(null);
   }
 
+  public RegionAttributesType.EvictionAttributes convertToConfigEvictionAttributes() {
+    RegionAttributesType.EvictionAttributes configAttributes =
+        new RegionAttributesType.EvictionAttributes();
+    EnumActionDestroyOverflow action = EnumActionDestroyOverflow.fromValue(this.getAction()
+        .toString());
+    EvictionAlgorithm algorithm = getAlgorithm();
+    String objectSizerClass = getObjectSizer().getClass().toString();
+    Integer maximum = getMaximum();
+
+    if (algorithm.isLRUHeap()) {
+      RegionAttributesType.EvictionAttributes.LruHeapPercentage heapPercentage =
+          new RegionAttributesType.EvictionAttributes.LruHeapPercentage();
+      heapPercentage.setAction(action);
+      heapPercentage.setClassName(objectSizerClass);
+      configAttributes.setLruHeapPercentage(heapPercentage);
+    } else if (algorithm.isLRUMemory()) {
+      RegionAttributesType.EvictionAttributes.LruMemorySize memorySize =
+          new RegionAttributesType.EvictionAttributes.LruMemorySize();
+      memorySize.setAction(action);
+      memorySize.setClassName(objectSizerClass);
+      memorySize.setMaximum(maximum.toString());
+      configAttributes.setLruMemorySize(memorySize);
+    } else {
+      RegionAttributesType.EvictionAttributes.LruEntryCount entryCount =
+          new RegionAttributesType.EvictionAttributes.LruEntryCount();
+      entryCount.setAction(action);
+      entryCount.setMaximum(maximum.toString());
+      configAttributes.setLruEntryCount(entryCount);
+    }
+
+    return configAttributes;
+  }
+
+  public boolean isEmpty() {
+    return getAction() == EvictionAction.NONE && getAlgorithm() == EvictionAlgorithm.NONE;
+  }
+
 }
diff --git a/geode-core/src/main/java/org/apache/geode/cache/ExpirationAction.java b/geode-core/src/main/java/org/apache/geode/cache/ExpirationAction.java
index ea7df84..0b66ddc 100644
--- a/geode-core/src/main/java/org/apache/geode/cache/ExpirationAction.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/ExpirationAction.java
@@ -120,6 +120,21 @@ public class ExpirationAction implements Serializable {
     return this.name;
   }
 
+  public String toXmlString() {
+    switch (this.name) {
+      case "INVALIDATE":
+        return "invalidate";
+      case "DESTROY":
+        return "destroy";
+      case "LOCAL_DESTROY":
+        return "local-destroy";
+      case "LOCAL_INVALIDATE":
+        return "local-invalidate";
+      default:
+        return null;
+    }
+  }
+
   // The 4 declarations below are necessary for serialization
   private static int nextOrdinal = 0;
   public final int ordinal = nextOrdinal++;
diff --git a/geode-core/src/main/java/org/apache/geode/cache/ExpirationAttributes.java b/geode-core/src/main/java/org/apache/geode/cache/ExpirationAttributes.java
index 8cf2fb0..ebd08ee 100644
--- a/geode-core/src/main/java/org/apache/geode/cache/ExpirationAttributes.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/ExpirationAttributes.java
@@ -21,6 +21,7 @@ import java.io.IOException;
 
 import org.apache.geode.DataSerializable;
 import org.apache.geode.DataSerializer;
+import org.apache.geode.cache.configuration.ExpirationAttributesType;
 import org.apache.geode.internal.InternalDataSerializer;
 
 /**
@@ -149,7 +150,7 @@ public class ExpirationAttributes implements DataSerializable {
 
   public void fromData(DataInput in) throws IOException, ClassNotFoundException {
     this.timeout = in.readInt();
-    this.action = (ExpirationAction) DataSerializer.readObject(in);
+    this.action = DataSerializer.readObject(in);
 
   }
 
@@ -157,4 +158,17 @@ public class ExpirationAttributes implements DataSerializable {
     out.writeInt(this.timeout);
     DataSerializer.writeObject(this.action, out);
   }
+
+  public ExpirationAttributesType toConfigType() {
+    ExpirationAttributesType t = new ExpirationAttributesType();
+    t.setTimeout(Integer.toString(this.timeout));
+    t.setAction(this.action.toXmlString());
+
+    return t;
+  }
+
+  public boolean isDefault() {
+    return (this.action == null || this.action == ExpirationAction.INVALIDATE)
+        && (this.timeout == 0);
+  }
 }
diff --git a/geode-core/src/main/java/org/apache/geode/cache/PartitionAttributes.java b/geode-core/src/main/java/org/apache/geode/cache/PartitionAttributes.java
index d137fa3..2cab775 100644
--- a/geode-core/src/main/java/org/apache/geode/cache/PartitionAttributes.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/PartitionAttributes.java
@@ -18,6 +18,8 @@ package org.apache.geode.cache;
 import java.util.List;
 import java.util.Properties;
 
+import org.apache.geode.cache.configuration.DeclarableType;
+import org.apache.geode.cache.configuration.RegionAttributesType;
 import org.apache.geode.cache.partition.PartitionListener;
 
 /**
@@ -153,4 +155,21 @@ public interface PartitionAttributes<K, V> {
    */
   List<FixedPartitionAttributes> getFixedPartitionAttributes();
 
+  default RegionAttributesType.PartitionAttributes convertToConfigPartitionAttributes() {
+    RegionAttributesType.PartitionAttributes configAttributes =
+        new RegionAttributesType.PartitionAttributes();
+    configAttributes.setColocatedWith(getColocatedWith());
+    configAttributes.setLocalMaxMemory(Integer.toString(getLocalMaxMemory()));
+    if (getPartitionResolver() != null) {
+      configAttributes.setPartitionResolver(new DeclarableType(getPartitionResolver().getName()));
+    }
+    configAttributes.setRecoveryDelay(Long.toString(getRecoveryDelay()));
+    configAttributes.setStartupRecoveryDelay(Long.toString(getStartupRecoveryDelay()));
+    configAttributes.setRedundantCopies(Integer.toString(getRedundantCopies()));
+    configAttributes.setTotalMaxMemory(Long.toString(getTotalMaxMemory()));
+    configAttributes.setTotalNumBuckets(Long.toString(getTotalNumBuckets()));
+
+    return configAttributes;
+  }
+
 }
diff --git a/geode-core/src/main/java/org/apache/geode/cache/configuration/RegionAttributesType.java b/geode-core/src/main/java/org/apache/geode/cache/configuration/RegionAttributesType.java
index e579879..436bd36 100644
--- a/geode-core/src/main/java/org/apache/geode/cache/configuration/RegionAttributesType.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/configuration/RegionAttributesType.java
@@ -1795,6 +1795,17 @@ public class RegionAttributesType {
     @XmlElement(name = "lru-memory-size", namespace = "http://geode.apache.org/schema/cache")
     protected RegionAttributesType.EvictionAttributes.LruMemorySize lruMemorySize;
 
+    public String toStringRep() {
+      return "lru-entry-count: " +
+          this.lruEntryCount.getMaximum() + ", " +
+          this.lruEntryCount.getAction().toString() + ", " +
+          "\nlru-heap-percentage: " +
+          this.lruHeapPercentage.getAction().toString() +
+          "\nlru-memory-size: " +
+          this.lruMemorySize.getMaximum() +
+          this.lruMemorySize.getAction().toString();
+    }
+
     /**
      * Gets the value of the lruEntryCount property.
      *
diff --git a/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalConfigurationPersistenceService.java b/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalConfigurationPersistenceService.java
index 74aba94..c86a969 100644
--- a/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalConfigurationPersistenceService.java
+++ b/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalConfigurationPersistenceService.java
@@ -226,29 +226,6 @@ public class InternalConfigurationPersistenceService implements ConfigurationPer
   }
 
   /**
-   * Finds xml element in a group's xml, with the tagName that has given attribute and value
-   */
-  public Element getXmlElement(String group, String tagName, String attribute, String value)
-      throws IOException, SAXException, ParserConfigurationException {
-    if (group == null) {
-      group = "cluster";
-    }
-    Configuration config = getConfiguration(group);
-    Document document = XmlUtils.createDocumentFromXml(config.getCacheXmlContent());
-    NodeList elements = document.getElementsByTagName(tagName);
-    if (elements == null || elements.getLength() == 0) {
-      return null;
-    } else {
-      for (int i = 0; i < elements.getLength(); i++) {
-        Element eachElement = (Element) elements.item(i);
-        if (eachElement.getAttribute(attribute).equals(value))
-          return eachElement;
-      }
-    }
-    return null;
-  }
-
-  /**
    * Adds/replaces the xml entity in the shared configuration we don't need to trigger the change
    * listener for this modification, so it's ok to operate on the original configuration object
    */
diff --git a/geode-core/src/main/java/org/apache/geode/internal/config/JAXBService.java b/geode-core/src/main/java/org/apache/geode/internal/config/JAXBService.java
index 6927d2f..221e154 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/config/JAXBService.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/config/JAXBService.java
@@ -51,6 +51,7 @@ public class JAXBService {
       marshaller = jaxbContext.createMarshaller();
       unmarshaller = jaxbContext.createUnmarshaller();
       marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
+      marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
 
       String schemas = Arrays.stream(xsdRootClasses).map(c -> {
         XSDRootElement element = c.getAnnotation(XSDRootElement.class);
@@ -69,7 +70,7 @@ public class JAXBService {
 
   public void validateWith(URL url) {
     SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
-    Schema schema = null;
+    Schema schema;
     try {
       schema = factory.newSchema(url);
     } catch (SAXException e) {
@@ -88,6 +89,7 @@ public class JAXBService {
   public String marshall(Object object) {
     StringWriter sw = new StringWriter();
     try {
+      sw.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>");
       marshaller.marshal(object, sw);
     } catch (Exception e) {
       throw new RuntimeException(e.getMessage(), e);
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/AlterRegionCommand.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/AlterRegionCommand.java
index 9c24efe..6d8175d 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/AlterRegionCommand.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/AlterRegionCommand.java
@@ -26,7 +26,6 @@ import org.apache.geode.cache.CacheWriter;
 import org.apache.geode.cache.CustomExpiry;
 import org.apache.geode.cache.ExpirationAction;
 import org.apache.geode.distributed.DistributedMember;
-import org.apache.geode.distributed.internal.InternalConfigurationPersistenceService;
 import org.apache.geode.internal.cache.InternalCache;
 import org.apache.geode.management.cli.CliMetaData;
 import org.apache.geode.management.cli.ConverterHint;
@@ -139,7 +138,7 @@ public class AlterRegionCommand extends InternalGfshCommand {
     XmlEntity xmlEntity = findXmlEntity(regionAlterResults);
     if (xmlEntity != null) {
       persistClusterConfiguration(result,
-          () -> ((InternalConfigurationPersistenceService) getConfigurationPersistenceService())
+          () -> getConfigurationPersistenceService()
               .addXmlEntity(xmlEntity, groups));
     }
     return result;
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/CreateRegionCommand.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/CreateRegionCommand.java
index b28569a..4686f01 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/CreateRegionCommand.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/CreateRegionCommand.java
@@ -39,9 +39,12 @@ import org.apache.geode.cache.ExpirationAction;
 import org.apache.geode.cache.Region;
 import org.apache.geode.cache.RegionAttributes;
 import org.apache.geode.cache.RegionShortcut;
+import org.apache.geode.cache.configuration.CacheConfig;
+import org.apache.geode.cache.configuration.RegionConfig;
 import org.apache.geode.cache.execute.ResultCollector;
 import org.apache.geode.distributed.DistributedMember;
 import org.apache.geode.internal.cache.InternalCache;
+import org.apache.geode.internal.logging.LogService;
 import org.apache.geode.management.DistributedRegionMXBean;
 import org.apache.geode.management.DistributedSystemMXBean;
 import org.apache.geode.management.ManagementService;
@@ -50,11 +53,13 @@ import org.apache.geode.management.RegionMXBean;
 import org.apache.geode.management.cli.CliMetaData;
 import org.apache.geode.management.cli.ConverterHint;
 import org.apache.geode.management.cli.Result;
+import org.apache.geode.management.cli.SingleGfshCommand;
 import org.apache.geode.management.internal.cli.AbstractCliAroundInterceptor;
 import org.apache.geode.management.internal.cli.CliUtil;
 import org.apache.geode.management.internal.cli.GfshParseResult;
 import org.apache.geode.management.internal.cli.LogWrapper;
 import org.apache.geode.management.internal.cli.domain.ClassName;
+import org.apache.geode.management.internal.cli.domain.RegionConfigFactory;
 import org.apache.geode.management.internal.cli.exceptions.EntityExistsException;
 import org.apache.geode.management.internal.cli.functions.CliFunctionResult;
 import org.apache.geode.management.internal.cli.functions.FetchRegionAttributesFunction;
@@ -63,18 +68,18 @@ import org.apache.geode.management.internal.cli.functions.RegionCreateFunction;
 import org.apache.geode.management.internal.cli.functions.RegionFunctionArgs;
 import org.apache.geode.management.internal.cli.i18n.CliStrings;
 import org.apache.geode.management.internal.cli.result.ResultBuilder;
+import org.apache.geode.management.internal.cli.result.model.ResultModel;
 import org.apache.geode.management.internal.cli.util.RegionPath;
-import org.apache.geode.management.internal.configuration.domain.XmlEntity;
 import org.apache.geode.management.internal.security.ResourceOperation;
 import org.apache.geode.security.ResourcePermission;
 
-public class CreateRegionCommand extends InternalGfshCommand {
+public class CreateRegionCommand extends SingleGfshCommand {
   @CliCommand(value = CliStrings.CREATE_REGION, help = CliStrings.CREATE_REGION__HELP)
   @CliMetaData(relatedTopic = CliStrings.TOPIC_GEODE_REGION,
       interceptor = "org.apache.geode.management.internal.cli.commands.CreateRegionCommand$Interceptor")
   @ResourceOperation(resource = ResourcePermission.Resource.DATA,
       operation = ResourcePermission.Operation.MANAGE)
-  public Result createRegion(
+  public ResultModel createRegion(
       @CliOption(key = CliStrings.CREATE_REGION__REGION, mandatory = true,
           optionContext = ConverterHint.REGION_PATH,
           help = CliStrings.CREATE_REGION__REGION__HELP) String regionPath,
@@ -180,15 +185,13 @@ public class CreateRegionCommand extends InternalGfshCommand {
           help = CliStrings.CREATE_REGION__VALUECONSTRAINT__HELP) String valueConstraint
   // NOTICE: keep the region attributes params in alphabetical order
   ) {
-    Result result;
-
     if (regionShortcut != null && templateRegion != null) {
-      return ResultBuilder.createUserErrorResult(
+      return ResultModel.createError(
           CliStrings.CREATE_REGION__MSG__ONLY_ONE_OF_REGIONSHORTCUT_AND_USEATTRIBUESFROM_CAN_BE_SPECIFIED);
     }
 
     if (regionShortcut == null && templateRegion == null) {
-      return ResultBuilder.createUserErrorResult(
+      return ResultModel.createError(
           CliStrings.CREATE_REGION__MSG__ONE_OF_REGIONSHORTCUT_AND_USEATTRIBUTESFROM_IS_REQUIRED);
     }
 
@@ -217,6 +220,7 @@ public class CreateRegionCommand extends InternalGfshCommand {
 
       // we first make sure E and C have the compatible data policy
       if (regionShortcut.isPartition() && !existingDataPolicy.contains("PARTITION")) {
+        LogService.getLogger().info("Create region command: got EntityExists exception");
         throw new EntityExistsException("The existing region is not a partitioned region",
             ifNotExists);
       }
@@ -244,7 +248,7 @@ public class CreateRegionCommand extends InternalGfshCommand {
     String parentRegionPath = regionPathData.getParent();
     if (parentRegionPath != null && !Region.SEPARATOR.equals(parentRegionPath)) {
       if (!regionExists(cache, parentRegionPath)) {
-        return ResultBuilder.createUserErrorResult(
+        return ResultModel.createError(
             CliStrings.format(CliStrings.CREATE_REGION__MSG__PARENT_REGION_FOR_0_DOES_NOT_EXIST,
                 new Object[] {regionPath}));
       }
@@ -282,16 +286,17 @@ public class CreateRegionCommand extends InternalGfshCommand {
     RegionAttributes<?, ?> regionAttributes = null;
     if (regionShortcut != null) {
       if (!regionShortcut.name().startsWith("PARTITION") && functionArgs.hasPartitionAttributes()) {
-        return ResultBuilder.createUserErrorResult(CliStrings.format(
+        return ResultModel.createError(CliStrings.format(
             CliStrings.CREATE_REGION__MSG__OPTION_0_CAN_BE_USED_ONLY_FOR_PARTITIONEDREGION,
             functionArgs.getPartitionArgs().getUserSpecifiedPartitionAttributes()) + " "
             + CliStrings.format(CliStrings.CREATE_REGION__MSG__0_IS_NOT_A_PARITIONEDREGION,
                 regionPath));
       }
       functionArgs.setRegionShortcut(regionShortcut);
+      functionArgs.setRegionAttributes(cache.getRegionAttributes(regionShortcut.toString()));
     } else { // templateRegion != null
       if (!regionExists(cache, templateRegion)) {
-        return ResultBuilder.createUserErrorResult(CliStrings.format(
+        return ResultModel.createError(CliStrings.format(
             CliStrings.CREATE_REGION__MSG__SPECIFY_VALID_REGION_PATH_FOR_0_REGIONPATH_1_NOT_FOUND,
             CliStrings.CREATE_REGION__USEATTRIBUTESFROM, templateRegion));
       }
@@ -299,14 +304,14 @@ public class CreateRegionCommand extends InternalGfshCommand {
       RegionAttributesWrapper<?, ?> wrappedAttributes = getRegionAttributes(cache, templateRegion);
 
       if (wrappedAttributes == null) {
-        return ResultBuilder.createGemFireErrorResult(CliStrings.format(
+        return ResultModel.createError(CliStrings.format(
             CliStrings.CREATE_REGION__MSG__COULD_NOT_RETRIEVE_REGION_ATTRS_FOR_PATH_0_VERIFY_REGION_EXISTS,
             templateRegion));
       }
 
       if (wrappedAttributes.getRegionAttributes().getPartitionAttributes() == null
           && functionArgs.hasPartitionAttributes()) {
-        return ResultBuilder.createUserErrorResult(CliStrings.format(
+        return ResultModel.createError(CliStrings.format(
             CliStrings.CREATE_REGION__MSG__OPTION_0_CAN_BE_USED_ONLY_FOR_PARTITIONEDREGION,
             functionArgs.getPartitionArgs().getUserSpecifiedPartitionAttributes()) + " "
             + CliStrings.format(CliStrings.CREATE_REGION__MSG__0_IS_NOT_A_PARITIONEDREGION,
@@ -358,14 +363,14 @@ public class CreateRegionCommand extends InternalGfshCommand {
         DistributedRegionMXBean distributedRegionMXBean =
             mgmtService.getDistributedRegionMXBean(prColocatedWith);
         if (distributedRegionMXBean == null) {
-          return ResultBuilder.createUserErrorResult(CliStrings.format(
+          return ResultModel.createError(CliStrings.format(
               CliStrings.CREATE_REGION__MSG__SPECIFY_VALID_REGION_PATH_FOR_0_REGIONPATH_1_NOT_FOUND,
               CliStrings.CREATE_REGION__COLOCATEDWITH, prColocatedWith));
         }
         String regionType = distributedRegionMXBean.getRegionType();
         if (!(DataPolicy.PARTITION.toString().equals(regionType)
             || DataPolicy.PERSISTENT_PARTITION.toString().equals(regionType))) {
-          return ResultBuilder.createUserErrorResult(CliStrings.format(
+          return ResultModel.createError(CliStrings.format(
               CliStrings.CREATE_REGION__MSG__COLOCATEDWITH_REGION_0_IS_NOT_PARTITIONEDREGION,
               new Object[] {prColocatedWith}));
         }
@@ -377,14 +382,14 @@ public class CreateRegionCommand extends InternalGfshCommand {
       Set<String> existingGatewaySenders =
           Arrays.stream(dsMBean.listGatewaySenders()).collect(Collectors.toSet());
       if (existingGatewaySenders.size() == 0) {
-        return ResultBuilder
-            .createUserErrorResult(CliStrings.CREATE_REGION__MSG__NO_GATEWAYSENDERS_IN_THE_SYSTEM);
+        return ResultModel
+            .createError(CliStrings.CREATE_REGION__MSG__NO_GATEWAYSENDERS_IN_THE_SYSTEM);
       } else {
         Set<String> specifiedGatewaySenders =
             Arrays.stream(gatewaySenderIds).collect(Collectors.toSet());
         specifiedGatewaySenders.removeAll(existingGatewaySenders);
         if (!specifiedGatewaySenders.isEmpty()) {
-          return ResultBuilder.createUserErrorResult(CliStrings.format(
+          return ResultModel.createError(CliStrings.format(
               CliStrings.CREATE_REGION__MSG__SPECIFY_VALID_GATEWAYSENDER_ID_UNKNOWN_0,
               (Object[]) gatewaySenderIds));
         }
@@ -402,11 +407,11 @@ public class CreateRegionCommand extends InternalGfshCommand {
                 CliStrings.CREATE_REGION__MSG__USE_ATTRIBUTES_FROM_REGION_0_IS_NOT_WITH_PERSISTENCE,
                 new Object[] {String.valueOf(functionArgs.getTemplateRegion())});
 
-        return ResultBuilder.createUserErrorResult(message);
+        return ResultModel.createError(message);
       }
 
       if (!diskStoreExists(cache, diskStore)) {
-        return ResultBuilder.createUserErrorResult(CliStrings.format(
+        return ResultModel.createError(CliStrings.format(
             CliStrings.CREATE_REGION__MSG__SPECIFY_VALID_DISKSTORE_UNKNOWN_DISKSTORE_0,
             new Object[] {diskStore}));
       }
@@ -425,9 +430,9 @@ public class CreateRegionCommand extends InternalGfshCommand {
     // just in case we found no members with this group name
     if (membersToCreateRegionOn.isEmpty()) {
       if (groups == null || groups.length == 0) {
-        return ResultBuilder.createUserErrorResult(CliStrings.NO_MEMBERS_FOUND_MESSAGE);
+        return ResultModel.createError(CliStrings.NO_MEMBERS_FOUND_MESSAGE);
       } else {
-        return ResultBuilder.createUserErrorResult(
+        return ResultModel.createError(
             CliStrings.format(CliStrings.CREATE_REGION__MSG__GROUPS_0_ARE_INVALID,
                 (Object[]) groups));
       }
@@ -436,14 +441,70 @@ public class CreateRegionCommand extends InternalGfshCommand {
     List<CliFunctionResult> regionCreateResults = executeAndGetFunctionResult(
         RegionCreateFunction.INSTANCE, functionArgs, membersToCreateRegionOn);
 
-    result = ResultBuilder.buildResult(regionCreateResults);
-    XmlEntity xmlEntity = findXmlEntity(regionCreateResults);
-    if (xmlEntity != null) {
+    ResultModel resultModel = ResultModel.createMemberStatusResult(regionCreateResults);
+    if (resultModel.isSuccessful()) {
       verifyDistributedRegionMbean(cache, regionPath);
-      persistClusterConfiguration(result,
-          () -> getConfigurationPersistenceService().addXmlEntity(xmlEntity, groups));
+      RegionConfig config = (new RegionConfigFactory()).generate(functionArgs);
+      resultModel.setConfigObject(new CreateRegionResultConfig(config,
+          functionArgs.getRegionPath()));
+    }
+
+    return resultModel;
+  }
+
+  private class CreateRegionResultConfig {
+    RegionConfig getRegionConfig() {
+      return regionConfig;
+    }
+
+    String getFullRegionPath() {
+      return fullRegionPath;
+    }
+
+    private final RegionConfig regionConfig;
+    private final String fullRegionPath;
+
+    public CreateRegionResultConfig(RegionConfig regionConfig, String fullRegionPath) {
+      this.regionConfig = regionConfig;
+      this.fullRegionPath = fullRegionPath;
+    }
+  }
+
+  @Override
+  public boolean updateConfigForGroup(String group, CacheConfig config, Object configObject) {
+    if (configObject == null) {
+      return false;
+    }
+
+    CreateRegionResultConfig regionResultConfigObject = (CreateRegionResultConfig) configObject;
+    RegionConfig regionConfig = regionResultConfigObject.getRegionConfig();
+    String regionPath = regionResultConfigObject.getFullRegionPath();
+
+    RegionPath regionPathData = new RegionPath(regionPath);
+    if (regionPathData.getParent() == null) {
+      config.getRegions().add(regionConfig);
+      return true;
+    }
+
+    String[] regionsOnPath = regionPathData.getRegionsOnParentPath();
+    RegionConfig rootConfig = config.getRegions().stream()
+        .filter(r -> r.getName().equals(regionsOnPath[0]))
+        .findFirst()
+        .get();
+
+    RegionConfig currentConfig = rootConfig;
+    for (int i = 1; i < regionsOnPath.length; i++) {
+      final String curRegionName = regionsOnPath[i];
+      currentConfig = currentConfig.getRegions()
+          .stream()
+          .filter(r -> r.getName().equals(curRegionName))
+          .findFirst()
+          .get();
     }
-    return result;
+
+    currentConfig.getRegions().add(regionConfig);
+
+    return true;
   }
 
   boolean verifyDistributedRegionMbean(InternalCache cache, String regionName) {
@@ -655,6 +716,14 @@ public class CreateRegionCommand extends InternalGfshCommand {
             .format(CliStrings.CREATE_REGION__MSG__INVALID_COMPRESSOR, new Object[] {compressor}));
       }
 
+      Boolean cloningEnabled =
+          (Boolean) parseResult.getParamValue(CliStrings.CREATE_REGION__CLONINGENABLED);
+      if (compressor != null && cloningEnabled != null && !cloningEnabled) {
+        return ResultBuilder.createUserErrorResult(CliStrings
+            .format(CliStrings.CREATE_REGION__MSG__CANNOT_DISABLE_CLONING_WITH_COMPRESSOR,
+                new Object[] {compressor}));
+      }
+
       String diskStore = parseResult.getParamValueAsString(CliStrings.CREATE_REGION__DISKSTORE);
       if (diskStore != null) {
         String regionShortcut =
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/RegionAttributeGetFunction.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/RegionAttributeGetFunction.java
new file mode 100644
index 0000000..ebee2db
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/RegionAttributeGetFunction.java
@@ -0,0 +1,22 @@
+/*
+ * 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.cli.domain;
+
+import org.apache.geode.cache.configuration.RegionAttributesType;
+
+@FunctionalInterface
+public interface RegionAttributeGetFunction {
+  Object getValue(RegionAttributesType attributesType);
+}
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/RegionAttributeSetFunction.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/RegionAttributeSetFunction.java
new file mode 100644
index 0000000..051e815
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/RegionAttributeSetFunction.java
@@ -0,0 +1,22 @@
+/*
+ * 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.cli.domain;
+
+import org.apache.geode.cache.configuration.RegionAttributesType;
+
+@FunctionalInterface
+public interface RegionAttributeSetFunction {
+  void setAttributeValue(RegionAttributesType attributesType);
+}
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/RegionConfigFactory.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/RegionConfigFactory.java
new file mode 100644
index 0000000..59a01a7
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/RegionConfigFactory.java
@@ -0,0 +1,331 @@
+/*
+ * 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.cli.domain;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.apache.geode.cache.RegionAttributes;
+import org.apache.geode.cache.configuration.ClassNameType;
+import org.apache.geode.cache.configuration.DeclarableType;
+import org.apache.geode.cache.configuration.ExpirationAttributesType;
+import org.apache.geode.cache.configuration.RegionAttributesType;
+import org.apache.geode.cache.configuration.RegionConfig;
+import org.apache.geode.management.internal.cli.functions.RegionFunctionArgs;
+
+public class RegionConfigFactory {
+  public RegionConfig generate(RegionFunctionArgs args) {
+    RegionConfig regionConfig = new RegionConfig();
+    regionConfig.setName(getLeafRegion(args.getRegionPath()));
+
+    RegionAttributes<?, ?> regionAttributes = args.getRegionAttributes();
+    if (args.getKeyConstraint() != null) {
+      addAttribute(regionConfig, a -> a.setKeyConstraint(args.getKeyConstraint()));
+    }
+
+    if (args.getValueConstraint() != null) {
+      addAttribute(regionConfig, a -> a.setValueConstraint(args.getValueConstraint()));
+    }
+
+    if (args.getStatisticsEnabled() != null) {
+      addAttribute(regionConfig, a -> a.setStatisticsEnabled(args.getStatisticsEnabled()));
+    } else if (regionAttributes != null) {
+      addAttribute(regionConfig, a -> a.setStatisticsEnabled(regionAttributes
+          .getStatisticsEnabled()));
+    }
+
+    if (args.getEntryExpirationIdleTime() != null) {
+      RegionAttributesType.EntryIdleTime entryIdleTime = new RegionAttributesType.EntryIdleTime();
+      entryIdleTime.setExpirationAttributes(
+          args.getEntryExpirationIdleTime().getExpirationAttributes().toConfigType());
+      addAttribute(regionConfig, a -> a.setEntryIdleTime(entryIdleTime));
+    } else if (regionAttributes != null &&
+        regionAttributes.getEntryIdleTimeout() != null &&
+        !regionAttributes.getEntryIdleTimeout().isDefault()) {
+      RegionAttributesType.EntryIdleTime entryIdleTime = new RegionAttributesType.EntryIdleTime();
+      entryIdleTime.setExpirationAttributes(regionAttributes
+          .getEntryIdleTimeout().toConfigType());
+      addAttribute(regionConfig, a -> a.setEntryIdleTime(entryIdleTime));
+    }
+
+    if (args.getEntryIdleTimeCustomExpiry() != null) {
+      Object maybeEntryIdleAttr = getRegionAttributeValue(regionConfig, a -> a.getEntryIdleTime());
+      RegionAttributesType.EntryIdleTime entryIdleTime =
+          maybeEntryIdleAttr != null ? (RegionAttributesType.EntryIdleTime) maybeEntryIdleAttr
+              : new RegionAttributesType.EntryIdleTime();
+
+      ExpirationAttributesType expirationAttributes;
+      if (entryIdleTime.getExpirationAttributes() == null) {
+        expirationAttributes = new ExpirationAttributesType();
+        expirationAttributes.setTimeout("0");
+      } else {
+        expirationAttributes = entryIdleTime.getExpirationAttributes();
+      }
+
+      DeclarableType customExpiry = new DeclarableType();
+      customExpiry.setClassName(args.getEntryIdleTimeCustomExpiry().getClassName());
+      expirationAttributes.setCustomExpiry(customExpiry);
+      entryIdleTime.setExpirationAttributes(expirationAttributes);
+
+      if (maybeEntryIdleAttr == null) {
+        addAttribute(regionConfig, a -> a.setEntryIdleTime(entryIdleTime));
+      }
+    }
+
+    if (args.getEntryExpirationTTL() != null) {
+      RegionAttributesType.EntryTimeToLive entryExpTime =
+          new RegionAttributesType.EntryTimeToLive();
+      entryExpTime.setExpirationAttributes(
+          args.getEntryExpirationTTL().getExpirationAttributes().toConfigType());
+      addAttribute(regionConfig, a -> a.setEntryTimeToLive(entryExpTime));
+    } else if (regionAttributes != null
+        && regionAttributes.getEntryTimeToLive() != null
+        && !regionAttributes.getEntryTimeToLive().isDefault()) {
+      RegionAttributesType.EntryTimeToLive entryExpTime =
+          new RegionAttributesType.EntryTimeToLive();
+      entryExpTime.setExpirationAttributes(
+          regionAttributes.getEntryTimeToLive().toConfigType());
+      addAttribute(regionConfig, a -> a.setEntryTimeToLive(entryExpTime));
+    }
+
+    if (args.getRegionExpirationIdleTime() != null) {
+      RegionAttributesType.RegionIdleTime regionIdleTime =
+          new RegionAttributesType.RegionIdleTime();
+      regionIdleTime.setExpirationAttributes(
+          args.getRegionExpirationIdleTime().getExpirationAttributes().toConfigType());
+      addAttribute(regionConfig, a -> a.setRegionIdleTime(regionIdleTime));
+    } else if (regionAttributes != null
+        && regionAttributes.getRegionIdleTimeout() != null
+        && !regionAttributes.getRegionIdleTimeout().isDefault()) {
+      RegionAttributesType.RegionIdleTime regionIdleTime =
+          new RegionAttributesType.RegionIdleTime();
+      regionIdleTime.setExpirationAttributes(
+          regionAttributes.getRegionIdleTimeout().toConfigType());
+      addAttribute(regionConfig, a -> a.setRegionIdleTime(regionIdleTime));
+    }
+
+    if (args.getRegionExpirationTTL() != null) {
+      RegionAttributesType.RegionTimeToLive regionExpTime =
+          new RegionAttributesType.RegionTimeToLive();
+      regionExpTime.setExpirationAttributes(
+          args.getRegionExpirationTTL().getExpirationAttributes().toConfigType());
+      addAttribute(regionConfig, a -> a.setRegionTimeToLive(regionExpTime));
+    } else if (regionAttributes != null
+        && regionAttributes.getRegionTimeToLive() != null
+        && !regionAttributes.getRegionTimeToLive().isDefault()) {
+      RegionAttributesType.RegionTimeToLive regionExpTime =
+          new RegionAttributesType.RegionTimeToLive();
+      regionExpTime.setExpirationAttributes(
+          regionAttributes.getRegionTimeToLive().toConfigType());
+      addAttribute(regionConfig, a -> a.setRegionTimeToLive(regionExpTime));
+    }
+
+    if (args.getEntryTTLCustomExpiry() != null) {
+      Object maybeEntryTTLAttr = getRegionAttributeValue(regionConfig, a -> a.getEntryTimeToLive());
+      RegionAttributesType.EntryTimeToLive entryTimeToLive =
+          maybeEntryTTLAttr != null ? (RegionAttributesType.EntryTimeToLive) maybeEntryTTLAttr
+              : new RegionAttributesType.EntryTimeToLive();
+
+      ExpirationAttributesType expirationAttributes;
+      if (entryTimeToLive.getExpirationAttributes() == null) {
+        expirationAttributes = new ExpirationAttributesType();
+        expirationAttributes.setTimeout("0");
+      } else {
+        expirationAttributes = entryTimeToLive.getExpirationAttributes();
+      }
+
+      DeclarableType customExpiry = new DeclarableType();
+      customExpiry.setClassName(args.getEntryTTLCustomExpiry().getClassName());
+      expirationAttributes.setCustomExpiry(customExpiry);
+      entryTimeToLive.setExpirationAttributes(expirationAttributes);
+
+      if (maybeEntryTTLAttr == null) {
+        addAttribute(regionConfig, a -> a.setEntryTimeToLive(entryTimeToLive));
+      }
+    }
+
+    if (args.getDiskStore() != null) {
+      addAttribute(regionConfig, a -> a.setDiskStoreName(args.getDiskStore()));
+    } else if (regionAttributes != null) {
+      addAttribute(regionConfig, a -> a.setDiskStoreName(regionAttributes.getDiskStoreName()));
+    }
+
+    if (args.getDiskSynchronous() != null) {
+      addAttribute(regionConfig, a -> a.setDiskSynchronous(args.getDiskSynchronous()));
+    } else if (regionAttributes != null) {
+      addAttribute(regionConfig, a -> a.setDiskSynchronous(regionAttributes.isDiskSynchronous()));
+    }
+
+    if (args.getEnableAsyncConflation() != null) {
+      addAttribute(regionConfig, a -> a.setEnableAsyncConflation(args.getEnableAsyncConflation()));
+    } else if (regionAttributes != null) {
+      addAttribute(regionConfig, a -> a.setEnableAsyncConflation(regionAttributes
+          .getEnableAsyncConflation()));
+    }
+
+    if (args.getEnableSubscriptionConflation() != null) {
+      addAttribute(regionConfig,
+          a -> a.setEnableSubscriptionConflation(args.getEnableSubscriptionConflation()));
+    } else if (regionAttributes != null) {
+      addAttribute(regionConfig, a -> a.setEnableSubscriptionConflation(regionAttributes
+          .getEnableSubscriptionConflation()));
+    }
+
+    if (args.getConcurrencyChecksEnabled() != null) {
+      addAttribute(regionConfig, a -> a.setConcurrencyChecksEnabled(
+          args.getConcurrencyChecksEnabled()));
+    } else if (regionAttributes != null) {
+      addAttribute(regionConfig, a -> a.setConcurrencyChecksEnabled(regionAttributes
+          .getConcurrencyChecksEnabled()));
+    }
+
+    if (args.getCloningEnabled() != null) {
+      addAttribute(regionConfig, a -> a.setCloningEnabled(args.getCloningEnabled()));
+    } else if (regionAttributes != null) {
+      addAttribute(regionConfig, a -> a.setCloningEnabled(regionAttributes
+          .getCloningEnabled()));
+    }
+
+    if (args.getOffHeap() != null) {
+      addAttribute(regionConfig, a -> a.setOffHeap(args.getOffHeap()));
+    } else if (regionAttributes != null) {
+      addAttribute(regionConfig, a -> a.setOffHeap(regionAttributes.getOffHeap()));
+    }
+
+    if (args.getMcastEnabled() != null) {
+      addAttribute(regionConfig, a -> a.setMulticastEnabled(args.getMcastEnabled()));
+    } else if (regionAttributes != null) {
+      addAttribute(regionConfig, a -> a.setMulticastEnabled(regionAttributes
+          .getMulticastEnabled()));
+    }
+
+    if (args.getPartitionArgs() != null) {
+      RegionAttributesType.PartitionAttributes partitionAttributes =
+          new RegionAttributesType.PartitionAttributes();
+      RegionFunctionArgs.PartitionArgs partitionArgs = args.getPartitionArgs();
+      partitionAttributes.setColocatedWith(partitionArgs.getPrColocatedWith());
+      partitionAttributes.setLocalMaxMemory(int2string(partitionArgs.getPrLocalMaxMemory()));
+      partitionAttributes.setRecoveryDelay(long2string(partitionArgs.getPrRecoveryDelay()));
+      partitionAttributes.setRedundantCopies(int2string(partitionArgs.getPrRedundantCopies()));
+      partitionAttributes
+          .setStartupRecoveryDelay(long2string(partitionArgs.getPrStartupRecoveryDelay()));
+      partitionAttributes.setTotalMaxMemory(long2string(partitionArgs.getPrTotalMaxMemory()));
+      partitionAttributes.setTotalNumBuckets(int2string(partitionArgs.getPrTotalNumBuckets()));
+
+      if (partitionArgs.getPartitionResolver() != null) {
+        DeclarableType partitionResolverType = new DeclarableType();
+        partitionResolverType.setClassName(partitionArgs.getPartitionResolver());
+        partitionAttributes.setPartitionResolver(partitionResolverType);
+      }
+
+      addAttribute(regionConfig, a -> a.setPartitionAttributes(partitionAttributes));
+    } else if (regionAttributes != null && regionAttributes.getPartitionAttributes() != null) {
+      addAttribute(regionConfig, a -> a.setPartitionAttributes(
+          regionAttributes.getPartitionAttributes().convertToConfigPartitionAttributes()));
+    }
+
+    if (args.getGatewaySenderIds() != null && !args.getGatewaySenderIds().isEmpty()) {
+      addAttribute(regionConfig, a -> a.setGatewaySenderIds(String.join(",",
+          args.getGatewaySenderIds())));
+    }
+
+    if (args.getEvictionAttributes() != null) {
+      addAttribute(regionConfig, a -> a.setEvictionAttributes(
+          args.getEvictionAttributes().convertToConfigEvictionAttributes()));
+    } else if (regionAttributes != null &&
+        regionAttributes.getEvictionAttributes() != null &&
+        !regionAttributes.getEvictionAttributes().isEmpty()) {
+      addAttribute(regionConfig, a -> a.setEvictionAttributes(
+          regionAttributes.getEvictionAttributes().convertToConfigEvictionAttributes()));
+    }
+
+    if (args.getAsyncEventQueueIds() != null && !args.getAsyncEventQueueIds().isEmpty()) {
+      addAttribute(regionConfig,
+          a -> a.setAsyncEventQueueIds(String.join(",", args.getAsyncEventQueueIds())));
+    }
+
+    if (args.getCacheListeners() != null && !args.getCacheListeners().isEmpty()) {
+      addAttribute(regionConfig, a -> a.getCacheListeners().addAll(
+          args.getCacheListeners().stream().map(l -> {
+            DeclarableType declarableType = new DeclarableType();
+            declarableType.setClassName(l.getClassName());
+            return declarableType;
+          }).collect(Collectors.toList())));
+    }
+
+    if (args.getCacheLoader() != null) {
+      DeclarableType declarableType = new DeclarableType();
+      declarableType.setClassName(args.getCacheLoader().getClassName());
+      addAttribute(regionConfig, a -> a.setCacheLoader(declarableType));
+    }
+
+    if (args.getCacheWriter() != null) {
+      DeclarableType declarableType = new DeclarableType();
+      declarableType.setClassName(args.getCacheWriter().getClassName());
+      addAttribute(regionConfig, a -> a.setCacheWriter(declarableType));
+    }
+
+    if (args.getCompressor() != null) {
+      addAttribute(regionConfig, a -> a.setCompressor(new ClassNameType(args.getCompressor())));
+      addAttribute(regionConfig, a -> a.setCloningEnabled(true));
+    }
+
+    if (args.getConcurrencyLevel() != null) {
+      addAttribute(regionConfig, a -> a.setConcurrencyLevel(args.getConcurrencyLevel().toString()));
+    } else if (regionAttributes != null) {
+      addAttribute(regionConfig, a -> a.setConcurrencyLevel(Integer.toString(
+          regionAttributes.getConcurrencyLevel())));
+    }
+
+    if (regionAttributes != null && regionAttributes.getDataPolicy() != null) {
+      addAttribute(regionConfig,
+          a -> a.setDataPolicy(regionAttributes.getDataPolicy().toConfigType()));
+    }
+
+    return regionConfig;
+  }
+
+  private String int2string(Integer i) {
+    return Optional.ofNullable(i).map(j -> j.toString()).orElse(null);
+  }
+
+  private String long2string(Long i) {
+    return Optional.ofNullable(i).map(j -> j.toString()).orElse(null);
+  }
+
+  private String getLeafRegion(String fullPath) {
+    String regionPath = fullPath;
+    String[] regions = regionPath.split("/");
+
+    return regions[regions.length - 1];
+  }
+
+  private void addAttribute(RegionConfig config, RegionAttributeSetFunction func) {
+    final List<RegionAttributesType> regionAttributes = config.getRegionAttributes();
+    if (regionAttributes.isEmpty()) {
+      regionAttributes.add(new RegionAttributesType());
+    }
+
+    func.setAttributeValue(regionAttributes.get(0));
+  }
+
+  private Object getRegionAttributeValue(RegionConfig config, RegionAttributeGetFunction function) {
+    return config.getRegionAttributes().stream()
+        .findFirst()
+        .map(a -> function.getValue(a))
+        .orElse(null);
+  }
+}
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/RegionAlterFunction.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/RegionAlterFunction.java
index fe53185..d568041 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/RegionAlterFunction.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/RegionAlterFunction.java
@@ -111,8 +111,8 @@ public class RegionAlterFunction implements InternalFunction {
 
     AttributesMutator mutator = region.getAttributesMutator();
 
-    if (regionAlterArgs.isCloningEnabled() != null) {
-      mutator.setCloningEnabled(regionAlterArgs.isCloningEnabled());
+    if (regionAlterArgs.getCloningEnabled() != null) {
+      mutator.setCloningEnabled(regionAlterArgs.getCloningEnabled());
       if (logger.isDebugEnabled()) {
         logger.debug("Region successfully altered - cloning");
       }
@@ -128,7 +128,7 @@ public class RegionAlterFunction implements InternalFunction {
     // Alter expiration attributes
     final RegionFunctionArgs.ExpirationAttrs newEntryExpirationIdleTime =
         regionAlterArgs.getEntryExpirationIdleTime();
-    if (newEntryExpirationIdleTime.isTimeOrActionSet()) {
+    if (newEntryExpirationIdleTime != null && newEntryExpirationIdleTime.isTimeOrActionSet()) {
       mutator.setEntryIdleTimeout(
           newEntryExpirationIdleTime.getExpirationAttributes(region.getEntryIdleTimeout()));
       if (logger.isDebugEnabled()) {
@@ -138,7 +138,7 @@ public class RegionAlterFunction implements InternalFunction {
 
     final RegionFunctionArgs.ExpirationAttrs newEntryExpirationTTL =
         regionAlterArgs.getEntryExpirationTTL();
-    if (newEntryExpirationTTL.isTimeOrActionSet()) {
+    if (newEntryExpirationTTL != null && newEntryExpirationTTL.isTimeOrActionSet()) {
       mutator.setEntryTimeToLive(
           newEntryExpirationTTL.getExpirationAttributes(region.getEntryTimeToLive()));
       if (logger.isDebugEnabled()) {
@@ -167,7 +167,7 @@ public class RegionAlterFunction implements InternalFunction {
 
     final RegionFunctionArgs.ExpirationAttrs newRegionExpirationIdleTime =
         regionAlterArgs.getRegionExpirationIdleTime();
-    if (newRegionExpirationIdleTime.isTimeOrActionSet()) {
+    if (newRegionExpirationIdleTime != null && newRegionExpirationIdleTime.isTimeOrActionSet()) {
       mutator.setRegionIdleTimeout(
           newRegionExpirationIdleTime.getExpirationAttributes(region.getRegionIdleTimeout()));
       if (logger.isDebugEnabled()) {
@@ -177,7 +177,7 @@ public class RegionAlterFunction implements InternalFunction {
 
     final RegionFunctionArgs.ExpirationAttrs newRegionExpirationTTL =
         regionAlterArgs.getRegionExpirationTTL();
-    if (newRegionExpirationTTL.isTimeOrActionSet()) {
+    if (newRegionExpirationTTL != null && newRegionExpirationTTL.isTimeOrActionSet()) {
       mutator.setRegionTimeToLive(
           newRegionExpirationTTL.getExpirationAttributes(region.getRegionTimeToLive()));
       if (logger.isDebugEnabled()) {
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/RegionCreateFunction.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/RegionCreateFunction.java
index 26ca760..353ee30 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/RegionCreateFunction.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/RegionCreateFunction.java
@@ -16,6 +16,7 @@ package org.apache.geode.management.internal.cli.functions;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
 
 import org.apache.commons.lang3.StringUtils;
@@ -84,21 +85,22 @@ public class RegionCreateFunction implements InternalFunction {
     if (regionCreateArgs.isIfNotExists()) {
       Region<Object, Object> region = cache.getRegion(regionCreateArgs.getRegionPath());
       if (region != null) {
-        resultSender.lastResult(new CliFunctionResult(memberNameOrId, true,
-            CliStrings.format(
-                CliStrings.CREATE_REGION__MSG__SKIPPING_0_REGION_PATH_1_ALREADY_EXISTS,
-                memberNameOrId, regionCreateArgs.getRegionPath())));
+        resultSender
+            .lastResult(new CliFunctionResult(memberNameOrId, CliFunctionResult.StatusState.OK,
+                CliStrings.format(
+                    CliStrings.CREATE_REGION__MSG__SKIPPING_0_REGION_PATH_1_ALREADY_EXISTS,
+                    memberNameOrId, regionCreateArgs.getRegionPath())));
         return;
       }
     }
 
     try {
       Region<?, ?> createdRegion = createRegion(cache, regionCreateArgs);
-      XmlEntity xmlEntity = getXmlEntityForRegion(createdRegion);
 
-      resultSender.lastResult(new CliFunctionResult(memberNameOrId, xmlEntity,
-          CliStrings.format(CliStrings.CREATE_REGION__MSG__REGION_0_CREATED_ON_1,
-              createdRegion.getFullPath(), memberNameOrId)));
+      resultSender
+          .lastResult(new CliFunctionResult(memberNameOrId, CliFunctionResult.StatusState.OK,
+              CliStrings.format(CliStrings.CREATE_REGION__MSG__REGION_0_CREATED_ON_1,
+                  createdRegion.getFullPath(), memberNameOrId)));
     } catch (IllegalStateException e) {
       String exceptionMsg = e.getMessage();
       String localizedString =
@@ -154,18 +156,9 @@ public class RegionCreateFunction implements InternalFunction {
     final RegionShortcut regionShortcut = regionCreateArgs.getRegionShortcut();
 
     // create the region factory using the arguments
-    boolean isPartitioned = false;
-    RegionFactory<K, V> factory = null;
-    RegionAttributes<K, V> regionAttributes = null;
-    if (regionShortcut != null) {
-      regionAttributes = cache.getRegionAttributes(regionShortcut.toString());
-      regionCreateArgs.setRegionAttributes(regionAttributes);
-    } else {
-      regionAttributes = regionCreateArgs.getRegionAttributes();
-    }
-
-    isPartitioned = regionAttributes.getPartitionAttributes() != null;
-    factory = cache.createRegionFactory(regionAttributes);
+    RegionAttributes<K, V> regionAttributes = regionCreateArgs.getRegionAttributes();
+    boolean isPartitioned = regionAttributes.getPartitionAttributes() != null;
+    RegionFactory<K, V> factory = cache.createRegionFactory(regionAttributes);
 
     if (isPartitioned) {
       PartitionAttributes<K, V> partitionAttributes =
@@ -197,7 +190,7 @@ public class RegionCreateFunction implements InternalFunction {
     // Expiration attributes
     final RegionFunctionArgs.ExpirationAttrs entryExpirationIdleTime =
         regionCreateArgs.getEntryExpirationIdleTime();
-    if (entryExpirationIdleTime.isTimeOrActionSet()) {
+    if (entryExpirationIdleTime != null && entryExpirationIdleTime.isTimeOrActionSet()) {
       factory.setEntryIdleTimeout(entryExpirationIdleTime.getExpirationAttributes());
     }
 
@@ -213,21 +206,23 @@ public class RegionCreateFunction implements InternalFunction {
 
     final RegionFunctionArgs.ExpirationAttrs entryExpirationTTL =
         regionCreateArgs.getEntryExpirationTTL();
-    if (entryExpirationTTL.isTimeOrActionSet()) {
+    if (entryExpirationTTL != null && entryExpirationTTL.isTimeOrActionSet()) {
       factory.setEntryTimeToLive(entryExpirationTTL.getExpirationAttributes());
     }
     final RegionFunctionArgs.ExpirationAttrs regionExpirationIdleTime =
         regionCreateArgs.getRegionExpirationIdleTime();
-    if (regionExpirationIdleTime.isTimeOrActionSet()) {
+    if (regionExpirationIdleTime != null && regionExpirationIdleTime.isTimeOrActionSet()) {
       factory.setRegionIdleTimeout(regionExpirationIdleTime.getExpirationAttributes());
     }
     final RegionFunctionArgs.ExpirationAttrs regionExpirationTTL =
         regionCreateArgs.getRegionExpirationTTL();
-    if (regionExpirationTTL.isTimeOrActionSet()) {
+    if (regionExpirationTTL != null && regionExpirationTTL.isTimeOrActionSet()) {
       factory.setRegionTimeToLive(regionExpirationTTL.getExpirationAttributes());
     }
 
-    EvictionAttributes evictionAttributes = regionCreateArgs.getEvictionAttributes();
+    EvictionAttributes evictionAttributes = Optional
+        .ofNullable(regionCreateArgs.getEvictionAttributes())
+        .map(a -> a.convertToEvictionAttributes()).orElse(null);
     if (evictionAttributes != null) {
       ObjectSizer sizer = evictionAttributes.getObjectSizer();
       if (sizer != null && !(sizer instanceof Declarable)) {
@@ -243,24 +238,24 @@ public class RegionCreateFunction implements InternalFunction {
       factory.setDiskStoreName(diskStore);
     }
 
-    if (regionCreateArgs.isDiskSynchronous() != null) {
-      factory.setDiskSynchronous(regionCreateArgs.isDiskSynchronous());
+    if (regionCreateArgs.getDiskSynchronous() != null) {
+      factory.setDiskSynchronous(regionCreateArgs.getDiskSynchronous());
     }
 
-    if (regionCreateArgs.isOffHeap() != null) {
-      factory.setOffHeap(regionCreateArgs.isOffHeap());
+    if (regionCreateArgs.getOffHeap() != null) {
+      factory.setOffHeap(regionCreateArgs.getOffHeap());
     }
 
-    if (regionCreateArgs.isStatisticsEnabled() != null) {
-      factory.setStatisticsEnabled(regionCreateArgs.isStatisticsEnabled());
+    if (regionCreateArgs.getStatisticsEnabled() != null) {
+      factory.setStatisticsEnabled(regionCreateArgs.getStatisticsEnabled());
     }
 
-    if (regionCreateArgs.isEnableAsyncConflation() != null) {
-      factory.setEnableAsyncConflation(regionCreateArgs.isEnableAsyncConflation());
+    if (regionCreateArgs.getEnableAsyncConflation() != null) {
+      factory.setEnableAsyncConflation(regionCreateArgs.getEnableAsyncConflation());
     }
 
-    if (regionCreateArgs.isEnableSubscriptionConflation() != null) {
-      factory.setEnableSubscriptionConflation(regionCreateArgs.isEnableSubscriptionConflation());
+    if (regionCreateArgs.getEnableSubscriptionConflation() != null) {
+      factory.setEnableSubscriptionConflation(regionCreateArgs.getEnableSubscriptionConflation());
     }
 
     // Gateway Sender Ids
@@ -279,20 +274,20 @@ public class RegionCreateFunction implements InternalFunction {
       }
     }
 
-    if (regionCreateArgs.isConcurrencyChecksEnabled() != null) {
-      factory.setConcurrencyChecksEnabled(regionCreateArgs.isConcurrencyChecksEnabled());
+    if (regionCreateArgs.getConcurrencyChecksEnabled() != null) {
+      factory.setConcurrencyChecksEnabled(regionCreateArgs.getConcurrencyChecksEnabled());
     }
 
     if (regionCreateArgs.getConcurrencyLevel() != null) {
       factory.setConcurrencyLevel(regionCreateArgs.getConcurrencyLevel());
     }
 
-    if (regionCreateArgs.isCloningEnabled() != null) {
-      factory.setCloningEnabled(regionCreateArgs.isCloningEnabled());
+    if (regionCreateArgs.getCloningEnabled() != null) {
+      factory.setCloningEnabled(regionCreateArgs.getCloningEnabled());
     }
 
-    if (regionCreateArgs.isMcastEnabled() != null) {
-      factory.setMulticastEnabled(regionCreateArgs.isMcastEnabled());
+    if (regionCreateArgs.getMcastEnabled() != null) {
+      factory.setMulticastEnabled(regionCreateArgs.getMcastEnabled());
     }
 
     // Set plugins
@@ -341,9 +336,8 @@ public class RegionCreateFunction implements InternalFunction {
   @SuppressWarnings("unchecked")
   private static <K, V> PartitionAttributes<K, V> extractPartitionAttributes(Cache cache,
       RegionAttributes<K, V> regionAttributes, RegionFunctionArgs regionCreateArgs) {
-    RegionFunctionArgs.PartitionArgs partitionArgs = regionCreateArgs.getPartitionArgs();
 
-    PartitionAttributesFactory<K, V> prAttrFactory = null;
+    PartitionAttributesFactory<K, V> prAttrFactory;
 
     PartitionAttributes<K, V> partitionAttributes = regionAttributes.getPartitionAttributes();
     if (partitionAttributes != null) {
@@ -352,46 +346,49 @@ public class RegionCreateFunction implements InternalFunction {
       prAttrFactory = new PartitionAttributesFactory<>();
     }
 
-    String colocatedWith = partitionArgs.getPrColocatedWith();
-    if (colocatedWith != null) {
-      Region<Object, Object> colocatedWithRegion = cache.getRegion(colocatedWith);
-      if (colocatedWithRegion == null) {
-        throw new IllegalArgumentException(CliStrings.format(
-            CliStrings.CREATE_REGION__MSG__COLOCATEDWITH_REGION_0_DOES_NOT_EXIST, colocatedWith));
+    if (regionCreateArgs.hasPartitionAttributes()) {
+      RegionFunctionArgs.PartitionArgs partitionArgs = regionCreateArgs.getPartitionArgs();
+      String colocatedWith = partitionArgs.getPrColocatedWith();
+      if (colocatedWith != null) {
+        Region<Object, Object> colocatedWithRegion = cache.getRegion(colocatedWith);
+        if (colocatedWithRegion == null) {
+          throw new IllegalArgumentException(CliStrings.format(
+              CliStrings.CREATE_REGION__MSG__COLOCATEDWITH_REGION_0_DOES_NOT_EXIST, colocatedWith));
+        }
+        if (!colocatedWithRegion.getAttributes().getDataPolicy().withPartitioning()) {
+          throw new IllegalArgumentException(CliStrings.format(
+              CliStrings.CREATE_REGION__MSG__COLOCATEDWITH_REGION_0_IS_NOT_PARTITIONEDREGION,
+              colocatedWith));
+        }
+        prAttrFactory.setColocatedWith(colocatedWith);
       }
-      if (!colocatedWithRegion.getAttributes().getDataPolicy().withPartitioning()) {
-        throw new IllegalArgumentException(CliStrings.format(
-            CliStrings.CREATE_REGION__MSG__COLOCATEDWITH_REGION_0_IS_NOT_PARTITIONEDREGION,
-            colocatedWith));
+      if (partitionArgs.getPrLocalMaxMemory() != null) {
+        prAttrFactory.setLocalMaxMemory(partitionArgs.getPrLocalMaxMemory());
+      }
+      if (partitionArgs.getPrTotalMaxMemory() != null) {
+        prAttrFactory.setTotalMaxMemory(partitionArgs.getPrTotalMaxMemory());
+      }
+      if (partitionArgs.getPrTotalNumBuckets() != null) {
+        prAttrFactory.setTotalNumBuckets(partitionArgs.getPrTotalNumBuckets());
+      }
+      if (partitionArgs.getPrRedundantCopies() != null) {
+        prAttrFactory.setRedundantCopies(partitionArgs.getPrRedundantCopies());
+      }
+      if (partitionArgs.getPrRecoveryDelay() != null) {
+        prAttrFactory.setRecoveryDelay(partitionArgs.getPrRecoveryDelay());
+      }
+      if (partitionArgs.getPrStartupRecoveryDelay() != null) {
+        prAttrFactory.setStartupRecoveryDelay(partitionArgs.getPrStartupRecoveryDelay());
       }
-      prAttrFactory.setColocatedWith(colocatedWith);
-    }
-    if (partitionArgs.getPrLocalMaxMemory() != null) {
-      prAttrFactory.setLocalMaxMemory(partitionArgs.getPrLocalMaxMemory());
-    }
-    if (partitionArgs.getPrTotalMaxMemory() != null) {
-      prAttrFactory.setTotalMaxMemory(partitionArgs.getPrTotalMaxMemory());
-    }
-    if (partitionArgs.getPrTotalNumBuckets() != null) {
-      prAttrFactory.setTotalNumBuckets(partitionArgs.getPrTotalNumBuckets());
-    }
-    if (partitionArgs.getPrRedundantCopies() != null) {
-      prAttrFactory.setRedundantCopies(partitionArgs.getPrRedundantCopies());
-    }
-    if (partitionArgs.getPrRecoveryDelay() != null) {
-      prAttrFactory.setRecoveryDelay(partitionArgs.getPrRecoveryDelay());
-    }
-    if (partitionArgs.getPrStartupRecoveryDelay() != null) {
-      prAttrFactory.setStartupRecoveryDelay(partitionArgs.getPrStartupRecoveryDelay());
-    }
 
-    if (regionCreateArgs.getPartitionArgs().getPartitionResolver() != null) {
-      Class<PartitionResolver> partitionResolverClass =
-          forName(regionCreateArgs.getPartitionArgs().getPartitionResolver(),
-              CliStrings.CREATE_REGION__PARTITION_RESOLVER);
-      prAttrFactory
-          .setPartitionResolver((PartitionResolver<K, V>) newInstance(partitionResolverClass,
-              CliStrings.CREATE_REGION__PARTITION_RESOLVER));
+      if (regionCreateArgs.getPartitionArgs().getPartitionResolver() != null) {
+        Class<PartitionResolver> partitionResolverClass =
+            forName(regionCreateArgs.getPartitionArgs().getPartitionResolver(),
+                CliStrings.CREATE_REGION__PARTITION_RESOLVER);
+        prAttrFactory
+            .setPartitionResolver((PartitionResolver<K, V>) newInstance(partitionResolverClass,
+                CliStrings.CREATE_REGION__PARTITION_RESOLVER));
+      }
     }
     return prAttrFactory.create();
   }
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/RegionFunctionArgs.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/RegionFunctionArgs.java
index 948ba29..21d975d 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/RegionFunctionArgs.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/RegionFunctionArgs.java
@@ -33,6 +33,8 @@ import org.apache.geode.cache.ExpirationAction;
 import org.apache.geode.cache.ExpirationAttributes;
 import org.apache.geode.cache.RegionAttributes;
 import org.apache.geode.cache.RegionShortcut;
+import org.apache.geode.cache.configuration.EnumActionDestroyOverflow;
+import org.apache.geode.cache.configuration.RegionAttributesType;
 import org.apache.geode.cache.util.ObjectSizer;
 import org.apache.geode.internal.ClassPathLoader;
 import org.apache.geode.management.internal.cli.domain.ClassName;
@@ -80,9 +82,7 @@ public class RegionFunctionArgs implements Serializable {
   private Boolean offHeap;
   private RegionAttributes<?, ?> regionAttributes;
 
-  public RegionFunctionArgs() {
-    this.partitionArgs = new PartitionArgs();
-  }
+  public RegionFunctionArgs() {}
 
   public void setRegionPath(String regionPath) {
     this.regionPath = regionPath;
@@ -114,18 +114,34 @@ public class RegionFunctionArgs implements Serializable {
   }
 
   public void setEntryExpirationIdleTime(Integer timeout, ExpirationAction action) {
+    if (timeout == null && action == null) {
+      return;
+    }
+
     this.entryExpirationIdleTime = new ExpirationAttrs(timeout, action);
   }
 
   public void setEntryExpirationTTL(Integer timeout, ExpirationAction action) {
+    if (timeout == null && action == null) {
+      return;
+    }
+
     this.entryExpirationTTL = new ExpirationAttrs(timeout, action);
   }
 
   public void setRegionExpirationIdleTime(Integer timeout, ExpirationAction action) {
+    if (timeout == null && action == null) {
+      return;
+    }
+
     this.regionExpirationIdleTime = new ExpirationAttrs(timeout, action);
   }
 
   public void setRegionExpirationTTL(Integer timeout, ExpirationAction action) {
+    if (timeout == null && action == null) {
+      return;
+    }
+
     this.regionExpirationTTL = new ExpirationAttrs(timeout, action);
   }
 
@@ -199,6 +215,19 @@ public class RegionFunctionArgs implements Serializable {
   public void setPartitionArgs(String prColocatedWith, Integer prLocalMaxMemory,
       Long prRecoveryDelay, Integer prRedundantCopies, Long prStartupRecoveryDelay,
       Long prTotalMaxMemory, Integer prTotalNumBuckets, String partitionResolver) {
+    if (prColocatedWith == null &&
+        prLocalMaxMemory == null &&
+        prRecoveryDelay == null &&
+        prRedundantCopies == null &&
+        prStartupRecoveryDelay == null &&
+        prTotalMaxMemory == null &&
+        prTotalNumBuckets == null &&
+        partitionResolver == null) {
+      return;
+    }
+    if (partitionArgs == null) {
+      partitionArgs = new PartitionArgs();
+    }
     partitionArgs.setPrColocatedWith(prColocatedWith);
     partitionArgs.setPrLocalMaxMemory(prLocalMaxMemory);
     partitionArgs.setPrRecoveryDelay(prRecoveryDelay);
@@ -270,7 +299,7 @@ public class RegionFunctionArgs implements Serializable {
   /**
    * @return the statisticsEnabled
    */
-  public Boolean isStatisticsEnabled() {
+  public Boolean getStatisticsEnabled() {
     return this.statisticsEnabled;
   }
 
@@ -312,25 +341,25 @@ public class RegionFunctionArgs implements Serializable {
   /**
    * @return the diskSynchronous
    */
-  public Boolean isDiskSynchronous() {
+  public Boolean getDiskSynchronous() {
     return this.diskSynchronous;
   }
 
-  public Boolean isOffHeap() {
+  public Boolean getOffHeap() {
     return this.offHeap;
   }
 
   /**
    * @return the enableAsyncConflation
    */
-  public Boolean isEnableAsyncConflation() {
+  public Boolean getEnableAsyncConflation() {
     return this.enableAsyncConflation;
   }
 
   /**
    * @return the enableSubscriptionConflation
    */
-  public Boolean isEnableSubscriptionConflation() {
+  public Boolean getEnableSubscriptionConflation() {
     return this.enableSubscriptionConflation;
   }
 
@@ -381,21 +410,21 @@ public class RegionFunctionArgs implements Serializable {
   /**
    * @return the concurrencyChecksEnabled
    */
-  public Boolean isConcurrencyChecksEnabled() {
+  public Boolean getConcurrencyChecksEnabled() {
     return this.concurrencyChecksEnabled;
   }
 
   /**
    * @return the cloningEnabled
    */
-  public Boolean isCloningEnabled() {
+  public Boolean getCloningEnabled() {
     return this.cloningEnabled;
   }
 
   /**
    * @return the mcastEnabled setting
    */
-  public Boolean isMcastEnabled() {
+  public Boolean getMcastEnabled() {
     return this.mcastEnabled;
   }
 
@@ -415,7 +444,7 @@ public class RegionFunctionArgs implements Serializable {
    * @return the partitionArgs
    */
   public boolean hasPartitionAttributes() {
-    return this.partitionArgs.hasPartitionAttributes();
+    return this.partitionArgs != null && this.partitionArgs.hasPartitionAttributes();
   }
 
   /**
@@ -439,8 +468,8 @@ public class RegionFunctionArgs implements Serializable {
     return this.compressor;
   }
 
-  public EvictionAttributes getEvictionAttributes() {
-    return evictionAttributes != null ? evictionAttributes.convertToEvictionAttributes() : null;
+  public EvictionAttrs getEvictionAttributes() {
+    return this.evictionAttributes;
   }
 
   /**
@@ -601,6 +630,35 @@ public class RegionFunctionArgs implements Serializable {
         return EvictionAttributes.createLRUEntryAttributes(maxEntryCount, action);
       }
     }
+
+    public RegionAttributesType.EvictionAttributes convertToConfigEvictionAttributes() {
+      RegionAttributesType.EvictionAttributes configAttributes =
+          new RegionAttributesType.EvictionAttributes();
+      EnumActionDestroyOverflow action = EnumActionDestroyOverflow.fromValue(evictionAction);
+
+      if (maxMemory == null && maxEntryCount == null) {
+        RegionAttributesType.EvictionAttributes.LruHeapPercentage heapPercentage =
+            new RegionAttributesType.EvictionAttributes.LruHeapPercentage();
+        heapPercentage.setAction(action);
+        heapPercentage.setClassName(objectSizer);
+        configAttributes.setLruHeapPercentage(heapPercentage);
+      } else if (maxMemory != null) {
+        RegionAttributesType.EvictionAttributes.LruMemorySize memorySize =
+            new RegionAttributesType.EvictionAttributes.LruMemorySize();
+        memorySize.setAction(action);
+        memorySize.setClassName(objectSizer);
+        memorySize.setMaximum(maxMemory.toString());
+        configAttributes.setLruMemorySize(memorySize);
+      } else {
+        RegionAttributesType.EvictionAttributes.LruEntryCount entryCount =
+            new RegionAttributesType.EvictionAttributes.LruEntryCount();
+        entryCount.setAction(action);
+        entryCount.setMaximum(maxEntryCount.toString());
+        configAttributes.setLruEntryCount(entryCount);
+      }
+
+      return configAttributes;
+    }
   }
 
 
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/i18n/CliStrings.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/i18n/CliStrings.java
index a7cbd16..bd96248 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/i18n/CliStrings.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/i18n/CliStrings.java
@@ -946,6 +946,8 @@ public class CliStrings {
   public static final String CREATE_REGION__TOTALNUMBUCKETS__HELP =
       "Sets the total number of hash buckets to be used by the region in all processes. (Default: "
           + PartitionAttributesFactory.GLOBAL_MAX_BUCKETS_DEFAULT + ").";
+  public static final String CREATE_REGION__MSG__CANNOT_DISABLE_CLONING_WITH_COMPRESSOR =
+      "Cannot set enable-cloning to false when compressor is provided";
   public static final String CREATE_REGION__MSG__SPECIFY_VALID_REGION_PATH =
       "Specify a valid " + CliStrings.CREATE_REGION__REGION;
   public static final String CREATE_REGION__MSG__PARENT_REGION_FOR_0_DOES_NOT_EXIST =
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/RegionPath.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/RegionPath.java
index aac49bf..3bd548b 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/RegionPath.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/RegionPath.java
@@ -14,7 +14,9 @@
  */
 package org.apache.geode.management.internal.cli.util;
 
+import java.util.ArrayList;
 import java.util.LinkedList;
+import java.util.List;
 
 import org.apache.geode.cache.Cache;
 import org.apache.geode.cache.Region;
@@ -67,6 +69,20 @@ public class RegionPath {
     return regionParentPath;
   }
 
+  public String[] getRegionsOnParentPath() {
+    String[] regionsOnPath = getParent().split(Region.SEPARATOR);
+
+    // Ignore preceding separator if there is one
+    int start = regionsOnPath[0] == null || regionsOnPath[0].isEmpty() ? 1 : 0;
+
+    List<String> regions = new ArrayList<>();
+    for (int i = start; i < regionsOnPath.length; i++) {
+      regions.add(regionsOnPath[i]);
+    }
+
+    return regions.toArray(new String[] {});
+  }
+
   /**
    * @return Parent RegionPath of this RegionPath. null if this is a root region
    */
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/RegionProvider.java b/geode-core/src/main/java/org/apache/geode/redis/internal/RegionProvider.java
index 8d38c49..d3649f1 100644
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/RegionProvider.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/RegionProvider.java
@@ -41,9 +41,9 @@ import org.apache.geode.cache.query.QueryService;
 import org.apache.geode.cache.query.RegionNotFoundException;
 import org.apache.geode.internal.cache.GemFireCacheImpl;
 import org.apache.geode.internal.hll.HyperLogLogPlus;
-import org.apache.geode.management.cli.Result;
 import org.apache.geode.management.cli.Result.Status;
 import org.apache.geode.management.internal.cli.commands.CreateRegionCommand;
+import org.apache.geode.management.internal.cli.result.model.ResultModel;
 import org.apache.geode.redis.GeodeRedisServer;
 import org.apache.geode.redis.internal.executor.ExpirationExecutor;
 import org.apache.geode.redis.internal.executor.ListQuery;
@@ -399,17 +399,19 @@ public class RegionProvider implements Closeable {
       return r;
     do {
       createRegionCmd.setCache(cache);
-      Result result = createRegionCmd.createRegion(regionPath, defaultRegionType, null, null, true,
-          null, null, null, null, null, null, null, null, false, false, true, false, false, false,
-          true, null, null, null, null, null, null, null, null, null, null, null, null, null, false,
-          null, null, null, null, null, null, null, null, null, null, null);
+      ResultModel resultModel =
+          createRegionCmd.createRegion(regionPath, defaultRegionType, null, null, true,
+              null, null, null, null, null, null, null, null, false, false, true, false, false,
+              false,
+              true, null, null, null, null, null, null, null, null, null, null, null, null, null,
+              false,
+              null, null, null, null, null, null, null, null, null, null, null);
 
       r = cache.getRegion(regionPath);
-      if (result.getStatus() == Status.ERROR && r == null) {
+      if (resultModel.getStatus() == Status.ERROR && r == null) {
         String err = "Unable to create region named \"" + regionPath + "\":\n";
-        while (result.hasNextLine())
-          err += result.nextLine();
-        throw new RegionCreationException(err);
+        // TODO: check this
+        throw new RegionCreationException(err + resultModel.toJson());
       }
     } while (r == null); // The region can be null in the case that it is concurrently destroyed by
     // a remote even triggered internally by Geode
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/CreateRegionCommandTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/CreateRegionCommandTest.java
index 4d68704..83ea16b 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/CreateRegionCommandTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/CreateRegionCommandTest.java
@@ -172,31 +172,31 @@ public class CreateRegionCommandTest {
     assertThat(args.isIfNotExists()).isFalse();
     assertThat(args.getKeyConstraint()).isNull();
     assertThat(args.getValueConstraint()).isNull();
-    assertThat(args.isStatisticsEnabled()).isNull();
+    assertThat(args.getStatisticsEnabled()).isNull();
 
     ExpirationAttrs empty = new ExpirationAttrs(null, null);
-    assertThat(args.getEntryExpirationIdleTime()).isEqualTo(empty);
-    assertThat(args.getEntryExpirationTTL()).isEqualTo(empty);
-    assertThat(args.getRegionExpirationIdleTime()).isEqualTo(empty);
-    assertThat(args.getRegionExpirationTTL()).isEqualTo(empty);
+    assertThat(args.getEntryExpirationIdleTime()).isNull();
+    assertThat(args.getEntryExpirationTTL()).isNull();
+    assertThat(args.getRegionExpirationIdleTime()).isNull();
+    assertThat(args.getRegionExpirationTTL()).isNull();
 
     assertThat(args.getDiskStore()).isNull();
-    assertThat(args.isDiskSynchronous()).isNull();
-    assertThat(args.isEnableAsyncConflation()).isNull();
-    assertThat(args.isEnableSubscriptionConflation()).isNull();
+    assertThat(args.getDiskSynchronous()).isNull();
+    assertThat(args.getEnableAsyncConflation()).isNull();
+    assertThat(args.getEnableSubscriptionConflation()).isNull();
     assertThat(args.getCacheListeners()).isEmpty();
     assertThat(args.getCacheLoader()).isNull();
     assertThat(args.getCacheWriter()).isNull();
     assertThat(args.getAsyncEventQueueIds()).isEmpty();
     assertThat(args.getGatewaySenderIds()).isEmpty();
-    assertThat(args.isConcurrencyChecksEnabled()).isNull();
-    assertThat(args.isCloningEnabled()).isNull();
-    assertThat(args.isMcastEnabled()).isNull();
+    assertThat(args.getConcurrencyChecksEnabled()).isNull();
+    assertThat(args.getCloningEnabled()).isNull();
+    assertThat(args.getMcastEnabled()).isNull();
     assertThat(args.getConcurrencyLevel()).isNull();
-    assertThat(args.getPartitionArgs()).isNotNull();
+    assertThat(args.getPartitionArgs()).isNull();
     assertThat(args.getEvictionMax()).isNull();
     assertThat(args.getCompressor()).isNull();
-    assertThat(args.isOffHeap()).isNull();
+    assertThat(args.getOffHeap()).isNull();
     assertThat(args.getRegionAttributes()).isNull();
   }
 
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/domain/RegionConfigFactoryTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/domain/RegionConfigFactoryTest.java
new file mode 100644
index 0000000..31b5bd9
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/domain/RegionConfigFactoryTest.java
@@ -0,0 +1,273 @@
+/*
+ * 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.cli.domain;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.geode.cache.EvictionAction;
+import org.apache.geode.cache.ExpirationAction;
+import org.apache.geode.cache.configuration.ClassNameType;
+import org.apache.geode.cache.configuration.DeclarableType;
+import org.apache.geode.cache.configuration.EnumActionDestroyOverflow;
+import org.apache.geode.cache.configuration.RegionAttributesType;
+import org.apache.geode.cache.configuration.RegionConfig;
+import org.apache.geode.management.internal.cli.functions.RegionFunctionArgs;
+
+public class RegionConfigFactoryTest {
+
+  RegionConfigFactory subject;
+  RegionFunctionArgs args;
+
+  @Before
+  public void setup() {
+    subject = new RegionConfigFactory();
+    args = new RegionFunctionArgs();
+    args.setRegionPath("region-name");
+  }
+
+  @Test
+  public void generatesConfigForRegion() {
+    RegionConfig config = subject.generate(args);
+    assertThat(config.getName()).isEqualTo("region-name");
+  }
+
+  @Test
+  public void generatesConfigForSubRegion() {
+    args.setRegionPath("region-name/subregion");
+
+    RegionConfig config = subject.generate(args);
+    assertThat(config.getName()).isEqualTo("subregion");
+  }
+
+  @Test
+  public void generatesWithNoAttributes() {
+    RegionConfig config = subject.generate(args);
+    assertThat(config.getRegionAttributes()).isEmpty();
+  }
+
+  @Test
+  public void generatesWithConstraintAttributes() {
+    args.setKeyConstraint("key-const");
+    args.setValueConstraint("value-const");
+
+    RegionConfig config = subject.generate(args);
+    assertThat(getRegionAttributeValue(config, t -> t.getKeyConstraint())).isEqualTo("key-const");
+    assertThat(getRegionAttributeValue(config, t -> t.getValueConstraint()))
+        .isEqualTo("value-const");
+  }
+
+  @Test
+  public void generatesWithExpirationIdleTimeAttributes() {
+    args.setRegionExpirationTTL(10, ExpirationAction.DESTROY);
+    args.setRegionExpirationIdleTime(3, ExpirationAction.INVALIDATE);
+    args.setEntryExpirationTTL(1, ExpirationAction.LOCAL_DESTROY);
+    args.setEntryExpirationIdleTime(12, ExpirationAction.LOCAL_DESTROY);
+    args.setEntryIdleTimeCustomExpiry(new ClassName<>("java.lang.String"));
+
+    RegionConfig config = subject.generate(args);
+    RegionAttributesType.RegionTimeToLive regionTimeToLive =
+        (RegionAttributesType.RegionTimeToLive) getRegionAttributeValue(config,
+            t -> t.getRegionTimeToLive());
+    assertThat(regionTimeToLive.getExpirationAttributes().getTimeout()).isEqualTo("10");
+
+    RegionAttributesType.EntryTimeToLive entryTimeToLive =
+        (RegionAttributesType.EntryTimeToLive) getRegionAttributeValue(config,
+            t -> t.getEntryTimeToLive());
+    assertThat(entryTimeToLive.getExpirationAttributes().getAction())
+        .isEqualTo(ExpirationAction.LOCAL_DESTROY.toXmlString());
+
+    RegionAttributesType.EntryIdleTime entryIdleTime =
+        (RegionAttributesType.EntryIdleTime) getRegionAttributeValue(config,
+            t -> t.getEntryIdleTime());
+    DeclarableType customExpiry = entryIdleTime.getExpirationAttributes().getCustomExpiry();
+    assertThat(customExpiry.getClassName()).isEqualTo("java.lang.String");
+    assertThat(entryIdleTime.getExpirationAttributes().getAction())
+        .isEqualTo(ExpirationAction.LOCAL_DESTROY.toXmlString());
+    assertThat(entryIdleTime.getExpirationAttributes().getTimeout())
+        .isEqualTo("12");
+  }
+
+  @Test
+  public void generatesWithDiskAttributes() {
+    args.setDiskStore("disk-store");
+    args.setDiskSynchronous(false);
+
+    RegionConfig config = subject.generate(args);
+    assertThat(getRegionAttributeValue(config, t -> t.getDiskStoreName())).isEqualTo("disk-store");
+    assertThat(getRegionAttributeValue(config, t -> t.isDiskSynchronous())).isEqualTo(false);
+  }
+
+  @Test
+  public void generatesWithPrAttributes() {
+    args.setPartitionArgs("colo-with", 100,
+        100L, 100, 100L,
+        100L, 100, "java.lang.String");
+
+    RegionConfig config = subject.generate(args);
+    RegionAttributesType.PartitionAttributes partitionAttributes =
+        (RegionAttributesType.PartitionAttributes) getRegionAttributeValue(config,
+            t -> t.getPartitionAttributes());
+    assertThat(partitionAttributes).isNotNull();
+    assertThat(partitionAttributes.getColocatedWith()).isEqualTo("colo-with");
+    assertThat(partitionAttributes.getLocalMaxMemory()).isEqualTo("100");
+    assertThat(partitionAttributes.getRecoveryDelay()).isEqualTo("100");
+    assertThat(partitionAttributes.getRedundantCopies()).isEqualTo("100");
+    assertThat(partitionAttributes.getStartupRecoveryDelay()).isEqualTo("100");
+    assertThat(partitionAttributes.getTotalMaxMemory()).isEqualTo("100");
+    assertThat(partitionAttributes.getTotalNumBuckets()).isEqualTo("100");
+
+    DeclarableType partitionResolverType = partitionAttributes.getPartitionResolver();
+    assertThat(partitionResolverType.getClassName()).isEqualTo("java.lang.String");
+  }
+
+  @Test
+  public void generatesWithMiscBooleanFlags() {
+    args.setStatisticsEnabled(false);
+    args.setEnableAsyncConflation(false);
+    args.setConcurrencyChecksEnabled(true);
+    args.setEnableSubscriptionConflation(true);
+    args.setMcastEnabled(false);
+    args.setCloningEnabled(false);
+    args.setOffHeap(true);
+    RegionConfig config = subject.generate(args);
+
+    assertThat(getRegionAttributeValue(config, t -> t.isStatisticsEnabled())).isEqualTo(false);
+    assertThat(getRegionAttributeValue(config, t -> t.isEnableSubscriptionConflation()))
+        .isEqualTo(true);
+    assertThat(getRegionAttributeValue(config, t -> t.isConcurrencyChecksEnabled()))
+        .isEqualTo(true);
+    assertThat(getRegionAttributeValue(config, t -> t.isEnableSubscriptionConflation()))
+        .isEqualTo(true);
+    assertThat(getRegionAttributeValue(config, t -> t.isMulticastEnabled()))
+        .isEqualTo(false);
+    assertThat(getRegionAttributeValue(config, t -> t.isCloningEnabled())).isEqualTo(false);
+    assertThat(getRegionAttributeValue(config, t -> t.isOffHeap())).isEqualTo(true);
+  }
+
+  @Test
+  public void generatesWithGatewayFlags() {
+    args.setGatewaySenderIds(new String[] {"some-id", "some-other-id"});
+    RegionConfig config = subject.generate(args);
+
+    assertThat((String) getRegionAttributeValue(config, t -> t.getGatewaySenderIds()))
+        .contains("some-id");
+    assertThat((String) getRegionAttributeValue(config, t -> t.getGatewaySenderIds()))
+        .contains("some-other-id");
+  }
+
+  @Test
+  public void generatesWithEvictionHeapPercentageFlags() {
+    args.setEvictionAttributes(EvictionAction.LOCAL_DESTROY.toString(), null, null,
+        "java.lang.String");
+    RegionConfig config = subject.generate(args);
+
+    RegionAttributesType.EvictionAttributes evictionAttributes =
+        (RegionAttributesType.EvictionAttributes) getRegionAttributeValue(config,
+            t -> t.getEvictionAttributes());
+    assertThat(evictionAttributes).isNotNull();
+    assertThat(evictionAttributes.getLruHeapPercentage().getAction())
+        .isSameAs(EnumActionDestroyOverflow.LOCAL_DESTROY);
+    assertThat(evictionAttributes.getLruHeapPercentage().getClassName())
+        .isEqualTo("java.lang.String");
+  }
+
+  @Test
+  public void generatesWithEvictionMaxMemory() {
+    args.setEvictionAttributes(EvictionAction.LOCAL_DESTROY.toString(), 100, null,
+        null);
+    RegionConfig config = subject.generate(args);
+
+    RegionAttributesType.EvictionAttributes evictionAttributes =
+        (RegionAttributesType.EvictionAttributes) getRegionAttributeValue(config,
+            t -> t.getEvictionAttributes());
+    assertThat(evictionAttributes).isNotNull();
+    assertThat(evictionAttributes.getLruMemorySize().getAction())
+        .isSameAs(EnumActionDestroyOverflow.LOCAL_DESTROY);
+    assertThat(evictionAttributes.getLruMemorySize().getMaximum()).isEqualTo("100");
+  }
+
+  @Test
+  public void generatesWithEvictionMaxEntry() {
+    args.setEvictionAttributes(EvictionAction.OVERFLOW_TO_DISK.toString(), null, 1,
+        null);
+    RegionConfig config = subject.generate(args);
+    RegionAttributesType.EvictionAttributes evictionAttributes =
+        (RegionAttributesType.EvictionAttributes) getRegionAttributeValue(config,
+            t -> t.getEvictionAttributes());
+    assertThat(evictionAttributes).isNotNull();
+    assertThat(evictionAttributes.getLruEntryCount().getAction())
+        .isSameAs(EnumActionDestroyOverflow.OVERFLOW_TO_DISK);
+    assertThat(evictionAttributes.getLruEntryCount().getMaximum()).isEqualTo("1");
+  }
+
+  @Test
+  public void generatesWithAsyncEventQueueIds() {
+    args.setAsyncEventQueueIds(new String[] {"id-1", "id-2"});
+    RegionConfig config = subject.generate(args);
+
+    assertThat((String) getRegionAttributeValue(config, t -> t.getAsyncEventQueueIds()))
+        .contains("id-1");
+    assertThat((String) getRegionAttributeValue(config, t -> t.getAsyncEventQueueIds()))
+        .contains("id-2");
+  }
+
+  @Test
+  public void generatesWithCacheClasses() {
+    args.setCacheListeners(new ClassName[] {new ClassName("java.lang.String")});
+    args.setCacheLoader(new ClassName("java.lang.String"));
+    args.setCacheWriter(new ClassName("java.lang.String"));
+    RegionConfig config = subject.generate(args);
+
+    List<DeclarableType> cacheListeners = config.getRegionAttributes().stream()
+        .filter(a -> !a.getCacheListeners().isEmpty())
+        .findFirst()
+        .map(a -> a.getCacheListeners())
+        .orElse(null);
+
+    assertThat(cacheListeners).isNotNull();
+    assertThat(cacheListeners.get(0).getClassName()).isEqualTo("java.lang.String");
+    assertThat(
+        ((DeclarableType) getRegionAttributeValue(config, t -> t.getCacheLoader())).getClassName())
+            .isEqualTo("java.lang.String");
+    assertThat(
+        ((DeclarableType) getRegionAttributeValue(config, t -> t.getCacheWriter())).getClassName())
+            .isEqualTo("java.lang.String");
+  }
+
+  @Test
+  public void generatesWithOtherMiscSimpleFlags() {
+    args.setCompressor("java.lang.String");
+    args.setConcurrencyLevel(1);
+
+    RegionConfig config = subject.generate(args);
+
+    assertThat(
+        ((ClassNameType) getRegionAttributeValue(config, t -> t.getCompressor())).getClassName())
+            .isEqualTo("java.lang.String");
+    assertThat(getRegionAttributeValue(config, t -> t.getConcurrencyLevel())).isEqualTo("1");
+  }
+
+  private Object getRegionAttributeValue(RegionConfig config, RegionAttributeGetFunction function) {
+    return config.getRegionAttributes().stream()
+        .findFirst()
+        .map(a -> function.getValue(a))
+        .orElse(null);
+  }
+}
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/functions/RegionFunctionArgsTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/functions/RegionFunctionArgsTest.java
index de22efb..12d6dd5 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/functions/RegionFunctionArgsTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/functions/RegionFunctionArgsTest.java
@@ -37,16 +37,40 @@ public class RegionFunctionArgsTest {
 
   @Test
   public void defaultRegionFunctionArgs() throws Exception {
-    assertThat(args.isDiskSynchronous()).isNull();
-    assertThat(args.isCloningEnabled()).isNull();
-    assertThat(args.isConcurrencyChecksEnabled()).isNull();
+    assertThat(args.getDiskSynchronous()).isNull();
+    assertThat(args.getCloningEnabled()).isNull();
+    assertThat(args.getConcurrencyChecksEnabled()).isNull();
     assertThat(args.getConcurrencyLevel()).isNull();
-    assertThat(args.getPartitionArgs()).isNotNull();
-    assertThat(args.getPartitionArgs().hasPartitionAttributes()).isFalse();
+    assertThat(args.getPartitionArgs()).isNull();
+    assertThat(args.hasPartitionAttributes()).isFalse();
     assertThat(args.getEvictionAttributes()).isNull();
   }
 
   @Test
+  public void emptyPartitionArgsShouldBeNull() throws Exception {
+    args.setPartitionArgs(null, null, null,
+        null, null, null,
+        null, null);
+    assertThat(args.getPartitionArgs()).isNull();
+    assertThat(args.hasPartitionAttributes()).isFalse();
+  }
+
+  @Test
+  public void emptyExpirationAttributesShouldBeNull() throws Exception {
+    args.setEntryExpirationIdleTime(null, null);
+    assertThat(args.getEntryExpirationIdleTime()).isNull();
+
+    args.setEntryExpirationTTL(null, null);
+    assertThat(args.getEntryExpirationTTL()).isNull();
+
+    args.setRegionExpirationIdleTime(null, null);
+    assertThat(args.getRegionExpirationIdleTime()).isNull();
+
+    args.setRegionExpirationTTL(null, null);
+    assertThat(args.getRegionExpirationTTL()).isNull();
+  }
+
+  @Test
   public void defaultPartitionArgs() throws Exception {
     assertThat(partitionArgs.hasPartitionAttributes()).isFalse();
 
@@ -64,19 +88,22 @@ public class RegionFunctionArgsTest {
     assertThat(args.getEvictionAttributes()).isNull();
 
     args.setEvictionAttributes("local-destroy", null, null, null);
-    EvictionAttributes attributes = args.getEvictionAttributes();
+    EvictionAttributes attributes = args.getEvictionAttributes()
+        .convertToEvictionAttributes();
     assertThat(attributes.getAlgorithm()).isEqualTo(EvictionAlgorithm.LRU_HEAP);
     assertThat(attributes.getAction()).isEqualTo(EvictionAction.LOCAL_DESTROY);
     assertThat(attributes.getMaximum()).isEqualTo(0);
 
     args.setEvictionAttributes("overflow-to-disk", 1000, null, null);
-    EvictionAttributes attributes1 = args.getEvictionAttributes();
+    EvictionAttributes attributes1 = args.getEvictionAttributes()
+        .convertToEvictionAttributes();
     assertThat(attributes1.getAlgorithm()).isEqualTo(EvictionAlgorithm.LRU_MEMORY);
     assertThat(attributes1.getAction()).isEqualTo(EvictionAction.OVERFLOW_TO_DISK);
     assertThat(attributes1.getMaximum()).isEqualTo(1000);
 
     args.setEvictionAttributes("local-destroy", null, 1000, null);
-    EvictionAttributes attributes2 = args.getEvictionAttributes();
+    EvictionAttributes attributes2 = args.getEvictionAttributes()
+        .convertToEvictionAttributes();
     assertThat(attributes2.getAlgorithm()).isEqualTo(EvictionAlgorithm.LRU_ENTRY);
     assertThat(attributes2.getAction()).isEqualTo(EvictionAction.LOCAL_DESTROY);
     assertThat(attributes2.getMaximum()).isEqualTo(1000);
@@ -85,7 +112,7 @@ public class RegionFunctionArgsTest {
   @Test
   public void evictionAttributesWithNullAction() throws Exception {
     args.setEvictionAttributes(null, null, 1000, null);
-    EvictionAttributes attributes3 = args.getEvictionAttributes();
+    RegionFunctionArgs.EvictionAttrs attributes3 = args.getEvictionAttributes();
     assertThat(attributes3).isNull();
   }
 }
diff --git a/geode-wan/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/CreateRegionCommandDUnitTest.java b/geode-wan/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/CreateRegionCommandDUnitTest.java
index 365eb73..57d91a3 100644
--- a/geode-wan/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/CreateRegionCommandDUnitTest.java
+++ b/geode-wan/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/CreateRegionCommandDUnitTest.java
@@ -14,13 +14,17 @@
  */
 package org.apache.geode.management.internal.cli.commands;
 
+import static org.assertj.core.api.Assertions.assertThat;
+
 import org.junit.BeforeClass;
 import org.junit.ClassRule;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.rules.TestName;
 
+import org.apache.geode.cache.Region;
 import org.apache.geode.test.dunit.IgnoredException;
 import org.apache.geode.test.dunit.rules.ClusterStartupRule;
 import org.apache.geode.test.dunit.rules.MemberVM;
@@ -65,9 +69,9 @@ public class CreateRegionCommandDUnitTest {
         + " --async-event-queue-id=" + asyncQueueName)
         .statusIsError()
         .containsOutput("server-1",
-            "ERROR: Parallel Async Event Queue " + asyncQueueName
+            "Parallel Async Event Queue " + asyncQueueName
                 + " can not be used with replicated region /" + regionName)
-        .containsOutput("server-2", "ERROR: Parallel Async Event Queue " + asyncQueueName
+        .containsOutput("server-2", "Parallel Async Event Queue " + asyncQueueName
             + " can not be used with replicated region /" + regionName);
 
     // The exception must be thrown early in the initialization, so the region itself shouldn't be
@@ -91,13 +95,56 @@ public class CreateRegionCommandDUnitTest {
         + " --gateway-sender-id=" + gatewaySenderName)
         .statusIsError()
         .containsOutput("server-1",
-            "ERROR: Parallel gateway sender " + gatewaySenderName
+            "Parallel gateway sender " + gatewaySenderName
                 + " can not be used with replicated region /" + regionName)
-        .containsOutput("server-2", "ERROR: Parallel gateway sender " + gatewaySenderName
+        .containsOutput("server-2", "Parallel gateway sender " + gatewaySenderName
             + " can not be used with replicated region /" + regionName);
 
     // The exception must be thrown early in the initialization, so the region itself shouldn't be
     // added to the root regions.
     gfsh.executeAndAssertThat("list regions").statusIsSuccess().doesNotContainOutput(regionName);
   }
+
+  /**
+   * Ignored this test until we refactor the FetchRegionAttributesFunction to not use
+   * AttributesFactory, and instead use RegionConfig, which we will do as part of implementing
+   * GEODE-6103
+   */
+  @Ignore
+  @Test
+  public void createRegionFromTemplateWithGatewaySender() throws Exception {
+    String regionName = testName.getMethodName();
+    String sender = "sender1";
+    String remoteDS = "2";
+    IgnoredException.addIgnoredException("could not get remote locator information");
+
+    gfsh.executeAndAssertThat("create gateway-sender"
+        + " --id=" + sender
+        + " --remote-distributed-system-id=" + remoteDS).statusIsSuccess();
+
+    // Give gateway sender time to get created
+    Thread.sleep(2000);
+
+    gfsh.executeAndAssertThat("create region"
+        + " --name=" + regionName
+        + " --type=REPLICATE_PERSISTENT"
+        + " --gateway-sender-id=" + sender).statusIsSuccess();
+
+    String regionNameFromTemplate = regionName + "-from-template";
+    gfsh.executeAndAssertThat("create region --name=" + regionNameFromTemplate
+        + " --template-region=" + regionName)
+        .statusIsSuccess();
+
+    server1.invoke(() -> {
+      Region region1 = ClusterStartupRule.getCache().getRegion(regionNameFromTemplate);
+      assertThat(region1.getAttributes().getGatewaySenderIds())
+          .describedAs("region1 contains gateway sender")
+          .contains(sender);
+
+      Region region2 = ClusterStartupRule.getCache().getRegion(regionNameFromTemplate);
+      assertThat(region2.getAttributes().getGatewaySenderIds())
+          .describedAs("region2 contains gateway sender")
+          .contains(sender);
+    });
+  }
 }