Close twitter streaming connection in play framework - scala

I connect to twitter streaming API using Play 2.2 WS API in the code example below. I'm stuck trying to figure out how to disconnect from stream once it's established. Is there any proper way to do that rather than stopping application? Any help will be appreciated.
def watchTweets(keywords : String) = Action { implicit request =>
Logger.debug(s"watchTweets invoked with: $keywords")
val (tweetsOut, tweetChanel) = Concurrent.broadcast[JsValue]
WS.url(s"https://stream.twitter.com/1.1/statuses/filter.json?track=" + URLEncoder.encode(keywords, "UTF-8"))
.sign(OAuthCalculator(Twitter.KEY, Twitter.sessionTokenPair.get))
.postAndRetrieveStream("")(headers => Iteratee.foreach[Array[Byte]] { ba =>
val msg = new String(ba, "UTF-8")
Logger.debug(s"received message: $msg")
val tweet = Json.parse(msg)
tweetChanel.push(tweet)
}).flatMap(_.run)
Ok.chunked(tweetsOut &> Comet(callback = "parent.cometMessage")) }

This is a known issue... the answer is that you close the connection, but the problem is, the enumerator won't notice that the connection is closed until it tries to feed a tweet to the client, and that won't happen until it receives another tweet from Twitter, which might take a long time to happen.
This is of course a problem when doing Twitter streaming because Twitter will only let a user create one stream at a time, so that effectively means that you can't do a second stream until the first receives some data.
Unfortunately we don't have a work around as yet, but we are looking at introducing a new lower level streaming API into Play around the 2.4 time line, which will certainly allow this.

Related

Scala Play session always empty

