Non availability of Application class variable between requests - playframework-2.4

I have an application controller which has 2 methods. In the first method I access database and retrieve a list of drop-down elements for the form. I store the retrieved elements in class level variables. In the second method if there is an error it displays the form as it is else it shows the result. The problem is I class level variables return empty.
class Application extends Controller {
var appList = Seq[AppDetail]()
var countryList = Seq[Country]()
def home(page:Int,filter: String): Action[AnyContent] = Action.async { implicit request =>
val futures = for {
appDetails <- AppDetailsDAO.listAll() //DB retrieve
countries <- CountriesDAO.listAll() //DB retrieve
} yield (appDetails, countries)
futures.map{case (appDetails, countries) => {
appList = appDetails
countryList = countries
Ok(appList, countryList)
}
}
def result(page:Int, filter: String): Action[AnyContent] = Action.async { implicit request =>
analyticForm.bindFromRequest.fold(
formWithErrors => {
Future.successful(BadRequest(html.home(formWithErrors, appList, countryList)) //appList, countryList returns empty
},
analyticData => {
Ok(appList, countryList) //appList, countryList returns empty
}
}
}

Play is a RESTful framework. This means, among others, you should not share information between two requests. Requests are always stateless.
To solve your problem, you simply fetch appList and countryList in both of your actions. Also remove the two vars in your controller. Whenever possible avoid mutable variables in scala (they can be useful in some cases though). See Daniel C. Sobral's answer on the difference between var and val.
Untested code ahead:
class Application extends Controller {
def home(page:Int,filter: String): Action[AnyContent] = Action.async { implicit request =>
fetchDb.map{case (appDetails, countries) => {
appList = appDetails
countryList = countries
Ok(appList, countryList)
}}
}
def result(page:Int, filter: String): Action[AnyContent] = Action.async { implicit request =>
fetchDb.map{case (appDetails, countries) => {
analyticForm.bindFromRequest.fold(
formWithErrors => {
BadRequest(html.home(formWithErrors, appList, countryList))
},
analyticData => {
Ok(appList, countryList) //appList, countryList returns empty
}
)
}}
}
def fetchDb = {
val fAppDetails = AppDetailsDAO.listAll()
val fCountries = CountriesDAO.listAll()
for {
appDetails <- fAppDetails
countries <- fCountries
} yield (appDetails, countries)
}
}
Side note: You may noticed that I create the Future instances in fetchDb outside of the for comprehension. This is because a for comprehension is just another representation of a nested flat map. This means countries will not run until appDetails has been completed. The futures will run sequentially. If you put them outside of the for comprehension they will run in parallel.

Related

How do I compose nested case classes populated with async mongodb queries in play framework

I have been trying to convert mongodb queries I have working with await to using totally async. The problem is, I cannot find any examples or get code working to populate a list of objects where for each object there is a nested find returning futures.
I have seen examples for a single object, such as
val user = mongoDao.getUser(id)
val address = mongoDao.getAddress(user.id)
for that I see for comprehension works just fine. However, I have a list of objects (similar to users) and I cant seem to get the code right.
What I need to do is get all the users in an async manner, then when they complete, get all the addresses and populate a field (or create a new case class.)
val usersFuture : Future[List[User]] = mongoDao.getUsers()
val fullFutures : Future[List[FullUser]] = usersFuture.map(users: List[User] => {
users.map(user: User => {
val futureAddress : Future[Address] = mongoDao.getAddress()
// Now create a object
futureAddress.map(address: Address) {
FullUserInfo(user, address)
}
}
}
So, I'd like to end up with a Future[List[FullUser]] that I can return to the play framework. I've included the cutdown I've tried.
thanks
// OBJECTS HERE
case class Outer(id: Int, name: String)
case class Inner(id: Int, name: String)
case class Combined(id: Int, name: String, inner: Inner)
// FAKE DAO to reproduct
#Singleton
class StatInner #Inject()( implicit val ec: ExecutionContext) {
def outer() = {
Future {
val lb = new ListBuffer[Outer]()
Thread.sleep(1000)
println("Done")
for (id <- 1 to 5) {
lb += Outer(id, s"Hello $id")
}
lb.toList
}
}
def inner(id: Int) : Future[Inner] = {
Future {
Thread.sleep(1000)
Inner(id, s"inner $id")
}
}
}
// CODE to query that is not working
def nestedTree = Action.async {
val statInner : StatInner = new StatInner()
val listouter : Future[List[Outer]] = statInner.outer()
val combined = listouter.map((listOuter : List[Outer]) => {
listOuter.flatMap((outer: Outer) => {
val futInner : Future[Inner] = statInner.inner(outer.id)
futInner.map((inner: Inner) => {
Combined(outer, inner)
})
})
})
combined.map(Json.toJson(_))
}
```
Use Future.{flatMap.sequence}:
val usersFuture: Future[List[User]] = mongoDao.getUsers()
val fullFutures: Future[List[FullUser]] = usersFuture.flatMap { users =>
Future.sequence(users.map { user =>
mongoDao.getAddress().map { adress =>
FullUserInfo(user, address)
}
})
}

Evaluating a Future Boolean asynchronously using Scala and Play

I have a method that returns a Future[Boolean] in a Play controller and i want to evaluate that using async but i can't seem to get it to compile.
The following will work:
def health = Action {
logger.info("Endpoint method: health")
val isHealthy = healthCheckService.checkDynamo()
val b: Boolean = Await.result(isHealthy, scala.concurrent.duration.Duration(5, "seconds"))
Ok(Json.toJson(HealthCheckResponse(b.toString)))
}
But i don't think i want that Await in there. So i'm trying things like this with no success:
def health =
Action.async {
Future {
logger.info("Endpoint method: health")
healthCheckService.checkDynamo() match {
case Future.successful(true) => Ok(Json.toJson("false"))
case false => Ok(Json.toJson("true"))
}
val r = healthCheckService.checkDynamo() match {
case true => Ok(Json.toJson("false"))
case false => Ok(Json.toJson("true"))
}
}
}
I can't even get those to compile to test them out.
Any suggestions?
Try this:
def health = Action.async {
healthCheckService.checkDynamo().map {
case true => Ok(Json.toJson("false"))
case false => Ok(Json.toJson("true"))
}
}
Let Play handle the awaiting for you under the hood. That is, Action.async accepts a Future, which checkDynamo() already returns. All you have to do is map it to the appropriate result.
With Futures you have to use combinators like map and flatMap to express the final value. For example:
Action.async {
healthCheckService.checkDynamo()
.map { result => // boolean
HealthCheckResponse(result.toString)
}
.map(Json.toJson(_))
.map(Ok(_))
}
(You can merge maps above to one map and construct the final Ok value there; it is more or less a matter of taste)
If you have, say, two async calls which you want to execute and return a result based on their results, you can use flatMap, which could be easily expressed using a for comprehension:
Action.async {
for {
result1 <- someService.someCall()
result2 <- anotherService.anotherCall(result1.someProperty)
finalResult = SomeFinalResultType(result1, result2)
} yield Ok(Json.toJson(finalResult))
}
If you are not familiar with futures, you might want to read some tutorial which explains their nature, how to combine them and how to get useful results from them, like this one: http://hello-scala.com/920-scala-futures.html

Return value from method definition that has multiple case statements

val value = authenticateUser
private def authenticateUser = {
val holder = WS.url(platformUrl + "/userApi/auth/login?username=testuser&password=testPass")
val res = holder.post(Results.EmptyContent()).onComplete {
case Success(response) => response.cookies.map{cookie => println(cookie.value.get)}
case Failure(errors) => println("")
// The `Future` failed.
}
}
How to return cookie.value.get from authenticateUser method?
First of all, you seem to have many cookies, so it's not clear, the value of which one you want to return.
More importantly, you should not actually return the value itself. The idea is to return the Future, that can be further transformed downstream:
def authenticateUser = WS.url(..)
.post(Results.EmptyContent)
.map { _.cookies }
.map { _.find(_.name == "Auth").flatMap(_.value).getOrElse("") }
Now, somewhere downstream, you can have something like:
def serve(request: Request): Future[Response] = authenticateUser
.map { user =>
serveRequest(user, request)
}.map { result => makeResponse(result) }
I don't know the specifics of Play, so, consider the Request/Response stuff "pseudocode", but the basic idea is that your entire processing should be a chain of Future transformations. The general rule is to never block the service thread.

Calling multiple web services

I'm using Play 2.5 with Scala, I created a class that will call multiple times external web services.
External web services are called on some condition and get a simple response of ok or nok. If it is ok then I should update internal objects status and if nok I do nothing for now.
Here is my class, it takes a list of list of String as paramters and return a Future list of object to be handled in the controller.
def callWSAndGetResponse(listOfList: List[List[String]]): Future[List[MyObject]] = {
val res = listOfList map { listOfIds =>
listOfIds map { id =>
val foundObj = allMyObject.find(obj => obj.id == id)
if(foundObj.isDefined) {
foundObj.get.urls map { url =>
val futureReponse: Future[WSResponse] = ws.url(url).get()
futureResponse map { response =>
(response.json \ "response").as[String]
}
}
}
}
}
// if responses are ok create a list of MyObject to return for example.
}
val res is of type list of list but I would like it to be just a simple list of response.
1) How to simplify and correct my code in order to get just a list of response, for later check if ok or not ?
2) How to check if responses are ok or have failed ?
It looks like you want this block to return a List[Future[Json]], from then you can use "List[Future] to Future[List] disregarding failed futures" or similar.
To do this you should use map and flatMap (rather than isDefined/get):
val res = listOfList map {
_.map { id =>
allMyObject
.find(obj => obj.id == id)
.map {
_.flatMap {url =>
val futureReponse: Future[WSResponse] = ws.url(url).get()
futureResponse map { response =>
(response.json \ "response").as[String]
}
}
}
}
}
if blocks assign to AnyVal rather than to a specific type (which will cause you issues here):
scala> if (true) 23
23: AnyVal
scala> if (false) 23
(): AnyVal
Using map keeps you in the Option monad:
scala> (None: Option[Int]).map(_ * 2)
None: Option[Int]

slick returning a dictionaries object from database

I am trying to select from a few tables and put their results in an Object. I want to do this because those are dictionaries and I want all of them at startup.
This is what I have right now:
Controller:
def getDictionaries = Action.async { implicit request =>
Future.successful(Ok(Json.toJson(dictionaryService.getDictionaries)))
}
DictionaryService:
override def getDictionaries : Dictionaries = {
val currencies: Future[Seq[Currency]] = dictionaryDao.getCurrencies
val propertyTypes: Future[Seq[PropertyType]] = dictionaryDao.getPropertyTypes
Dictionaries(
currencies.result(Duration(10L, TimeUnit.SECONDS)),
propertyTypes.result(Duration(10L, TimeUnit.SECONDS))
)
}
DictionaryDAO:
override def getCurrencies: Future[Seq[Currency]] = {
db.run(slickCurrencies.result)
}
... getPropertyTypes ...
Dictionaries case class and companion object
case class Dictionaries (currencies: Seq[Currency], propertyTypes: Seq[PropertyType])
object Dictionaries {
implicit val jsonFormat = Json.format[Dictionaries]
}
I am not very proud of currencies.result(Duration(10L, TimeUnit.SECONDS)) but I am not sure what I should retrieve from this function so that I can easily transform it to JSON. Also this line of code is still not working because the compiler is telling me to use Await object instead.
Also for PropertyType I need to do the same thing as for currencies.
What is the best way to obtain the desired result?
I am in the learning phase so I don't get much of this.
LATER EDIT: the flow is: request -> getDictionaries -> dictionaryService.getDictionaries -> dictionaryDAO.getCurrencies&PropertyTypes
It seems to me that I need to get them in a synchronized way.
The purpose of this is to not create a request for each type of dictionary. If I have 10 dictionaries, I want to get all of them in one request.
Later EDIT 2
This is my working example which does not look very well:
Controller:
def getDictionaries = Action.async { implicit request =>
dictionaryService.getDictionaries.map {
dictionaries => Ok(Json.toJson(dictionaries))
}
}
DictionaryService:
override def getDictionaries : Future[Dictionaries] = {
dictionaryDao.getCurrencies.flatMap { currencies =>
dictionaryDao.getPropertyTypes.flatMap { propertyTypes =>
Future.successful(Dictionaries(
currencies,
propertyTypes
))
}
}
}
Your method getDictionaries should return Future[Dictionaries]:
override def getDictionaries : Future[Dictionaries] = {
val futureCurrencies = dictionaryDao.getCurrencies
futureCurrencies map { currencies => // currencies is here Seq[Currency]
Dictionaries(
currencies,
List(PropertyType(1, "x"))
)
}
And your controller method:
def getDictionaries = Action.async { implicit request =>
val futureDictionaries = dictionaryService.getDictionaries
futureDictionaries map { dictionaries =>
Ok(Json.toJson(dictionaries))
}
}
EXPLANATION: You have to use Futures all the way bottom up if you want your actions to be really asynchronous. You'll have to learn how to compose them with map, flatMap and for comprehensions. I didn't find any gentle and short introduction for that, you'll have to search it by yourself. Basically, when you map over Future, you are transforming it's(successful) result, and thus getting another Future. With flatMap you can have some ordering of futures, like: when f1 finishes, start f2 etc...