/**
   * Get service state by serviceLink.
   *
   * @param path
   * @return
   * @throws Throwable
   */
  private ImageReplicatorService.State getLatestState(String path) throws Throwable {
    Operation getOperation =
        Operation.createGet(UriUtils.buildUri(dcpHost, path))
            .setReferer(UriUtils.buildUri(dcpHost, REFERRER_PATH))
            .setExpiration(Utils.getNowMicrosUtc() + dcpOperationTimeoutMicros)
            .setContextId(LoggingUtils.getRequestId());
    OperationLatch opLatch = new OperationLatch(getOperation);

    dcpHost.sendRequest(getOperation);
    return opLatch.await().getBody(ImageReplicatorService.State.class);
  }
  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);
  }
  /**
   * 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;
  }
    @Test(
        dataProvider = "InvalidStageTransitions",
        expectedExceptions = IllegalStateException.class)
    public void testInvalidStageTransition(
        TaskState.TaskStage startStage, TaskState.TaskStage patchStage) throws Throwable {
      startService(startStage);
      CreateContainerSpecTaskService.State patchState =
          createContainerSpecTaskService.buildPatch(patchStage, null);

      Operation patchOperation =
          Operation.createPatch(UriUtils.buildUri(testHost, TestHost.SERVICE_URI, null))
              .setBody(patchState);

      testHost.sendRequestAndWait(patchOperation);
    }
  /**
   * 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);
    }
  }
    @Test(
        dataProvider = "InvalidPatchStateAttributes",
        expectedExceptions = IllegalStateException.class)
    public void testInvalidPatchStateInvalidAttributeSet(String attributeName, Object value)
        throws Throwable {
      startService(TaskState.TaskStage.CREATED);

      CreateContainerSpecTaskService.State patchState =
          createContainerSpecTaskService.buildPatch(TaskState.TaskStage.STARTED, null);

      patchState.getClass().getDeclaredField(attributeName).set(patchState, value);

      Operation patchOperation =
          Operation.createPatch(UriUtils.buildUri(testHost, TestHost.SERVICE_URI, null))
              .setBody(patchState);

      testHost.sendRequestAndWait(patchOperation);
    }
    @Test(dataProvider = "ValidStageTransitions")
    public void testValidStageTransition(
        TaskState.TaskStage startStage, TaskState.TaskStage patchStage) throws Throwable {
      startService(startStage);
      CreateContainerSpecTaskService.State patchState =
          createContainerSpecTaskService.buildPatch(patchStage, null);

      Operation patchOperation =
          Operation.createPatch(UriUtils.buildUri(testHost, TestHost.SERVICE_URI, null))
              .setBody(patchState);

      Operation result = testHost.sendRequestAndWait(patchOperation);
      assertThat(result.getStatusCode(), is(200));

      CreateContainerSpecTaskService.State savedState =
          testHost.getServiceState(CreateContainerSpecTaskService.State.class);

      assertThat(savedState.taskState.stage, is(patchStage));
    }
 /**
  * Send a patch message to ourselves to update the execution stage.
  *
  * @param s
  */
 private void sendSelfPatch(State s) {
   Operation patch = Operation.createPatch(UriUtils.buildUri(getHost(), getSelfLink())).setBody(s);
   this.sendRequest(patch);
 }