David Ferris David Ferris - 23 days ago 6
Java Question

Java Spring - return from asynchronous callback?

Working on a backend with Spring (Java) and Firebase. We are using the firebase tokens (appended as an authentication header) to identify the user, using the built in UID.

Unfortunately, extracting this UID from the token must be done asynchronously, so I can only get the token from the

onSuccess
callback.
To serve a response, I must return an object from the below
deleteUser
method, however I cannot know what the response will be until I get a success/failure callback!

I can imagine a way to do this by waiting on a flag which is set my the callback, or with some messy timing, but I'm wondering if there is a clean way of handling this without introducing race conditions or lots of extra code. Can anyone help?

Request Mapping (handles request, serves response)



@RequestMapping(value = "/users", method = RequestMethod.DELETE)
public @ResponseBody String deleteUser(@RequestHeader("Authentication") String token) {
FirebaseUtil.getUid(token, new OnSuccessListener<FirebaseToken>() {
@Override
public void onSuccess(FirebaseToken decodedToken) {
String uid = decodedToken.getUid();
//RETURN SUCCESSFUL HERE
}
}, new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
//RETURN FAILURE HERE
}
});

//MUST RETURN SOMETHING HERE?
User userToDelete = userDao.get(uid); //DONT HAVE THE uid HERE
userDao.delete(uid);
clearUserAccounts(userToDelete);
return uid + " was deleted";
}


FirebaseUtil.getUid()



public static void getUid(String token, OnSuccessListener<FirebaseToken> successListener, OnFailureListener failureListener) {
FirebaseAuth.getInstance()
.verifyIdToken(token)
.addOnSuccessListener(successListener)
.addOnFailureListener(failureListener);
}

Answer

While there are ways to block the thread until the asynchronous request finishes, there is a simple and more resource-effective solution since Spring 3.2.

You can use DeferredResult<T> as your return type to enable asynchronous processing. This allows the servlet container to reuse the HTTP worker thread right away, while sparing you the headache of forcefully serializing a chain of asynchronous requests.

By filling out the comments, your code would look like this:

@RequestMapping(value = "/users", method = RequestMethod.DELETE)
public DeferredResult<String> deleteUser(@RequestHeader("Authentication") String token) {
    final DeferredResult<String> result = new DeferredResult<>();

    FirebaseUtil.getUid(token, new OnSuccessListener<FirebaseToken>() {
        @Override
        public void onSuccess(FirebaseToken decodedToken) {
            String uid = decodedToken.getUid();
            User userToDelete = userDao.get(uid);
            userDao.delete(uid);
            clearUserAccounts(userToDelete);
            result.setResult(uid + " was deleted");
        }
    }, new OnFailureListener() {
        @Override
        public void onFailure(@NonNull Exception e) {
            result.setErrorResult(e);
        }
    });

    return result;
}