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());
    }
  }
  @DB
  protected void scheduleSnapshots() {
    String displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, _currentTimestamp);
    s_logger.debug("Snapshot scheduler.poll is being called at " + displayTime);

    List<SnapshotScheduleVO> snapshotsToBeExecuted =
        _snapshotScheduleDao.getSchedulesToExecute(_currentTimestamp);
    s_logger.debug(
        "Got " + snapshotsToBeExecuted.size() + " snapshots to be executed at " + displayTime);

    // This is done for recurring snapshots, which are executed by the system automatically
    // Hence set user id to that of system
    long userId = 1;

    for (SnapshotScheduleVO snapshotToBeExecuted : snapshotsToBeExecuted) {
      long policyId = snapshotToBeExecuted.getPolicyId();
      long volumeId = snapshotToBeExecuted.getVolumeId();
      VolumeVO volume = _volsDao.findById(volumeId);
      if (volume.getPoolId() == null) {
        // this volume is not attached
        continue;
      }
      if (_snapshotPolicyDao.findById(policyId) == null) {
        _snapshotScheduleDao.remove(snapshotToBeExecuted.getId());
      }
      if (s_logger.isDebugEnabled()) {
        Date scheduledTimestamp = snapshotToBeExecuted.getScheduledTimestamp();
        displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, scheduledTimestamp);
        s_logger.debug(
            "Scheduling 1 snapshot for volume "
                + volumeId
                + " for schedule id: "
                + snapshotToBeExecuted.getId()
                + " at "
                + displayTime);
      }
      long snapshotScheId = snapshotToBeExecuted.getId();
      SnapshotScheduleVO tmpSnapshotScheduleVO = null;
      try {
        tmpSnapshotScheduleVO = _snapshotScheduleDao.acquireInLockTable(snapshotScheId);

        Long eventId =
            EventUtils.saveScheduledEvent(
                User.UID_SYSTEM,
                Account.ACCOUNT_ID_SYSTEM,
                EventTypes.EVENT_SNAPSHOT_CREATE,
                "creating snapshot for volume Id:" + volumeId,
                0);

        Map<String, String> params = new HashMap<String, String>();
        params.put("volumeid", "" + volumeId);
        params.put("policyid", "" + policyId);
        params.put("ctxUserId", "1");
        params.put("ctxAccountId", "1");
        params.put("ctxStartEventId", String.valueOf(eventId));

        CreateSnapshotCmd cmd = new CreateSnapshotCmd();
        ApiDispatcher.getInstance().dispatchCreateCmd(cmd, params);
        params.put("id", "" + cmd.getEntityId());
        params.put("ctxStartEventId", "1");

        AsyncJobVO job = new AsyncJobVO();
        job.setUserId(userId);
        // Just have SYSTEM own the job for now.  Users won't be able to see this job, but
        // it's an internal job so probably not a huge deal.
        job.setAccountId(1L);
        job.setCmd(CreateSnapshotCmd.class.getName());
        job.setInstanceId(cmd.getEntityId());
        job.setCmdInfo(GsonHelper.getBuilder().create().toJson(params));

        long jobId = _asyncMgr.submitAsyncJob(job);

        tmpSnapshotScheduleVO.setAsyncJobId(jobId);
        _snapshotScheduleDao.update(snapshotScheId, tmpSnapshotScheduleVO);
      } finally {
        if (tmpSnapshotScheduleVO != null) {
          _snapshotScheduleDao.releaseFromLockTable(snapshotScheId);
        }
      }
    }
  }