Java – Google Cloud Endpoint executes asynchronous code in the endpoint

Google Cloud Endpoint executes asynchronous code in the endpoint… here is a solution to the problem.

Google Cloud Endpoint executes asynchronous code in the endpoint

I’m trying to validate a client token created by the Firebase authentication library in Android in the GCE endpoint.

A guide on how to do this can be found here

Basically I need to call this code fragment from the endpoint (i.e. server backend code instead of android code).

FirebaseAuth.getInstance().verifyIdToken(idToken)
    .addOnSuccessListener(new OnSuccessListener<FirebaseToken>() {
        @Override
        public void onSuccess(FirebaseToken decodedToken) {
            String uid = decodedToken.getUid();
            // ...
        }
});

Let’s say I want to execute that code and return the user to the android client code. What should I do?

Here is my sample code that doesn’t make any sense. But it proves what I want to do!

@ApiMethod(name = "serverAuth")
public MyUser serverAuth(@Named("token") String token) {
    FirebaseAuth.getInstance().verifyIdToken(token)
            .addOnSuccessListener(new OnSuccessListener<FirebaseToken>() {
                @Override
                public void onSuccess(FirebaseToken decodedToken) {
                    String uid = decodedToken.getUid();
                    String email = decodedToken.getEmail();
                    String name = decodedToken.getName();
                    Map<String, Object> claims = decodedToken.getClaims();

String claimString = "";

for (Object claim : claims.values()) {
                        claimString += claims.toString();
                    }

MyUser user = new MyUser(uid, email, name, claimString);
                    How to return this user?

}
            });

This is compile error since user varriable does not exist here    
    return user;

}

I googled how to execute async code in the GCE endpoint. But nowhere. What I get is something about code execution that keeps blocking until it’s done and then goes back to the user. But how do you write code to make the above asynchronous code blocking?

Solution

CountDownLatch is the magic class you need. It will make you wait until OnSuccessListener is actually done.

Adjust your approach this way: (I removed the step that led to the creation of MyUser so I can focus on the point.) )

@ApiMethod(name = "serverAuth")
public MyUser serverAuth(@Named("token") String token) {
    final List<MyUser> users = new ArrayList<>();
    final CountDownLatch cdl = new CountDownLatch(1);
    FirebaseAuth.getInstance().verifyIdToken(token)
            .addOnSuccessListener(new OnSuccessListener<FirebaseToken>() {
                @Override
                public void onSuccess(FirebaseToken decodedToken) {
                    // ... init uid, email, name and claimString
                    users.add(new MyUser(uid, email, name, claimString));
                    cdl.countDown();
                }
            });
    try {
        cdl.await();  This line blocks execution till count down latch is 0
    } catch (InterruptedException ie) {

}
    if (users.size() > 0) {
        return users.get(0);
    } else {
        return null ;
    }
}

This is the basic version you need. IMHO, it also needs 2 improvements:

  • You should also consider the possibility of failure:

    FirebaseAuth.getInstance().verifyIdToken(token)
    .addOnSuccessListener(new OnSuccessListener<FirebaseToken>() {
        @Override
        public void onSuccess(FirebaseToken decodedToken) {
            cdl.countDown();
        }
    }).addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(@NonNull Exception e) {
             log error, ...
            cdl.countDown();
        }
    });
    
  • You should also consider the possibility that no listeners are invoked. In this case, your method will never return. To avoid this, you can set a timeout on the await() method:

    try {
          This line blocks execution till count down latch is 0
          or after 30 seconds.
        cdl.await(30l, TimeUnit.SECONDS);
    } catch (InterruptedException ie) {
    
    }
    

That’s it. Hope this might help.

Related Problems and Solutions