WebSocket Action call Ignored in runtime - scala

Hye Geeks. I am coding for a live notification module in my project. I am tyring to call WebSocket Action method from a function to pass the notification data over the connection to the client.
Here's my code..
def liveNotification(data: String) = WebSocket.using[JsValue] { implicit request =>
val iteratee = Iteratee.ignore[JsValue]
val enumerator = Enumerator[JsValue](Json.toJson(data))
(iteratee,enumerator)
}
def createNotification(notificationTo: BigInteger, notiParams:Tuple5[String,String,BigInteger,BigInteger,BigInteger]) = {
val retData = NotificationModel.createNotification(notificationTo,notiParams)
val strData = write(retData)
liveNotification(strData)
}
Problem is that the 'liveNotification()' call is simply ignored. Please help me with any suggestions that what i am doing wrong ?

Be sure to invoke it with a Json value, at least an empty object. The parser will only match against something that it recognizes as Json.

Related

Is this instance created only once in my example?

I have the following code. In the other class, I tried to create the S3ClientClass2 object as val s3 = new S3ClientClass2(). After creating the s3, then calling the readFromS3 method for every single request.
In this scenario, I am wondering that the amazonS3Client is created only once or created many times for every request. I think that is is created only once.
Is this right?
class S3ClientClass2 {
lazy val amazonS3Client = this.getS3Client()
private def getS3Client() = {
AmazonS3ClientBuilder
.standard()
.withRegion(Regions.AP_NORTHEAST_1)
.build()
}
def readFromS3(s3Bucket: String, filepath: String): String = {
var s3object: S3Object = null
try {
s3object = amazonS3Client.getObject(s3Bucket, filepath)
readFromS3(s3object)
}
finally {
if (s3object != null) {
s3object.close()
}
}
}
def readFromS3(obj: S3Object): String = {
val reader = new BufferedReader(new InputStreamReader(obj.getObjectContent))
reader.lines().collect(Collectors.joining())
}
}
yes, lazy val is initialised only once when it is first used. That means, the first time you use amazonS3Client the getS3Client method will be called, every subsequent usage of amazonS3Client will use the cached value.
Some other hints. You are mixing in Java stuff in readFromS3(obj: S3Object) method for no good reason, it could be easily rewritten to pure Scala:
def readFromS3(obj: S3Object): String = {
scala.io.Source.fromInputStream(obj.getObjectContent).mkString
}
Regarding readFromS3(s3Bucket: String, filepath: String), you should never used null in scala, if you are working with something that might or might not have a value see Option, for things that might crash with some error see scala.util.Either and scala.util.Try. Also, what is the expected behaviour of this function when exception is thrown in the try block? In the current design it will rethrow it and escalate up your call stack.

unable to refactor Scala code to make it more readable

I struggle to understand Scala and thus thought to refactor a piece of code to make it more de-sugared. But I am unable to do so.
The original code is
def index(signupMessage:String = "") = addToken { //addToken is of CSRFAddToken
silhouette.UserAwareAction { implicit request =>
println(s"index action called with request ${utilities.printPlayHttpRequest(request)}")
//TODOM - fix as signup value is coming up as ;signup=error or ;signup=success instead of just success or error
println(s"index page's argument "+signupMessage)
val Token(name, value) = CSRF.getToken.get
println(s"Token name ${name}, value ${value}")
Ok(views.html.index(signupMessage,messagesApi("app.title")(langs.availables(0))))
}
}
I want to de-sugar the code and make calls more explicit. I am thinking of something in the following line
def index(signupMessage:String = "") = {
val block:Action[AnyContent] = (implicit request) =>{ //THE ERROR IS HERE - '=>' expected but ')' found
println(s"index action called with request ${utilities.printPlayHttpRequest(request)}")
//TODOM - fix as signup value is coming up as ;signup=error or ;signup=success instead of just success or error
println(s"index page's argument "+signupMessage)
val Token(name, value) = CSRF.getToken.get
println(s"Token name ${name}, value ${value}")
Ok(views.html.index(signupMessage,messagesApi("app.title")(langs.availables(0))))
}
val silhouttedCode = silhouette.UserAwareAction.apply(block)
addToken.apply(silhouettedCode)
}
the above piece of code doesn't compile. What am I doing wrong?
I think you need to write your Action like this:
val block = Action { implicit request =>
// Rest of code here
}
See the docs for Actions.

Trying to understand Scala enumerator/iteratees

