I have a client that makes a REST call to a server that exposes REST API's. Here is what I have so far implemented:
override def getData[T](reqURL: String, requestParam: Option[Seq[(String, String)]])(responseHandler: T => Unit) = {
val onSuccess: PartialFunction[String, Unit] = {
case s => responseHandler(s.asInstanceOf[T])
}
val onFailure: PartialFunction[Throwable, Unit] = {
case e => e.printStackTrace
}
val request = requestParam match {
case Some(params) => url(reqURL).as_!("user", "pass") <<? params
case None => url(reqURL).as_!("user", "pass")
}
Http(request OK as.String).onSuccess(onSuccess).onFailure(onFailure)
}
It works perfectly fine, but what I noticed is that the HTTP connection is open even after the call is complete. How can I close this connection? I might want to do it after the onSuccess or onFailure call has happened. Any suggestions?
Related
I want to refactor by update action below to look a little more readable and also handle the failure case better
The userService has the following functions:
class UserService {
def getUserByUsername: Future[Option[Int]] // which is the UserId
def getUserById: Future[User]
}
My action looks like:
def update(userId: Int) = Action.async { implicit request =>
request.body.validate[User] match {
case JsSuccess(user, _) => {
userService.getUserByUsername(user.username).map { userId =>
userService.getUserById(userId.get).map { existingUser =>
userService.update(user.username)
Ok
}
}
}
case JsError(err) => Future.sucessful(BadRequest(err))
}
}
How do I handle the situation where getUserByUsername returns a None?
Would this look cleaner if it was in a for comprehension, is it better style?
You have some missing data in your questions such as case classes for the User model, userService class.
also better to attach the original function.
Anyways, I will do something as follows:
def update(userId: Int) = Action { implicit request =>
request.body.validate[User] match {
case JsSuccess(user: User, _) => {
val userId = getUserByUsername(user.username)
userId match {
case Some(userId) => {
for {
_ <- userService.getUserById(userId)
_ <- userService.update(user.username)
} yield Ok
}.recover {
case t: Throwable =>
Metrics.errOnUpdate.increment() // Some metric to monitor
logger.error(s"update userId: $userId failed with ex: ${t.getMessage}") // log the error
InternalServerError(Json.toJson(Json.obj("error" -> "Failure occured on update"))) // return custom made exception to the client
}
case None => Future.successful(NotFound(s"No such user with ${user.username}"))
}
}
case JsError(err) => Future.sucessful(BadRequest(err))
}
}
Note: If .update returns Future, you actually not waiting to update before returning Ok to the user, thus, if its fails, its still returns Ok.
To fix that, use flatMap and then map the value of update response.
You can also separate the recovering for the getUserById and update if you prefer.
Edit:
def update(userId: Int) = Action { implicit request =>
request.body.validate[User] match {
case JsSuccess(user: User, _) => {
getUserByUsername(user.username).flatMap {
case Some(userId) => for {
_ <- userService.getUserById(userId)
_ <- userService.update(user.username)
} yield Ok
case None => Future.successful(NotFound(s"No such user with ${user.username}"))
}
}.recover {
case t: Throwable =>
Metrics.errOnUpdate.increment() // Some metric to monitor
logger.error(s"update userId: $userId failed with ex: ${t.getMessage}") // log the error
InternalServerError(Json.toJson(Json.obj("error" -> "Failure occured on update"))) // return custom made exception to the client
}
}
case JsError(err) => Future.sucessful(BadRequest(err))
}
}
First, you probably need to use Option.fold:
#inline final def fold[B](ifEmpty: => B)(f: A => B)
Then you can do something like this:
def update(userId: Int) = Action.async { implicit request =>
def handleJsonErrors(errors: Seq[(JsPath, collection.Seq[JsonValidationError])]): Future[Result] = ???
def updateUser(userWithoutId: User): Future[Result] = {
for {
userId <- userService.getUserByUsername(userWithoutId.username)
_ <- userService.getUserById(userId.get)
_ <- userService.update(userWithoutId.username)
} yield {
Ok
}
}
request.body.asJson.fold {
Future.successful(BadRequest("Bad json"))
} {
_.validate[User].fold(handleJsonErrors, updateUser).recover {
case NonFatal(ex) =>
InternalServerError
}
}
}
In my Play application, I service my requests usings cats-effect's IO, instead of Future in the controller, like this (super-simplified):
def handleServiceResult(serviceResult: ServiceResult): Result = ...
def serviceMyRequest(request: Request): IO[ServiceResult] = ...
def myAction = Action { request =>
handleServiceResult(
serviceMyRequest(request).unsafeRunSync()
)
}
Requests are then processed (asynchronously) on Play's default thread pool. Now, I want to implement multiple thread pools to handle different sorts of requests. Were I using Futures, I could do this:
val myCustomExecutionContext: ExecutionContext = ...
def serviceMyRequest(request: Request): Future[ServiceResult] = ...
def myAction = Action.async { request =>
Future(serviceMyRequest(request))(myCustomExecutionContext)
.map(handleServiceResult)(defaultExecutionContext)
}
But I'm not using Futures, I'm using IO, and I'm not sure about the right way to go about implementing it. This looks promising, but seems a bit clunky:
def serviceMyRequest(request: Request): IO[ServiceResult] = ...
def myAction = Action { request =>
val ioServiceResult = for {
_ <- IO.shift(myCustomExecutionContext)
serviceResult <- serviceMyRequest(request)
_ <- IO.shift(defaultExecutionContext)
} yield {
serviceResult
}
handleServiceResult(ioServiceResult.unsafeRunSync())
}
Is this the right way to implement it? Is there a best practice here? Am I screwing up badly? Thanks.
Ok, so since this doesn't seem to be well-trodden ground, this is what I ended up implementing:
trait PlayIO { self: BaseControllerHelpers =>
implicit class IOActionBuilder[A](actionBuilder: ActionBuilder[Request, A]) {
def io(block: Request[A] => IO[Result]): Action[A] = {
actionBuilder.apply(block.andThen(_.unsafeRunSync()))
}
def io(executionContext: ExecutionContext)(block: Request[A] => IO[Result]): Action[A] = {
val shiftedBlock = block.andThen(IO.shift(executionContext) *> _ <* IO.shift(defaultExecutionContext))
actionBuilder.apply(shiftedBlock.andThen(_.unsafeRunSync()))
}
}
}
Then (using the framework from the question) if I mix PlayIO into the controller, I can do this,
val myCustomExecutionContext: ExecutionContext = ...
def handleServiceResult(serviceResult: ServiceResult): Result = ...
def serviceMyRequest(request: Request): IO[ServiceResult] = ...
def myAction = Action.io(myCustomExecutionContext) { request =>
serviceMyRequest(request).map(handleServiceResult)
}
such that I execute the action's code block on myCustomExecutionContext and then, once complete, thread-shift back to Play's default execution context.
Update:
This is a bit more flexible:
trait PlayIO { self: BaseControllerHelpers =>
implicit class IOActionBuilder[R[_], A](actionBuilder: ActionBuilder[R, A]) {
def io(block: R[A] => IO[Result]): Action[A] = {
actionBuilder.apply(block.andThen(_.unsafeRunSync()))
}
def io(executionContext: ExecutionContext)(block: R[A] => IO[Result]): Action[A] = {
if (executionContext == defaultExecutionContext) io(block) else {
val shiftedBlock = block.andThen(IO.shift(executionContext) *> _ <* IO.shift(defaultExecutionContext))
io(shiftedBlock)
}
}
}
}
Update2:
Per the comment above, this will ensure we always shift back to the default thread pool:
trait PlayIO { self: BaseControllerHelpers =>
implicit class IOActionBuilder[R[_], A](actionBuilder: ActionBuilder[R, A]) {
def io(block: R[A] => IO[Result]): Action[A] = {
actionBuilder.apply(block.andThen(_.unsafeRunSync()))
}
def io(executionContext: ExecutionContext)(block: R[A] => IO[Result]): Action[A] = {
if (executionContext == defaultExecutionContext) io(block) else {
val shiftedBlock = block.andThen { ioResult =>
IO.shift(executionContext).bracket(_ => ioResult)(_ => IO.shift(defaultExecutionContext))
}
io(shiftedBlock)
}
}
}
}
private def responseValidationFlow[T](responsePair: ResponsePair)(implicit evidence: FromByteStringUnmarshaller[T]) = responsePair match {
case (Success(response), _) => {
response.entity.dataBytes
.via(Framing.delimiter(ByteString("\n"), maximumFrameLength = 8192))
.mapAsyncUnordered(Runtime.getRuntime.availableProcessors()) { body =>
if (response.status == OK) {
val obj: Future[T] = Unmarshal(body).to[T]
obj.foreach(x => log.debug("Received {}: {}.", x.getClass.getSimpleName, x))
obj.map(Right(_))
} else {
val reason = body.utf8String
log.error("Non 200 response status: {}, body: {}.", response.status.intValue(), reason)
Future.successful(reason)
.map(Left(_))
}
}
}
case (Failure(t), _) => {
Source.single(Left(t.getMessage))
}
}
What I’d like to do is parameterize both sides of the Either. That’s not hard to do, but what I’m having trouble with is creating a Left or Right that doesn’t have a value. In that case, the body should be consumed fully and discarded. I tried using ClassTags, but the compiler thinks that the type is Any, not S or T. An sample invocation of this a method would look like responseValidationFlow[String, Unit] producing an Source[Either[String, Unit]]
I believe, you can just define an implicit unmarshaller to Unit in scope:
implicit val unitUnmarshaller: FromByteStringUnmarshaller[Unit] =
Unmarshaller.strict(_ => ())
Based on what #Kolmar suggested, here is the working code.
private def responseValidationFlow[L, R](responsePair: ResponsePair)(
implicit ev1: FromByteStringUnmarshaller[L], ev2: FromByteStringUnmarshaller[R]
): Source[Either[L, R], Any] = {
responsePair match {
case (Success(response), _) => {
response.entity.dataBytes
.via(Framing.delimiter(ByteString("\n"), maximumFrameLength = 8192))
.mapAsyncUnordered(Runtime.getRuntime.availableProcessors()) { body =>
if (response.status == OK) {
val obj: Future[R] = Unmarshal(body).to[R]
obj.foreach(x => log.debug("Received {}.", x.getClass.getSimpleName))
obj.map(Right(_))
} else {
log.error("Non 200 response status: {}.", response.status.intValue())
Unmarshal(body).to[L]
.map(Left(_))
}
}
}
case (Failure(t), _) => {
log.error(t, "Request failed.")
Source.empty
}
}
}
If the method is invoked like this responseValidationFlow[Status, Unit], then a FromByteStringUnmarshaller[Unit] is made available at call site. The compiler uses the implicit evidence to find the required Unmarshaller.
I am trying to get the time spend for each request to send it to a remote system in my Play application.
The case class TimeAction which works well with asynchronous action doesn't work with synchronous action
//Helper
case class TimeAction(label: String] = Map()) = {
def logging[A](action: Action[A])(implicit ec: ExecutionContext) =
Action.async(action.parser) { request =>
MetricLogger.timeFuture(s"controllers.action.$label") {
action(request)
}
}
}
//controller
def test =
TimeAction("toto.test.sync").logging {
Action { _ =>
Thread.sleep(4000)
Ok
}
}
// expected output 4003 milliseconds but got 3 milliseconds
def testAsync =
TimeAction("toto.test.async").logging {
Action.async { _ =>
Future {
Thread.sleep(4000)
Ok
}
}
}
// go 4003 milliseconds as expected
//calculate time elapsed during a future
def timeImpl[T](f: Future[T],
onError: (Duration, Throwable) => Unit,
onSuccess: (Duration, T) => Unit)(implicit ec: ExecutionContext): Future[T] = {
val start = System.nanoTime
f.onComplete {
case Success(d) => onSuccess(calculateTimeDiff(start, System.nanoTime()), d)
case Failure(e) => onError(calculateTimeDiff(start, System.nanoTime()), e)
}
f
}
I suppose the action is executed before it got to my TimeAction and is only wrapped in a instant Future. Do you know where ? What am I missing ?
I am using Play 2.3
Following code when written using generic give a compilation error.
Without Generic
def getData(id: String) = Action.async {
val items = getItems(id)
sendResult(items)
}
private def sendResult(result: Future[Any]) = {
result.map {
items => {
try {
val itemStr = items.asInstanceOf[String]
Ok(itemStr)
} catch {
case t: ClassCastException => InternalServerError(s"Casting Exception while processing output $t")
}
}
}.recover {
case t:TimeoutException => InternalServerError("Api Timed out")
case t: Throwable => InternalServerError(s"Exception in the api $t")
}
}
With Generic
def getData(id: String) = Action.async {
val items = getItems(id)
sendResult[String](items)
}
private def sendResult[T](result: Future[Any]) = {
result.map {
items => {
try {
val itemStr = items.asInstanceOf[T]
Ok(itemStr)
} catch {
case t: ClassCastException => InternalServerError(s"Casting Exception while processing output $t")
}
}
}.recover {
case t:TimeoutException => InternalServerError("Api Timed out")
case t: Throwable => InternalServerError(s"Exception in the api $t")
}
}
The code is part of play app's contorller method. First one works fine. Second one gives following compilation error
Cannot write an instance of T to HTTP response. Try to define a
Writeable[T] [error] Ok(itemStr) [error]
Using Any with a generic function doesn't make much sense.
private def sendResult[T](result: Future[Any])
// Should better be
private def sendResult[T](result: Future[T])
// ... also remove the unsafe cast
Then this T needs to be provided an instance of Writeable, so it can be written a Array[Byte] over network.
// Either ...
private def sendResult[T: Writeable](result: Future[T])
// ... or ...
private def sendResult[T](result: Future[T])(implicit w: Writeable[T])
The Ok(...) is calling the method apply[C](content: C)(implicit writeable: Writeable[C]): Result on Status class. You need to have an implicit value for Writeable[T] in scope.
As a side note, rather than using Future[Any], you may as well use Future[T] and remove the cast. Also you can use Writes for JSON serialization.
private def sendResult[T](result: Future[T])(implicit writeable: Writes[T]) = {
result.map {
items => {
Ok(Json.toJson(items))
}
}.recover {
case t:TimeoutException => InternalServerError("Api Timed out")
case t: Throwable => InternalServerError(s"Exception in the api $t")
}
}