jump to navigation

Dispatch with Scalatra on AsyncServlet January 15, 2013

Posted by CK in Software.
Tags: , , ,
3 comments

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
}