@VisibleForTesting
  @SuppressWarnings("deprecation")
  public Object execute(Object command, GfshParseResult parseResult) {
    String userInput = parseResult.getUserInput();
    if (userInput != null) {
      logger.info("Executing command: " + ArgumentRedactor.redact(userInput));
    }

    boolean locked = lockCMS(command);
    try {
      Object result = invokeCommand(command, parseResult);

      // if some custom command returns Result instead of ResultModel, we need to turn that
      // into ResultModel in order to be processed later on.
      if (result instanceof CommandResult) {
        result = ((CommandResult) result).getResultData();
      } else if (result instanceof Result) {
        Result customResult = (Result) result;
        result = new ResultModel();
        InfoResultModel info = ((ResultModel) result).addInfo();
        while (customResult.hasNextLine()) {
          info.addLine(customResult.nextLine());
        }
        customResult.resetToFirstLine();
      }

      if (result == null) {
        return ResultModel.createError("Command returned null: " + parseResult);
      }
      return result;
    }
    // for Authorization Exception, we need to throw them for higher level code to catch
    catch (NotAuthorizedException e) {
      logger.error("Not authorized to execute \"" + parseResult + "\".", e);
      throw e;
    }

    // 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 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 ResultModel.createInfo("Skipping: " + e.getMessage());
      } else {
        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 ResultModel.createError(
          "Error while processing command <" + parseResult + "> Reason : " + e.getMessage());
    }

    // for errors more lower-level than Exception, just throw them.
    catch (VirtualMachineError e) {
      org.apache.geode.SystemFailure.initiateFailure(e);
      throw e;
    } catch (Throwable t) {
      org.apache.geode.SystemFailure.checkFailure();
      throw t;
    } finally {
      unlockCMS(locked);
    }
  }
@return always return ResultModel for online command, for offline command, return either
        ResultModel or ExitShellRequest
geode-gfsh/src/main/java/org/apache/geode/management/internal/cli/remote/CommandExecutor.java