/** 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);
  }
  private void createTenantEntity(final String tenantId) {

    final Service service = this;

    Operation.CompletionHandler completionHandler =
        new Operation.CompletionHandler() {
          @Override
          public void handle(Operation operation, Throwable throwable) {
            if (null != throwable) {
              failTask(throwable);
            }

            try {
              State patchState = buildPatch(TaskState.TaskStage.FINISHED, null);
              patchState.tenantServiceLink =
                  operation.getBody(TenantService.State.class).documentSelfLink;
              TaskUtils.sendSelfPatch(service, patchState);
            } catch (Throwable t) {
              failTask(t);
            }
          }
        };

    Operation op =
        Operation.createPost(UriUtils.buildUri(getHost(), TenantFactoryService.SELF_LINK))
            .setBody(createTenantServiceState(tenantId))
            .setCompletion(completionHandler);
    sendRequest(op);
  }
  /**
   * 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);
    }
  }
  /**
   * Trigger image replication.
   *
   * @param request
   * @return
   * @throws Throwable
   */
  private String triggerReplication(ReplicateImageRequest request) throws Throwable {
    // Prepare replication service call.
    ImageReplicatorService.State postReq = new ImageReplicatorService.State();
    postReq.image = request.getImage();
    postReq.datastore = request.getDatastore();

    // Create the operation and call for replication.
    Operation postOperation =
        Operation.createPost(UriUtils.buildUri(dcpHost, ImageReplicatorServiceFactory.class))
            .setBody(postReq)
            .setReferer(UriUtils.buildUri(dcpHost, REFERRER_PATH))
            .setExpiration(Utils.getNowMicrosUtc() + dcpOperationTimeoutMicros)
            .setContextId(LoggingUtils.getRequestId());
    OperationLatch opLatch = new OperationLatch(postOperation);
    dcpHost.sendRequest(postOperation);
    Operation op = opLatch.await();

    // Return operation id.
    return op.getBody(ImageReplicatorService.State.class).documentSelfLink;
  }