@Override
  public Future<Void> undeleteBlob(String blobId, String serviceId, Callback<Void> callback,
      QuotaChargeCallback quotaChargeCallback) {
    if (blobId == null) {
      throw new IllegalArgumentException("blobId must not be null");
    }
    currentOperationsCount.incrementAndGet();
    routerMetrics.undeleteBlobOperationRate.mark();
    routerMetrics.operationQueuingRate.mark();
    FutureResult<Void> futureResult = new FutureResult<>();
    if (isOpen.get()) {
      if (notFoundCache.getIfPresent(blobId) != null) {
        // If we know that blob doesn't exist, complete the operation.
        logger.info("Blob {} is known to be missing in servers", blobId);
        RouterException routerException =
            new RouterException("UndeleteOperation failed because of BlobNotFound", RouterErrorCode.BlobDoesNotExist);
        completeOperation(futureResult, callback, null, routerException);
      } else {
        getOperationController().undeleteBlob(blobId, serviceId, futureResult,
            new BlobOperationCallbackWrapper<>(blobId, callback), quotaChargeCallback);
      }
    } else {
      RouterException routerException =
          new RouterException("Cannot accept operation because Router is closed", RouterErrorCode.RouterClosed);
      routerMetrics.operationDequeuingRate.mark();
      routerMetrics.onUndeleteBlobError(routerException);
      completeOperation(futureResult, callback, null, routerException);
    }
    return futureResult;
  }
Requests for a blob to be undeleted asynchronously and invokes the {@link Callback} when the request completes.
@param blobId The ID of the blob that needs to be undeleted.
@param serviceId The service ID of the service undeleting the blob. This can be null if unknown.
@param callback The {@link Callback} which will be invoked on the completion of a request.
@return A future that would contain information about whether the undelete succeeded or not, eventually.
ambry-router/src/main/java/com/github/ambry/router/NonBlockingRouter.java