Martin GOYOT
Martin GOYOT

Reputation: 296

Action composition and async in Play Framework 2.5 in Java

I want to create an action that I can use with the @With annotation style. This action will need to proceed to an RPC call so if I understood correctly the documentation I should rather put this in an async way.

This is what I tried to do until now:

public class GetUserIdAction extends play.mvc.Action.Simple {

  @Override
  public CompletionStage<Result> call(Http.Context context) {
    String token = "";

    if (StringUtils.isEmpty(token)) {
      return delegate.call(context);
    }

    CompletionStage<Http.Context> promiseOfUpdatedContext = CompletableFuture.supplyAsync(() -> setUserIdForToken(context, token));

    return promiseOfUpdatedContext.thenApply(ctx -> delegate.call(ctx));
  }

  private Http.Context setUserIdForToken(Http.Context context, String token) {
    context.args.put("user_id", authenticationManager.getUserIdForToken(token));
    // The AuthenticationManager is issuing an RPC call and thus may take some time to complete.
    return context;
  }
}

Set aside the fact that token is always empty and authenticationManager is not set, this is just a quick meaningless example, my IDE is complaining on the thenApply part. For what I understand, it is expecting a CompletionStage<Result> and gets something more like a CompletionStage<CompletionStage<Result>>.

What is a way to deal with it? Cause here all I want is to put some information in the Context and then continue the delegate.call chain.

Or maybe I'm trying to do something stupid and composed actions are already asynchronous?

Upvotes: 2

Views: 534

Answers (1)

Salem
Salem

Reputation: 12996

You have a CompletionStage<Something> and want to end with a CompletionStage<Result>. The easiest way to achieve that is using thenCompose.

Here is an example, with a small change: I have a CompletableFuture to get the token and only then I add it to the HttpContext

@Override
public CompletionStage<Result> call(final Http.Context context) {
    final String token = "";

    if (StringUtils.isEmpty(token)) {
        return delegate.call(context);
    }

    return CompletableFuture.supplyAsync(() -> {
        // do something to fetch that token
        return "your_new_token";
    }).thenCompose(tokenReceived -> {
        context.args.put("user_id", tokenReceived);
        return delegate.call(context);
    });
}

Upvotes: 1

Related Questions