public String getSerializedApiError(
      ServerApiException ex, Map<String, Object[]> apiCommandParams, String responseType) {
    String responseName = null;
    Class<?> cmdClass = null;
    String responseText = null;

    if (ex == null) {
      // this call should not be invoked with null exception
      return getSerializedApiError(
          HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
          "Some internal error happened",
          apiCommandParams,
          responseType);
    }
    try {
      if (ex.getErrorCode() == ApiErrorCode.UNSUPPORTED_ACTION_ERROR
          || apiCommandParams == null
          || apiCommandParams.isEmpty()) {
        responseName = "errorresponse";
      } else {
        Object cmdObj = apiCommandParams.get("command");
        // cmd name can be null when "command" parameter is missing in
        // the request
        if (cmdObj != null) {
          String cmdName = ((String[]) cmdObj)[0];
          cmdClass = getCmdClass(cmdName);
          if (cmdClass != null) {
            responseName = ((BaseCmd) cmdClass.newInstance()).getCommandName();
          } else {
            responseName = "errorresponse";
          }
        }
      }
      ExceptionResponse apiResponse = new ExceptionResponse();
      apiResponse.setErrorCode(ex.getErrorCode().getHttpCode());
      apiResponse.setErrorText(ex.getDescription());
      apiResponse.setResponseName(responseName);
      ArrayList<String> idList = ex.getIdProxyList();
      if (idList != null) {
        for (int i = 0; i < idList.size(); i++) {
          apiResponse.addProxyObject(idList.get(i));
        }
      }
      // Also copy over the cserror code and the function/layer in which
      // it was thrown.
      apiResponse.setCSErrorCode(ex.getCSErrorCode());

      SerializationContext.current().setUuidTranslation(true);
      responseText = ApiResponseSerializer.toSerializedString(apiResponse, responseType);

    } catch (Exception e) {
      s_logger.error("Exception responding to http request", e);
    }
    return responseText;
  }
  public String getSerializedApiError(
      int errorCode,
      String errorText,
      Map<String, Object[]> apiCommandParams,
      String responseType) {
    String responseName = null;
    Class<?> cmdClass = null;
    String responseText = null;

    try {
      if (apiCommandParams == null || apiCommandParams.isEmpty()) {
        responseName = "errorresponse";
      } else {
        Object cmdObj = apiCommandParams.get("command");
        // cmd name can be null when "command" parameter is missing in the request
        if (cmdObj != null) {
          String cmdName = ((String[]) cmdObj)[0];
          cmdClass = getCmdClass(cmdName);
          if (cmdClass != null) {
            responseName = ((BaseCmd) cmdClass.newInstance()).getCommandName();
          } else {
            responseName = "errorresponse";
          }
        }
      }
      ExceptionResponse apiResponse = new ExceptionResponse();
      apiResponse.setErrorCode(errorCode);
      apiResponse.setErrorText(errorText);
      apiResponse.setResponseName(responseName);
      SerializationContext.current().setUuidTranslation(true);
      responseText = ApiResponseSerializer.toSerializedString(apiResponse, responseType);

    } catch (Exception e) {
      s_logger.error("Exception responding to http request", e);
    }
    return responseText;
  }
  private String queueCommand(BaseCmd cmdObj, Map<String, String> params) throws Exception {
    UserContext ctx = UserContext.current();
    Long callerUserId = ctx.getCallerUserId();
    Account caller = ctx.getCaller();

    // Queue command based on Cmd super class:
    // BaseCmd: cmd is dispatched to ApiDispatcher, executed, serialized and returned.
    // BaseAsyncCreateCmd: cmd params are processed and create() is called, then same workflow as
    // BaseAsyncCmd.
    // BaseAsyncCmd: cmd is processed and submitted as an AsyncJob, job related info is serialized
    // and returned.
    if (cmdObj instanceof BaseAsyncCmd) {
      Long objectId = null;
      String objectUuid = null;
      if (cmdObj instanceof BaseAsyncCreateCmd) {
        BaseAsyncCreateCmd createCmd = (BaseAsyncCreateCmd) cmdObj;
        _dispatcher.dispatchCreateCmd(createCmd, params);
        objectId = createCmd.getEntityId();
        objectUuid = createCmd.getEntityUuid();
        params.put("id", objectId.toString());
      } else {
        ApiDispatcher.processParameters(cmdObj, params);
      }

      BaseAsyncCmd asyncCmd = (BaseAsyncCmd) cmdObj;

      if (callerUserId != null) {
        params.put("ctxUserId", callerUserId.toString());
      }
      if (caller != null) {
        params.put("ctxAccountId", String.valueOf(caller.getId()));
      }

      long startEventId = ctx.getStartEventId();
      asyncCmd.setStartEventId(startEventId);

      // save the scheduled event
      Long eventId =
          EventUtils.saveScheduledEvent(
              (callerUserId == null) ? User.UID_SYSTEM : callerUserId,
              asyncCmd.getEntityOwnerId(),
              asyncCmd.getEventType(),
              asyncCmd.getEventDescription(),
              startEventId);
      if (startEventId == 0) {
        // There was no create event before, set current event id as start eventId
        startEventId = eventId;
      }

      params.put("ctxStartEventId", String.valueOf(startEventId));

      ctx.setAccountId(asyncCmd.getEntityOwnerId());

      Long instanceId = (objectId == null) ? asyncCmd.getInstanceId() : objectId;
      AsyncJobVO job =
          new AsyncJobVO(
              callerUserId,
              caller.getId(),
              cmdObj.getClass().getName(),
              ApiGsonHelper.getBuilder().create().toJson(params),
              instanceId,
              asyncCmd.getInstanceType());

      long jobId = _asyncMgr.submitAsyncJob(job);

      if (jobId == 0L) {
        String errorMsg = "Unable to schedule async job for command " + job.getCmd();
        s_logger.warn(errorMsg);
        throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, errorMsg);
      }

      if (objectId != null) {
        String objUuid = (objectUuid == null) ? objectId.toString() : objectUuid;
        return ((BaseAsyncCreateCmd) asyncCmd).getResponse(jobId, objUuid);
      }

      SerializationContext.current().setUuidTranslation(true);
      return ApiResponseSerializer.toSerializedString(
          asyncCmd.getResponse(jobId), asyncCmd.getResponseType());
    } else {
      _dispatcher.dispatch(cmdObj, params);

      // if the command is of the listXXXCommand, we will need to also return the
      // the job id and status if possible
      // For those listXXXCommand which we have already created DB views, this step is not needed
      // since async job is joined in their db views.
      if (cmdObj instanceof BaseListCmd
          && !(cmdObj instanceof ListVMsCmd)
          && !(cmdObj instanceof ListRoutersCmd)
          && !(cmdObj instanceof ListSecurityGroupsCmd)
          && !(cmdObj instanceof ListTagsCmd)
          && !(cmdObj instanceof ListEventsCmd)
          && !(cmdObj instanceof ListVMGroupsCmd)
          && !(cmdObj instanceof ListProjectsCmd)
          && !(cmdObj instanceof ListProjectAccountsCmd)
          && !(cmdObj instanceof ListProjectInvitationsCmd)
          && !(cmdObj instanceof ListHostsCmd)
          && !(cmdObj instanceof ListVolumesCmd)
          && !(cmdObj instanceof ListUsersCmd)
          && !(cmdObj instanceof ListAccountsCmd)
          && !(cmdObj instanceof ListStoragePoolsCmd)) {
        buildAsyncListResponse((BaseListCmd) cmdObj, caller);
      }

      SerializationContext.current().setUuidTranslation(true);
      return ApiResponseSerializer.toSerializedString(
          (ResponseObject) cmdObj.getResponseObject(), cmdObj.getResponseType());
    }
  }