/** Handle service periodic maintenance calls. */
  @Override
  public void handleMaintenance(Operation post) {
    post.complete();

    Operation.CompletionHandler handler =
        (Operation op, Throwable failure) -> {
          if (null != failure) {
            // query failed so abort and retry next time
            logFailure(failure);
            return;
          }

          NodeSelectorService.SelectOwnerResponse rsp =
              op.getBody(NodeSelectorService.SelectOwnerResponse.class);
          if (!getHost().getId().equals(rsp.ownerNodeId)) {
            ServiceUtils.logInfo(
                TaskTriggerService.this,
                "Host[%s]: Not owner of scheduler [%s] (Owner Info [%s])",
                getHost().getId(),
                getSelfLink(),
                Utils.toJson(rsp));
            return;
          }

          State state = new State();
          sendSelfPatch(state);
        };

    Operation selectOwnerOp =
        Operation.createPost(null)
            .setExpiration(ServiceUtils.computeExpirationTime(OWNER_SELECTION_TIMEOUT_MILLIS))
            .setCompletion(handler);
    getHost().selectOwner(null, getSelfLink(), selectOwnerOp);
  }
  @Override
  public void handleStart(Operation start) {
    ServiceUtils.logInfo(this, "Starting service %s", getSelfLink());

    try {
      // Initialize the task state.
      State s = start.getBody(State.class);

      if (s.taskInfo == null || s.taskInfo.stage == TaskState.TaskStage.CREATED) {
        s.taskInfo = new TaskState();
        s.taskInfo.stage = TaskState.TaskStage.STARTED;
        s.taskInfo.subStage = TaskState.SubStage.UPDATE_DATASTORE_COUNTS;
      }

      if (s.documentExpirationTimeMicros <= 0) {
        s.documentExpirationTimeMicros =
            ServiceUtils.computeExpirationTime(ServiceUtils.DEFAULT_DOC_EXPIRATION_TIME);
      }

      if (s.queryPollDelay == null) {
        s.queryPollDelay = DEFAULT_QUERY_POLL_DELAY;
      }

      validateState(s);
      start.setBody(s).complete();

      sendStageProgressPatch(s, s.taskInfo.stage, s.taskInfo.subStage);
    } catch (Throwable e) {
      ServiceUtils.logSevere(this, e);
      if (!OperationUtils.isCompleted(start)) {
        start.fail(e);
      }
    }
  }
  /**
   * Does any additional processing after the patch operation has been completed.
   *
   * @param current
   */
  private void processPatch(final State current) {
    try {
      Type stateType = Class.forName(current.triggerStateClassName);
      ServiceDocument postState = Utils.fromJson(current.serializedTriggerState, stateType);
      postState.documentExpirationTimeMicros =
          ServiceUtils.computeExpirationTime(current.taskExpirationAgeMillis);

      Operation post =
          Operation.createPost(UriUtils.buildUri(getHost(), current.factoryServiceLink))
              .setBody(postState);
      this.sendRequest(post);
    } catch (ClassNotFoundException ex) {
      logFailure(ex);
    }
  }