Play! Framework - Can my view template be localised when rendering it as an AsyncResult?
internationalization, playframework-2.0
Solution
Gonna answer my own question here. A colleague of mine found what was ultimately a simple solution:
public static Result myActionMethod() {
final Context ctx = ctx(); // (1)
Promise<MyWSResponse> wsResponse;
// Perform a web service call that will return the promise of a MyWSResponse...
return async(wsResponse.map(new Function<MyWSResponse, Result>() {
@Override
public Result apply(MyWSResponse response) {
Context.current.set(ctx); // (2)
// Validate response...
return ok(myScalaViewTemplate.render(response.data()));
}
}));
}
- Obtain a reference to the HTTP context at the beginning of the action
- Restore it in the ThreadLocal once you're in the `async` block
Problem
I've recently started using the Play! framework (v2.0.4) for writing a Java web application. In the majority of my controllers I'm following the paradigm of suspending the HTTP request until the promise of a web service response has been fulfilled. Once the promise has been fulfilled, I return an `AsyncResult`. This is what most of my actions look like (with a bunch of code omitted): ``` public static Result myActionMethod() { Promise<MyWSResponse> wsResponse; // Perform a web service call that will return the promise of a MyWSResponse... return async(wsResponse.map(new Function<MyWSResponse, Result>() { @Override public Result apply(MyWSResponse response) { // Validate response... return ok(myScalaViewTemplate.render(response.data())); } })); } ``` I'm now trying to internationalise my app, but hit the following error when I try to render a template from an `async` method: ``` [error] play - Waiting for a promise, but got an error: There is no HTTP Context available from here. java.lang.RuntimeException: There is no HTTP Context available from here. at play.mvc.Http$Context.current(Http.java:27) ~[play_2.9.1.jar:2.0.4] at play.mvc.Http$Context$Implicit.lang(Http.java:124) ~[play_2.9.1.jar:2.0.4] at play.i18n.Messages.get(Messages.java:38) ~[play_2.9.1.jar:2.0.4] at views.html.myScalaViewTemplate$.apply(myScalaViewTemplate.template.scala:40) ~[classes/:na] at views.html.myScalaViewTemplate$.render(myScalaViewTemplate.template.scala:87) ~[classes/:na] at views.html.myScalaViewTemplate.render(myScalaViewTemplate.template.scala) ~[classes/:na] ``` In short, where I've got a message bundle lookup in my view template, some Play! code is attempting to access the original HTTP request and retrieve the `accept-languages` header, in order to know which message bundle to use. But it seems that the HTTP request is inaccessible from the async method. I can see a couple of (unsatisfactory) ways to work around this: - Go back to the 'one thread per request' paradigm and have threads block waiting for responses. - Figure out which language to use at Controller level, and feed that choice into my template. I also suspect this might not be an issue on trunk. I know that there is a similar issue in 2.0.4 with regards to not being able to access or modify the `Session` object which has recently been fixed. However I'm stuck on 2.0.4 for the time being, so is there a better way that I can resolve this problem?