download a file with play web api (async) - scala

I am trying to download a file using play api framework. Since all the data access layer has already been implemented with Futures I would like to get download to work with async action as well. However, the following piece of code does not work. And by not working I mean that the file sent to the client is not the same as the file on server.
val sourcePath = "/tmp/sample.pdf"
def downloadAsync = Action.async {
Future.successful(Ok.sendFile(new java.io.File(sourcePath)))
}
However, this piece works:
def download = Action {
Ok.sendFile(new java.io.File(sourcePath))
}
Any suggestion on how I can get the async method to work?

You actually don't need to use Action.async here, since Ok.sendFile is non-blocking already. From the docs:
Play actions are asynchronous by default. For instance, in the controller code below, the { Ok(...) } part of the code is not the method body of the controller. It is an anonymous function that is being passed to the Action object’s apply method, which creates an object of type Action. Internally, the anonymous function that you wrote will be called and its result will be enclosed in a Future.
def echo = Action { request =>
Ok("Got request [" + request + "]")
}
Note: Both Action.apply and Action.async create Action objects that are handled internally in the same way. There is a single kind of Action, which is asynchronous, and not two kinds (a synchronous one and an asynchronous one). The .async builder is just a facility to simplify creating actions based on APIs that return a Future, which makes it easier to write non-blocking code.
In other words, at this specific case, don't worry about wrapping your Result into a Future and just return Ok.sendFile.
Finally, both versions works as expected to me (the file was properly delivered). Maybe you are having another problem not related to how you have declared your actions.

Related

Scala Dynamic Variable problem with Akka-Http asynchronous requests

