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 ?
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