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/05/03 15:21:00 UTC

[geode] branch develop updated: GEODE-5010: refactor commands and CommandExecutor to use ResultModel (#1896)

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

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


The following commit(s) were added to refs/heads/develop by this push:
     new a7c1601  GEODE-5010: refactor commands and CommandExecutor to use ResultModel (#1896)
a7c1601 is described below

commit a7c1601708bcf7da6d91b728be8f8a37b8859d90
Author: jinmeiliao <ji...@pivotal.io>
AuthorDate: Thu May 3 08:20:56 2018 -0700

    GEODE-5010: refactor commands and CommandExecutor to use ResultModel (#1896)
---
 .../jdbc/internal/cli/AlterConnectionCommand.java  | 12 ++--
 .../jdbc/internal/cli/AlterMappingCommand.java     | 12 ++--
 .../jdbc/internal/cli/CreateConnectionCommand.java | 10 +--
 .../jdbc/internal/cli/CreateMappingCommand.java    | 14 ++--
 .../internal/cli/DestroyConnectionCommand.java     | 13 ++--
 .../jdbc/internal/cli/DestroyMappingCommand.java   | 13 ++--
 .../cli/AlterConnectionCommandIntegrationTest.java |  3 +-
 .../cli/AlterMappingCommandIntegrationTest.java    |  5 +-
 .../CreateConnectionCommandIntegrationTest.java    |  8 ++-
 .../cli/CreateMappingCommandIntegrationTest.java   | 11 ++--
 .../DestroyConnectionCommandIntegrationTest.java   |  5 +-
 .../cli/DestroyMappingCommandIntegrationTest.java  |  5 +-
 .../cli/commands/AlterAsyncEventQueueCommand.java  | 12 ++--
 .../internal/cli/commands/CreateIndexCommand.java  | 11 ++--
 .../cli/commands/DescribeClientCommand.java        | 10 +--
 .../cli/commands/DescribeConfigCommand.java        |  6 +-
 .../cli/commands/DescribeJndiBindingCommand.java   |  4 +-
 .../internal/cli/remote/CommandExecutor.java       | 33 +++++-----
 .../internal/cli/result/CommandResult.java         |  4 --
 .../internal/cli/result/LegacyCommandResult.java   | 11 ----
 .../internal/cli/result/ModelCommandResult.java    | 27 +-------
 .../internal/cli/result/ResultBuilder.java         |  1 -
 .../internal/cli/result/model/ResultModel.java     | 77 ++++++++++++++++++----
 .../cli/result/model/TabularResultModel.java       | 23 +++++++
 .../internal/cli/shell/GfshExecutionStrategy.java  |  7 +-
 .../cli/commands/ConfigurePDXCommandTest.java      |  4 +-
 .../internal/cli/remote/CommandExecutorTest.java   | 16 ++---
 .../internal/cli/result/TabularResultDataTest.java |  1 +
 .../model/LegacyVsResultModelComparisonTest.java   |  6 +-
 .../cli/result/model/TabularResultModelTest.java   | 61 +++++++++++++++++
 .../cli/shell/GfshExecutionStrategyTest.java       | 15 +++++
 31 files changed, 272 insertions(+), 168 deletions(-)

diff --git a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/AlterConnectionCommand.java b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/AlterConnectionCommand.java
index 1ffed60..4b1c210 100644
--- a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/AlterConnectionCommand.java
+++ b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/AlterConnectionCommand.java
@@ -28,13 +28,11 @@ import org.apache.geode.connectors.jdbc.internal.configuration.ConnectorService;
 import org.apache.geode.distributed.ConfigurationPersistenceService;
 import org.apache.geode.distributed.DistributedMember;
 import org.apache.geode.management.cli.CliMetaData;
-import org.apache.geode.management.cli.Result;
 import org.apache.geode.management.cli.SingleGfshCommand;
 import org.apache.geode.management.internal.cli.exceptions.EntityNotFoundException;
 import org.apache.geode.management.internal.cli.functions.CliFunctionResult;
 import org.apache.geode.management.internal.cli.i18n.CliStrings;
-import org.apache.geode.management.internal.cli.result.CommandResult;
-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.security.ResourceOperation;
 import org.apache.geode.security.ResourcePermission;
 
@@ -61,7 +59,7 @@ public class AlterConnectionCommand extends SingleGfshCommand {
   @CliMetaData(relatedTopic = CliStrings.DEFAULT_TOPIC_GEODE)
   @ResourceOperation(resource = ResourcePermission.Resource.CLUSTER,
       operation = ResourcePermission.Operation.MANAGE)
-  public Result alterConnection(
+  public ResultModel alterConnection(
       @CliOption(key = ALTER_CONNECTION__NAME, mandatory = true,
           help = ALTER_CONNECTION__NAME__HELP) String name,
       @CliOption(key = ALTER_CONNECTION__URL, specifiedDefaultValue = "",
@@ -103,9 +101,9 @@ public class AlterConnectionCommand extends SingleGfshCommand {
     ConnectorService.Connection mergedConnection =
         (ConnectorService.Connection) successResult.getResultObject();
 
-    CommandResult commandResult = ResultBuilder.buildResult(results);
-    commandResult.setConfigObject(mergedConnection);
-    return commandResult;
+    ResultModel result = ResultModel.createMemberStatusResult(results);
+    result.setConfigObject(mergedConnection);
+    return result;
   }
 
   @Override
diff --git a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/AlterMappingCommand.java b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/AlterMappingCommand.java
index f1c8a31..7c16ab3 100644
--- a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/AlterMappingCommand.java
+++ b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/AlterMappingCommand.java
@@ -28,13 +28,11 @@ import org.apache.geode.connectors.jdbc.internal.configuration.ConnectorService;
 import org.apache.geode.distributed.ConfigurationPersistenceService;
 import org.apache.geode.distributed.DistributedMember;
 import org.apache.geode.management.cli.CliMetaData;
-import org.apache.geode.management.cli.Result;
 import org.apache.geode.management.cli.SingleGfshCommand;
 import org.apache.geode.management.internal.cli.exceptions.EntityNotFoundException;
 import org.apache.geode.management.internal.cli.functions.CliFunctionResult;
 import org.apache.geode.management.internal.cli.i18n.CliStrings;
-import org.apache.geode.management.internal.cli.result.CommandResult;
-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.security.ResourceOperation;
 import org.apache.geode.security.ResourcePermission;
 
@@ -64,7 +62,7 @@ public class AlterMappingCommand extends SingleGfshCommand {
   @CliMetaData(relatedTopic = CliStrings.DEFAULT_TOPIC_GEODE)
   @ResourceOperation(resource = ResourcePermission.Resource.CLUSTER,
       operation = ResourcePermission.Operation.MANAGE)
-  public Result alterMapping(
+  public ResultModel alterMapping(
       @CliOption(key = ALTER_MAPPING__REGION_NAME, mandatory = true,
           help = ALTER_MAPPING__REGION_NAME__HELP) String regionName,
       @CliOption(key = ALTER_MAPPING__CONNECTION_NAME, specifiedDefaultValue = "",
@@ -104,15 +102,15 @@ public class AlterMappingCommand extends SingleGfshCommand {
     // action
     List<CliFunctionResult> results =
         executeAndGetFunctionResult(new AlterMappingFunction(), newMapping, targetMembers);
-    CommandResult commandResult = ResultBuilder.buildResult(results);
+    ResultModel result = ResultModel.createMemberStatusResult(results);
 
     // find the merged regionMapping from the function result
     CliFunctionResult successResult =
         results.stream().filter(CliFunctionResult::isSuccessful).findAny().get();
     ConnectorService.RegionMapping mergedMapping =
         (ConnectorService.RegionMapping) successResult.getResultObject();
-    commandResult.setConfigObject(mergedMapping);
-    return commandResult;
+    result.setConfigObject(mergedMapping);
+    return result;
   }
 
   @Override
diff --git a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/CreateConnectionCommand.java b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/CreateConnectionCommand.java
index be2e4d7..02f77f1 100644
--- a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/CreateConnectionCommand.java
+++ b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/CreateConnectionCommand.java
@@ -32,8 +32,8 @@ import org.apache.geode.management.internal.cli.AbstractCliAroundInterceptor;
 import org.apache.geode.management.internal.cli.GfshParseResult;
 import org.apache.geode.management.internal.cli.functions.CliFunctionResult;
 import org.apache.geode.management.internal.cli.i18n.CliStrings;
-import org.apache.geode.management.internal.cli.result.CommandResult;
 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.security.ResourceOperation;
 import org.apache.geode.security.ResourcePermission;
 
@@ -61,7 +61,7 @@ public class CreateConnectionCommand extends SingleGfshCommand {
       interceptor = "org.apache.geode.connectors.jdbc.internal.cli.CreateConnectionCommand$Interceptor")
   @ResourceOperation(resource = ResourcePermission.Resource.CLUSTER,
       operation = ResourcePermission.Operation.MANAGE)
-  public Result createConnection(
+  public ResultModel createConnection(
       @CliOption(key = CREATE_CONNECTION__NAME, mandatory = true,
           help = CREATE_CONNECTION__NAME__HELP) String name,
       @CliOption(key = CREATE_CONNECTION__URL, mandatory = true,
@@ -81,9 +81,9 @@ public class CreateConnectionCommand extends SingleGfshCommand {
     List<CliFunctionResult> results =
         executeAndGetFunctionResult(new CreateConnectionFunction(), connection, targetMembers);
 
-    CommandResult commandResult = ResultBuilder.buildResult(results);
-    commandResult.setConfigObject(connection);
-    return commandResult;
+    ResultModel result = ResultModel.createMemberStatusResult(results);
+    result.setConfigObject(connection);
+    return result;
   }
 
   @Override
diff --git a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/CreateMappingCommand.java b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/CreateMappingCommand.java
index 1a06c2b..1965bf6 100644
--- a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/CreateMappingCommand.java
+++ b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/CreateMappingCommand.java
@@ -24,12 +24,10 @@ import org.apache.geode.cache.configuration.CacheConfig;
 import org.apache.geode.connectors.jdbc.internal.configuration.ConnectorService;
 import org.apache.geode.distributed.DistributedMember;
 import org.apache.geode.management.cli.CliMetaData;
-import org.apache.geode.management.cli.Result;
 import org.apache.geode.management.cli.SingleGfshCommand;
 import org.apache.geode.management.internal.cli.functions.CliFunctionResult;
 import org.apache.geode.management.internal.cli.i18n.CliStrings;
-import org.apache.geode.management.internal.cli.result.CommandResult;
-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.security.ResourceOperation;
 import org.apache.geode.security.ResourcePermission;
 
@@ -59,7 +57,7 @@ public class CreateMappingCommand extends SingleGfshCommand {
   @CliMetaData(relatedTopic = CliStrings.DEFAULT_TOPIC_GEODE)
   @ResourceOperation(resource = ResourcePermission.Resource.CLUSTER,
       operation = ResourcePermission.Operation.MANAGE)
-  public Result createMapping(
+  public ResultModel createMapping(
       @CliOption(key = CREATE_MAPPING__REGION_NAME, mandatory = true,
           help = CREATE_MAPPING__REGION_NAME__HELP) String regionName,
       @CliOption(key = CREATE_MAPPING__CONNECTION_NAME, mandatory = true,
@@ -73,7 +71,6 @@ public class CreateMappingCommand extends SingleGfshCommand {
           specifiedDefaultValue = "true") boolean keyInValue,
       @CliOption(key = CREATE_MAPPING__FIELD_MAPPING,
           help = CREATE_MAPPING__FIELD_MAPPING__HELP) String[] fieldMappings) {
-
     // input
     Set<DistributedMember> targetMembers = getMembers(null, null);
     ConnectorService.RegionMapping mapping = new ConnectorService.RegionMapping(regionName,
@@ -84,10 +81,9 @@ public class CreateMappingCommand extends SingleGfshCommand {
     List<CliFunctionResult> results =
         executeAndGetFunctionResult(new CreateMappingFunction(), mapping, targetMembers);
 
-
-    CommandResult commandResult = ResultBuilder.buildResult(results);
-    commandResult.setConfigObject(mapping);
-    return commandResult;
+    ResultModel result = ResultModel.createMemberStatusResult(results);
+    result.setConfigObject(mapping);
+    return result;
   }
 
   @Override
diff --git a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/DestroyConnectionCommand.java b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/DestroyConnectionCommand.java
index ff82fa2..161ff80 100644
--- a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/DestroyConnectionCommand.java
+++ b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/DestroyConnectionCommand.java
@@ -25,12 +25,10 @@ import org.apache.geode.cache.configuration.CacheElement;
 import org.apache.geode.connectors.jdbc.internal.configuration.ConnectorService;
 import org.apache.geode.distributed.DistributedMember;
 import org.apache.geode.management.cli.CliMetaData;
-import org.apache.geode.management.cli.Result;
 import org.apache.geode.management.cli.SingleGfshCommand;
 import org.apache.geode.management.internal.cli.functions.CliFunctionResult;
 import org.apache.geode.management.internal.cli.i18n.CliStrings;
-import org.apache.geode.management.internal.cli.result.CommandResult;
-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.security.ResourceOperation;
 import org.apache.geode.security.ResourcePermission;
 
@@ -45,18 +43,17 @@ public class DestroyConnectionCommand extends SingleGfshCommand {
   @CliMetaData(relatedTopic = CliStrings.DEFAULT_TOPIC_GEODE)
   @ResourceOperation(resource = ResourcePermission.Resource.CLUSTER,
       operation = ResourcePermission.Operation.MANAGE)
-  public Result destroyConnection(@CliOption(key = DESTROY_CONNECTION__NAME, mandatory = true,
+  public ResultModel destroyConnection(@CliOption(key = DESTROY_CONNECTION__NAME, mandatory = true,
       help = DESTROY_CONNECTION__NAME__HELP) String name) {
-
     // input
     Set<DistributedMember> targetMembers = getMembers(null, null);
 
     // action
     List<CliFunctionResult> results =
         executeAndGetFunctionResult(new DestroyConnectionFunction(), name, targetMembers);
-    CommandResult commandResult = ResultBuilder.buildResult(results);
-    commandResult.setConfigObject(name);
-    return commandResult;
+    ResultModel result = ResultModel.createMemberStatusResult(results);
+    result.setConfigObject(name);
+    return result;
   }
 
   @Override
diff --git a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/DestroyMappingCommand.java b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/DestroyMappingCommand.java
index e2db96d..924f10f 100644
--- a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/DestroyMappingCommand.java
+++ b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/DestroyMappingCommand.java
@@ -25,12 +25,10 @@ import org.apache.geode.cache.configuration.CacheElement;
 import org.apache.geode.connectors.jdbc.internal.configuration.ConnectorService;
 import org.apache.geode.distributed.DistributedMember;
 import org.apache.geode.management.cli.CliMetaData;
-import org.apache.geode.management.cli.Result;
 import org.apache.geode.management.cli.SingleGfshCommand;
 import org.apache.geode.management.internal.cli.functions.CliFunctionResult;
 import org.apache.geode.management.internal.cli.i18n.CliStrings;
-import org.apache.geode.management.internal.cli.result.CommandResult;
-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.security.ResourceOperation;
 import org.apache.geode.security.ResourcePermission;
 
@@ -46,9 +44,8 @@ public class DestroyMappingCommand extends SingleGfshCommand {
   @CliMetaData(relatedTopic = CliStrings.DEFAULT_TOPIC_GEODE)
   @ResourceOperation(resource = ResourcePermission.Resource.CLUSTER,
       operation = ResourcePermission.Operation.MANAGE)
-  public Result destroyMapping(@CliOption(key = DESTROY_MAPPING__REGION_NAME, mandatory = true,
+  public ResultModel destroyMapping(@CliOption(key = DESTROY_MAPPING__REGION_NAME, mandatory = true,
       help = DESTROY_MAPPING__REGION_NAME__HELP) String regionName) {
-
     // input
     Set<DistributedMember> targetMembers = getMembers(null, null);
 
@@ -56,9 +53,9 @@ public class DestroyMappingCommand extends SingleGfshCommand {
     List<CliFunctionResult> results =
         executeAndGetFunctionResult(new DestroyMappingFunction(), regionName, targetMembers);
 
-    CommandResult commandResult = ResultBuilder.buildResult(results);
-    commandResult.setConfigObject(regionName);
-    return commandResult;
+    ResultModel result = ResultModel.createMemberStatusResult(results);
+    result.setConfigObject(regionName);
+    return result;
   }
 
   @Override
diff --git a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/AlterConnectionCommandIntegrationTest.java b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/AlterConnectionCommandIntegrationTest.java
index fa52781..e498a1f 100644
--- a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/AlterConnectionCommandIntegrationTest.java
+++ b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/AlterConnectionCommandIntegrationTest.java
@@ -27,6 +27,7 @@ import org.apache.geode.connectors.jdbc.internal.JdbcConnectorService;
 import org.apache.geode.connectors.jdbc.internal.configuration.ConnectorService;
 import org.apache.geode.internal.cache.InternalCache;
 import org.apache.geode.management.cli.Result;
+import org.apache.geode.management.internal.cli.result.model.ResultModel;
 import org.apache.geode.test.junit.categories.IntegrationTest;
 
 @Category(IntegrationTest.class)
@@ -63,7 +64,7 @@ public class AlterConnectionCommandIntegrationTest {
   @Test
   public void altersConnectionConfigurationInService() {
     String[] newParams = new String[] {"key1:value1", "key2:value2"};
-    Result result =
+    ResultModel result =
         alterConnectionCommand.alterConnection(name, "newUrl", "newUser", "newPassword", newParams);
 
     assertThat(result.getStatus()).isSameAs(Result.Status.OK);
diff --git a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/AlterMappingCommandIntegrationTest.java b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/AlterMappingCommandIntegrationTest.java
index 86143fd..f24fccc 100644
--- a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/AlterMappingCommandIntegrationTest.java
+++ b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/AlterMappingCommandIntegrationTest.java
@@ -27,6 +27,7 @@ import org.apache.geode.connectors.jdbc.internal.JdbcConnectorService;
 import org.apache.geode.connectors.jdbc.internal.configuration.ConnectorService;
 import org.apache.geode.internal.cache.InternalCache;
 import org.apache.geode.management.cli.Result;
+import org.apache.geode.management.internal.cli.result.model.ResultModel;
 import org.apache.geode.test.junit.categories.IntegrationTest;
 
 @Category(IntegrationTest.class)
@@ -65,8 +66,8 @@ public class AlterMappingCommandIntegrationTest {
   @Test
   public void altersRegionMappingInService() {
     String[] newMappings = new String[] {"field3:column3", "field4:column4"};
-    Result result = alterRegionMappingCommand.alterMapping(regionName, "newConnection", "newTable",
-        "newPdxClass", false, newMappings);
+    ResultModel result = alterRegionMappingCommand.alterMapping(regionName, "newConnection",
+        "newTable", "newPdxClass", false, newMappings);
 
     assertThat(result.getStatus()).isSameAs(Result.Status.OK);
 
diff --git a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/CreateConnectionCommandIntegrationTest.java b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/CreateConnectionCommandIntegrationTest.java
index 405eb80..54ae98d 100644
--- a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/CreateConnectionCommandIntegrationTest.java
+++ b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/CreateConnectionCommandIntegrationTest.java
@@ -28,6 +28,7 @@ import org.apache.geode.connectors.jdbc.internal.JdbcConnectorService;
 import org.apache.geode.connectors.jdbc.internal.configuration.ConnectorService;
 import org.apache.geode.internal.cache.InternalCache;
 import org.apache.geode.management.cli.Result;
+import org.apache.geode.management.internal.cli.result.model.ResultModel;
 import org.apache.geode.test.dunit.IgnoredException;
 import org.apache.geode.test.junit.categories.IntegrationTest;
 
@@ -65,7 +66,8 @@ public class CreateConnectionCommandIntegrationTest {
 
   @Test
   public void createsConnectionConfigurationInService() throws Exception {
-    Result result = createConnectionCommand.createConnection(name, url, user, password, params);
+    ResultModel result =
+        createConnectionCommand.createConnection(name, url, user, password, params);
 
     assertThat(result.getStatus()).isSameAs(Result.Status.OK);
 
@@ -91,7 +93,7 @@ public class CreateConnectionCommandIntegrationTest {
     IgnoredException ignoredException =
         IgnoredException.addIgnoredException(ConnectionConfigExistsException.class.getName());
 
-    Result result;
+    ResultModel result;
     try {
       result = createConnectionCommand.createConnection(name, url, user, password, params);
     } finally {
@@ -104,7 +106,7 @@ public class CreateConnectionCommandIntegrationTest {
 
   @Test
   public void createsConnectionConfigurationWithMinimumParams() throws Exception {
-    Result result = createConnectionCommand.createConnection(name, url, null, null, null);
+    ResultModel result = createConnectionCommand.createConnection(name, url, null, null, null);
 
     assertThat(result.getStatus()).isSameAs(Result.Status.OK);
 
diff --git a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/CreateMappingCommandIntegrationTest.java b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/CreateMappingCommandIntegrationTest.java
index bb644f1..2885e85 100644
--- a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/CreateMappingCommandIntegrationTest.java
+++ b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/CreateMappingCommandIntegrationTest.java
@@ -30,6 +30,7 @@ import org.apache.geode.connectors.jdbc.internal.TableMetaDataView;
 import org.apache.geode.connectors.jdbc.internal.configuration.ConnectorService;
 import org.apache.geode.internal.cache.InternalCache;
 import org.apache.geode.management.cli.Result;
+import org.apache.geode.management.internal.cli.result.model.ResultModel;
 import org.apache.geode.test.dunit.IgnoredException;
 import org.apache.geode.test.junit.categories.IntegrationTest;
 
@@ -69,8 +70,8 @@ public class CreateMappingCommandIntegrationTest {
 
   @Test
   public void createsRegionMappingInService() {
-    Result result = createRegionMappingCommand.createMapping(regionName, connectionName, tableName,
-        pdxClass, keyInValue, fieldMappings);
+    ResultModel result = createRegionMappingCommand.createMapping(regionName, connectionName,
+        tableName, pdxClass, keyInValue, fieldMappings);
 
     assertThat(result.getStatus()).isSameAs(Result.Status.OK);
 
@@ -100,7 +101,7 @@ public class CreateMappingCommandIntegrationTest {
     IgnoredException ignoredException =
         IgnoredException.addIgnoredException(RegionMappingExistsException.class.getName());
 
-    Result result;
+    ResultModel result;
     try {
       result = createRegionMappingCommand.createMapping(regionName, connectionName, tableName,
           pdxClass, keyInValue, fieldMappings);
@@ -115,8 +116,8 @@ public class CreateMappingCommandIntegrationTest {
 
   @Test
   public void createsRegionMappingWithMinimumParams() {
-    Result result = createRegionMappingCommand.createMapping(regionName, connectionName, null, null,
-        false, null);
+    ResultModel result = createRegionMappingCommand.createMapping(regionName, connectionName, null,
+        null, false, null);
 
     assertThat(result.getStatus()).isSameAs(Result.Status.OK);
 
diff --git a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/DestroyConnectionCommandIntegrationTest.java b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/DestroyConnectionCommandIntegrationTest.java
index cc8287d..c27997c 100644
--- a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/DestroyConnectionCommandIntegrationTest.java
+++ b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/DestroyConnectionCommandIntegrationTest.java
@@ -27,6 +27,7 @@ import org.apache.geode.connectors.jdbc.internal.JdbcConnectorService;
 import org.apache.geode.connectors.jdbc.internal.configuration.ConnectorService;
 import org.apache.geode.internal.cache.InternalCache;
 import org.apache.geode.management.cli.Result;
+import org.apache.geode.management.internal.cli.result.model.ResultModel;
 import org.apache.geode.test.junit.categories.IntegrationTest;
 
 @Category(IntegrationTest.class)
@@ -64,7 +65,7 @@ public class DestroyConnectionCommandIntegrationTest {
     service.createConnectionConfig(connectionConfig);
     assertThat(service.getConnectionConfig(connectionName)).isSameAs(connectionConfig);
 
-    Result result = command.destroyConnection(connectionName);
+    ResultModel result = command.destroyConnection(connectionName);
     assertThat(result.getStatus()).isSameAs(Result.Status.OK);
 
     assertThat(service.getConnectionConfig(connectionName)).isNull();
@@ -75,7 +76,7 @@ public class DestroyConnectionCommandIntegrationTest {
     JdbcConnectorService service = cache.getService(JdbcConnectorService.class);
     assertThat(service.getConnectionConfig(connectionName)).isNull();
 
-    Result result = command.destroyConnection(connectionName);
+    ResultModel result = command.destroyConnection(connectionName);
     assertThat(result.getStatus()).isSameAs(Result.Status.ERROR);
 
     assertThat(service.getConnectionConfig(connectionName)).isNull();
diff --git a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/DestroyMappingCommandIntegrationTest.java b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/DestroyMappingCommandIntegrationTest.java
index a651dc5..5608de7 100644
--- a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/DestroyMappingCommandIntegrationTest.java
+++ b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/DestroyMappingCommandIntegrationTest.java
@@ -27,6 +27,7 @@ import org.apache.geode.connectors.jdbc.internal.JdbcConnectorService;
 import org.apache.geode.connectors.jdbc.internal.configuration.ConnectorService;
 import org.apache.geode.internal.cache.InternalCache;
 import org.apache.geode.management.cli.Result;
+import org.apache.geode.management.internal.cli.result.model.ResultModel;
 import org.apache.geode.test.junit.categories.IntegrationTest;
 
 @Category(IntegrationTest.class)
@@ -63,7 +64,7 @@ public class DestroyMappingCommandIntegrationTest {
     service.createRegionMapping(mapping);
     assertThat(service.getMappingForRegion(regionName)).isSameAs(mapping);
 
-    Result result = command.destroyMapping(regionName);
+    ResultModel result = command.destroyMapping(regionName);
 
     assertThat(result.getStatus()).isSameAs(Result.Status.OK);
     assertThat(service.getMappingForRegion(regionName)).isNull();
@@ -74,7 +75,7 @@ public class DestroyMappingCommandIntegrationTest {
     JdbcConnectorService service = cache.getService(JdbcConnectorService.class);
     assertThat(service.getMappingForRegion(regionName)).isNull();
 
-    Result result = command.destroyMapping(regionName);
+    ResultModel result = command.destroyMapping(regionName);
     assertThat(result.getStatus()).isSameAs(Result.Status.ERROR);
 
     assertThat(service.getMappingForRegion(regionName)).isNull();
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/AlterAsyncEventQueueCommand.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/AlterAsyncEventQueueCommand.java
index 565c8cf..9999d26 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/AlterAsyncEventQueueCommand.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/AlterAsyncEventQueueCommand.java
@@ -82,23 +82,22 @@ public class AlterAsyncEventQueueCommand extends InternalGfshCommand {
           unspecifiedDefaultValue = "false") boolean ifExists)
       throws IOException, SAXException, ParserConfigurationException, TransformerException {
 
-    ResultModel result = new ResultModel();
-
     // need not check if any running servers has this async-event-queue. A server with this queue id
     // may be shutdown, but we still need to update Cluster Configuration.
     InternalConfigurationPersistenceService service =
         (InternalConfigurationPersistenceService) getConfigurationPersistenceService();
 
     if (service == null) {
-      return result.createError("Cluster Configuration Service is not available. "
+      return ResultModel.createError("Cluster Configuration Service is not available. "
           + "Please connect to a locator with running Cluster Configuration Service.");
     }
 
     boolean locked = service.lockSharedConfiguration();
     if (!locked) {
-      return result.createCommandProcessingError("Unable to lock the cluster configuration.");
+      return ResultModel.createCommandProcessingError("Unable to lock the cluster configuration.");
     }
 
+    ResultModel result = new ResultModel();
     TabularResultModel tableData = result.addTable();
     boolean xmlUpdated = false;
     try {
@@ -166,11 +165,10 @@ public class AlterAsyncEventQueueCommand extends InternalGfshCommand {
       Object batchTimeInterval = parseResult.getParamValue(BATCH_TIME_INTERVAL);
       Object maxQueueMemory = parseResult.getParamValue(MAX_QUEUE_MEMORY);
 
-      ResultModel resultModel = new ResultModel();
       if (batchSize == null && batchTimeInterval == null && maxQueueMemory == null) {
-        resultModel.createError("need to specify at least one option to modify.");
+        return ResultModel.createError("need to specify at least one option to modify.");
       }
-      return resultModel;
+      return new ResultModel();
     }
   }
 }
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/CreateIndexCommand.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/CreateIndexCommand.java
index 60df346..3dbf515 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/CreateIndexCommand.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/CreateIndexCommand.java
@@ -27,14 +27,12 @@ import org.apache.geode.cache.query.IndexType;
 import org.apache.geode.distributed.DistributedMember;
 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.exceptions.EntityNotFoundException;
 import org.apache.geode.management.internal.cli.functions.CliFunctionResult;
 import org.apache.geode.management.internal.cli.functions.CreateIndexFunction;
 import org.apache.geode.management.internal.cli.i18n.CliStrings;
-import org.apache.geode.management.internal.cli.result.CommandResult;
-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.security.ResourceOperation;
 import org.apache.geode.security.ResourcePermission;
 
@@ -45,7 +43,7 @@ public class CreateIndexCommand extends SingleGfshCommand {
   @CliMetaData(relatedTopic = {CliStrings.TOPIC_GEODE_REGION, CliStrings.TOPIC_GEODE_DATA})
   @ResourceOperation(resource = ResourcePermission.Resource.CLUSTER,
       operation = ResourcePermission.Operation.MANAGE, target = ResourcePermission.Target.QUERY)
-  public Result createIndex(@CliOption(key = CliStrings.CREATE_INDEX__NAME, mandatory = true,
+  public ResultModel createIndex(@CliOption(key = CliStrings.CREATE_INDEX__NAME, mandatory = true,
       help = CliStrings.CREATE_INDEX__NAME__HELP) final String indexName,
 
       @CliOption(key = CliStrings.CREATE_INDEX__EXPRESSION, mandatory = true,
@@ -67,11 +65,10 @@ public class CreateIndexCommand extends SingleGfshCommand {
           optionContext = ConverterHint.MEMBERGROUP,
           help = CliStrings.CREATE_INDEX__GROUP__HELP) final String[] groups) {
 
-    CommandResult result;
     final Set<DistributedMember> targetMembers = findMembers(groups, memberNameOrID);
 
     if (targetMembers.isEmpty()) {
-      return ResultBuilder.createUserErrorResult(CliStrings.NO_MEMBERS_FOUND_MESSAGE);
+      return ResultModel.createError(CliStrings.NO_MEMBERS_FOUND_MESSAGE);
     }
 
     RegionConfig.Index index = new RegionConfig.Index();
@@ -87,7 +84,7 @@ public class CreateIndexCommand extends SingleGfshCommand {
 
     List<CliFunctionResult> functionResults =
         executeAndGetFunctionResult(createIndexFunction, index, targetMembers);
-    result = ResultBuilder.buildResult(functionResults);
+    ResultModel result = ResultModel.createMemberStatusResult(functionResults);
     result.setConfigObject(index);
     return result;
   }
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DescribeClientCommand.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DescribeClientCommand.java
index 66bc4aa..1075ea4 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DescribeClientCommand.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DescribeClientCommand.java
@@ -67,7 +67,7 @@ public class DescribeClientCommand extends InternalGfshCommand {
     ManagementService service = getManagementService();
     ObjectName[] cacheServers = service.getDistributedSystemMXBean().listCacheServerObjectNames();
     if (cacheServers.length == 0) {
-      return result.createCommandProcessingError(
+      return ResultModel.createCommandProcessingError(
           CliStrings.format(CliStrings.DESCRIBE_CLIENT_COULD_NOT_RETRIEVE_SERVER_LIST));
     }
 
@@ -82,11 +82,11 @@ public class DescribeClientCommand extends InternalGfshCommand {
           try {
             clientHealthStatus = serverMbean.showClientStats(clientId);
             if (clientHealthStatus == null) {
-              return result.createCommandProcessingError(CliStrings.format(
+              return ResultModel.createCommandProcessingError(CliStrings.format(
                   CliStrings.DESCRIBE_CLIENT_COULD_NOT_RETRIEVE_STATS_FOR_CLIENT_0, clientId));
             }
           } catch (Exception eee) {
-            return result.createCommandProcessingError(CliStrings.format(
+            return ResultModel.createCommandProcessingError(CliStrings.format(
                 CliStrings.DESCRIBE_CLIENT_COULD_NOT_RETRIEVE_STATS_FOR_CLIENT_0_REASON_1, clientId,
                 eee.getMessage()));
           }
@@ -95,7 +95,7 @@ public class DescribeClientCommand extends InternalGfshCommand {
     }
 
     if (clientHealthStatus == null) {
-      return result.createCommandProcessingError(
+      return ResultModel.createCommandProcessingError(
           CliStrings.format(CliStrings.DESCRIBE_CLIENT__CLIENT__ID__NOT__FOUND__0, clientId));
     }
 
@@ -145,7 +145,7 @@ public class DescribeClientCommand extends InternalGfshCommand {
 
       buildTableResult(result, clientHealthStatus, isDurable, primaryServers, secondaryServers);
     } else {
-      result.createCommandProcessingError(CliStrings.DESCRIBE_CLIENT_NO_MEMBERS);
+      result = ResultModel.createCommandProcessingError(CliStrings.DESCRIBE_CLIENT_NO_MEMBERS);
     }
 
     LogWrapper.getInstance(getCache()).info("describe client result " + result);
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DescribeConfigCommand.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DescribeConfigCommand.java
index 8d31bd4..fe024ae 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DescribeConfigCommand.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DescribeConfigCommand.java
@@ -29,7 +29,6 @@ import org.apache.geode.distributed.DistributedMember;
 import org.apache.geode.internal.util.ArgumentRedactor;
 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.internal.cli.domain.MemberConfigurationInfo;
 import org.apache.geode.management.internal.cli.functions.GetMemberConfigInformationFunction;
 import org.apache.geode.management.internal.cli.i18n.CliStrings;
@@ -103,11 +102,10 @@ public class DescribeConfigCommand extends InternalGfshCommand {
       }
 
     } catch (FunctionInvocationTargetException e) {
-      result.createCommandProcessingError(CliStrings
+      result = ResultModel.createCommandProcessingError(CliStrings
           .format(CliStrings.COULD_NOT_EXECUTE_COMMAND_TRY_AGAIN, CliStrings.DESCRIBE_CONFIG));
     } catch (Exception e) {
-      result.createError(e.getMessage());
-      result.setStatus(Result.Status.ERROR);
+      result = ResultModel.createError(e.getMessage());
     }
     return result;
   }
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DescribeJndiBindingCommand.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DescribeJndiBindingCommand.java
index 0b14af9..08175cb 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DescribeJndiBindingCommand.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DescribeJndiBindingCommand.java
@@ -56,13 +56,13 @@ public class DescribeJndiBindingCommand extends InternalGfshCommand {
     if (ccService != null) {
       CacheConfig cacheConfig = ccService.getCacheConfig("cluster");
       if (cacheConfig == null) {
-        return crm.createError(String.format("JNDI binding : %s not found", bindingName));
+        return ResultModel.createError(String.format("JNDI binding : %s not found", bindingName));
       }
       List<JndiBindingsType.JndiBinding> jndiBindings = cacheConfig.getJndiBindings();
 
       if (jndiBindings.stream().noneMatch(b -> b.getJndiName().equals(bindingName)
           || b.getJndiName().equals("java:" + bindingName))) {
-        return crm.createError(String.format("JNDI binding : %s not found", bindingName));
+        return ResultModel.createError(String.format("JNDI binding : %s not found", bindingName));
       }
 
       for (JndiBindingsType.JndiBinding binding : jndiBindings) {
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/remote/CommandExecutor.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/remote/CommandExecutor.java
index 6c6abd8..0a156ce 100755
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/remote/CommandExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/remote/CommandExecutor.java
@@ -25,8 +25,8 @@ import org.apache.geode.management.cli.SingleGfshCommand;
 import org.apache.geode.management.internal.cli.GfshParseResult;
 import org.apache.geode.management.internal.cli.exceptions.EntityNotFoundException;
 import org.apache.geode.management.internal.cli.exceptions.UserErrorException;
-import org.apache.geode.management.internal.cli.result.CommandResult;
-import org.apache.geode.management.internal.cli.result.ResultBuilder;
+import org.apache.geode.management.internal.cli.result.model.InfoResultModel;
+import org.apache.geode.management.internal.cli.result.model.ResultModel;
 import org.apache.geode.security.NotAuthorizedException;
 
 /**
@@ -49,7 +49,7 @@ public class CommandExecutor {
       Object result = invokeCommand(command, parseResult);
 
       if (result == null) {
-        return ResultBuilder.createGemFireErrorResult("Command returned null: " + parseResult);
+        return ResultModel.createError("Command returned null: " + parseResult);
       }
       return result;
     }
@@ -63,23 +63,23 @@ public class CommandExecutor {
     // for these exceptions, needs to create a UserErrorResult (still reported as error by gfsh)
     // no need to log since this is a user error
     catch (UserErrorException | IllegalStateException | IllegalArgumentException e) {
-      return ResultBuilder.createUserErrorResult(e.getMessage());
+      return ResultModel.createError(e.getMessage());
     }
 
     // if entity not found, depending on the thrower's intention, report either as success or error
     // no need to log since this is a user error
     catch (EntityNotFoundException e) {
       if (e.isStatusOK()) {
-        return ResultBuilder.createInfoResult("Skipping: " + e.getMessage());
+        return ResultModel.createInfo("Skipping: " + e.getMessage());
       } else {
-        return ResultBuilder.createUserErrorResult(e.getMessage());
+        return ResultModel.createError(e.getMessage());
       }
     }
 
     // all other exceptions, log it and build an error result.
     catch (Exception e) {
       logger.error("Could not execute \"" + parseResult + "\".", e);
-      return ResultBuilder.createGemFireErrorResult(
+      return ResultModel.createError(
           "Error while processing command <" + parseResult + "> Reason : " + e.getMessage());
     }
 
@@ -107,16 +107,18 @@ public class CommandExecutor {
     }
 
     SingleGfshCommand gfshCommand = (SingleGfshCommand) command;
-    CommandResult commandResult = (CommandResult) result;
-    if (commandResult.getStatus() == Result.Status.ERROR) {
+    ResultModel resultModel = (ResultModel) result;
+    if (resultModel.getStatus() == Result.Status.ERROR) {
       return result;
     }
 
     // if command result is ok, we will need to see if we need to update cluster configuration
+    InfoResultModel infoResultModel = resultModel.addInfo();
     ConfigurationPersistenceService ccService = gfshCommand.getConfigurationPersistenceService();
     if (parseResult.getParamValue("member") != null || ccService == null) {
-      commandResult.setCommandPersisted(false);
-      return commandResult;
+      infoResultModel.addLine(
+          "Cluster configuration is not updated because the command is executed on specific member.");
+      return resultModel;
     }
 
     String groupInput = parseResult.getParamValueAsString("group");
@@ -127,17 +129,18 @@ public class CommandExecutor {
     for (String group : groups) {
       ccService.updateCacheConfig(group, cc -> {
         try {
-          gfshCommand.updateClusterConfig(group, cc, commandResult.getConfigObject());
+          gfshCommand.updateClusterConfig(group, cc, resultModel.getConfigObject());
         } catch (Exception e) {
-          logger.error("failed to update cluster config for " + group, e);
+          String message = "failed to update cluster config for " + group;
+          logger.error(message, e);
           // for now, if one cc update failed, we will set this flag. Will change this when we can
           // add lines to the result returned by the command
-          commandResult.setCommandPersisted(false);
+          infoResultModel.addLine(message + ". Reason: " + e.getMessage());
           return null;
         }
         return cc;
       });
     }
-    return commandResult;
+    return resultModel;
   }
 }
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/CommandResult.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/CommandResult.java
index 64d3788..d027a6c 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/CommandResult.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/CommandResult.java
@@ -34,10 +34,6 @@ public interface CommandResult extends Result {
 
   Object getResultData();
 
-  Object getConfigObject();
-
-  void setConfigObject(Object configObject);
-
   void resetToFirstLine();
 
   boolean hasIncomingFiles();
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/LegacyCommandResult.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/LegacyCommandResult.java
index 19a597e..a56fe67 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/LegacyCommandResult.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/LegacyCommandResult.java
@@ -63,7 +63,6 @@ public class LegacyCommandResult implements CommandResult {
   }
 
   private Path fileToDownload;
-  private Object configObject;
 
   public LegacyCommandResult(ResultData resultData) {
     this.resultData = resultData;
@@ -77,16 +76,6 @@ public class LegacyCommandResult implements CommandResult {
     this.fileToDownload = fileToDownload.toAbsolutePath();
   }
 
-  @Override
-  public Object getConfigObject() {
-    return configObject;
-  }
-
-  @Override
-  public void setConfigObject(Object configObject) {
-    this.configObject = configObject;
-  }
-
   public boolean hasFileToDownload() {
     return fileToDownload != null;
   }
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/ModelCommandResult.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/ModelCommandResult.java
index bdd9b42..092c3b2 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/ModelCommandResult.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/ModelCommandResult.java
@@ -15,7 +15,6 @@
 
 package org.apache.geode.management.internal.cli.result;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.ArrayList;
@@ -24,9 +23,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
 
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
 import org.apache.geode.management.internal.cli.GfshParser;
 import org.apache.geode.management.internal.cli.json.GfJsonObject;
 import org.apache.geode.management.internal.cli.result.model.AbstractResultModel;
@@ -40,7 +36,6 @@ public class ModelCommandResult implements CommandResult {
   private ResultModel result;
   private List<String> commandOutput;
   private int commandOutputIndex;
-  private Object configObject;
   private static final Map<String, List<String>> EMPTY_TABLE_MAP = new LinkedHashMap<>();
   private static final List<String> EMPTY_LIST = new ArrayList<>();
 
@@ -90,18 +85,6 @@ public class ModelCommandResult implements CommandResult {
 
   }
 
-  @JsonIgnore
-  @Override
-  public Object getConfigObject() {
-    return configObject;
-  }
-
-  @JsonIgnore
-  @Override
-  public void setConfigObject(Object configObject) {
-    this.configObject = configObject;
-  }
-
   @Override
   public boolean hasNextLine() {
     if (commandOutput == null) {
@@ -120,15 +103,7 @@ public class ModelCommandResult implements CommandResult {
 
   @Override
   public String toJson() {
-    ObjectMapper mapper = new ObjectMapper();
-    ByteArrayOutputStream baos = new ByteArrayOutputStream();
-    try {
-      mapper.writeValue(baos, getResultData());
-    } catch (IOException e) {
-      return e.getMessage();
-    }
-
-    return baos.toString();
+    return getResultData().toJson();
   }
 
   @Override
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/ResultBuilder.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/ResultBuilder.java
index b677dab..e0dbf7c 100755
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/ResultBuilder.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/ResultBuilder.java
@@ -215,7 +215,6 @@ public class ResultBuilder {
     return ResultBuilder.buildResult(tabularData);
   }
 
-
   /**
    * Prepare Result from JSON. Type of result is expected to there in the JSON as 'contentType'
    * which should be one of {@link ResultData#TYPE_TABULAR}, {@link ResultData#TYPE_COMPOSITE},
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/model/ResultModel.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/model/ResultModel.java
index be698f7..a132ac9 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/model/ResultModel.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/model/ResultModel.java
@@ -15,15 +15,19 @@
 
 package org.apache.geode.management.internal.cli.result.model;
 
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.databind.ObjectMapper;
 
 import org.apache.geode.management.cli.GfshCommand;
 import org.apache.geode.management.cli.Result;
+import org.apache.geode.management.internal.cli.functions.CliFunctionResult;
 
 /**
  * This class is the primary container for results returned from a {@link GfshCommand}.
@@ -45,7 +49,6 @@ import org.apache.geode.management.cli.Result;
  *
  */
 public class ResultModel {
-
   private String header;
   private String footer;
   private Map<String, AbstractResultModel> sections = new LinkedHashMap<>();
@@ -76,6 +79,9 @@ public class ResultModel {
   }
 
   public void setStatus(Result.Status status) {
+    if (this.status == Result.Status.ERROR && status != this.status) {
+      throw new IllegalStateException("Can't change the error state of the result.");
+    }
     this.status = status;
   }
 
@@ -166,19 +172,66 @@ public class ResultModel {
     return (DataResultModel) sections.get(name);
   }
 
-  /**
-   * Convenience method which creates an {@code InfoResultModel} section. The provided message is
-   * prepended with the string "Error processing command:". The status will be set to
-   * {@code Result.Status.ERROR}
-   */
-  public ResultModel createCommandProcessingError(String message) {
-    return createError("Error processing command: " + message);
+  public String toJson() {
+    ObjectMapper mapper = new ObjectMapper();
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    try {
+      mapper.writeValue(baos, this);
+    } catch (IOException e) {
+      return e.getMessage();
+    }
+    return baos.toString();
+  }
+
+  @Override
+  public String toString() {
+    return toJson();
   }
 
-  public ResultModel createError(String message) {
-    addInfo().addLine(message);
-    setStatus(Result.Status.ERROR);
 
-    return this;
+  // ********************************************
+  // static convenience methods
+  // ********************************************
+
+  public static ResultModel createCommandProcessingError(String message) {
+    return createError("Error processing command: " + message);
+  }
+
+  public static ResultModel createError(String message) {
+    ResultModel result = new ResultModel();
+    result.addInfo().addLine(message);
+    result.setStatus(Result.Status.ERROR);
+    return result;
+  }
+
+  public static ResultModel createInfo(String message) {
+    ResultModel result = new ResultModel();
+    result.addInfo().addLine(message);
+    result.setStatus(Result.Status.OK);
+    return result;
+  }
+
+  public static ResultModel createMemberStatusResult(List<CliFunctionResult> functionResults) {
+    return createMemberStatusResult(functionResults, null, null);
+  }
+
+  public static ResultModel createMemberStatusResult(List<CliFunctionResult> functionResults,
+      String header, String footer) {
+    ResultModel result = new ResultModel();
+    boolean atLeastOneSuccess = false;
+    TabularResultModel tabularResultModel = result.addTable();
+    tabularResultModel.setHeader(header);
+    tabularResultModel.setFooter(footer);
+    tabularResultModel.setColumnHeader("Member", "Status");
+    for (CliFunctionResult functionResult : functionResults) {
+      tabularResultModel.addRow(functionResult.getMemberIdOrName(), functionResult.getStatus());
+      if (functionResult.isSuccessful()) {
+        atLeastOneSuccess = true;
+      }
+    }
+    if (!atLeastOneSuccess) {
+      result.setStatus(Result.Status.ERROR);
+    }
+    return result;
   }
 }
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/model/TabularResultModel.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/model/TabularResultModel.java
index 9833699..8d24f35 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/model/TabularResultModel.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/result/model/TabularResultModel.java
@@ -20,6 +20,9 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import org.apache.commons.lang.ArrayUtils;
+
 public class TabularResultModel extends AbstractResultModel {
 
   /*
@@ -27,6 +30,9 @@ public class TabularResultModel extends AbstractResultModel {
    */
   private Map<String, List<String>> table = new LinkedHashMap<>();
 
+  @JsonIgnore
+  private String[] columnHeaders = new String[0];
+
   TabularResultModel() {}
 
   public TabularResultModel accumulate(String column, String value) {
@@ -36,6 +42,7 @@ public class TabularResultModel extends AbstractResultModel {
       List<String> list = new ArrayList<>();
       list.add(value);
       table.put(column, list);
+      columnHeaders = (String[]) ArrayUtils.add(columnHeaders, column);
     }
 
     return this;
@@ -45,4 +52,20 @@ public class TabularResultModel extends AbstractResultModel {
   public Map<String, List<String>> getContent() {
     return table;
   }
+
+  public void setColumnHeader(String... columnHeaders) {
+    this.columnHeaders = columnHeaders;
+    for (String columnHeader : columnHeaders) {
+      table.put(columnHeader, new ArrayList<>());
+    }
+  }
+
+  public void addRow(String... values) {
+    if (values.length != columnHeaders.length) {
+      throw new IllegalStateException("row size is different than the column header size.");
+    }
+    for (int i = 0; i < values.length; i++) {
+      table.get(columnHeaders[i]).add(values[i]);
+    }
+  }
 }
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/GfshExecutionStrategy.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/GfshExecutionStrategy.java
index 6fd6143..7402c24 100755
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/GfshExecutionStrategy.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/GfshExecutionStrategy.java
@@ -80,7 +80,12 @@ public class GfshExecutionStrategy implements ExecutionStrategy {
       synchronized (mutex) {
         Assert.isTrue(isReadyForCommands(), "Not yet ready for commands");
 
-        return new CommandExecutor().execute((GfshParseResult) parseResult);
+        Object exeuctionResult = new CommandExecutor().execute((GfshParseResult) parseResult);
+        if (exeuctionResult instanceof ResultModel) {
+          return new ModelCommandResult((ResultModel) exeuctionResult);
+        }
+        return exeuctionResult;
+
       }
     }
 
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/ConfigurePDXCommandTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/ConfigurePDXCommandTest.java
index d293d66..d377b60 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/ConfigurePDXCommandTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/ConfigurePDXCommandTest.java
@@ -92,12 +92,12 @@ public class ConfigurePDXCommandTest {
     gfshParserRule
         .executeAndAssertThat(command,
             BASE_COMMAND_STRING + "--auto-serializable-classes=" + new String[] {})
-        .statusIsError().containsOutput("Could not process command due to error.")
+        .statusIsError().containsOutput("Error while processing command")
         .containsOutput("Can't create ReflectionBasedAutoSerializer.");
     gfshParserRule
         .executeAndAssertThat(command,
             BASE_COMMAND_STRING + "--portable-auto-serializable-classes=" + new String[] {})
-        .statusIsError().containsOutput("Could not process command due to error.")
+        .statusIsError().containsOutput("Error while processing command")
         .containsOutput("Can't create ReflectionBasedAutoSerializer.");
 
     verify(command, times(0)).persistClusterConfiguration(any(), any());
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/remote/CommandExecutorTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/remote/CommandExecutorTest.java
index f6b846b..b97de97 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/remote/CommandExecutorTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/remote/CommandExecutorTest.java
@@ -31,7 +31,7 @@ import org.apache.geode.management.cli.Result;
 import org.apache.geode.management.internal.cli.GfshParseResult;
 import org.apache.geode.management.internal.cli.exceptions.EntityNotFoundException;
 import org.apache.geode.management.internal.cli.exceptions.UserErrorException;
-import org.apache.geode.management.internal.cli.result.CommandResult;
+import org.apache.geode.management.internal.cli.result.model.ResultModel;
 import org.apache.geode.security.NotAuthorizedException;
 import org.apache.geode.test.junit.categories.UnitTest;
 
@@ -52,7 +52,7 @@ public class CommandExecutorTest {
   @Test
   public void executeWhenGivenDummyParseResult() throws Exception {
     Object result = executor.execute(parseResult);
-    assertThat(result).isInstanceOf(CommandResult.class);
+    assertThat(result).isInstanceOf(ResultModel.class);
     assertThat(result.toString()).contains("Error while processing command");
   }
 
@@ -75,7 +75,7 @@ public class CommandExecutorTest {
     ;
     doThrow(new RuntimeException("my message here")).when(executor).invokeCommand(any(), any());
     Object thisResult = executor.execute(parseResult);
-    assertThat(((CommandResult) thisResult).getStatus()).isEqualTo(Result.Status.ERROR);
+    assertThat(((ResultModel) thisResult).getStatus()).isEqualTo(Result.Status.ERROR);
     assertThat(thisResult.toString()).contains("my message here");
   }
 
@@ -93,7 +93,7 @@ public class CommandExecutorTest {
     doThrow(new IllegalArgumentException("my message here")).when(executor).invokeCommand(any(),
         any());
     Object thisResult = executor.execute(parseResult);
-    assertThat(((CommandResult) thisResult).getStatus()).isEqualTo(Result.Status.ERROR);
+    assertThat(((ResultModel) thisResult).getStatus()).isEqualTo(Result.Status.ERROR);
     assertThat(thisResult.toString()).contains("my message here");
   }
 
@@ -103,7 +103,7 @@ public class CommandExecutorTest {
     doThrow(new IllegalStateException("my message here")).when(executor).invokeCommand(any(),
         any());
     Object thisResult = executor.execute(parseResult);
-    assertThat(((CommandResult) thisResult).getStatus()).isEqualTo(Result.Status.ERROR);
+    assertThat(((ResultModel) thisResult).getStatus()).isEqualTo(Result.Status.ERROR);
     assertThat(thisResult.toString()).contains("my message here");
   }
 
@@ -112,7 +112,7 @@ public class CommandExecutorTest {
     ;
     doThrow(new UserErrorException("my message here")).when(executor).invokeCommand(any(), any());
     Object thisResult = executor.execute(parseResult);
-    assertThat(((CommandResult) thisResult).getStatus()).isEqualTo(Result.Status.ERROR);
+    assertThat(((ResultModel) thisResult).getStatus()).isEqualTo(Result.Status.ERROR);
     assertThat(thisResult.toString()).contains("my message here");
   }
 
@@ -122,7 +122,7 @@ public class CommandExecutorTest {
     doThrow(new EntityNotFoundException("my message here", true)).when(executor)
         .invokeCommand(any(), any());
     Object thisResult = executor.execute(parseResult);
-    assertThat(((CommandResult) thisResult).getStatus()).isEqualTo(Result.Status.OK);
+    assertThat(((ResultModel) thisResult).getStatus()).isEqualTo(Result.Status.OK);
     assertThat(thisResult.toString()).contains("Skipping: my message here");
   }
 
@@ -132,7 +132,7 @@ public class CommandExecutorTest {
     doThrow(new EntityNotFoundException("my message here")).when(executor).invokeCommand(any(),
         any());
     Object thisResult = executor.execute(parseResult);
-    assertThat(((CommandResult) thisResult).getStatus()).isEqualTo(Result.Status.ERROR);
+    assertThat(((ResultModel) thisResult).getStatus()).isEqualTo(Result.Status.ERROR);
     assertThat(thisResult.toString()).contains("my message here");
   }
 }
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/result/TabularResultDataTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/result/TabularResultDataTest.java
index 496039e..4485d05 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/result/TabularResultDataTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/result/TabularResultDataTest.java
@@ -65,6 +65,7 @@ public class TabularResultDataTest {
     data.accumulate("col1", "value3");
 
     assertThat(data.retrieveAllValues("col1")).containsExactly("value1", "value2", "value3");
+
   }
 
   @Test
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/result/model/LegacyVsResultModelComparisonTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/result/model/LegacyVsResultModelComparisonTest.java
index 81613ba..054474d 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/result/model/LegacyVsResultModelComparisonTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/result/model/LegacyVsResultModelComparisonTest.java
@@ -153,8 +153,7 @@ public class LegacyVsResultModelComparisonTest {
     CommandResult legacyErrorResult = ResultBuilder.fromJson(legacyString);
 
     // Create the new model results
-    ResultModel newCrm = new ResultModel();
-    newCrm.createError("This is a bad line");
+    ResultModel newCrm = ResultModel.createError("This is a bad line");
     newCrm.getInfoSections().get(0).addLine("This is another bad line");
 
     String newModelString = CommandResponseBuilder.createCommandResponseJson("server1", newCrm);
@@ -177,8 +176,7 @@ public class LegacyVsResultModelComparisonTest {
     CommandResult legacyErrorResult = ResultBuilder.fromJson(legacyString);
 
     // Create the new model results
-    ResultModel newCrm = new ResultModel();
-    newCrm.createError("This is an error message");
+    ResultModel newCrm = ResultModel.createError("This is an error message");
 
     String newModelString = CommandResponseBuilder.createCommandResponseJson("server1", newCrm);
     CommandResult newErrorModelResult = ResultBuilder.fromJson(newModelString);
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/result/model/TabularResultModelTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/result/model/TabularResultModelTest.java
new file mode 100644
index 0000000..e7c89d1
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/result/model/TabularResultModelTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.result.model;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.test.junit.categories.UnitTest;
+
+
+@Category(UnitTest.class)
+public class TabularResultModelTest {
+  private TabularResultModel table1;
+
+  @Before
+  public void setUp() throws Exception {
+    table1 = new TabularResultModel();
+  }
+
+  @Test
+  public void accumulateAndAddRowHasTheSameEffect() {
+    table1.accumulate("c1", "v1");
+    table1.accumulate("c1", "v4");
+    table1.accumulate("c2", "v2");
+    table1.accumulate("c2", "v5");
+    table1.accumulate("c3", "v3");
+    table1.accumulate("c3", "v6");
+
+    TabularResultModel table2 = new TabularResultModel();
+    table2.setColumnHeader("c1", "c2", "c3");
+    table2.addRow("v1", "v2", "v3");
+    table2.addRow("v4", "v5", "v6");
+
+    assertThat(table1.getContent()).isEqualToComparingFieldByFieldRecursively(table2.getContent());
+  }
+
+  @Test
+  public void cannotAddRowWithDifferentSize() {
+    table1.setColumnHeader("c1", "c2", "c3");
+    assertThatThrownBy(() -> table1.addRow("v1", "v2")).isInstanceOf(IllegalStateException.class);
+  }
+}
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/shell/GfshExecutionStrategyTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/shell/GfshExecutionStrategyTest.java
index 201e1f8..3b01b2e 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/shell/GfshExecutionStrategyTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/shell/GfshExecutionStrategyTest.java
@@ -32,6 +32,7 @@ import org.apache.geode.management.internal.cli.GfshParseResult;
 import org.apache.geode.management.internal.cli.LogWrapper;
 import org.apache.geode.management.internal.cli.result.CommandResult;
 import org.apache.geode.management.internal.cli.result.ResultBuilder;
+import org.apache.geode.management.internal.cli.result.model.ResultModel;
 import org.apache.geode.test.junit.categories.UnitTest;
 
 /**
@@ -41,6 +42,7 @@ import org.apache.geode.test.junit.categories.UnitTest;
 public class GfshExecutionStrategyTest {
   private static final String COMMAND1_SUCCESS = "Command1 Executed successfully";
   private static final String COMMAND2_SUCCESS = "Command2 Executed successfully";
+  private static final String COMMAND3_SUCCESS = "Command3 Executed successfully";
 
   private Gfsh gfsh;
   private GfshParseResult parsedCommand;
@@ -65,6 +67,14 @@ public class GfshExecutionStrategyTest {
     assertThat(result.nextLine().trim()).isEqualTo(COMMAND1_SUCCESS);
   }
 
+  @Test
+  public void testOfflineCommandThatReturnsResultModel() throws NoSuchMethodException {
+    when(parsedCommand.getMethod()).thenReturn(Commands.class.getDeclaredMethod("offlineCommand2"));
+    when(parsedCommand.getInstance()).thenReturn(new Commands());
+    Result result = (Result) gfshExecutionStrategy.execute(parsedCommand);
+    assertThat(result.nextLine().trim()).isEqualTo(COMMAND3_SUCCESS);
+  }
+
   /**
    * tests execute online command
    */
@@ -102,6 +112,11 @@ public class GfshExecutionStrategyTest {
       return ResultBuilder.createInfoResult(COMMAND1_SUCCESS);
     }
 
+    @CliMetaData(shellOnly = true)
+    public ResultModel offlineCommand2() {
+      return ResultModel.createInfo(COMMAND3_SUCCESS);
+    }
+
     @CliMetaData(shellOnly = false)
     public Result onlineCommand() {
       return ResultBuilder.createInfoResult(COMMAND2_SUCCESS);

-- 
To stop receiving notification emails like this one, please contact
jinmeiliao@apache.org.