My application is using Akka-Http to handle requests. I would like to somehow pass incoming extracted request context (fe. token) from routes down to other methods calls without explicitly passing them (because that would require modyfing a lot of code).
I tried using Dynamic Variable to handle this but it seems to not work as intended.
Example how I used and tested this:
object holding Dynamic Variable instance:
object TestDynamicContext {
val dynamicContext = new DynamicVariable[String]("")
}
routes wrapper for extracting and setting request context (token)
private def wrapper: Directive0 = {
Directive { routes => ctx =>
val newValue = UUID.randomUUID().toString
TestDynamicContext.dynamicContext.withValue(newValue) {
routes(())(ctx)
}
}
I expected to all calls of TestDynamicContext.dynamicContext.value for single request under my wrapper to return same value defined in said wrapper but thats not the case. I verified this by generating for each request separate UUID and passing it explicitly down the method calls - but for single request TestDynamicContext.dynamicContext.value sometimes returns different values.
I think its worth mentioning that some operations underneath use Futures and I though that this may be the issue but solution proposed in this thread did not solve this for me: https://stackoverflow.com/a/49256600/16511727.
If somebody has any suggestions how to handle this (not necessarily using Dynamic Variable) I would be very grateful.

How to manage Form request asynchronously

I would like to manage a POST request from a form component in Play Framework asynchronously. In particular, I would like to use the form parameters in a handler that already returns a Future.
I'm aware that in order to handle the POST request, I must use an overload that gets a MessagesRequest[AnyContent] instance in input, e.g.:
def action = Action { implicit request: MessagesRequest[AnyContent] =>
Ok(views.html.view( ... ))
}
In order to manage a Future, anyway, I must use the Action.async method, but I didn't found any overload of this method that accept in input an instance of MessagesRequest[AnyContent].
I've already tried to cast the handler using a simple Request[AnyContent] instance, but it didn't integrate with the Play framework infrastructure in this particular case.
From the documentation, it is clear that under the hood, both Action and Action.async are asynchronous in nature, and that the Action.async is just a helper facility to enable writing code in a non blocking way, which is exactly what I would like to achieve.
According to my understanding it would be sufficient to return a non-asynchronous result that would be wrapped in a Future anyway, but is there a way to return a Future, handling the MessagesRequest[AnyContent] from the Play form implementation?
Wrap the response in a Future like so
import scala.concurrent.ExecutionContext.Implicits.global
def action = Action.async { implicit request: MessagesRequest[AnyContent] =>
Future {
Ok(views.html.view( ... ))
}
}
Mario Galic Answer is the right answer to the question.
Just to add more details, my mistake was using the AbstractController as a base class for my controller, instead of the MessagesAbstractController.
The MessagesAbstractController class would indeed enable the following signature of the method Action, defined in the MessagesBaseController trait:
def Action: ActionBuilder[MessagesRequest, AnyContent]
instead of the one inherited from the AbstractController, defined in the BaseController trait:
def Action: ActionBuilder[Request, AnyContent]
This would make possible to define a handler that receives a MessagesRequest[AnyContent] in input.

Akka-http logging request identifier

I've been using akka-http for a while now, and so far I've mostly logged things using scala-logging by extending either StrictLogging or LazyLogging and then calling the:
log.info
log.debug
....
This is kinda ok, but its hard to understand which logs were generated for which request.
As solutions for this go, I've only seen:
adding an implicit logging context that gets passed around (this is kinda verbose and would force me to add this context to all method calls) + custom logger that adds the context info to the logging message.
using the MDC and a custom dispatcher; in order to implement this approach one would have to use the prepare() call which has just been deprecated.
using AspectJ
Are there any other solutions that are more straightforward and less verbose ? It would be ok to change the logging library btw..
Personally I would go with implicit context approach. I'd start with:
(path("api" / "test") & get) {
val context = generateContext
action(requestId)
}
Then I'd would make it implicit:
(path("api" / "test") & get) {
implicit val context = generateContext
action
}
Then I would make the context generation a directive, like e.g.:
val withContext: Directive1[MyContext] = Directive[Tuple1[MyContext]] {
inner => ctx => inner(Tuple1(generateContext))(ctx)
}
withContext { implicit context =>
(path("api" / "test") & get) {
action
}
}
Of course, you would have to take context as an implicit parameter to every action. But, it would have some advantages over MDC and AspectJ - it would be easier to test things, as you just need to pass value. Besides, who said you only ever need to pass request id and use it for logging? The context could as well pass data about logged in user, its entitlements and other things that you could resolve once, use even before calling action and reuse inside action.
As you probably guessed, this would not work if you want the ability to e.g. remove logging completely. In such case AspectJ would make more sense.
I would have most doubts with MDC. If I understand correctly it has build in assumption that all logic would happen in the same thread. If you are using Futures or Tasks, could you actually guarantee such thing? I would expect that at best all logging calls would happen in the same thread pool, but not necessarily the same thread.
Bottom line is, all possible posiltions would be some variant of what you already figured out, so the question is your exact use case.

How can I perform session based logging in Play Framework

We are currently using the Play Framework and we are using the standard logging mechanism. We have implemented a implicit context to support passing username and session id to all service methods. We want to implement logging so that it is session based. This requires implementing our own logger. This works for our own logs but how do we do the same for basic exception handling and logs as a result. Maybe there is a better way to capture this then with implicits or how can we override the exception handling logging. Essentially, we want to get as many log messages to be associated to the session.
It depends if you are doing reactive style development or standard synchronous development:
If standard synchronous development (i.e. no futures, 1 thread per request) - then I'd recommend you just use MDC, which adds values onto Threadlocal for logging. You can then customise the output in logback / log4j. When you get the username / session (possibly in a Filter or in your controller), you can then set the values there and then and you do not need to pass them around with implicits.
If you are doing reactive development you have a couple options:
You can still use MDC, except you'd have to use a custom Execution Context that effectively copies the MDC values to the thread, since each request could in theory be handled by multiple threads. (as described here: http://code.hootsuite.com/logging-contextual-info-in-an-asynchronous-scala-application/)
The alternative is the solution which I tend to use (and close to what you have now): You could make a class which represents MyAppRequest. Set the username, session info, and anything else, on that. You can continue to pass it around as an implicit. However, instead of using Action.async, you make your own MyAction class which an be used like below
myAction.async { implicit myRequest => //some code }
Inside the myAction, you'd have to catch all Exceptions and deal with future failures, and do the error handling manually instead of relying on the ErrorHandler. I often inject myAction into my Controllers and put common filter functionality in it.
The down side of this is, it is just a manual method. Also I've made MyAppRequest hold a Map of loggable values which can be set anywhere, which means it had to be a mutable map. Also, sometimes you need to make more than one myAction.async. The pro is, it is quite explicit and in your control without too much ExecutionContext/ThreadLocal magic.
Here is some very rough sample code as a starter, for the manual solution:
def logErrorAndRethrow(myrequest:MyRequest, x:Throwable): Nothing = {
//log your error here in the format you like
throw x //you can do this or handle errors how you like
}
class MyRequest {
val attr : mutable.Map[String, String] = new mutable.HashMap[String, String]()
}
//make this a util to inject, or move it into a common parent controller
def myAsync(block: MyRequest => Future[Result] ): Action[AnyContent] = {
val myRequest = new MyRequest()
try {
Action.async(
block(myRequest).recover { case cause => logErrorAndRethrow(myRequest, cause) }
)
} catch {
case x:Throwable =>
logErrorAndRethrow(myRequest, x)
}
}
//the method your Route file refers to
def getStuff = myAsync { request:MyRequest =>
//execute your code here, passing around request as an implicit
Future.successful(Results.Ok)
}

My http request becomes null inside an Akka future

My server application uses Scalatra, with json4s, and Akka.
Most of the requests it receives are POSTs, and they return immediately to the client with a fixed response. The actual responses are sent asynchronously to a server socket at the client. To do this, I need to getRemoteAddr from the http request. I am trying with the following code:
case class MyJsonParams(foo:String, bar:Int)
class MyServices extends ScalatraServlet {
implicit val formats = DefaultFormats
post("/test") {
withJsonFuture[MyJsonParams]{ params =>
// code that calls request.getRemoteAddr goes here
// sometimes request is null and I get an exception
println(request)
}
}
def withJsonFuture[A](closure: A => Unit)(implicit mf: Manifest[A]) = {
contentType = "text/json"
val params:A = parse(request.body).extract[A]
future{
closure(params)
}
Ok("""{"result":"OK"}""")
}
}
The intention of the withJsonFuture function is to move some boilerplate out of my route processing.
This sometimes works (prints a non-null value for request) and sometimes request is null, which I find quite puzzling. I suspect that I must be "closing over" the request in my future. However, the error also happens with controlled test scenarios when there are no other requests going on. I would imagine request to be immutable (maybe I'm wrong?)
In an attempt to solve the issue, I have changed my code to the following:
case class MyJsonParams(foo:String, bar:Int)
class MyServices extends ScalatraServlet {
implicit val formats = DefaultFormats
post("/test") {
withJsonFuture[MyJsonParams]{ (addr, params) =>
println(addr)
}
}
def withJsonFuture[A](closure: (String, A) => Unit)(implicit mf: Manifest[A]) = {
contentType = "text/json"
val addr = request.getRemoteAddr()
val params:A = parse(request.body).extract[A]
future{
closure(addr, params)
}
Ok("""{"result":"OK"}""")
}
}
This seems to work. However, I really don't know if it is still includes any bad concurrency-related programming practice that could cause an error in the future ("future" meant in its most common sense = what lies ahead :).
Scalatra is not so well suited for asynchronous code. I recently stumbled on the very same problem as you.
The problem is that scalatra tries to make the code as declarative as possible by exposing a dsl that removes as much fuss as possible, and in particular does not require you to explicitly pass data around.
I'll try to explain.
In your example, the code inside post("/test") is an anonymous function. Notice that it does not take any parameter, not even the current request object.
Instead, scalatra will store the current request object inside a thread local value just before it calls your own handler, and you can then get it back through ScalatraServlet.request.
This is the classical Dynamic Scope pattern. It has the advantage that you can write many utility methods that access the current request and call them from your handlers, without explicitly passing the request.
Now, the problem comes when you use asynchronous code, as you do.
In your case, the code inside withJsonFuture executes on another thread than the original thread that the handler was initially called (it will execute on a thread from the ExecutionContext's thread pool).
Thus when accessing the thread local, you are accessing a totally distinct instance of the thread local variable.
Simply put, the classical Dynamic Scope pattern is no fit in an asynchronous context.
The solution here is to capture the request at the very start of your handler, and then exclusively reference that:
post("/test") {
val currentRequest = request
withJsonFuture[MyJsonParams]{ params =>
// code that calls request.getRemoteAddr goes here
// sometimes request is null and I get an exception
println(currentRequest)
}
}
Quite frankly, this is too easy to get wrong IMHO, so I would personally avoid using Scalatra altogether if you are in an synchronous context.
I don't know Scalatra, but it's fishy that you are accessing a value called request that you do not define yourself. My guess is that it is coming as part of extending ScalatraServlet. If that's the case, then it's probably mutable state that it being set (by Scalatra) at the start of the request and then nullified at the end. If that's happening, then your workaround is okay as would be assigning request to another val like val myRequest = request before the future block and then accessing it as myRequest inside of the future and closure.
I do not know scalatra but at first glance, the withJsonFuture function returns an OK but also creates a thread via the future { closure(addr, params) } call.
If that latter thread is run after the OK is processed, the response has been sent and the request is closed/GCed.
Why create a Future to run you closure ?
if withJsonFuture needs to return a Future (again, sorry, I do not know scalatra), you should wrap the whole body of that function in a Future.
Try to put with FutureSupport on your class declaration like this
class MyServices extends ScalatraServlet with FutureSupport {}