Question

I'm looking at the example at https://github.com/playframework/Play20/tree/master/samples/scala/websocket-chat

To make a websocket controller, you write something like:

def chat(username: String) = WebSocket.async[JsValue] { request  =>
    ChatRoom.join(username)
}  

Chatroom.join returns a scala.concurrent.Future[(Iteratee[JsValue,_],Enumerator[JsValue])] . But where are the iteratee and the enumerator used within the Play! framework? The WebSocket class (WebSocket.scala) seems to ignore the inputs:

case class WebSocket[A](f: RequestHeader => (Enumerator[A], Iteratee[A, Unit]) => Unit)        (implicit val frameFormatter: WebSocket.FrameFormatter[A]) extends Handler {

  type FRAMES_TYPE = A

  /**
   * Returns itself, for better support in the routes file.
   *
   * @return itself
   */
   def apply() = this
}

How does Play! manage the changing state of the iteratee as it consumes input?

Was it helpful?

Solution

It's worth noting that the WebSocket itself is just a dumb container. The magic happens in various classes within play.core.server.netty.

To understand what that magic is, it's instructive to look at the signature of f (the function that a WebSocket contains:

RequestHeader => (Enumerator[A], Iteratee[A, Unit]) => Unit

This is a function that takes a RequestHeader, an Enumerator, and an Iteratee, and does something with them.

So, at some point in the future, the framework will provide our WebSocket with a RequestHeader (which should be self explanatory), an Enumerator[A] (Enumerators are sources, in this case, these are the messages being received from the client), and an Iteratee[A, Unit] (Iteratees are sinks, in this case, this is where we send messages to go back to the client).

In the case of WebSocket.adapter, the WebSocket will connect the Enumerator to the Iteratee via an Enumeratee. In the case of WebSocket.using, the WebSocket will connect the remote Enumerator to a local Iteratee, and connect the remove Iteratee to a local Enumerator.

Rather than defining WebSocket directly, it's likely to be easier to use one of the convenience methods in the WebSocket object. The following code will echo the previous message received:

  def mySocket = WebSocket.adapter {implicit req =>
    var lastMessage = "No previous message"
    Enumeratee.map[String] {msg =>
      val response = lastMessage
      lastMessage = msg
      response
    }
  }

Note that this code almost certainly has thread safety issues - in Scala, you should try to avoid mutable state whenever possible, or use actors or similar if not.

Or, try WebSocket.using, and look at a pushee Enumerator, in conjunction with a foreach Iteratee, although it's a little fiddler. Perhaps understandably, the pushee enumerator is deprecated in Play 2.1, as it's being superseded by the new channels system.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top