2

I have a REST service (Play Framework 2.0 w/Scala) that receives messages via a POST request.

I want to allow a user to see the queue of messages received in a webpage. I wanted to create a SSE channel between browser and server, so the server pushes new messages to the browser.

To create that SSE stream, as per documentation, I'm using a chain of Enumerator/Enumeratee/Iteratee.

My problem is: how do I inject the messages received from the POST request to the enumerator. So given a code like follows:

def receive(msg: String) = Action {
  sendToEnumerator() 
  Ok
}

val enumerator =  Enumerator.fromCallback( ??? )

def sseStream() = Action {

  Ok.stream(enumerator &> anotherEnumeratee ><> EventStrem()).as("text/evetn-stream")

}

What should I put in both sendToEnumerator and enumerator (where the ??? are). Or should I just use WebSockets and Actors instead? (I favour SEE due to broader compatibility, so would like to use SSE if possible)

2
  • Just suggestion: last time I built the a'la FB wall page I was using websockets playframework.org/documentation/2.0.4/ScalaWebSockets and it did the trick for me. Commented Nov 9, 2012 at 10:12
  • @biesior if I can I'd like to use SSE, although I see websockets is a simpler approach. But as Websockets seem to be less supported... Commented Nov 9, 2012 at 10:46

1 Answer 1

3

Ok, found a way:

 // The enum for pushing data to spread to all connected users
  val hubEnum = Enumerator.imperative[String]()

  // The hub used to get multiple output of a common input (the hubEnum)
  val hub = Concurrent.hub[String](hubEnum)

  // Converts message to Json for the web version
  private val asJson: Enumeratee[String, JsValue] = Enumeratee.map[String] {
    text => JsObject(
      List(
        "eventName" -> JsString("eventName"),
        "text" -> JsString(text)
      )
    )
  }

  // loads data into hubEnum
  def receiveData(msg: String) = Action { implicit request =>
    hubEnum push msg
  } 

  // read the Hub iterator and pushes back to clients
  def stream = Action { implicit request =>
     Ok.stream(hub.getPatchCord &> asJson ><> EventSource()).as("text/event-stream")
  }

The trick is to create an imperative Enumerator. This enumerator allows you to push data into it when it becomes available. With this then you can follow the standard procedure: create a Hub based on the enumerator, convert it with some Enumeratee and send it back to browsers via SSE.

Thanks to this website for giving me the solution :)

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.