jump to navigation

Dispatch with Scalatra on AsyncServlet January 15, 2013

Posted by CK in Software.
Tags: , , ,
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
}
Advertisements

Comments»

1. Ivan Porto Carrero - January 15, 2013

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

2. casualjim - January 15, 2013

you can also check this gist

It’s a lot simpler than that

CK - January 15, 2013

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.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: