Search code examples
javaplayframeworkakkaplayframework-2.2

Play Framework 2.2.x recover flatmap


I want to recover my promise and return delegate.call(context) from it, but i can only return SimpleResult.

public class Authenticate extends Action<Authentication> {
    @Override
    public Promise<SimpleResult> call(final Context context) throws Throwable {
        WSRequestHolder holder = WS.url(...);
        final Promise<Response> promise = holder.get();
        Promise<SimpleResult> result = promise.flatMap(new Function<Response, Promise<SimpleResult>>() {
            @Override
            public Promise<SimpleResult> apply(Response response) throws Throwable {
                JsonNode user = response.asJson().path("response");
                context.args.put("user", user);
                return delegate.call(context);
            }
        }).recover(new Function<Throwable, SimpleResult>() {
            @Override
            public SimpleResult apply(Throwable e) {
                /* cant return delegate.call(context); from here */
                return redirect(routes.Application.index());
            }
        });
        return result;
    }
}

Maybe there are other standard and better ways to store userinfo before method calls?


Solution

  • The solution you posted is not a good solution, you should never, ever use Await.result when doing asynchronous programming, you risk causing deadlocks and massive performance issues.

    We need to add a recoverWith method to the Play promise API, but until that happens, you can do this (using JDK8 syntax for brevity):

    public class Authenticate extends Action<Authentication> {
      public Promise<SimpleResult> call(final Context context) throws Throwable {
        Promise<SimpleResult> result = WS.url(...).get();
        Promise<SimpleResult> result = promise.flatMap( response -> {
          JsonNode user = response.asJson().path("response");
          context.args.put("user", user);
          return delegate.call(context);
        }).map(Promise::pure).recover( e -> {
          return delegate.call(ontext)(routes.Application.index());
        }).flatMap(a -> a);
        return result;
      }
    }
    

    Basically, the above unflattens the Promise<SimpleResult> into Promise<Promise<SimpleResult>> by mapping with Promise::pure, then you can recover returning Promise<SimpleResult>, then you it flattens the promise of a promise by flatMapping with the identity function.