Dispatch with Scalatra on AsyncServlet January 15, 2013
Posted by CK in Software.Tags: akka, dispatch, Scala, scalatra
trackback
A basic example of using Dispatch with Scalatra on Jetty 8.x / AsyncServlet 3.0. It took me a lot of time to connect the dots how to use Dispatch asynchronously and actually process the results (not simply return them) and I could not find examples how to use Scalatra with AsyncServlet either. So here it is for anyone who could find it useful and save themselves a few –or more– hours.
import _root_.akka.dispatch._
import _root_.akka.actor._
import org.scalatra._
import org.scalatra.akka.AkkaSupport
import dispatch._
get("/asynctest") {
// Get the AsyncContext -- or create one.
val asctx =
if (request.isAsyncStarted) request.getAsyncContext
else request.startAsync
// Execute the rest of the route in an Akka Future
val result = Future {
// Create a RequestBuilder to be used by Dispatch
val url = host("slashdot.org") <:< Map(
("Accept-Charset" -> "utf-8")
)
// Make the request, receive it as Bytes not to
// disturb binary results. Http returns a Promise
// to a result and execution moves along. More
// information on the concepts is provided on
// the Dispatch web site.
val response = dispatch.Http(url OK as.Bytes)
// Set up a handler for Dispatch' return. This
// is bound on the Promise created earlier.
response.onComplete { x =>
// Get a handle to the response and its
// output stream, we'll need this to write
// results as soon as we have them.
val res = asctx.getResponse
val os = res.getOutputStream
// 'x' is the response of the upstream web
// service
x match {
// An error occurred, the exception is
// sent to the client (of course you
// would prefer to handle this otherwise
// in real-world code)
case Left(exc) => {
os.print(exc.getMessage)
}
// Response was received with a 200 HTTP
// code, everything went fine. Data is
// written to the output stream as bytes,
// after having set the encoding to UTF-8.
case Right(output) => {
res.setCharacterEncoding("utf-8")
os.write(output)
}
}
// It is crucial to "complete" the Asynchronous
// Context, otherwise no response will be sent
// to the client.
asctx.complete
}
}
}
Big fat disclaimer: I’m fairly new to Scala/Scalatra (and the JVM for that matter), so if there are mistakes in this example –or improvements to make– let me know and I’ll correct them in the post.
Update: Ivan Porto Carrero of the Scalatra fame comments below on a much easier/concise way to achieve the same result (minor edits of my own in the code that follows but the point is the same). So it turns out you can call .complete directly on an Akka Promise, without a need to move the servlet’s asynchronous context around:
get("/async2") {
import dispatch._
import _root_.akka.dispatch.{Promise => AkkaPromise}
val prom = AkkaPromise[String]()
Http(host("slashdot.org") OK as.String) onComplete {
case Left(ex) => println(ex.getMessage)
case r => prom.complete(r)
}
prom.future
}
you could just mix in AkkaSupport and return a future from your action.
get(“/async”) {
Future { /* Do Stuff Here Asynchronously */ )
}
see here http://scalatra.org/2.2/guides/async/akka.html
you can also check this gist
https://gist.github.com/8092fb26215d32d518c6
It’s a lot simpler than that
Thanks Ivan, indeed your example on gist.github is much more consise and works as advertised. I was missing the fact that I can call .complete on a Future’s Promise and do the trick. I’ll edit the post.