protected void undeleteBlob(final String blobIdStr, final String serviceId, FutureResult<Void> futureResult,
      final Callback<Void> callback, QuotaChargeCallback quotaChargeCallback) {
    doOperationTowardsMaybeCompositeBlob(blobIdStr, futureResult, callback,
        new CompositeBlobOperationHelper("UNDELETE", GetOption.Include_Deleted_Blobs, routerMetrics.ageAtUndelete,
            (blobId, chunkIds) -> doUndeleteOperation(blobId, chunkIds, serviceId, futureResult, callback,
                quotaChargeCallback), blobInfo -> false, (exception) -> {
          nonBlockingRouter.completeUndeleteBlobOperation(exception, futureResult, callback);
        }), quotaChargeCallback);
  }
Requests for a blob to be undeleted asynchronously and invokes the {@link Callback} when the request completes.
@param blobIdStr The ID of the blob that needs to be undeleted in string form
@param serviceId The service ID of the service undeleting the blob. This can be null if unknown.
@param futureResult A future that would contain information about whether the undelete succeeded or not,
                    eventually.
@param callback The {@link Callback} which will be invoked on the completion of a request.
ambry-router/src/main/java/com/github/ambry/router/OperationController.java