How to move from deprecated Task to ApiFuture for firebase admin SDK 5.4 and above

Viewed 4583

I'm just trying to solve the deprecation notes from my Java code of new firebase-admin SDK, the code is written in version 5.3.1 but after upgrading the version into 5.5.0 the deprecation notes appeared, here is a sample of my code:

Using FirebaseAuth (deprecatation on: Task, addOnSuccessListener and addOnFailureListener) :

private CompletableFuture<FirebaseToken> getDecryptedTokenCompletableFuture(String firebaseTokenString) {
        CompletableFuture<FirebaseToken> tokenFuture = new CompletableFuture<>();
        Task<FirebaseToken> tokenTask = FirebaseAuth.getInstance(firebaseApp).verifyIdToken(firebaseTokenString);
        tokenTask.addOnSuccessListener(tokenFuture::complete);
        tokenTask.addOnFailureListener(exception -> tokenFuture.completeExceptionally(new AuthorizationException("Failed to verify token", exception)));
        return tokenFuture;
    }

And for FirebaseDatabase (deprecatation on: Task, addOnSuccessListener, addOnFailureListener, updateChildren and removeValue) :

public static <T> CompletableFuture<T> toCompletableFuture(Task<T> task) {
    CompletableFuture<T> future = new CompletableFuture<>();
    task.addOnCompleteListener(result -> {
        future.complete(result.getResult());
    }).addOnFailureListener(future::completeExceptionally);
    return future;
}

/**
 * @param updatedParams if null it will removed child
 * @param path          path to update
 * @return void when complete
 */
public CompletableFuture<Void> updateObjectData(Map<String, Object> updatedParams, String path) {
    if (updatedParams == null) {
        return removeObjectData(path);
    }
    logger.debug("Update ObjectData in firebase of ref ({}) with data: {}", path, updatedParams.toString());
    DatabaseReference child = this.getUserDataReference().child(path);
    return toCompletableFuture(child.updateChildren(updatedParams));
}

/**
 * @param path path to of node to remove
 * @return void when complete
 */
public CompletableFuture<Void> removeObjectData(String path) {
    logger.debug("Remove ObjectData in firebase of ref ({})", path);
    DatabaseReference child = this.getUserDataReference().child(path);
    return toCompletableFuture(child.removeValue());
}

The deprecation note saying I have to use ApiFuture as what the release notes saying: https://firebase.google.com/support/release-notes/admin/java

And inside source, as for example:

  /**
   * Similar to {@link #updateChildrenAsync(Map)} but returns a Task.
   *
   * @param update The paths to update and their new values
   * @return The {@link Task} for this operation.
   * @deprecated Use {@link #updateChildrenAsync(Map)}
   */

And

/**
 * Represents an asynchronous operation.
 *
 * @param <T> the type of the result of the operation
 * @deprecated {@code Task} has been deprecated in favor of
 *     <a href="https://googleapis.github.io/api-common-java/1.1.0/apidocs/com/google/api/core/ApiFuture.html">{@code ApiFuture}</a>.
 *     For every method x() that returns a {@code Task<T>}, you should be able to find a
 *     corresponding xAsync() method that returns an {@code ApiFuture<T>}.
 */
3 Answers

The code to verify a Token with FirebaseAuth from Firebase Admin SDK Java using ApiFuture, would be:

ApiFutures.addCallback(FirebaseAuth.getInstance().verifyIdTokenAsync(token),
  new ApiFutureCallback<FirebaseToken>() {
      @Override
      public void onFailure(Throwable t) {
        // TODO handle failure
      }
      @Override
      public void onSuccess(FirebaseToken decodedToken) {
        // TODO handle success
      }
});

Similar approach can be used for your code that uses FirebaseDatabase.

Hiranya Jayathilaka wrote a very detailed article explaining how to migrate from Task to ApiFuture and the rational behind it: https://medium.com/google-cloud/firebase-asynchronous-operations-with-admin-java-sdk-82ca9b4f6022

This code works for versions 5.4.0 - 5.8.0, the most recent at the time of this writing. The release notes are available here: https://firebase.google.com/support/release-notes/admin/java

Take a look at the ApiFutures util class, which allows adding callbacks to an ApiFuture.

Almost 2 years, but need to add my answers, current answers gave me a guide but were not a full solution, so Kudos for @Enylton Machado and @Hiranya Jayathilaka it is still good to refer to their answers, give them upvotes.

Currently, I did change toCompletableFuture to:

public <T> CompletableFuture<T> toCompletableFuture(ApiFuture<T> apiFuture) {
    final CompletableFuture<T> future = new CompletableFuture<>();
    apiFuture.addListener(() -> {
        try {
            future.complete(apiFuture.get());
        } catch (InterruptedException | ExecutionException ex) {
            logger.error(ex.getMessage());
            future.completeExceptionally(ex);
        }
    }, executionContext);
    return future;
}

Where executionContext got injected in the constructor, since I'm using Play Framework, or you can initialize your own using Executors.newFixedThreadPool(10) as for example! but you need to handle shutting down it and other stuff, more details can be learned from here ExecutorService in Java or A Guide to the Java ExecutorService.

So when I do call like this.getUserDataReference().child(path).push().setValueAsync(value), I do call it like:

public CompletableFuture<String> pushDataToArray(String path, Map<String, Object> paramsToAdd) {
    final DatabaseReference databaseReference = getUserDataReference().child(path).push();
    paramsToAdd.put("createdAt", ServerValue.TIMESTAMP);

    return toCompletableFuture(databaseReference.setValueAsync(paramsToAdd), this.executionContext)
            .thenApply(voidResult -> databaseReference.getKey());
}

Edit: if you prefer use ApiFutures:

public <T> CompletableFuture<T> toCompletableFuture(ApiFuture<T> apiFuture) {
    final CompletableFuture<T> future = new CompletableFuture<>();
    ApiFutures.addCallback(apiFuture, new ApiFutureCallback<T>() {
        @Override
        public void onFailure(Throwable t) {
            future.completeExceptionally(t);
        }

        @Override
        public void onSuccess(T result) {
            try {
                future.complete(apiFuture.get());
            } catch (InterruptedException | ExecutionException ex) {
                logger.error(ex.getMessage());
                future.completeExceptionally(ex);
            }
        }
    }, executionContext);
    return future;
}
Related