I am new to Scala and Play!, but have a reasonable amount of experience of building webapps with Django and Python and of programming in general.
I've been doing an exercise of my own to try to improve my understanding - simply pull some records from a database and output them as a JSON array. I'm trying to use the Enumarator/Iteratee functionality to do this.
My code follows:
TestObjectController.scala:
def index = Action {
db.withConnection { conn=>
val stmt = conn.createStatement()
val result = stmt.executeQuery("select * from datatable")
logger.debug(result.toString)
val resultEnum:Enumerator[TestDataObject] = Enumerator.generateM {
logger.debug("called enumerator")
result.next() match {
case true =>
val obj = TestDataObject(result.getString("name"), result.getString("object_type"),
result.getString("quantity").toInt, result.getString("cost").toFloat)
logger.info(obj.toJsonString)
Future(Some(obj))
case false =>
logger.warn("reached end of iteration")
stmt.close()
null
}
}
val consume:Iteratee[TestDataObject,Seq[TestDataObject]] = {
Iteratee.fold[TestDataObject,Seq[TestDataObject]](Seq.empty[TestDataObject]) { (result,chunk) => result :+ chunk }
}
val newIteree = Iteratee.flatten(resultEnum(consume))
val eventuallyResult:Future[Seq[TestDataObject]] = newIteree.run
eventuallyResult.onSuccess { case x=> println(x)}
Ok("")
}
}
TestDataObject.scala:
package models
case class TestDataObject (name: String, objtype: String, quantity: Int, cost: Float){
def toJsonString: String = {
val mapper = new ObjectMapper()
mapper.registerModule(DefaultScalaModule)
mapper.writeValueAsString(this)
}
}
I have two main questions:
How do i signal that the input is complete from the Enumerator callback? The documentation says "this method takes a callback function e: => Future[Option[E]] that will be called each time the iteratee this Enumerator is applied to is ready to take some input." but I am unable to pass any kind of EOF that I've found because it;s the wrong type. Wrapping it in a Future does not help, but instinctively I am not sure that's the right approach.
How do I get the final result out of the Future to return from the controller view? My understanding is that I would effectively need to pause the main thread to wait for the subthreads to complete, but the only examples I've seen and only things i've found in the future class is the onSuccess callback - but how can I then return that from the view? Does Iteratee.run block until all input has been consumed?
A couple of sub-questions as well, to help my understanding:
Why do I need to wrap my object in Some() when it's already in a Future? What exactly does Some() represent?
When I run the code for the first time, I get a single record logged from logger.info and then it reports "reached end of iteration". Subsequent runs in the same session call nothing. I am closing the statement though, so why do I get no results the second time around? I was expecting it to loop indefinitely as I don't know how to signal the correct termination for the loop.
Thanks a lot in advance for any answers, I thought I was getting the hang of this but obviously not yet!
How do i signal that the input is complete from the Enumerator callback?
You return a Future(None).
How do I get the final result out of the Future to return from the controller view?
You can use Action.async (doc):
def index = Action.async {
db.withConnection { conn=>
...
val eventuallyResult:Future[Seq[TestDataObject]] = newIteree.run
eventuallyResult map { data =>
OK(...)
}
}
}
Why do I need to wrap my object in Some() when it's already in a Future? What exactly does Some() represent?
The Future represents the (potentially asynchronous) processing to obtain the next element. The Option represents the availability of the next element: Some(x) if another element is available, None if the enumeration is completed.

How can I use and return Source queue to caller without materializing it?

I'm trying to use new Akka streams and wonder how I can use and return Source queue to caller without materializing it in my code ?
Imagine we have library that makes number of async calls and returns results via Source. Function looks like this
def findArticlesByTitle(text: String): Source[String, SourceQueue[String]] = {
val source = Source.queue[String](100, backpressure)
source.mapMaterializedValue { case queue =>
val url = s"http://.....&term=$text"
httpclient.get(url).map(httpResponseToSprayJson[SearchResponse]).map { v =>
v.idlist.foreach { id =>
queue.offer(id)
}
queue.complete()
}
}
source
}
and caller might use it like this
// There is implicit ActorMaterializer somewhere
val stream = plugin.findArticlesByTitle(title)
val results = stream.runFold(List[String]())((result, article) => article :: result)
When I run this code within mapMaterializedValue is never executed.
I can't understand why I don't have access to instance of SourceQueue if it should be up to caller to decide how to materialize the source.
How should I implement this ?
In your code example you're returning source instead of the return value of source.mapMaterializedValue (the method call doesn't mutate the Source object).

Scala play http filters: how to find the request body

I'm trying to write a filter similar to the simple one described in http://www.playframework.com/documentation/2.1.1/ScalaHttpFilters but I need to access the request body. The documentation below states that "when we invoke next, we get back an Iteratee. You could wrap this in an Enumeratee to do some transformations if you wished." I'm trying to figure out how to wrap the Iteratee so I can get the request body as a string within the filter so I can log that as well.
First thing you have to know is when the Filter is invoked, the request body is not parsed yet. That's why it's giving you a RequestHeader. You'll have to find out the type of body, and call the proper body parser accordingly.
You can find a example of body parsing in the CSRF filter (It can lookup for CSRF tokens in the first bytes of the request body).
See: https://github.com/playframework/playframework/blob/master/framework/src/play-filters-helpers/src/main/scala/csrf.scala#L221-L233.
Hope it helps.
I spent some time on this.
I am no means a Scala expert but this works pretty well! :)
object AccessLog extends EssentialFilter {
def apply(nextFilter: EssentialAction) = new EssentialAction {
def apply(requestHeader: RequestHeader) = {
val startTime = System.currentTimeMillis
nextFilter(requestHeader).map { result =>
val endTime = System.currentTimeMillis
val requestTime = endTime - startTime
val bytesToString: Enumeratee[ Array[Byte], String ] = Enumeratee.map[Array[Byte]]{ bytes => new String(bytes) }
val consume: Iteratee[String,String] = Iteratee.consume[String]()
val resultBody : Future[String] = result.body |>>> bytesToString &>> consume
resultBody.map {
body =>
Logger.info(s"${requestHeader.method} ${requestHeader.uri}" +
s" took ${requestTime}ms and returned ${result.header.status}")
val jsonBody = Json.parse(body)
Logger.debug(s"Response\nHeader:\n${result.header.headers.toString}\nBody:\n${Json.prettyPrint(jsonBody)}")
}
result.withHeaders("Request-Time" -> requestTime.toString)
}
}
}
The end result will print the body as a Json String (pretty printed).
In the controller method that routes to the action, simply call
Map<String, String[]> params = request().queryString();
This will get you a map of parameters, where you can then call
params.get("someParam")[0]
to get the param (if it is a single value). If the param is a list, ignore the indexing andthat will return an array.