hackmabrain hackmabrain - 17 days ago 5
Scala Question

Play 2.5 preserve context in async calls

In our controller class we reach out to another service to get some data :

Future<JsonNode> futureSite = someClient.getSite(siteId, queryParams);

return FutureConverters.toJava(futureSite).thenApplyAsync((siteJson) -> {
Site site = Json.fromJson(siteJson, Site.class);
try {
return function.apply(site);
} catch (RequestException e) {
return e.result;
}
}).exceptionally(throwable -> {
if(throwable instanceof OurClientException) {
if(((OurClientException) throwable).httpStatusCode == 404) {
return entityNotFound("Site", siteId);
}
}
return null;
});


What we notice is that context which is set in unit tests (we use scalatest-play) is lost and becomes null after we make the Async call
(FutureConverters.toJava(futureSite).thenApplyAsync((siteJson)
, as t is on a separate thread.

Which causes problem down in the controller code, where we use the above function ... request() would now throw a runtime exception saying there is no context available.

How can we preserve the context ?

Answer

You should inject play.libs.concurrent.HttpExecutionContext to your controller and then specify current context as second argument for CompletionStage#thenApplyAsync(..,..).

public class Application extends Controller {
@Inject HttpExecutionContext ec;

public CompletionStage<Result> index() {
    someCompletableFuture.supplyAsync(() -> { 
      // do something with request()
    }, ec.current());
}}

P.S. https://www.playframework.com/documentation/2.5.x/JavaAsync#Using-CompletionStage-inside-an-Action

Comments