I use Finagle as a web server which I want to return Scala-Futures from my application logic. How to convert scala.concurrent.Future to com.twitter.util.Future, in a non-blocking way of course?
Have not enough environment to test this, but here is what i write for "com.twitter" %% "finagle-http" % "6.25.0":
import com.twitter.{util => twitter}
import scala.concurrent.{ExecutionContext, Promise, Future}
import scala.util.{Failure, Success, Try}
import language.implicitConversions
object TwitterConverters {
implicit def scalaToTwitterTry[T](t: Try[T]): twitter.Try[T] = t match {
case Success(r) => twitter.Return(r)
case Failure(ex) => twitter.Throw(ex)
}
implicit def twitterToScalaTry[T](t: twitter.Try[T]): Try[T] = t match {
case twitter.Return(r) => Success(r)
case twitter.Throw(ex) => Failure(ex)
}
implicit def scalaToTwitterFuture[T](f: Future[T])(implicit ec: ExecutionContext): twitter.Future[T] = {
val promise = twitter.Promise[T]()
f.onComplete(promise update _)
promise
}
implicit def twitterToScalaFuture[T](f: twitter.Future[T]): Future[T] = {
val promise = Promise[T]()
f.respond(promise complete _)
promise.future
}
}
It's also possible to use Twitter bijections library: https://github.com/twitter/bijection
Namely com.twitter.bijection.twitter_util.UtilBijections (https://github.com/twitter/bijection/blob/develop/bijection-util/src/main/scala/com/twitter/bijection/twitter_util/UtilBijections.scala)
This library handles important details, for example if you convert object forth and back, it just unwraps original object.
Here's an example using the twitter bijection library:
import scala.concurrent.{Future => ScalaFuture}
import com.twitter.util.{Future => TwitterFuture}
// extend values by adding the conversion method "as"
import com.twitter.bijection.Conversion.asMethod
// pull in various implicit converters that "as" expects,
// including twitter2ScalaFuture:
import com.twitter.bijection.twitter_util.UtilBijections._
def doSomething: ScalaFuture[T] = {
val response: TwitterFuture[T] = ???
response.as[ScalaFuture[T]]
}
Related
I am starting to develop in Scala, so I started witha really simple RESTful API using AKKA HTTP actors and then wanted to add a PostgreSQL database to "close up" the project. The thing is that somewhere in the project, a Future that is returned by a db.run method is converted into a Promise and returning me errors. When I run the Main object and start the API and hit somewhere, I get this error:
Cannot cast scala.concurrent.impl.Promise$DefaultPromise to scala.collection.immutable.Seq or Cannot cast scala.concurrent.impl.Promise$DefaultPromise to api.Item depending on which route I an hitting.
Here is the main api.scala file:
package api
import akka.actor.{Actor, ActorSystem, Props}
import akka.http.scaladsl.Http
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import akka.pattern.ask
import akka.util.Timeout
import api.Main.ItemActor._
import slick.jdbc.JdbcBackend.Database
import spray.json.DefaultJsonProtocol._
import scala.concurrent.Future
import scala.concurrent.duration.DurationInt
import scala.util.{Failure, Success}
object Main extends App {
val db = Database.forConfig("scaladb");
val itemDao = new handler(db)
val system = ActorSystem("mySystem")
val itemActor = system.actorOf(Props(new ItemActor(db)))
implicit val actorSystem = system
implicit val itemFormat = jsonFormat3(Item)
implicit val timeout: Timeout = Timeout(5.seconds)
class ItemActor(db: Database) extends Actor {
import api.Main.ItemActor._
def receive = {
case CreateItem(item) =>
sender() ! itemDao.create(item)
case ReadItem(id) =>
sender() ! itemDao.read(id)
case ReadAllItems =>
sender() ! itemDao.readAll
case UpdateItem(item) =>
sender() ! itemDao.update(item)
case DeleteItem(id) =>
sender() ! itemDao.delete(id)
}
}
object ItemActor {
case class CreateItem(item: Item)
case class ReadItem(id: Int)
case object ReadAllItems
case class UpdateItem(item: Item)
case class DeleteItem(id: Int)
}
def handleResponse(futureResponse: Future[Item]): Route = {
onComplete(futureResponse) {
case Success(response) => complete(response)
case Failure(ex) => complete(StatusCodes.InternalServerError, s"An error occurred: ${ex.getMessage}")
}
}
def handleResponseSeq(futureResponse: Future[Seq[Item]]): Route = {
onComplete(futureResponse) {
case Success(response) => complete(response)
case Failure(ex) => complete(StatusCodes.InternalServerError, s"An error occurred: ${ex.getMessage}")
}
}
val routes = pathPrefix("items") {
pathEnd {
post {
entity(as[Item]) { item =>
handleResponse((itemActor ? CreateItem(item)).mapTo[Item])
}
} ~
get {
handleResponseSeq((itemActor ? ReadAllItems).mapTo[Seq[Item]])
}
} ~
path(IntNumber) { id =>
get {
handleResponse((itemActor ? ReadItem(id)).mapTo[Item])
} ~
put {
entity(as[Item]) { item =>
handleResponse((itemActor ? UpdateItem(item)).mapTo[Item])
}
} ~
delete {
handleResponse((itemActor ? DeleteItem(id)).mapTo[Item])
}
}
}
val bindRoutes = Http().bindAndHandle(routes, "localhost", 8888)
println("Server online at http://localhost:8888/")
}
Then the handler (Where I definde the methods that access the PostgreSQL database):
package api
import slick.jdbc.PostgresProfile.api._
import scala.concurrent.Future
class handler (db:Database){
val items = TableQuery[Items]
def create(item:Item): Future[Item] = {
db.run((items returning items.map(_.id.?) into ((item, id) => item.copy(id = id))) += item)
}
def read(id: Int): Future[Option[Item]] = {
db.run(items.filter(_.id === id).result.headOption)
}
def readAll: Future[Seq[Item]] = {
println((db.run(items.result)).getClass)
db.run(items.result)
}
def update(item: Item): Future[Int] = {
db.run(items.filter(_.id === item.id).update(item))
}
def delete(id: Int): Future[Int] = {
db.run(items.filter(_.id === id).delete)
}
}
And the items file:
package api
import slick.jdbc.PostgresProfile.api._
case class Item(id: Option[Int] = None, name: String, description: String)
class Items(tag: Tag) extends Table[Item](tag, "items") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def description = column[String]("description")
def * = (id.?, name, description) <> (Item.tupled, Item.unapply)
}
I've tried to use a getClass next to the db.run(items.result) in the handler file, and it prits class scala.concurrent.impl.Promise$DefaultPromise so it must be something of an implicit converter. Thanks.
You're mixing Futures and actors, which is generally not a great idea.
In your ItemActor, instead of sending the future as a reply, it's a better idea to pipe the future as a reply (the reply won't actually happen until the future is complete, that is to say, the DAO has a result).
import akka.pattern.pipe
class ItemActor(db: Database) extends Actor {
import ItemActor._
import context.dispatcher
def receive = {
case CreateItem(item) =>
itemDao.create(item).pipeTo(sender())
case ReadItem(id) =>
itemDao.read(id).pipeTo(sender())
}
}
That said, at least in this code, there doesn't really seem to be a good reason for ItemActor to exist, given that it's just forwarding operations to the DAO. Making the itemDao visible in the routes, you could just as well do:
handleResponse(itemDao.create(item))
Here: handleResponse((itemActor ? CreateItem(item)).mapTo[Item])
Actor returns Future[Item], mapTo[Item] tries to cast it to item and fails.
You want your actor to return the actual item, not Future result from db.run.
I haven't used akka in a while, but I think, something like this should work:
val replyTo = sender
...
case CreateItem(item) => itemDao.create(item).onComplete {
case Success(i) => replyTo ! i
case Failure(e) => throw e
}
...
That may be a dumb question, but starting with ZIO, I cannot manage to convert a Seq[ZIO] to ZIO[Seq]:
def translate(keys: Seq[String], locales: Seq[Locale]):RIO[Translator, Seq[Translation]] = {
for {
service <- ZIO.environment[Translator]
} yield {
// service.translate produce a zio.Task[Translation]
keys.map(k => service.translate(k, locales)
}
}
Required: RIO[Translator, Seq[Translation]]
Found : ZIO[Translator, Nothing, Seq[zio.Task[Translation]]
I tried flatMap, flatten, collectAll and merge but I was not able to get the expected result with anyone.
How can I transform a Seq[ZIO[_, _, B]] to a ZIO[_, _, Seq[B]] ?
Thanks
Edit: It seems that ZIO.foreach is the best option, however I still have it wrapped inside another ZIO due to the for comprehension.
Because for loops translate to flatMap except for the last line which is a map, you want to add the foreach call within the for-loop.
def translate(keys: Seq[String], locales: Seq[Locale]): RIO[Translator, Seq[Translation]] = {
for {
translator <- ZIO.environment[Translator]
translations <- ZIO.foreach(keys)(translator.translate(_, locales))
} yield translations
}
If I got you right you can do it using traverse function from cats:
import cats.instances.list._
import cats.syntax.traverse._
import zio.{RIO, Task, ZIO}
import zio.interop.catz._
import java.util.Locale
case class Translation()
trait Translator {
def translate(k: String, locales: Seq[Locale]): Task[Translation]
}
def translate(keys: Seq[String], locales: Seq[Locale]): RIO[Translator, Seq[Translation]] = {
val translator: Translator = ???
for {
service <- ZIO.effect(translator)
result <- keys.toList.traverse(k => service.translate(k, locales))
} yield result
}
For map List[ZIO[_, _, B]] to ZIO[_, _, List[B]] you can use sequence function and I would advice to use cats library for that.
import zio.ZIO
import zio.interop.catz._
import cats.syntax.traverse._
import cats.instances.list._
def ziosSequence[B](seqZIO: Seq[ZIO[Any, Throwable, B]]): ZIO[Any, Throwable, Seq[B]] =
seqZIO.toList.sequence.map(_.toSeq)
the sequence signature is:
def sequence[G[_]: Applicative, A](fga: F[G[A]]): G[F[A]] =
traverse(fga)(ga => ga)
Here we see what function do what we need.
it requires Applicative instance for G (G is ZIO in your case), and we just import it using import zio.interop.catz._
Also, to make list is able to call sequence we need import Traverse instance for List:
by import cats.instances.list._
Unfortunetaly we can not do the same tricks with Seq because we need Traverse instance for sequence, and we should convert Seq to List back and forth before and after sequence.
useful links:
typelevel documentation cats.Traverse
typelevel documentation cats.Applicative
To "exchange" List and ZIO you could dance this way:
def dance(x: List[ZIO[Any,Throwable,Int]]): ZIO[Any, Throwable, List[Int]] =
x.map ( a => a.map(x=> List(x)))
.fold ( ZIO.succeed( List[Int]()) )
((x, y) => x.map(a => y.map(b => a ++ b ) )
.flatten
)
I don't know how to description exactly, see the code please
def callForever(f: Future[Int]): Unit = {
f.onComplete {
case Failure(e) =>
//do something
case Success(c) =>
// do again
val nextConn: Future[Int] = connection()
callForever(nextConn)
}
}
Its a normal recursion,actually,I use it to listen socket wait a Async connection.
Because it always running I want make it better, can I refactor it by a tailrec way?
I just thought that you may want to look at this way to do this which looks a bit better for me:
import scala.concurrent.Future
import scala.util.{Failure, Success, Random}
import scala.concurrent.ExecutionContext.Implicits.global
/**
* Created by Alex on 2/29/2016.
*/
object Test {
def giveMeValue:Future[Int] = Future.successful{Random.nextInt()}
def callForever(f:Future[Int]):Future[Int] = {
println("iteration")
f flatMap(i => {println(i); callForever(giveMeValue)})
}
def main(args: Array[String]) {
callForever(giveMeValue)
while(true){}
}
}
I'm trying to understand how to use the new akka.http library. I would like to send an http request to a server and read the whole response body as a single String in order to produce a Source[String,?].
Here is the best solution I was able to produce so far:
def get(
modelID: String,
pool: Flow[(HttpRequest,Int),(Try[HttpResponse],Int),Http.HostConnectionPool]
): Source[String,Unit] = {
val uri = reactionsURL(modelID)
val req = HttpRequest(uri = uri)
Source.single( (req,0) )
.via( pool )
.map {
case (Success(resp),_) =>
resp.entity.dataBytes.map( _.decodeString("utf-8") )
}.flatten(FlattenStrategy.concat)
.grouped( 1024 )
.map( _.mkString )
It seems to work well (except the missing error path), but it is a bit clunky for such simple tasks. Is there a smarter solution ? Can I avoid the grouped/mkString ?
You can use toStrict method of HttpResponse with timeout. It gathers whole answer as Future.
def toStrict(timeout: FiniteDuration)(implicit ec: ExecutionContext, fm: Materializer): Future[Strict] Returns a sharable and serializable
copy of this message with a strict entity.
Example:
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.{HttpResponse, HttpRequest}
import akka.stream.{Materializer, ActorMaterializer}
import akka.stream.scaladsl.{Sink, Flow, Source}
import scala.concurrent.{ExecutionContext, Future}
import scala.concurrent.duration._
import scala.util.{Try, Success}
object Main extends App {
implicit val system = ActorSystem()
import system.dispatcher
implicit val materializer = ActorMaterializer()
val host = "127.0.0.1"
lazy val pool = Http().newHostConnectionPool[Int](host, 9000)
FlowBuilder.get("/path", pool).to(Sink.foreach(_.foreach(println))).run()
}
object FlowBuilder {
def get(modelID: String, pool: Flow[(HttpRequest, Int), (Try[HttpResponse], Int), Http.HostConnectionPool])
(implicit ec: ExecutionContext, mat: Materializer): Source[Future[String], Unit] = {
val uri = modelID
val req = HttpRequest(uri = modelID)
Source.single((req, 0)).via(pool)
.map {
case (Success(resp), _) => resp.entity.toStrict(5 seconds).map(_.data.decodeString("UTF-8"))
}
}
}
You can use Unmarshall which will also work on other types e.g. json from spray-json. This also as strict returns Future[_].
Example:
authedReq.via(authServerReqResFlow).mapAsync(1) { case (tryRes, _) =>
tryRes match {
case Failure(exception) => Future.failed[Principal](exception)
case Success(response # HttpResponse(StatusCodes.OK,_,_,_)) =>
val userContext = Unmarshal(response).to[UserContextData]
userContext.map {
case UserContextData(UserInfo(_, userName, fullName, email, title), _, _) =>
Principal(userName, fullName, email, title)
}
case Success(response # HttpResponse(responseCode,_,entity,_)) =>
Unmarshal(entity).to[String].flatMap(msg => Future.failed(new AuthenticationFailure(s"$responseCode\n$msg")))
}
}
I'd like to await a scala future that may have failed. If I use Await.result the exception will be thrown. Instead, if I have f: Future[String] I would like a method Await.resultOpt(f): Option[String] or Await.resultEither(f): Either[String].
I could get this by using scala.util.control.Exception.catching or I could f map (Right(_)) recover { case t: Throwable => Left(t) }, but there must be a more straightforward way.
You could use Await.ready which simply blocks until the Future has either succeeded or failed, then returns a reference back to that Future.
From there, you would probably want to get the Future's value, which is an Option[Try[T]]. Due to the Await.ready call, it should be safe to assume that the value is a Some. Then it's just a matter of mapping between a Try[T] and an Either[Throwable, T].
The short version:
val f: Future[T] = ...
val result: Try[T] = Await.ready(f, Duration.Inf).value.get
val resultEither = result match {
case Success(t) => Right(t)
case Failure(e) => Left(e)
}
The shorter version, just to promote the API:
scala> val f = Future(7)
f: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise#13965637
scala> f.value.get
res0: scala.util.Try[Int] = Success(7)
scala> import scala.util._
import scala.util._
scala> Either.cond(res0.isSuccess, res0.get, res0.failed.get)
res2: scala.util.Either[Throwable,Int] = Right(7)
scala> val f = Future[Int](???)
f: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise#64c4c1
scala> val v = f.value.get
v: scala.util.Try[Int] = Failure(java.util.concurrent.ExecutionException: Boxed Error)
scala> Either.cond(v.isSuccess, v.get, v.failed.get)
res4: scala.util.Either[Throwable,Int] = Left(java.util.concurrent.ExecutionException: Boxed Error)
It has a slight advantage in being a one-liner.
But of course, after adding a .toEither extension method, you don't care how many lines it took.
You could start to make your own type utils and do something like so
trait RichTypes {
import scala.util.{Try, Success, Failure}
import scala.concurrent.{Await, Future}
import scala.concurrent.duration.Duration
implicit class RichFuture[T](f: Future[T]) {
def awaitResult(d: Duration): Either[Throwable, T] = {
Try(Await.result(f, d)).toEither
}
}
implicit class RichTry[T](tri: Try[T]) {
def toEither(): Either[Throwable, T] = {
tri.fold[Either[Throwable, T]](Left(_), Right(_))
}
}
}
object Example
extends App
with RichTypes {
import scala.concurrent.Future
import scala.concurrent.duration._
val succ = Future.successful("hi").awaitResult(5.seconds)
val fail = Future.failed(new Exception("x")).awaitResult(5.seconds)
println(succ) // Right(hi)
println(fail) // Left(Exception(x))
}
I separated it out for a Try to also have a .fold :).