dhore
dhore

Reputation: 83

@Finally equivalent in play 2.0.x?

I'd like to be able to send back a response to the client before I do my logging/cleanup for a request.

In play 1.x this was possible with the @Finally annotation. I've read through some posts that say that those annotations were replaced by action composition, but I'm unclear how to emulate the @Finally annotation using it.

It seems to me that the response will only be returned after all the logic in my custom actions has completed.

Have I missed something, or is there no way to do this in Play 2.0?

[EDIT FOR CLARITY] In other words, I want to be able to run logic after I receive a request and send a response. So I'd like to be able to construct a timeline of the form:

  1. Client sends a request to my server
  2. Server sends back a 200 response, which the client receives
  3. The server does additional processing, logging, etc

In play 1.x I believe I could annote my additional processing logic with a @Finally and have it work like I want it to.

Upvotes: 0

Views: 145

Answers (1)

Julien Lafont
Julien Lafont

Reputation: 7877

Action composition is not sufficient to do the job, but Action composition + Future, or Action composition + Actors are good ways to achieve this.

Action Composition + Future

Generate your Response, launch your logging/processing in an async context and, in parallel, send the result.

def LoggedAction(f: Request[AnyContent] => Result) = Action { request =>
  val result = f(request)
  concurrent.future(myLogAction(request, result))
  result
}

Action composition + Actors

It's a cleaner way to achieve that. As in the previous case, generate your response, send logging/processing event to your(s) actor(s), and in parallel, send the result.

import play.api._
import play.api.mvc._
import play.libs._
import akka.actor._

object Application extends Controller with Finally {
  def index = LoggedAction { r =>
      Ok(views.html.index("Your new application is ready."))
  }
}

trait Finally {
  self: Controller =>

  lazy val logActor = Akka.system.actorOf(Props(new LogActor), name = "logActor")

  def LoggedAction(f: Request[AnyContent] => Result) = Action { request =>
    val result = f(request)         // Generate response
    logActor ! LogRequest(request)  // Send log event to LogActor
    println("-> now send page to client")
    result
  }

  case class LogRequest(request: Request[AnyContent]) 

  class LogActor extends Actor {
    def receive = {
      case LogRequest(req) => {
        println(req.host)
        // ....
      }
    }
  }
}

// Console
-> now send page to client
127.0.0.1:9000

Upvotes: 2

Related Questions