rendering a page after Future is done with Play2 - scala

Using Play2 framework with Slick DB manager, I have a class that encapsulates DB access, and a controller in charge of returning that information to the user. There are roughly like this:
Class with DB access:
override def getAllAnswersFuture(userID: Int): Future[List[UserAnswer]] = {
(DB QUERY CREATION AS allAnswersQuery)
val myquery : Future[Seq[(Int, String)]] =myDB.run(allAnswersQuery.result)
myquery.map{x => x.map(elem =>(
UserAnswer(elem._1, elem._2).toList}
}
Controller:
def index = Action { request =>
request.session.get("userID").map { userID =>
val myUser = userStorage.getUser(userID.toInt)
val allAnswersFuture = questionStorage.getAllAnswersFuture(myUser.id)
allAnswersFuture onComplete {case _ => Ok(views.html.app.stats())}
Ok(views.html.app.index("Future did not work"))
}.getOrElse {
Unauthorized("Oops, you are not connected")
}
}
For some reason, the content of onComplete is never shown, returning the "future did not work" instead.
My question is, How do I tell Play2 to wait until the DB query has been completed and then print the results?

Use Action.async for dealing with futures and try doing something like:
def index = Action.async { request =>
allAnswersFuture.map{_ => Ok(views.html.app.stats())}.recover {
case ex: Exception =>
Ok(views.html.app.index("Future did not work"))
}
}
Instead of using onComplete on future use Map and recover, If you want to return anything on future callback. OnComplete on Future won't return anything, Its return type is Unit.
In case of Future success, your stats page will be populated and in failure index page will be populated from recover block.
And in your getOrElse block use:
Future.successful(Unauthorized("Oops, you are not connected"))
This will do the needful

Related

Avoid syncronous calls in Akka Actors due of Await result

Because I do some "complex" operations, I think my Actor became asyncronous. The main problem I think is that I use Await.result inside the method which return responses.
actor:
def process(subscribers: Set[ActorRef]): Receive = {
case Join(ref) => context become process(subscribers + ref)
case Leave(ref) => context become process(subscribers - ref)
case Push(request) =>
val filteredSubscribers = (subscribers - sender())
.filter(s => exists(s, request)) // just some actor filters
filteredSubscribers.foreach { subscriber =>
// here I have a Map with each actor requests
val actorOptions = getActorOptions(subscriber)
subscriber ? getResponse(actorOptions, request)
}
}
The problem is inside getResponse (I think).
getResponse(actorOptions: JsValue, request: SocketRequest): JsValue = {
(actorOptions \ "dashboardId").asOpt[Int] match {
case Some(id) => {
val response = widgetsService.getByDashboadId(id) map { widgets =>
val widgetsResponse: List[Future[String]] = widgets.map(w => {
widgetsService.getDataById(w.id) map {
data => s"""{ "widgetId": ${w.id}, "data": $data }"""
}
})
var responses: List[String] = List.empty
widgetsResponse.foreach(f => {
f.onComplete {
case Success(value) => responses = value :: responses
case Failure(e) => println(s"Something happened: ${e.getMessage}")
}
})
// first time when I use Await.result
// used to populate the responses list with data from all futures
Await.result(Future.sequence(widgetsResponse), Duration.Inf)
Json.parse(s"""{
"dashboardId": $id,
"widgets": [${response.mkString(", ")}]
}""".stripMargin)
}
// second time when I use Await.result
// used to return a JsValue instead of a Future[JsValue]
Await.result(response, Duration.Inf)
}
case None => buildDefaultJson // return default json value, unimportant for this example
}
}
Due of that, In frontend, if I have 2 sockets clients, the response for the second will be send only after first.
I found that I can obtain a "fake" increase of performance if I embrance the getResponse in a future inside of my Actor.
filteredSubscribers.foreach { subscriber =>
val actorOptions = getActorOptions(subscriber)
Future(subscriber ? getResponse(actorOptions, request))
}
So, for both subscribers the action will be started in same time, but when the first will reach the Await.result, the second will be locked until first is done.
I need to avoid using Await.result there, but I don't know how to get the results of a list of futures, without using for-comprehension (because is a dynamically list) for first time where I use it.
Because Akka ask operator (?) return a Future[Any], I tried that my getResponse method to return directly a JsValue to be mapped then in Future[JsValue]. If I remove the second Await.result and my method will return Future[JsValue], then the actor will return a Future[Future[JsValue]] which I don't think is too right.
After some more researches and solutions found on so, my code become:
Future.sequence(widgetsResponse) map { responses =>
Json.parse(
s"""
|{
|"dashboardId": $id,
|"tableSourceId": $tableSourceId,
|"widgets": [ ${responses.mkString(", ")}]
|}""".stripMargin
)
}
getResponse returns a Future[JsValue] now, removing both Await.result cases, and actor case become:
filteredSubscribers.foreach { subscriber =>
val actorOptions = getActorOptions(subscriber)
getResponse(actorOptions, request) map { data =>
subscriber ? data
}
}
I don't know why, still have a synchronous behavior. Damn, this can be due of my subscribers type: Set[ActorRef]? I tried to use parallel foreach and this looks like solving my problem:
filteredSubscribers.par.foreach { subscriber =>
val actorOptions = getActorOptions(subscriber)
getResponse(actorOptions, request) map { data =>
subscriber ? data
}
}

How should I use the cache API with futures

I want to use the built in playframework cache API but not sure how to cache this web service request when it has futures.
def getUser(username: String): Future[Option[User]] = {
ws.url("...").get.map { response =>
response.json.validate[User] match {
case JsSuccess(user, _) => Some(user)
case JsError(errors) => {
// log errors
None
}
}
}
}
The cache key will just be the username for now.
How would I use the cache API here?
I wanted to use the getOrElse pattern:
val user: User = cache.getOrElse[User]("item.key") {
User.findById(connectedUser)
}
But the Future and Option is confusing me on how to go about using it.
I would do it like this:
def fromWebservice(username: String): Future[Option[User]] =
ws.url("..").get.map(validate) // validate does the json validate thing
def getUser(username: String): Future[Option[User]] =
cache.getAs[User](username) match {
case Some(x) =>
Future.successful(Some(x))
case None =>
fromWebservice(username).map(_.map { user =>
cache.set(username, user)
x
})
}
As an aside, the Play cache API does not have an API that returns Futures, but they're working on it: https://github.com/playframework/playframework/issues/5912

Getting the value out of a Future in Scala

I have the following code snippet that I use to read a record from the database and I'm using ReactiveMongo for this.
val futureList: Future[Option[BSONDocument]] = collection.find(query).cursor[BSONDocument].headOption
val os: Future[Option[Exam]] = futureList.map {
(list: Option[BSONDocument]) => list match {
case Some(examBSON) => {
val id = examBSON.getAs[Int]("id").get
val text = examBSON.getAs[String]("text").get
val description = examBSON.getAs[String]("description").get
val totalQuestions = examBSON.getAs[Int]("totalQuestions").get
val passingScore = examBSON.getAs[Int]("passingScore").get
Some(Exam(id, text, description, totalQuestions, passingScore))
}
case None => None
}
}.recover {
case t: Throwable => // Log exception
None
}
I do not want to change my method signature to return a Future. I want to get the value inside the Future and return it to the caller.
You need then to block using the awaitable object:
import scala.concurrent.duration._
val os: Future[Option[Exam]] = ???
val result = Await.result(os, 10 seconds)
result.getOrElse(/* some default */)
Note that blocking will block the thread until the future is completed or the timeout expires and an exception is thrown, note also that this kinda defeats the purpose of having async computation, but it may be ok depending on your use case.
If you don't need the result immediately you can attach a callback using onComplete
os onComplete {
case Success(someOption) => myMethod(someOption)
case Failure(t) => println("Error)
}
Note that onComplete will be fired only when the future is completed so the result is not immediately accessible, also the return type is Unit.

Getting data out of a Future in Scala

I've a Future[List[Person]][1] and I want to get the List[Person] from it. How can I do it ?
import scala.concurrent.Future
val futPersons : Future[List[Person]] = ....
There are multiple ways:
futPersons.map { personList =>
....
}
This map returns another Future composed with whatever you return from the map. The map will execute only if the future completes successfully. If you need to handle failure you can use onComplete
futPersons.onComplete {
case Success(personList) => ...
case Failure(exception) => ...
}
Or you can wait for the future to complete (this is blocking):
val personList: List[Person] = Await.result(futPersons, 1 minutes)
Blocking way (pauses your thread until you get the value back) using Await.result:
scala.concurrent.Await.result(futPersons, timeout)
Or, using a callback with onSuccess:
futPersons onSuccess {
case persons => // do something with persons
}

Scala return result from a Promise

Looking to achieve this:
HTTP request to REST API -> parse -> make async call to another API -> respond to http req with the result of the async call.
Currently, the code looks like:
def getItems(param: String): LiftResponse = {
#volatile var resp: LiftResponse = new BadResponse
param.toLowerCase match {
case "something" =>
val req = Async call returning a Future
req onSuccess {
case items =>
resp = new JsonResponse(items map (decompose(_)), S.getResponseHeaders(Nil), S.responseCookies, 200)
}
req onFailure {
case fail => resp = new BadResponse
}
resp
case _ => new OkResponse
}
}
But it looks like poor implementation.
What is the idiomatic Scala way to write the above?
Your code will probably not do what you think it should since it depends on scheduling whether it returns null or something else. Is LiftResponse a strict value or can it be deferred? If strict then you will have to return a Future[LiftResponse] obtained by mapping your req: Future.
Look into using Lift's RestHelper in conjunction with RestContinuation.async. It supports using continuations to suspend a request until data is available for it. There's some sample code at http://demo.liftweb.net/async_rest . After the async continuation is invoked and before the reply function is called with the result, the request thread will be released to the thread pool. Once the reply function is called, the request will be put back on a thread and the response sent to the client.
I think you can try to inline #volatile var resp by:
def getItems(param: String): LiftResponse = {
param.toLowerCase match {
case "something" =>
val req = Async call returning a Future
req.onComplete {
case Success(items) => new JsonResponse(items map (decompose(_)), S.getResponseHeaders(Nil), S.responseCookies, 200)
case Failure(t) => new BadResponse
}
case _ => new OkResponse
}
}
--edit--
sorry, onComplete returns Unit, how about using Await to get the result of future:
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent._
import scala.concurrent.duration._
def getItems(param: String): LiftResponse = {
param.toLowerCase match {
case "something" =>
val req = Async call returning a Future
val response = req map { items =>
new JsonResponse
} recover {
case t:Throwable => new BadResponse
}
Await.result(response, Duration(100, MILLISECONDS))
case _ => new OkResponse
}
}
Note (3 years later): with the recent (Nov. 2016) release of Lift3, you can use net.liftweb.http._, as described in "Request and Session Access in Lift Futures", from Piotr Dyraga.
As an example, say that you want to lazily render a list of users.
First, you execute database query asynchronously and get Future[Seq[User]] as a result of this operation.
If you use Lift 2, incorporate FutureBinds that I described in one my previous posts or if you use Lift3, import net.liftweb.http._ and do:
val allUsers: Future[Seq[User]] = ... // retrieve users from DB
".user-list-async-container" #> allUsers.map { users =>
".user-list" #> users.map { user =>
".user" #> userBindings(user)
}
}