I am trying to create a basic web login/authentication system in Play. Based on Play docs Im supposed to persist data across requests through Play's Session cookie; this is what my login looks like:
def login() = Action.async(parse.json) { implicit request =>
implicit val loginInfoReads: Reads[LoginInfo] = Json.reads[LoginInfo]
val newSession = request.session +
("test" -> "yep")
// #todo: add real error handling
val unauthedUser = request.body.validate(loginInfoReads)
.getOrElse(throw new Exception("Something went wrong with the login request"))
UserService.authAndGetUser(unauthedUser.email, unauthedUser.password).map { res =>
Ok(res.name).withSession(newSession)
}
I can see the cookie included in the response cookies in Chrome dev tools, but when I make a subsequent request to get the data inside the session, I get an empty map:
Logger.debug(request.session.data.toString)
logs:
Map()
and attempting to access "test" via request.session.get("test") fails.
What am I missing here? Why is my session data not persisting across requests?
Thanks
Turned out it wasn't a Scala/Play problem, more general problem with Chrome and cookies when hitting localhost. Here's what fixed it for me:
https://stackoverflow.com/a/39233628/747340

Playframework User Actor with User Session

I'm pretty new to Scala, the Play Framework and Akka. In the project I currently work on, the user of the web application should be able to ask the server several things to do (like starting a particular computation) in an asynchronous way. If the server is done it should notify the user also async. I solve this demand by a WebSocket connection which is established when the user first connects with the Application and the WebSocket is handled by a UserActor, which is attached to the User Session:
def ws = WebSocket.tryAcceptWithActor[JsValue, JsValue] { implicit request =>
Future.successful(request.session.get(UID) match {
case None => Left(Forbidden)
case Some(uid) => Logger.info("WebSocket has accepted the request with uid " + uid)
Right(UserActor.props(uid))
})
}
Currently, the only thing the UserActor does is receiving messages from the WebSocket as JsValue. The UID of the session is generated when requesting index:
def index = Action { implicit request => {
val uid = request.session.get(UID).getOrElse {
counter += 1
counter.toString
}
Ok(views.html.index(uid)).withSession {
Logger.debug("create uid " + uid)
request.session + (UID -> uid)
}}
}
The UserActor should represent the actual user on the Server and thus include the logic of all actions that the user can perform on the Server. This works fine as long as I send all user interaction over the WebSocket.
Now what is the case with other user input, like form submission? The application includes a form whose data should not go over the WebSocket, but rather be submitted with a POST request (perhaps with AJAX) and bound in a controller to the Model like described in the documentation.
def saveContact = Action { implicit request =>
contactForm.bindFromRequest.fold(
formWithErrors => {
BadRequest(views.html.contact.form(formWithErrors))
},
contact => {
val contactId = Contact.save(contact)
Redirect(routes.Application.showContact(contactId)).flashing("success" -> "Contact saved!")
}
)
}
This example is taken from the Playframework documentation.
Now, how do I link the Form Submission handler with the UserActor? Say I want to tell the user actor that a form has been submitted. A trivial example would be that the UserActor sends one value of the form back over the WebSocket to the client as soon it is received. So basically the problem reduces to the issue that I want to send the UserActor Messages from any Controller.
I might come up with the idea to send all form data over the WebSocket, but I also want to realize the upload of large data in the future, which I want to tackle like described in this blog post. Then one scenario I could imagine is that the UserActor should be messaged for each chunk it receives.
I guess one problem is that the UserActor and the WebSocketActor are the same and I rather should split their logic, such that the UserActor is only associated with the Session, but I have no idea how to accomplish this. Maybe I need another actor, say a UserManager, which keeps track of present UserActors and enables access to UserActors?
Do you have any suggestions, recommendations or perhaps an example application which also deals with this case? Thank you very much in advance.
Best regards
Don't use the actor that you pass to tryAcceptWithActor as a representation of the User. It should represent a particular session with that user. Possibly, one of many concurrent sessions (multiple browsers, or tabs) a user could have open at a particular time.
Create a separate actor to represent the user and all of the actions it can perform. Now the session actors should forward their messages to the user actor. Traditional controller methods can also forward requests to the corresponding user actors.

Play framework make http request from play server to "somesite.com" and send the response back to the browser

I'm developing an application using Play framework in scala. I have to handle the below use case in my application.
For a particular request from the browser to the play server the Play server should make an http request to some external server (for Eg: somesite.com) and send the response from this request back to the web browser.
I have written the below code to send the request to external serever in the controller.
val holder = WS.url("http://somesite.com")
val futureResponse = holder.get
Now how do I send back the response recieved from "somesite.com" back to the browser?
There's an example in the Play documentation for WS, under Using in a controller; I've adapted it to your scenario:
def showSomeSiteContent = Action.async {
WS.url("http://somesite.com").get().map { response =>
Ok(response.body)
}
}
The key thing to note is the idiomatic use of map() on the Future that you get back from the get call - code inside this map block will be executed once the Future has completed successfully.
The Action.async "wrapper" tells the Play framework that you'll be returning a Future[Response] and that you want it to do the necessary waiting for things to happen, as explained in the Handling Asynchronous Results documentation.
You may also be interested in dynamically returning the status and content type:
def showSomeSiteContent = Action.async {
WS.url("http://somesite.com").get().map { response =>
Status(response.status)(response.body).as(response.ahcResponse.getContentType)
}
}
Dynamic status could help if the URL/service you call fails to answer correctly.
Dynamic content type can be handy if your URL/service can return different content HTML/XML... depending on some dynamic parameter.

Use of Streams,Enumerator and Websockets in playframework

I'm looking for the proper way to use play's Enumerator (play.api.libs.iteratee.Enumerator[A]) in my code, i have a stream of object of type "InfoBlock" and i want to redirect it to a websocket.What i actually do is:
The data structure holding the blocks
private lazy val buf:mutable.Queue[InfoBlock] = new mutable.SynchronizedQueue[InfoBlock]
The callback to be used in the Enumerator
def getCallback: Future[Option[InfoBlock]] = Future{
if (!buf.isEmpty)
Some(buf.dequeue)
else
None}
Block are produced by another thread and added to the queue using:
buf += new InfoBlock(...)
Then in the controller i want to set up a websocket to stream that data,doing:
def stream = WebSocket.using[String]{ request =>
val in = Iteratee.consume[String]()
val enu:Enumerator[InfoBlock] = Enumerator.fromCallback1(
isFirst => extractor.getCallback
)
val out:Enumerator[String] = enu &> Enumeratee.map(blk => blk.author+" -> "+blk.msg)
(in,out)}
It works but with a big problem, when a connection is open it sends a bunch of blocks (=~ 50) and stops, if i open a new websocket then i get another bunch of blocks but no more.I tried to set some property to the js object WebSocket in particular i tried setting
websocket.binaryType = "arraybuffer"
because i thought using "blob" may be the cause but i was wrong the problem must be server side and i have no clue..
From the Enumerator ScalaDocs on Enumerator.fromCallback, describing the retriever function:
The input function. Returns a future eventually redeemed with Some value if there is input to pass, or a future eventually redeemed with None if the end of the stream has been reached.
This means that the enumerator will start by pulling everything off the queue. When it is empty, the callback will return a None. The enumerator sees this as the end of the stream, and closes sends a Done state downstream. It won't be looking for any more data
Rather than using a mutable queue for message passing, try and push the Enumerator/Iteratee paradigm into your worker. Create an Enumerator that outputs instances of the what you're creating, and have the iteratee pull from that instead. You can stick some enumerates in the middle to do some transforms if you need to.

PlayFramework Websocket HTTP Status

I am playing around with WebSockets in PlayFramework 2.2. I would like to do some checks on the initial request and possibly return an appropriate HTTP status. In principle it would look like something like this, asked in this question:
def ws(username: String) = {
if (username == "asoliman")
Action { request =>
Forbidden("Soliman is not allowed here")
}
else
WebSocket.using[String] { request =>
val in = Iteratee.foreach[String]( s => println("got: " + s)).mapDone(_ => println("Disconnected"))
val out = Enumerator[String]("Ahmed", "Mohamed", "Ibrahim").andThen(Enumerator.enumInput(Input.EOF))
(in, out)
}
}
As noted, this is not possible as the WebSocket's using and async need to return a Tuple2[Iteratee, Enumerator].
Is there a recommended approach to this?
Or, is there a way to send Websocket's Status Codes?
UPDATE 7 October 2015:
In newer PlayFramework versions, it is possible to reject a connection and therefore return, say, a Forbidden status. Check the documentation here: https://www.playframework.com/documentation/2.4.x/ScalaWebSockets
ORIGINAL ANSWER:
Answer is, it is not possible at the moment with PlayFramework 2.2. Regular HTTP statuses are not viable as the response needs to be a WebSocket (via using/async), and WebSocket statuses are not implemented. I filed an issue about it in their repo, we need to wait for future releases - or contribute to make it happen :-)