I just finished migrating Play’s http client to Ning’s own asynchronous library. What does it mean? A lot.
When you want to retrieve data from a different server, your web application behaves like a web client. Rather than serving a resource to a client, it requests one from a different server. For example, that’s what you would do if you want to interact with the Twitter API or if you want to build a web based feed reader.
HTTP Client Calls in Play 1.0
Play has a pretty cool http client library – pretty cool in the sense that it’s very simple to use compared to what you usually get in Java. I can get the content of a resource by calling:
String body = WS.url("http://erwan.jp/").get().getString();
In the same fashion, I can do a post and retrieve the JSON result:
JsonElement response = WS.url("http://api.server.tld/new").body("content").post().getJson();
As a comparison, see how it works with Apache HttpClient.
However there’s a big flaw in this API: it can only be used synchronously. That means that your Java thread will be blocked until the response from the server is received. Depending on the server it could take several seconds, so it is a real issue. If you need to do 5 calls then work on the result, you will have to wait for the previous call to be done before you can launch the second. In Play you can use jobs to have your calls executed in a separate thread, but it’s a burder to have to create jobs for that and kind of defeats the purpose of using play.libs.WS.
Play 1.1: Introducing Asynchronous Calls
Since a commit I did last week, additional methods are available on the request object. Now you can do:
Future<HttpResponse> response = WS.url("http://erwan.jp/").getAsync();
Now your call is done in a thread – you can manipulate your Future object to test if the result is available, to cancel the call or to block the thread until the result is ready. Let’s say you need to do 3 API calls, and then use the results together. With the synchronous library you have to serialize the calls, but now you can do the calls in parallel:
// Launch all three calls in parallel
Future<HttpResponse> future1 = WS.url("http://server/api/one").getAsync();
Future<HttpResponse> future2 = WS.url("http://server/api/two").getAsync();
Future<HttpResponse> future3 = WS.url("http://server/api/three").getAsync();
// Now that the calls are launched in separate threads, wait for the results
Document xml1 = future1.get().getXml();
Document xml2 = future2.get().getXml();
Document xml3 = future3.get().getXml();
Launch the calls in parallel means a much quicker response, so that’s an improvement in the case we have several calls to do. But we are still blocking one of Play’s threads until the three calls are done.
Avoid blocking Play’s threads
Before we go on, it’s important to understand Play’s threads pool. It’s configurable but usually there is one IO thread and 2 execution threads. When the IO thread gets a request it passes it to one of the execution thread, the execution thread calculates it and passes it back to the IO thread that will queue it to serve it to the client who originated the request.
That means that the execution of the action on the controller will block one thread. So if you do a synchronous web service call from your action, you’ll block a precious thread for all the time you wait for the remote server to respond to your server. When all threads are blocked waiting for a result, other requests get queued. Your site’s response is slow, and your users are unhappy.
So here is how you can free the thread while you wait for the remote server to respond. The following code is an action, within a controller:
private static Future<HttpResponse> response;
public static void mirrorFeed() throws Exception {
if (request.isNew) {
response = WS.url("http://planet.playframework.org/feed").getAsync();
waitFor(response);
} else {
renderXml(response.get().getXml());
}
}
This can be tricky to understand at a first glance, so here is the process.
- Your action gets called a first time: request.isNew is true. An asynchronous HTTP call is made to the playframework.org server.
- The “waitFor(Future>)” (static method on the Controller class) tells Play to wait until the response is received.
- When the answer is ready, Play will call the action again. This time, request.isNew is false. We know that the Future is ready so we can do a get() to retrieve the HttpResponse instance. Here we’re just serving it to the user, but you see how we could parse the feed to use just some information in our response.
So here you go: the use of an asynchronous call prevents to block an execution thread, and the performance of your application will not be affected by the response time of the remote server.
When you should still use a job
While the waitFor trick prevents you from blocking a Play thread, your user still has to wait for the web service call to be back before he gets his response. In other words, your application may feel slow to the user.
When the information you need is general enough, for example when it comes from a public feed, it can still be a better approach to keep the information up-to-date using a job. At the time the user will fetch the information it will not be the most recent, but the page will be served to the user much quicker.
This is what I am doing for the Twitter box on Planet Play: I don’t pull it for each visitor at request time (that would be insane), but I have a Job refreshing the information every 5 minutes. Any page rendered will never get anything older than 5 minutes.
@Every("5mn")
public class TwitterJob extends Job {
public void doJob() {
try {
TwitterSearch.refresh("playframework");
} catch (UnsupportedEncodingException e) {
Logger.error("Error refreshing Twitter search");
}
}
}
The information is then stored in cache and retrieved to render the front page.

Great post about one of the most interesting feature of Play! framework.
Thanks for your job Erwan
Nicolas.
This is pretty cool for mashup Play application using multiple API.
Thanks
Well done, I love open source.
Nice article, but I dont get the idea.
How is an async “wait” method better than a long running synchronous call? At the end of the day the user will have to wait in both scenarios
Or can you render something to the user while “waitfor” is executed?
It seems to me that a Job-based solution is the best one after all.
sakuraba: the waitFor is better that the synchronous call because it doesn’t block a Play thread for the time it’s waiting (and it could be several seconds). On a heavy traffic site the first visitor will block an execution thread, the second block the other thread and all the others will be queued. At the end of the day instead of waiting just for his request to the web service, the user has to wait for a thread to be freed and it can take a long time.
Now for the waitFor vs. a job: it depends what you’re doing. If it’s a general information (like the twitter box on Planet Play) a job is better. However if the WS is really specific to the visitor’s request, and you need to provide feedback, you can’t use a background job.
Example: your web application is a feed reader, and you want to provide a “refresh now” button. You’ll want to refresh your view of the feed after a success, and notify the user if the feed couldn’t be retrieved. You can’t notify the client of a success with a background job so you have to use waitFor (or use a synchronous call).
If you’re doing that in an AJAX call, then you can simply show a throbber and the user can still interact with your page during that time.
Thanks for clarification.
why you dont use a callback function with the async call?
Arno: Actually, there’s no closures in Java so no callback functions. I could however expose a function that takes an interface with the methods “onSuccess”, “onError” and so on, then people can define a class on the fly.
However that wouldn’t help if you want to return a response to the user after you get the result. That would only replace usage of jobs.
maybe you can give the callback also a helper object which holds more informations and also the response object (which is still open until the last callback is used).